constellation/internal/versionsapi/fetcher/fetcher.go
Moritz Sanft a274ac8a7c
ci: add cli k8s compatibility table artifact upload to ci (#1218)
* add cli k8s compatibility api to ci

* extend versionsapi package

* rework cli info upload via ci

* join errors natively

* fix semver

* upload from hack file

* fix ci checks

* add distributionid

* setup go before running hack file

* setup go after repo checkout

* use logger instead of panic, invalidate cache

* use provided ctx

Co-authored-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>

---------

Co-authored-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>
2023-02-24 12:00:04 +01:00

119 lines
3.2 KiB
Go

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
/*
Package fetcher implements a client for the versions API.
The fetcher is used to get information from the versions API without having to
authenticate with AWS, where the API is currently hosted. This package should be
used in user-facing application code most of the time, like the CLI.
*/
package fetcher
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
// Fetcher fetches versions API resources without authentication.
type Fetcher struct {
httpc httpc
}
// NewFetcher returns a new Fetcher.
func NewFetcher() *Fetcher {
return &Fetcher{
httpc: http.DefaultClient,
}
}
// FetchVersionList fetches the given version list from the versions API.
func (f *Fetcher) FetchVersionList(ctx context.Context, list versionsapi.List) (versionsapi.List, error) {
return fetch(ctx, f.httpc, list)
}
// FetchVersionLatest fetches the latest version from the versions API.
func (f *Fetcher) FetchVersionLatest(ctx context.Context, latest versionsapi.Latest) (versionsapi.Latest, error) {
return fetch(ctx, f.httpc, latest)
}
// FetchImageInfo fetches the given image info from the versions API.
func (f *Fetcher) FetchImageInfo(ctx context.Context, imageInfo versionsapi.ImageInfo) (versionsapi.ImageInfo, error) {
return fetch(ctx, f.httpc, imageInfo)
}
// FetchCLIInfo fetches the given cli info from the versions API.
func (f *Fetcher) FetchCLIInfo(ctx context.Context, cliInfo versionsapi.CLIInfo) (versionsapi.CLIInfo, error) {
return fetch(ctx, f.httpc, cliInfo)
}
type apiObject interface {
ValidateRequest() error
Validate() error
URL() (string, error)
}
func fetch[T apiObject](ctx context.Context, c httpc, obj T) (T, error) {
if err := obj.ValidateRequest(); err != nil {
return *new(T), fmt.Errorf("validating request for %T: %w", obj, err)
}
url, err := obj.URL()
if err != nil {
return *new(T), fmt.Errorf("getting URL for %T: %w", obj, err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
if err != nil {
return *new(T), fmt.Errorf("creating request for %T: %w", obj, err)
}
resp, err := c.Do(req)
if err != nil {
return *new(T), fmt.Errorf("sending request for %T: %w", obj, err)
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
case http.StatusNotFound:
return *new(T), &NotFoundError{fmt.Errorf("requesting resource at %s returned status code 404", url)}
default:
return *new(T), fmt.Errorf("unexpected status code %d while requesting resource", resp.StatusCode)
}
var newObj T
if err := json.NewDecoder(resp.Body).Decode(&newObj); err != nil {
return *new(T), fmt.Errorf("decoding %T: %w", obj, err)
}
if newObj.Validate() != nil {
return *new(T), fmt.Errorf("received invalid %T: %w", newObj, newObj.Validate())
}
return newObj, nil
}
// NotFoundError is an error that is returned when a resource is not found.
type NotFoundError struct {
err error
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("the requested resource was not found: %s", e.err.Error())
}
func (e *NotFoundError) Unwrap() error {
return e.err
}
type httpc interface {
Do(req *http.Request) (*http.Response, error)
}