cli: image info (v2)

This commit is contained in:
Malte Poll 2023-05-23 09:17:27 +02:00 committed by Malte Poll
parent cd7b116794
commit d0e53cbb59
37 changed files with 429 additions and 461 deletions

View File

@ -17,7 +17,6 @@ go_library(
deps = [ deps = [
"//cli/internal/clusterid", "//cli/internal/clusterid",
"//cli/internal/iamid", "//cli/internal/iamid",
"//cli/internal/image",
"//cli/internal/libvirt", "//cli/internal/libvirt",
"//cli/internal/terraform", "//cli/internal/terraform",
"//internal/atls", "//internal/atls",
@ -27,6 +26,7 @@ go_library(
"//internal/cloud/gcpshared", "//internal/cloud/gcpshared",
"//internal/config", "//internal/config",
"//internal/constants", "//internal/constants",
"//internal/imagefetcher",
"//internal/variant", "//internal/variant",
"@com_github_azure_azure_sdk_for_go//profiles/latest/attestation/attestation", "@com_github_azure_azure_sdk_for_go//profiles/latest/attestation/attestation",
"@com_github_azure_azure_sdk_for_go_sdk_azcore//policy", "@com_github_azure_azure_sdk_for_go_sdk_azcore//policy",
@ -54,6 +54,7 @@ go_test(
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/cloud/gcpshared", "//internal/cloud/gcpshared",
"//internal/config", "//internal/config",
"//internal/variant",
"@com_github_hashicorp_terraform_json//:terraform-json", "@com_github_hashicorp_terraform_json//:terraform-json",
"@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require", "@com_github_stretchr_testify//require",

View File

@ -12,13 +12,16 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/variant"
tfjson "github.com/hashicorp/terraform-json" tfjson "github.com/hashicorp/terraform-json"
) )
// imageFetcher gets an image reference from the versionsapi. // imageFetcher gets an image reference from the versionsapi.
type imageFetcher interface { type imageFetcher interface {
FetchReference(ctx context.Context, config *config.Config) (string, error) FetchReference(ctx context.Context,
provider cloudprovider.Provider, attestationVariant variant.Variant,
image, region string,
) (string, error)
} }
type terraformClient interface { type terraformClient interface {

View File

@ -13,7 +13,7 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/variant"
tfjson "github.com/hashicorp/terraform-json" tfjson "github.com/hashicorp/terraform-json"
"go.uber.org/goleak" "go.uber.org/goleak"
@ -103,7 +103,10 @@ type stubImageFetcher struct {
fetchReferenceErr error fetchReferenceErr error
} }
func (f *stubImageFetcher) FetchReference(_ context.Context, _ *config.Config) (string, error) { func (f *stubImageFetcher) FetchReference(_ context.Context,
_ cloudprovider.Provider, _ variant.Variant,
_, _ string,
) (string, error) {
return f.reference, f.fetchReferenceErr return f.reference, f.fetchReferenceErr
} }

View File

@ -25,12 +25,12 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid" "github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/image"
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt" "github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/imagefetcher"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
) )
@ -48,7 +48,7 @@ type Creator struct {
func NewCreator(out io.Writer) *Creator { func NewCreator(out io.Writer) *Creator {
return &Creator{ return &Creator{
out: out, out: out,
image: image.New(), image: imagefetcher.New(),
newTerraformClient: func(ctx context.Context) (terraformClient, error) { newTerraformClient: func(ctx context.Context) (terraformClient, error) {
return terraform.New(ctx, constants.TerraformWorkingDir) return terraform.New(ctx, constants.TerraformWorkingDir)
}, },
@ -56,7 +56,7 @@ func NewCreator(out io.Writer) *Creator {
return libvirt.New() return libvirt.New()
}, },
newRawDownloader: func() rawDownloader { newRawDownloader: func() rawDownloader {
return image.NewDownloader() return imagefetcher.NewDownloader()
}, },
policyPatcher: policyPatcher{}, policyPatcher: policyPatcher{},
} }
@ -75,7 +75,10 @@ type CreateOptions struct {
// Create creates the handed amount of instances and all the needed resources. // Create creates the handed amount of instances and all the needed resources.
func (c *Creator) Create(ctx context.Context, opts CreateOptions) (clusterid.File, error) { func (c *Creator) Create(ctx context.Context, opts CreateOptions) (clusterid.File, error) {
image, err := c.image.FetchReference(ctx, opts.Config) provider := opts.Config.GetProvider()
attestationVariant := opts.Config.GetAttestationConfig().GetVariant()
region := opts.Config.GetRegion()
image, err := c.image.FetchReference(ctx, provider, attestationVariant, opts.Config.Image, region)
if err != nil { if err != nil {
return clusterid.File{}, fmt.Errorf("fetching image reference: %w", err) return clusterid.File{}, fmt.Errorf("fetching image reference: %w", err)
} }

View File

@ -41,7 +41,6 @@ go_library(
"//cli/internal/clusterid", "//cli/internal/clusterid",
"//cli/internal/helm", "//cli/internal/helm",
"//cli/internal/iamid", "//cli/internal/iamid",
"//cli/internal/image",
"//cli/internal/kubernetes", "//cli/internal/kubernetes",
"//cli/internal/libvirt", "//cli/internal/libvirt",
"//cli/internal/terraform", "//cli/internal/terraform",
@ -62,6 +61,7 @@ go_library(
"//internal/file", "//internal/file",
"//internal/grpc/dialer", "//internal/grpc/dialer",
"//internal/grpc/retry", "//internal/grpc/retry",
"//internal/imagefetcher",
"//internal/kms/uri", "//internal/kms/uri",
"//internal/kubernetes/kubectl", "//internal/kubernetes/kubectl",
"//internal/license", "//internal/license",

View File

@ -16,7 +16,6 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid" "github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/helm" "github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/image"
"github.com/edgelesssys/constellation/v2/cli/internal/kubernetes" "github.com/edgelesssys/constellation/v2/cli/internal/kubernetes"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/cli/internal/upgrade" "github.com/edgelesssys/constellation/v2/cli/internal/upgrade"
@ -25,6 +24,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/imagefetcher"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -65,7 +65,7 @@ func runUpgradeApply(cmd *cobra.Command, _ []string) error {
return err return err
} }
fetcher := image.New() fetcher := imagefetcher.New()
applyCmd := upgradeApplyCmd{upgrader: upgrader, log: log, fetcher: fetcher} applyCmd := upgradeApplyCmd{upgrader: upgrader, log: log, fetcher: fetcher}
return applyCmd.upgradeApply(cmd, fileHandler) return applyCmd.upgradeApply(cmd, fileHandler)
@ -194,7 +194,10 @@ func (u *upgradeApplyCmd) migrateTerraform(cmd *cobra.Command, file file.Handler
func (u *upgradeApplyCmd) parseUpgradeVars(cmd *cobra.Command, conf *config.Config, fetcher imageFetcher) ([]string, terraform.Variables, error) { func (u *upgradeApplyCmd) parseUpgradeVars(cmd *cobra.Command, conf *config.Config, fetcher imageFetcher) ([]string, terraform.Variables, error) {
// Fetch variables to execute Terraform script with // Fetch variables to execute Terraform script with
imageRef, err := fetcher.FetchReference(cmd.Context(), conf) provider := conf.GetProvider()
attestationVariant := conf.GetAttestationConfig().GetVariant()
region := conf.GetRegion()
imageRef, err := fetcher.FetchReference(cmd.Context(), provider, attestationVariant, conf.Image, region)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("fetching image reference: %w", err) return nil, nil, fmt.Errorf("fetching image reference: %w", err)
} }
@ -264,7 +267,10 @@ func (u *upgradeApplyCmd) parseUpgradeVars(cmd *cobra.Command, conf *config.Conf
} }
type imageFetcher interface { type imageFetcher interface {
FetchReference(ctx context.Context, conf *config.Config) (string, error) FetchReference(ctx context.Context,
provider cloudprovider.Provider, attestationVariant variant.Variant,
image, region string,
) (string, error)
} }
// upgradeAttestConfigIfDiff checks if the locally configured measurements are different from the cluster's measurements. // upgradeAttestConfigIfDiff checks if the locally configured measurements are different from the cluster's measurements.

View File

@ -200,6 +200,9 @@ type stubImageFetcher struct {
fetchReferenceErr error fetchReferenceErr error
} }
func (s stubImageFetcher) FetchReference(context.Context, *config.Config) (string, error) { func (f stubImageFetcher) FetchReference(_ context.Context,
return "", s.fetchReferenceErr _ cloudprovider.Provider, _ variant.Variant,
_, _ string,
) (string, error) {
return "", f.fetchReferenceErr
} }

View File

@ -12,14 +12,15 @@ go_library(
visibility = ["//cli:__subpackages__"], visibility = ["//cli:__subpackages__"],
deps = [ deps = [
"//cli/internal/helm", "//cli/internal/helm",
"//cli/internal/image",
"//cli/internal/terraform", "//cli/internal/terraform",
"//cli/internal/upgrade", "//cli/internal/upgrade",
"//internal/attestation/measurements", "//internal/attestation/measurements",
"//internal/cloud/cloudprovider",
"//internal/compatibility", "//internal/compatibility",
"//internal/config", "//internal/config",
"//internal/constants", "//internal/constants",
"//internal/file", "//internal/file",
"//internal/imagefetcher",
"//internal/kubernetes", "//internal/kubernetes",
"//internal/kubernetes/kubectl", "//internal/kubernetes/kubectl",
"//internal/variant", "//internal/variant",
@ -45,10 +46,12 @@ go_test(
embed = [":kubernetes"], embed = [":kubernetes"],
deps = [ deps = [
"//internal/attestation/measurements", "//internal/attestation/measurements",
"//internal/cloud/cloudprovider",
"//internal/compatibility", "//internal/compatibility",
"//internal/config", "//internal/config",
"//internal/constants", "//internal/constants",
"//internal/logger", "//internal/logger",
"//internal/variant",
"//internal/versions", "//internal/versions",
"//internal/versions/components", "//internal/versions/components",
"//operators/constellation-node-operator/api/v1alpha1", "//operators/constellation-node-operator/api/v1alpha1",

View File

@ -16,14 +16,15 @@ import (
"time" "time"
"github.com/edgelesssys/constellation/v2/cli/internal/helm" "github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/image"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/cli/internal/upgrade" "github.com/edgelesssys/constellation/v2/cli/internal/upgrade"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility" "github.com/edgelesssys/constellation/v2/internal/compatibility"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/imagefetcher"
internalk8s "github.com/edgelesssys/constellation/v2/internal/kubernetes" internalk8s "github.com/edgelesssys/constellation/v2/internal/kubernetes"
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl" "github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
@ -122,7 +123,7 @@ func NewUpgrader(ctx context.Context, outWriter io.Writer, log debugLog) (*Upgra
stableInterface: &stableClient{client: kubeClient}, stableInterface: &stableClient{client: kubeClient},
dynamicInterface: &NodeVersionClient{client: unstructuredClient}, dynamicInterface: &NodeVersionClient{client: unstructuredClient},
helmClient: helmClient, helmClient: helmClient,
imageFetcher: image.New(), imageFetcher: imagefetcher.New(),
outWriter: outWriter, outWriter: outWriter,
tfUpgrader: tfUpgrader, tfUpgrader: tfUpgrader,
log: log, log: log,
@ -164,7 +165,10 @@ func (u *Upgrader) UpgradeHelmServices(ctx context.Context, config *config.Confi
// UpgradeNodeVersion upgrades the cluster's NodeVersion object and in turn triggers image & k8s version upgrades. // UpgradeNodeVersion upgrades the cluster's NodeVersion object and in turn triggers image & k8s version upgrades.
// The versions set in the config are validated against the versions running in the cluster. // The versions set in the config are validated against the versions running in the cluster.
func (u *Upgrader) UpgradeNodeVersion(ctx context.Context, conf *config.Config) error { func (u *Upgrader) UpgradeNodeVersion(ctx context.Context, conf *config.Config) error {
imageReference, err := u.imageFetcher.FetchReference(ctx, conf) provider := conf.GetProvider()
attestationVariant := conf.GetAttestationConfig().GetVariant()
region := conf.GetRegion()
imageReference, err := u.imageFetcher.FetchReference(ctx, provider, attestationVariant, conf.Image, region)
if err != nil { if err != nil {
return fmt.Errorf("fetching image reference: %w", err) return fmt.Errorf("fetching image reference: %w", err)
} }
@ -526,5 +530,8 @@ type debugLog interface {
// imageFetcher gets an image reference from the versionsapi. // imageFetcher gets an image reference from the versionsapi.
type imageFetcher interface { type imageFetcher interface {
FetchReference(ctx context.Context, config *config.Config) (string, error) FetchReference(ctx context.Context,
provider cloudprovider.Provider, attestationVariant variant.Variant,
image, region string,
) (string, error)
} }

View File

@ -14,10 +14,12 @@ import (
"testing" "testing"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility" "github.com/edgelesssys/constellation/v2/internal/compatibility"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versions" "github.com/edgelesssys/constellation/v2/internal/versions"
"github.com/edgelesssys/constellation/v2/internal/versions/components" "github.com/edgelesssys/constellation/v2/internal/versions/components"
updatev1alpha1 "github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api/v1alpha1" updatev1alpha1 "github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api/v1alpha1"
@ -550,6 +552,9 @@ type stubImageFetcher struct {
fetchReferenceErr error fetchReferenceErr error
} }
func (f *stubImageFetcher) FetchReference(_ context.Context, _ *config.Config) (string, error) { func (f *stubImageFetcher) FetchReference(_ context.Context,
_ cloudprovider.Provider, _ variant.Variant,
_, _ string,
) (string, error) {
return f.reference, f.fetchReferenceErr return f.reference, f.fetchReferenceErr
} }

View File

@ -14,10 +14,10 @@ go_library(
"//internal/attestation/measurements", "//internal/attestation/measurements",
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/constants", "//internal/constants",
"//internal/imagefetcher",
"//internal/logger", "//internal/logger",
"//internal/variant", "//internal/variant",
"//internal/versionsapi", "//internal/versionsapi",
"//internal/versionsapi/fetcher",
"@sh_helm_helm_v3//pkg/action", "@sh_helm_helm_v3//pkg/action",
"@sh_helm_helm_v3//pkg/cli", "@sh_helm_helm_v3//pkg/cli",
], ],

View File

@ -10,14 +10,13 @@ package upgrade
import ( import (
"context" "context"
"errors"
"net/http" "net/http"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/imagefetcher"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi" "github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/versionsapi/fetcher"
) )
type upgradeInfo struct { type upgradeInfo struct {
@ -27,13 +26,12 @@ type upgradeInfo struct {
} }
func fetchUpgradeInfo(ctx context.Context, csp cloudprovider.Provider, func fetchUpgradeInfo(ctx context.Context, csp cloudprovider.Provider,
attestationVariant variant.Variant, toImage string, attestationVariant variant.Variant, toImage, region string,
) (upgradeInfo, error) { ) (upgradeInfo, error) {
info := upgradeInfo{ info := upgradeInfo{
measurements: make(measurements.M), measurements: make(measurements.M),
shortPath: toImage, shortPath: toImage,
} }
versionsClient := fetcher.NewFetcher()
ver, err := versionsapi.NewVersionFromShortPath(toImage, versionsapi.VersionKindImage) ver, err := versionsapi.NewVersionFromShortPath(toImage, versionsapi.VersionKindImage)
if err != nil { if err != nil {
@ -55,11 +53,8 @@ func fetchUpgradeInfo(ctx context.Context, csp cloudprovider.Provider,
} }
info.measurements = fetchedMeasurements info.measurements = fetchedMeasurements
imageRef, err := fetchImageRef(ctx, versionsClient, csp, versionsapi.ImageInfo{ fetcher := imagefetcher.New()
Ref: ver.Ref, imageRef, err := fetcher.FetchReference(ctx, csp, attestationVariant, toImage, region)
Stream: ver.Stream,
Version: ver.Version,
})
if err != nil { if err != nil {
return upgradeInfo{}, err return upgradeInfo{}, err
} }
@ -67,21 +62,3 @@ func fetchUpgradeInfo(ctx context.Context, csp cloudprovider.Provider,
return info, nil return info, nil
} }
func fetchImageRef(ctx context.Context, client *fetcher.Fetcher, csp cloudprovider.Provider, imageInfo versionsapi.ImageInfo) (string, error) {
imageInfo, err := client.FetchImageInfo(ctx, imageInfo)
if err != nil {
return "", err
}
switch csp {
case cloudprovider.GCP:
return imageInfo.GCP["sev-es"], nil
case cloudprovider.Azure:
return imageInfo.Azure["cvm"], nil
case cloudprovider.AWS:
return imageInfo.AWS["eu-central-1"], nil
default:
return "", errors.New("finding wanted image")
}
}

View File

@ -245,6 +245,7 @@ func writeUpgradeConfig(require *require.Assertions, image string, kubernetes st
cfg.GetProvider(), cfg.GetProvider(),
cfg.GetAttestationConfig().GetVariant(), cfg.GetAttestationConfig().GetVariant(),
image, image,
cfg.GetRegion(),
) )
require.NoError(err) require.NoError(err)

View File

@ -16,10 +16,10 @@ import (
type archivist interface { type archivist interface {
Archive(ctx context.Context, Archive(ctx context.Context,
version versionsapi.Version, csp, variant string, img io.Reader, version versionsapi.Version, csp, attestationVariant string, img io.Reader,
) (string, error) ) (string, error)
} }
type uploader interface { type uploader interface {
Upload(ctx context.Context, req *osimage.UploadRequest) (map[string]string, error) Upload(ctx context.Context, req *osimage.UploadRequest) ([]versionsapi.ImageInfoEntry, error)
} }

View File

@ -84,14 +84,14 @@ func runAWS(cmd *cobra.Command, _ []string) error {
} }
uploadReq := &osimage.UploadRequest{ uploadReq := &osimage.UploadRequest{
Provider: flags.provider, Provider: flags.provider,
Version: flags.version, Version: flags.version,
Variant: flags.variant, AttestationVariant: flags.attestationVariant,
SBDatabase: sbDatabase, SBDatabase: sbDatabase,
UEFIVarStore: uefiVarStore, UEFIVarStore: uefiVarStore,
Size: size, Size: size,
Timestamp: flags.timestamp, Timestamp: flags.timestamp,
Image: file, Image: file,
} }
return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out) return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out)
} }

View File

@ -85,14 +85,14 @@ func runAzure(cmd *cobra.Command, _ []string) error {
} }
uploadReq := &osimage.UploadRequest{ uploadReq := &osimage.UploadRequest{
Provider: flags.provider, Provider: flags.provider,
Version: flags.version, Version: flags.version,
Variant: flags.variant, AttestationVariant: flags.attestationVariant,
SBDatabase: sbDatabase, SBDatabase: sbDatabase,
UEFIVarStore: uefiVarStore, UEFIVarStore: uefiVarStore,
Size: size, Size: size,
Timestamp: flags.timestamp, Timestamp: flags.timestamp,
Image: file, Image: file,
} }
return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out) return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out)
} }

View File

@ -18,16 +18,16 @@ import (
) )
type commonFlags struct { type commonFlags struct {
rawImage string rawImage string
pki string pki string
provider cloudprovider.Provider provider cloudprovider.Provider
variant string attestationVariant string
version versionsapi.Version version versionsapi.Version
timestamp time.Time timestamp time.Time
region string region string
bucket string bucket string
out string out string
logLevel zapcore.Level logLevel zapcore.Level
} }
func parseCommonFlags(cmd *cobra.Command) (commonFlags, error) { func parseCommonFlags(cmd *cobra.Command) (commonFlags, error) {
@ -43,7 +43,7 @@ func parseCommonFlags(cmd *cobra.Command) (commonFlags, error) {
if pki == "" { if pki == "" {
pki = filepath.Join(workspaceDir, "image/pki") pki = filepath.Join(workspaceDir, "image/pki")
} }
variant, err := cmd.Flags().GetString("variant") attestationVariant, err := cmd.Flags().GetString("attestation-variant")
if err != nil { if err != nil {
return commonFlags{}, err return commonFlags{}, err
} }
@ -88,15 +88,15 @@ func parseCommonFlags(cmd *cobra.Command) (commonFlags, error) {
} }
return commonFlags{ return commonFlags{
rawImage: rawImage, rawImage: rawImage,
pki: pki, pki: pki,
variant: variant, attestationVariant: attestationVariant,
version: ver, version: ver,
timestamp: timestmp, timestamp: timestmp,
region: region, region: region,
bucket: bucket, bucket: bucket,
out: out, out: out,
logLevel: logLevel, logLevel: logLevel,
}, nil }, nil
} }

View File

@ -85,14 +85,14 @@ func runGCP(cmd *cobra.Command, _ []string) error {
} }
uploadReq := &osimage.UploadRequest{ uploadReq := &osimage.UploadRequest{
Provider: flags.provider, Provider: flags.provider,
Version: flags.version, Version: flags.version,
Variant: flags.variant, AttestationVariant: flags.attestationVariant,
SBDatabase: sbDatabase, SBDatabase: sbDatabase,
UEFIVarStore: uefiVarStore, UEFIVarStore: uefiVarStore,
Size: size, Size: size,
Timestamp: flags.timestamp, Timestamp: flags.timestamp,
Image: file, Image: file,
} }
return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out) return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out)
} }

View File

@ -68,14 +68,14 @@ func runNOP(cmd *cobra.Command, provider cloudprovider.Provider, _ []string) err
} }
uploadReq := &osimage.UploadRequest{ uploadReq := &osimage.UploadRequest{
Provider: flags.provider, Provider: flags.provider,
Version: flags.version, Version: flags.version,
Variant: flags.variant, AttestationVariant: flags.attestationVariant,
SBDatabase: sbDatabase, SBDatabase: sbDatabase,
UEFIVarStore: uefiVarStore, UEFIVarStore: uefiVarStore,
Size: size, Size: size,
Timestamp: flags.timestamp, Timestamp: flags.timestamp,
Image: file, Image: file,
} }
return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out) return uploadImage(cmd.Context(), archiveC, uploadC, uploadReq, out)
} }

View File

@ -13,14 +13,13 @@ import (
"io" "io"
"strings" "strings"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/osimage" "github.com/edgelesssys/constellation/v2/internal/osimage"
"github.com/edgelesssys/constellation/v2/internal/versionsapi" "github.com/edgelesssys/constellation/v2/internal/versionsapi"
) )
func uploadImage(ctx context.Context, archiveC archivist, uploadC uploader, req *osimage.UploadRequest, out io.Writer) error { func uploadImage(ctx context.Context, archiveC archivist, uploadC uploader, req *osimage.UploadRequest, out io.Writer) error {
// upload to S3 archive // upload to S3 archive
archiveURL, err := archiveC.Archive(ctx, req.Version, strings.ToLower(req.Provider.String()), req.Variant, req.Image) archiveURL, err := archiveC.Archive(ctx, req.Version, strings.ToLower(req.Provider.String()), req.AttestationVariant, req.Image)
if err != nil { if err != nil {
return err return err
} }
@ -34,8 +33,12 @@ func uploadImage(ctx context.Context, archiveC archivist, uploadC uploader, req
return err return err
} }
if len(imageReferences) == 0 { if len(imageReferences) == 0 {
imageReferences = map[string]string{ imageReferences = []versionsapi.ImageInfoEntry{
req.Variant: archiveURL, {
CSP: req.Provider.String(),
AttestationVariant: req.AttestationVariant,
Reference: archiveURL,
},
} }
} }
@ -43,20 +46,7 @@ func uploadImage(ctx context.Context, archiveC archivist, uploadC uploader, req
Ref: req.Version.Ref, Ref: req.Version.Ref,
Stream: req.Version.Stream, Stream: req.Version.Stream,
Version: req.Version.Version, Version: req.Version.Version,
} List: imageReferences,
switch req.Provider {
case cloudprovider.AWS:
imageInfo.AWS = imageReferences
case cloudprovider.Azure:
imageInfo.Azure = imageReferences
case cloudprovider.GCP:
imageInfo.GCP = imageReferences
case cloudprovider.OpenStack:
imageInfo.OpenStack = imageReferences
case cloudprovider.QEMU:
imageInfo.QEMU = imageReferences
default:
return fmt.Errorf("uploading image: cloud provider %s is not yet supported", req.Provider.String())
} }
if err := json.NewEncoder(out).Encode(imageInfo); err != nil { if err := json.NewEncoder(out).Encode(imageInfo); err != nil {

View File

@ -42,7 +42,7 @@ func newRootCmd() *cobra.Command {
rootCmd.PersistentFlags().String("raw-image", "", "Path to os image in CSP specific format that should be uploaded.") rootCmd.PersistentFlags().String("raw-image", "", "Path to os image in CSP specific format that should be uploaded.")
rootCmd.PersistentFlags().String("pki", "", "Base path to the PKI (secure boot signing) files.") rootCmd.PersistentFlags().String("pki", "", "Base path to the PKI (secure boot signing) files.")
rootCmd.PersistentFlags().String("variant", "", "Variant of the image being uploaded.") rootCmd.PersistentFlags().String("attestation-variant", "", "Attestation variant of the image being uploaded.")
rootCmd.PersistentFlags().String("version", "", "Shortname of the os image version.") rootCmd.PersistentFlags().String("version", "", "Shortname of the os image version.")
rootCmd.PersistentFlags().String("timestamp", "", "Optional timestamp to use for resource names. Uses format 2006-01-02T15:04:05Z07:00.") rootCmd.PersistentFlags().String("timestamp", "", "Optional timestamp to use for resource names. Uses format 2006-01-02T15:04:05Z07:00.")
rootCmd.PersistentFlags().String("region", "eu-central-1", "AWS region of the archive S3 bucket") rootCmd.PersistentFlags().String("region", "eu-central-1", "AWS region of the archive S3 bucket")
@ -50,7 +50,7 @@ func newRootCmd() *cobra.Command {
rootCmd.PersistentFlags().String("out", "", "Optional path to write the upload result to. If not set, the result is written to stdout.") rootCmd.PersistentFlags().String("out", "", "Optional path to write the upload result to. If not set, the result is written to stdout.")
rootCmd.PersistentFlags().Bool("verbose", false, "Enable verbose output") rootCmd.PersistentFlags().Bool("verbose", false, "Enable verbose output")
must(rootCmd.MarkPersistentFlagRequired("raw-image")) must(rootCmd.MarkPersistentFlagRequired("raw-image"))
must(rootCmd.MarkPersistentFlagRequired("variant")) must(rootCmd.MarkPersistentFlagRequired("attestation-variant"))
must(rootCmd.MarkPersistentFlagRequired("version")) must(rootCmd.MarkPersistentFlagRequired("version"))
rootCmd.AddCommand(cmd.NewAWSCmd()) rootCmd.AddCommand(cmd.NewAWSCmd())

View File

@ -546,6 +546,23 @@ func (c *Config) GetAttestationConfig() AttestationCfg {
return &DummyCfg{} return &DummyCfg{}
} }
// GetRegion returns the configured region.
func (c *Config) GetRegion() string {
switch c.GetProvider() {
case cloudprovider.AWS:
return c.Provider.AWS.Region
case cloudprovider.Azure:
return c.Provider.Azure.Location
case cloudprovider.GCP:
return c.Provider.GCP.Region
case cloudprovider.OpenStack:
return c.Provider.OpenStack.RegionName
case cloudprovider.QEMU:
return ""
}
return ""
}
// UpdateMAAURL updates the MAA URL in the config. // UpdateMAAURL updates the MAA URL in the config.
func (c *Config) UpdateMAAURL(maaURL string) { func (c *Config) UpdateMAAURL(maaURL string) {
if c.Attestation.AzureSEVSNP != nil { if c.Attestation.AzureSEVSNP != nil {

View File

@ -2,16 +2,15 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//bazel/go:go_test.bzl", "go_test") load("//bazel/go:go_test.bzl", "go_test")
go_library( go_library(
name = "image", name = "imagefetcher",
srcs = [ srcs = [
"image.go", "imagegfetcher.go",
"raw.go", "raw.go",
], ],
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/image", importpath = "github.com/edgelesssys/constellation/v2/internal/imagefetcher",
visibility = ["//cli:__subpackages__"], visibility = ["//cli:__subpackages__"],
deps = [ deps = [
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/config",
"//internal/variant", "//internal/variant",
"//internal/versionsapi", "//internal/versionsapi",
"//internal/versionsapi/fetcher", "//internal/versionsapi/fetcher",
@ -21,16 +20,16 @@ go_library(
) )
go_test( go_test(
name = "image_test", name = "imagefetcher_test",
srcs = [ srcs = [
"image_test.go", "imagefetcher_test.go",
"raw_test.go", "raw_test.go",
], ],
embed = [":image"], embed = [":imagefetcher"],
deps = [ deps = [
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/config",
"//internal/file", "//internal/file",
"//internal/variant",
"//internal/versionsapi", "//internal/versionsapi",
"@com_github_spf13_afero//:afero", "@com_github_spf13_afero//:afero",
"@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//assert",

View File

@ -5,11 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only
*/ */
/* /*
Package image provides helping wrappers around a versionsapi fetcher. Package imagefetcher provides helping wrappers around a versionsapi fetcher.
It also enables local image overrides and download of raw images. It also enables local image overrides and download of raw images.
*/ */
package image package imagefetcher
import ( import (
"context" "context"
@ -20,7 +20,6 @@ import (
"regexp" "regexp"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi" "github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/versionsapi/fetcher" "github.com/edgelesssys/constellation/v2/internal/versionsapi/fetcher"
@ -42,14 +41,11 @@ func New() *Fetcher {
} }
// FetchReference fetches the image reference for a given image version uid, CSP and image variant. // FetchReference fetches the image reference for a given image version uid, CSP and image variant.
func (f *Fetcher) FetchReference(ctx context.Context, config *config.Config) (string, error) { func (f *Fetcher) FetchReference(ctx context.Context,
provider := config.GetProvider() provider cloudprovider.Provider, attestationVariant variant.Variant,
variant, err := imageVariant(provider, config) image, region string,
if err != nil { ) (string, error) {
return "", fmt.Errorf("determining variant: %w", err) ver, err := versionsapi.NewVersionFromShortPath(image, versionsapi.VersionKindImage)
}
ver, err := versionsapi.NewVersionFromShortPath(config.Image, versionsapi.VersionKindImage)
if err != nil { if err != nil {
return "", fmt.Errorf("parsing config image short path: %w", err) return "", fmt.Errorf("parsing config image short path: %w", err)
} }
@ -83,29 +79,18 @@ func (f *Fetcher) FetchReference(ctx context.Context, config *config.Config) (st
return "", fmt.Errorf("validating image info file: %w", err) return "", fmt.Errorf("validating image info file: %w", err)
} }
return getReferenceFromImageInfo(provider, variant, imgInfo) return getReferenceFromImageInfo(provider, attestationVariant.String(), imgInfo, filters(provider, region)...)
} }
// imageVariant returns the image variant for a given CSP and configuration. func filters(provider cloudprovider.Provider, region string) []filter {
func imageVariant(provider cloudprovider.Provider, config *config.Config) (string, error) { var filters []filter
switch provider { switch provider {
case cloudprovider.AWS: case cloudprovider.AWS:
return config.Provider.AWS.Region, nil filters = append(filters, func(i versionsapi.ImageInfoEntry) bool {
case cloudprovider.Azure: return i.Region == region
if config.GetAttestationConfig().GetVariant().Equal(variant.AzureTrustedLaunch{}) { })
return "trustedlaunch", nil
}
return "cvm", nil
case cloudprovider.GCP:
return "sev-es", nil
case cloudprovider.OpenStack:
return "sev", nil
case cloudprovider.QEMU:
return "default", nil
default:
return "", fmt.Errorf("unsupported provider: %s", provider)
} }
return filters
} }
func getFromFile(fs *afero.Afero, imgInfo versionsapi.ImageInfo) (versionsapi.ImageInfo, error) { func getFromFile(fs *afero.Afero, imgInfo versionsapi.ImageInfo) (versionsapi.ImageInfo, error) {
@ -132,36 +117,36 @@ func imageInfoFilename(imgInfo versionsapi.ImageInfo) string {
} }
// getReferenceFromImageInfo returns the image reference for a given CSP and image variant. // getReferenceFromImageInfo returns the image reference for a given CSP and image variant.
func getReferenceFromImageInfo(provider cloudprovider.Provider, variant string, imgInfo versionsapi.ImageInfo, func getReferenceFromImageInfo(provider cloudprovider.Provider,
attestationVariant string, imgInfo versionsapi.ImageInfo,
filt ...filter,
) (string, error) { ) (string, error) {
var providerList map[string]string var correctVariant versionsapi.ImageInfoEntry
switch provider { var found bool
case cloudprovider.AWS: variantLoop:
providerList = imgInfo.AWS for _, variant := range imgInfo.List {
case cloudprovider.Azure: gotCSP := cloudprovider.FromString(variant.CSP)
providerList = imgInfo.Azure if gotCSP != provider || variant.AttestationVariant != attestationVariant {
case cloudprovider.GCP: continue
providerList = imgInfo.GCP }
case cloudprovider.OpenStack: for _, f := range filt {
providerList = imgInfo.OpenStack if !f(variant) {
case cloudprovider.QEMU: continue variantLoop
providerList = imgInfo.QEMU }
default: }
return "", fmt.Errorf("image not available in image info for CSP %q", provider.String()) correctVariant = variant
found = true
break
}
if !found {
return "", fmt.Errorf("image not available in image info for CSP %q, variant %q and other filters", provider.String(), attestationVariant)
} }
if providerList == nil { return correctVariant.Reference, nil
return "", fmt.Errorf("image not available in image info for CSP %q", provider.String())
}
ref, ok := providerList[variant]
if !ok {
return "", fmt.Errorf("image not available in image info for variant %q", variant)
}
return ref, nil
} }
type versionsAPIImageInfoFetcher interface { type versionsAPIImageInfoFetcher interface {
FetchImageInfo(ctx context.Context, imageInfo versionsapi.ImageInfo) (versionsapi.ImageInfo, error) FetchImageInfo(ctx context.Context, imageInfo versionsapi.ImageInfo) (versionsapi.ImageInfo, error)
} }
type filter func(versionsapi.ImageInfoEntry) bool

View File

@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
package image package imagefetcher
import ( import (
"context" "context"
@ -14,8 +14,8 @@ import (
"testing" "testing"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi" "github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -32,49 +32,88 @@ func TestGetReference(t *testing.T) {
info versionsapi.ImageInfo info versionsapi.ImageInfo
provider cloudprovider.Provider provider cloudprovider.Provider
variant string variant string
filter filter
wantReference string wantReference string
wantErr bool wantErr bool
}{ }{
"reference exists with filter": {
info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "aws", AttestationVariant: "aws-nitro-tpm", Reference: "someReference"},
{CSP: "aws", AttestationVariant: "aws-nitro-tpm", Reference: "someOtherReference", Region: "someRegion"},
},
},
provider: cloudprovider.AWS,
variant: "aws-nitro-tpm",
filter: func(entry versionsapi.ImageInfoEntry) bool {
return entry.Region == "someRegion"
},
wantReference: "someOtherReference",
},
"reference exists aws": { "reference exists aws": {
info: versionsapi.ImageInfo{AWS: map[string]string{"someVariant": "someReference"}}, info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "aws", AttestationVariant: "aws-nitro-tpm", Reference: "someReference"},
},
},
provider: cloudprovider.AWS, provider: cloudprovider.AWS,
variant: "someVariant", variant: "aws-nitro-tpm",
wantReference: "someReference", wantReference: "someReference",
}, },
"reference exists azure": { "reference exists azure": {
info: versionsapi.ImageInfo{Azure: map[string]string{"someVariant": "someReference"}}, info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "azure", AttestationVariant: "azure-sev-snp", Reference: "someReference"},
},
},
provider: cloudprovider.Azure, provider: cloudprovider.Azure,
variant: "someVariant", variant: "azure-sev-snp",
wantReference: "someReference", wantReference: "someReference",
}, },
"reference exists gcp": { "reference exists gcp": {
info: versionsapi.ImageInfo{GCP: map[string]string{"someVariant": "someReference"}}, info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "gcp", AttestationVariant: "gcp-sev-es", Reference: "someReference"},
},
},
provider: cloudprovider.GCP, provider: cloudprovider.GCP,
variant: "someVariant", variant: "gcp-sev-es",
wantReference: "someReference", wantReference: "someReference",
}, },
"reference exists openstack": { "reference exists openstack": {
info: versionsapi.ImageInfo{OpenStack: map[string]string{"someVariant": "someReference"}}, info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "openstack", AttestationVariant: "qemu-vtpm", Reference: "someReference"},
},
},
provider: cloudprovider.OpenStack, provider: cloudprovider.OpenStack,
variant: "someVariant", variant: "qemu-vtpm",
wantReference: "someReference", wantReference: "someReference",
}, },
"reference exists qemu": { "reference exists qemu": {
info: versionsapi.ImageInfo{QEMU: map[string]string{"someVariant": "someReference"}}, info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "qemu", AttestationVariant: "qemu-vtpm", Reference: "someReference"},
},
},
provider: cloudprovider.QEMU, provider: cloudprovider.QEMU,
variant: "someVariant", variant: "qemu-vtpm",
wantReference: "someReference", wantReference: "someReference",
}, },
"csp does not exist": { "csp does not exist": {
info: versionsapi.ImageInfo{AWS: map[string]string{"someVariant": "someReference"}}, info: versionsapi.ImageInfo{List: []versionsapi.ImageInfoEntry{}},
provider: cloudprovider.Unknown, provider: cloudprovider.Unknown,
variant: "someVariant", variant: "someVariant",
wantErr: true, wantErr: true,
}, },
"variant does not exist": { "variant does not exist": {
info: versionsapi.ImageInfo{AWS: map[string]string{"someVariant": "someReference"}}, info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "aws", AttestationVariant: "dummy", Reference: "someReference"},
},
},
provider: cloudprovider.AWS, provider: cloudprovider.AWS,
variant: "nonExistingVariant", variant: "aws-nitro-tpm",
wantErr: true, wantErr: true,
}, },
"info is empty": { "info is empty": {
@ -83,12 +122,6 @@ func TestGetReference(t *testing.T) {
variant: "someVariant", variant: "someVariant",
wantErr: true, wantErr: true,
}, },
"csp is nil": {
info: versionsapi.ImageInfo{AWS: nil},
provider: cloudprovider.AWS,
variant: "someVariant",
wantErr: true,
},
} }
for name, tc := range testCases { for name, tc := range testCases {
@ -96,7 +129,11 @@ func TestGetReference(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
reference, err := getReferenceFromImageInfo(tc.provider, tc.variant, tc.info) var filters []filter
if tc.filter != nil {
filters = []filter{tc.filter}
}
reference, err := getReferenceFromImageInfo(tc.provider, tc.variant, tc.info, filters...)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
@ -108,79 +145,6 @@ func TestGetReference(t *testing.T) {
} }
} }
func TestImageVariant(t *testing.T) {
testCases := map[string]struct {
csp cloudprovider.Provider
config *config.Config
wantVariant string
wantErr bool
}{
"AWS region": {
csp: cloudprovider.AWS,
config: &config.Config{Image: "someImage", Provider: config.ProviderConfig{
AWS: &config.AWSConfig{Region: "someRegion"},
}},
wantVariant: "someRegion",
},
"Azure cvm": {
csp: cloudprovider.Azure,
config: &config.Config{
Image: "someImage", Provider: config.ProviderConfig{Azure: &config.AzureConfig{}},
Attestation: config.AttestationConfig{AzureSEVSNP: &config.AzureSEVSNP{}},
},
wantVariant: "cvm",
},
"Azure trustedlaunch": {
csp: cloudprovider.Azure,
config: &config.Config{
Image: "someImage", Provider: config.ProviderConfig{Azure: &config.AzureConfig{}},
Attestation: config.AttestationConfig{AzureTrustedLaunch: &config.AzureTrustedLaunch{}},
},
wantVariant: "trustedlaunch",
},
"GCP": {
csp: cloudprovider.GCP,
config: &config.Config{Image: "someImage", Provider: config.ProviderConfig{
GCP: &config.GCPConfig{},
}},
wantVariant: "sev-es",
},
"OpenStack": {
csp: cloudprovider.OpenStack,
config: &config.Config{Image: "someImage", Provider: config.ProviderConfig{
OpenStack: &config.OpenStackConfig{},
}},
wantVariant: "sev",
},
"QEMU": {
csp: cloudprovider.QEMU,
config: &config.Config{Image: "someImage", Provider: config.ProviderConfig{
QEMU: &config.QEMUConfig{},
}},
wantVariant: "default",
},
"invalid": {
csp: cloudprovider.Provider(9999),
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
vari, err := imageVariant(tc.csp, tc.config)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.wantVariant, vari)
})
}
}
func TestFetchReference(t *testing.T) { func TestFetchReference(t *testing.T) {
img := "ref/abc/stream/nightly/v1.2.3" img := "ref/abc/stream/nightly/v1.2.3"
newImgInfo := func() versionsapi.ImageInfo { newImgInfo := func() versionsapi.ImageInfo {
@ -188,49 +152,47 @@ func TestFetchReference(t *testing.T) {
Ref: "abc", Ref: "abc",
Stream: "nightly", Stream: "nightly",
Version: "v1.2.3", Version: "v1.2.3",
QEMU: map[string]string{"default": "someReference"}, List: []versionsapi.ImageInfoEntry{
AWS: map[string]string{"foo": "bar"}, {
Azure: map[string]string{"foo": "bar"}, CSP: "qemu",
GCP: map[string]string{"foo": "bar"}, AttestationVariant: "dummy",
Reference: "someReference",
},
},
} }
} }
imgInfoPath := imageInfoFilename(newImgInfo()) imgInfoPath := imageInfoFilename(newImgInfo())
testCases := map[string]struct { testCases := map[string]struct {
config *config.Config provider cloudprovider.Provider
image string
imageInfoFetcher versionsAPIImageInfoFetcher imageInfoFetcher versionsAPIImageInfoFetcher
localFile []byte localFile []byte
wantReference string wantReference string
wantErr bool wantErr bool
}{ }{
"reference fetched remotely": { "reference fetched remotely": {
config: &config.Config{ provider: cloudprovider.QEMU,
Image: img, image: img,
Provider: config.ProviderConfig{QEMU: &config.QEMUConfig{}},
},
imageInfoFetcher: &stubVersionsAPIImageFetcher{ imageInfoFetcher: &stubVersionsAPIImageFetcher{
fetchImageInfoInfo: newImgInfo(), fetchImageInfoInfo: newImgInfo(),
}, },
wantReference: "someReference", wantReference: "someReference",
}, },
"reference fetched remotely fails": { "reference fetched remotely fails": {
config: &config.Config{ provider: cloudprovider.QEMU,
Image: img, image: img,
Provider: config.ProviderConfig{QEMU: &config.QEMUConfig{}},
},
imageInfoFetcher: &stubVersionsAPIImageFetcher{ imageInfoFetcher: &stubVersionsAPIImageFetcher{
fetchImageInfoErr: errors.New("failed"), fetchImageInfoErr: errors.New("failed"),
}, },
wantErr: true, wantErr: true,
}, },
"reference fetched locally": { "reference fetched locally": {
config: &config.Config{ provider: cloudprovider.QEMU,
Image: img, image: img,
Provider: config.ProviderConfig{QEMU: &config.QEMUConfig{}},
},
localFile: func() []byte { localFile: func() []byte {
info := newImgInfo() info := newImgInfo()
info.QEMU["default"] = "localOverrideReference" info.List[0].Reference = "localOverrideReference"
file, err := json.Marshal(info) file, err := json.Marshal(info)
require.NoError(t, err) require.NoError(t, err)
return file return file
@ -238,16 +200,14 @@ func TestFetchReference(t *testing.T) {
wantReference: "localOverrideReference", wantReference: "localOverrideReference",
}, },
"local file first": { "local file first": {
config: &config.Config{ provider: cloudprovider.QEMU,
Image: img, image: img,
Provider: config.ProviderConfig{QEMU: &config.QEMUConfig{}},
},
imageInfoFetcher: &stubVersionsAPIImageFetcher{ imageInfoFetcher: &stubVersionsAPIImageFetcher{
fetchImageInfoInfo: newImgInfo(), fetchImageInfoInfo: newImgInfo(),
}, },
localFile: func() []byte { localFile: func() []byte {
info := newImgInfo() info := newImgInfo()
info.QEMU["default"] = "localOverrideReference" info.List[0].Reference = "localOverrideReference"
file, err := json.Marshal(info) file, err := json.Marshal(info)
require.NoError(t, err) require.NoError(t, err)
return file return file
@ -255,18 +215,14 @@ func TestFetchReference(t *testing.T) {
wantReference: "localOverrideReference", wantReference: "localOverrideReference",
}, },
"local file is invalid": { "local file is invalid": {
config: &config.Config{ provider: cloudprovider.QEMU,
Image: img, image: img,
Provider: config.ProviderConfig{QEMU: &config.QEMUConfig{}},
},
localFile: []byte("invalid"), localFile: []byte("invalid"),
wantErr: true, wantErr: true,
}, },
"local file has invalid image info": { "local file has invalid image info": {
config: &config.Config{ provider: cloudprovider.QEMU,
Image: img, image: img,
Provider: config.ProviderConfig{QEMU: &config.QEMUConfig{}},
},
localFile: func() []byte { localFile: func() []byte {
info := newImgInfo() info := newImgInfo()
info.Ref = "" info.Ref = ""
@ -277,11 +233,9 @@ func TestFetchReference(t *testing.T) {
wantErr: true, wantErr: true,
}, },
"image version does not exist": { "image version does not exist": {
config: &config.Config{ provider: cloudprovider.QEMU,
Image: "nonExistingImageName", image: "nonExistingImageName",
Provider: config.ProviderConfig{QEMU: &config.QEMUConfig{}}, wantErr: true,
},
wantErr: true,
}, },
} }
@ -302,7 +256,7 @@ func TestFetchReference(t *testing.T) {
fs: af, fs: af,
} }
reference, err := fetcher.FetchReference(context.Background(), tc.config) reference, err := fetcher.FetchReference(context.Background(), tc.provider, variant.Dummy{}, tc.image, "someRegion")
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)

View File

@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
package image package imagefetcher
import ( import (
"context" "context"

View File

@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
package image package imagefetcher
import ( import (
"bytes" "bytes"

View File

@ -51,7 +51,7 @@ func (a *Archivist) Archive(ctx context.Context, version versionsapi.Version, cs
if err != nil { if err != nil {
return "", err return "", err
} }
a.log.Debugf("Archiving OS image %s %s %v to s3://%v/%v", csp, variant, version.ShortPath(), a.bucket, key) a.log.Debugf("Archiving OS image %s %s %v to s3://%v/%v", csp, attestationVariant, version.ShortPath(), a.bucket, key)
_, err = a.uploadClient.Upload(ctx, &s3.PutObjectInput{ _, err = a.uploadClient.Upload(ctx, &s3.PutObjectInput{
Bucket: &a.bucket, Bucket: &a.bucket,
Key: &key, Key: &key,

View File

@ -72,7 +72,7 @@ func New(region, bucketName string, log *logger.Logger) (*Uploader, error) {
} }
// Upload uploads an OS image to AWS. // Upload uploads an OS image to AWS.
func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[string]string, error) { func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) ([]versionsapi.ImageInfoEntry, error) {
blobName := fmt.Sprintf("image-%s-%s-%d.raw", req.Version.Stream, req.Version.Version, req.Timestamp.Unix()) blobName := fmt.Sprintf("image-%s-%s-%d.raw", req.Version.Stream, req.Version.Version, req.Timestamp.Unix())
imageName := imageName(req.Version, req.Timestamp) imageName := imageName(req.Version, req.Timestamp)
allRegions := []string{u.region} allRegions := []string{u.region}
@ -129,6 +129,7 @@ func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[
} }
// wait for replication, tag, publish // wait for replication, tag, publish
var imageInfo []versionsapi.ImageInfoEntry
for _, region := range allRegions { for _, region := range allRegions {
if err := u.waitForImage(ctx, amiIDs[region], region); err != nil { if err := u.waitForImage(ctx, amiIDs[region], region); err != nil {
return nil, fmt.Errorf("waiting for image to become available in region %s: %w", region, err) return nil, fmt.Errorf("waiting for image to become available in region %s: %w", region, err)
@ -142,8 +143,15 @@ func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[
if err := u.publishImage(ctx, amiIDs[region], region); err != nil { if err := u.publishImage(ctx, amiIDs[region], region); err != nil {
return nil, fmt.Errorf("publishing image in region %s: %w", region, err) return nil, fmt.Errorf("publishing image in region %s: %w", region, err)
} }
imageInfo = append(imageInfo, versionsapi.ImageInfoEntry{
CSP: "aws",
AttestationVariant: req.AttestationVariant,
Reference: amiIDs[region],
Region: region,
})
} }
return amiIDs, nil
return imageInfo, nil
} }
func (u *Uploader) ensureBucket(ctx context.Context) error { func (u *Uploader) ensureBucket(ctx context.Context) error {

View File

@ -93,9 +93,9 @@ func New(subscription, location, resourceGroup string, log *logger.Logger) (*Upl
} }
// Upload uploads an OS image to Azure. // Upload uploads an OS image to Azure.
func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[string]string, error) { func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) ([]versionsapi.ImageInfoEntry, error) {
formattedTime := req.Timestamp.Format(timestampFormat) formattedTime := req.Timestamp.Format(timestampFormat)
diskName := fmt.Sprintf("constellation-%s-%s-%s", req.Version.Stream, formattedTime, req.Variant) diskName := fmt.Sprintf("constellation-%s-%s-%s", req.Version.Stream, formattedTime, req.AttestationVariant)
var sigName string var sigName string
switch req.Version.Stream { switch req.Version.Stream {
case "stable": case "stable":
@ -140,7 +140,7 @@ func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[
if err := u.ensureSIG(ctx, sigName); err != nil { if err := u.ensureSIG(ctx, sigName); err != nil {
return nil, fmt.Errorf("ensuring sig exists: %w", err) return nil, fmt.Errorf("ensuring sig exists: %w", err)
} }
if err := u.ensureImageDefinition(ctx, sigName, definitionName, req.Version, req.Variant); err != nil { if err := u.ensureImageDefinition(ctx, sigName, definitionName, req.Version, req.AttestationVariant); err != nil {
return nil, fmt.Errorf("ensuring image definition exists: %w", err) return nil, fmt.Errorf("ensuring image definition exists: %w", err)
} }
@ -154,8 +154,12 @@ func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[
return nil, fmt.Errorf("getting image reference: %w", err) return nil, fmt.Errorf("getting image reference: %w", err)
} }
return map[string]string{ return []versionsapi.ImageInfoEntry{
req.Variant: imageReference, {
CSP: "azure",
AttestationVariant: req.AttestationVariant,
Reference: imageReference,
},
}, nil }, nil
} }
@ -337,7 +341,7 @@ func (u *Uploader) ensureSIG(ctx context.Context, sigName string) error {
} }
// ensureImageDefinition creates an image definition (component of a SIG) if it does not exist yet. // ensureImageDefinition creates an image definition (component of a SIG) if it does not exist yet.
func (u *Uploader) ensureImageDefinition(ctx context.Context, sigName, definitionName string, version versionsapi.Version, variant string) error { func (u *Uploader) ensureImageDefinition(ctx context.Context, sigName, definitionName string, version versionsapi.Version, attestationVariant string) error {
_, err := u.image.Get(ctx, u.resourceGroup, sigName, definitionName, &armcomputev4.GalleryImagesClientGetOptions{}) _, err := u.image.Get(ctx, u.resourceGroup, sigName, definitionName, &armcomputev4.GalleryImagesClientGetOptions{})
if err == nil { if err == nil {
u.log.Debugf("Image definition %s/%s in %s exists", sigName, definitionName, u.resourceGroup) u.log.Debugf("Image definition %s/%s in %s exists", sigName, definitionName, u.resourceGroup)
@ -349,10 +353,10 @@ func (u *Uploader) ensureImageDefinition(ctx context.Context, sigName, definitio
// based on wether a VMGS was provided or not. // based on wether a VMGS was provided or not.
// VMGS provided: ConfidentialVM // VMGS provided: ConfidentialVM
// No VMGS provided: ConfidentialVMSupported // No VMGS provided: ConfidentialVMSupported
switch strings.ToLower(variant) { switch strings.ToLower(attestationVariant) {
case "cvm": case "azure-sev-snp":
securityType = string("ConfidentialVMSupported") securityType = string("ConfidentialVMSupported")
case "trustedlaunch": case "azure-trustedlaunch":
securityType = string(armcomputev4.SecurityTypesTrustedLaunch) securityType = string(armcomputev4.SecurityTypesTrustedLaunch)
} }
offer := imageOffer(version) offer := imageOffer(version)

View File

@ -61,8 +61,8 @@ func New(ctx context.Context, project, location, bucketName string, log *logger.
} }
// Upload uploads an OS image to GCP. // Upload uploads an OS image to GCP.
func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[string]string, error) { func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) ([]versionsapi.ImageInfoEntry, error) {
imageName := u.imageName(req.Version, req.Variant) imageName := u.imageName(req.Version, req.AttestationVariant)
blobName := imageName + ".tar.gz" blobName := imageName + ".tar.gz"
if err := u.ensureBucket(ctx); err != nil { if err := u.ensureBucket(ctx); err != nil {
return nil, fmt.Errorf("setup: ensuring bucket exists: %w", err) return nil, fmt.Errorf("setup: ensuring bucket exists: %w", err)
@ -86,8 +86,12 @@ func (u *Uploader) Upload(ctx context.Context, req *osimage.UploadRequest) (map[
if err != nil { if err != nil {
return nil, fmt.Errorf("creating image: %w", err) return nil, fmt.Errorf("creating image: %w", err)
} }
return map[string]string{ return []versionsapi.ImageInfoEntry{
req.Variant: imageRef, {
CSP: "gcp",
AttestationVariant: req.AttestationVariant,
Reference: imageRef,
},
}, nil }, nil
} }
@ -220,8 +224,8 @@ func (u *Uploader) blobURL(blobName string) string {
}).String() }).String()
} }
func (u *Uploader) imageName(version versionsapi.Version, variant string) string { func (u *Uploader) imageName(version versionsapi.Version, attestationVariant string) string {
return strings.ReplaceAll(version.Version, ".", "-") + "-" + variant + "-" + version.Stream return strings.ReplaceAll(version.Version, ".", "-") + "-" + attestationVariant + "-" + version.Stream
} }
func (u *Uploader) imageFamily(version versionsapi.Version) string { func (u *Uploader) imageFamily(version versionsapi.Version) string {

View File

@ -8,5 +8,6 @@ go_library(
deps = [ deps = [
"//internal/logger", "//internal/logger",
"//internal/osimage", "//internal/osimage",
"//internal/versionsapi",
], ],
) )

View File

@ -12,6 +12,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/osimage" "github.com/edgelesssys/constellation/v2/internal/osimage"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
) )
// Uploader is a no-op uploader. // Uploader is a no-op uploader.
@ -25,7 +26,7 @@ func New(log *logger.Logger) *Uploader {
} }
// Upload pretends to upload images to a csp. // Upload pretends to upload images to a csp.
func (u *Uploader) Upload(_ context.Context, req *osimage.UploadRequest) (map[string]string, error) { func (u *Uploader) Upload(_ context.Context, req *osimage.UploadRequest) ([]versionsapi.ImageInfoEntry, error) {
u.log.Debugf("Skipping image upload of %s since this CSP does not require images to be uploaded in advance.", req.Version.ShortPath()) u.log.Debugf("Skipping image upload of %s since this CSP does not require images to be uploaded in advance.", req.Version.ShortPath())
return nil, nil return nil, nil
} }

View File

@ -18,12 +18,12 @@ import (
// UploadRequest is a request to upload an os image. // UploadRequest is a request to upload an os image.
type UploadRequest struct { type UploadRequest struct {
Provider cloudprovider.Provider Provider cloudprovider.Provider
Version versionsapi.Version Version versionsapi.Version
Variant string AttestationVariant string
SBDatabase secureboot.Database SBDatabase secureboot.Database
UEFIVarStore secureboot.UEFIVarStore UEFIVarStore secureboot.UEFIVarStore
Size int64 Size int64
Timestamp time.Time Timestamp time.Time
Image io.ReadSeeker Image io.ReadSeeker
} }

View File

@ -211,24 +211,23 @@ func deleteImage(ctx context.Context, clients rmImageClients, ver versionsapi.Ve
return fmt.Errorf("fetching image info: %w", err) return fmt.Errorf("fetching image info: %w", err)
} }
log.Infof("Deleting AWS images from %s", imageInfo.JSONPath()) for _, entry := range imageInfo.List {
for awsRegion, awsImage := range imageInfo.AWS { switch entry.CSP {
if err := clients.aws.deleteImage(ctx, awsImage, awsRegion, dryrun, log); err != nil { case "aws":
retErr = errors.Join(retErr, fmt.Errorf("deleting AWS image %s: %w", awsImage, err)) log.Infof("Deleting AWS images from %s", imageInfo.JSONPath())
} if err := clients.aws.deleteImage(ctx, entry.Reference, entry.Region, dryrun, log); err != nil {
} retErr = errors.Join(retErr, fmt.Errorf("deleting AWS image %s: %w", entry.Reference, err))
}
log.Infof("Deleting GCP images from %s", imageInfo.JSONPath()) case "gcp":
for _, gcpImage := range imageInfo.GCP { log.Infof("Deleting GCP images from %s", imageInfo.JSONPath())
if err := clients.gcp.deleteImage(ctx, gcpImage, dryrun, log); err != nil { if err := clients.gcp.deleteImage(ctx, entry.Reference, dryrun, log); err != nil {
retErr = errors.Join(retErr, fmt.Errorf("deleting GCP image %s: %w", gcpImage, err)) retErr = errors.Join(retErr, fmt.Errorf("deleting GCP image %s: %w", entry.Reference, err))
} }
} case "azure":
log.Infof("Deleting Azure images from %s", imageInfo.JSONPath())
log.Infof("Deleting Azure images from %s", imageInfo.JSONPath()) if err := clients.az.deleteImage(ctx, entry.Reference, dryrun, log); err != nil {
for _, azImage := range imageInfo.Azure { retErr = errors.Join(retErr, fmt.Errorf("deleting Azure image %s: %w", entry.Reference, err))
if err := clients.az.deleteImage(ctx, azImage, dryrun, log); err != nil { }
retErr = errors.Join(retErr, fmt.Errorf("deleting Azure image %s: %w", azImage, err))
} }
} }

View File

@ -24,22 +24,22 @@ type ImageInfo struct {
Stream string `json:"stream,omitempty"` Stream string `json:"stream,omitempty"`
// Version is the version of the image. // Version is the version of the image.
Version string `json:"version,omitempty"` Version string `json:"version,omitempty"`
// AWS is a map of AWS regions to AMI IDs. // List contains the image variants for this version.
AWS map[string]string `json:"aws,omitempty"` List []ImageInfoEntry `json:"list,omitempty"`
// Azure is a map of image types to Azure image IDs. }
Azure map[string]string `json:"azure,omitempty"`
// GCP is a map of image types to GCP image IDs. // ImageInfoEntry contains information about a single image variant.
GCP map[string]string `json:"gcp,omitempty"` type ImageInfoEntry struct {
// OpenStack is a map of image types to OpenStack image IDs. CSP string `json:"csp"`
OpenStack map[string]string `json:"openstack,omitempty"` AttestationVariant string `json:"attestationVariant"`
// QEMU is a map of image types to QEMU image URLs. Reference string `json:"reference"`
QEMU map[string]string `json:"qemu,omitempty"` Region string `json:"region,omitempty"`
} }
// JSONPath returns the S3 JSON path for this object. // JSONPath returns the S3 JSON path for this object.
func (i ImageInfo) JSONPath() string { func (i ImageInfo) JSONPath() string {
return path.Join( return path.Join(
constants.CDNAPIPrefix, constants.CDNAPIPrefixV2,
"ref", i.Ref, "ref", i.Ref,
"stream", i.Stream, "stream", i.Stream,
i.Version, i.Version,
@ -71,20 +71,8 @@ func (i ImageInfo) ValidateRequest() error {
if !semver.IsValid(i.Version) { if !semver.IsValid(i.Version) {
retErr = errors.Join(retErr, fmt.Errorf("version %q is not a valid semver", i.Version)) retErr = errors.Join(retErr, fmt.Errorf("version %q is not a valid semver", i.Version))
} }
if len(i.AWS) != 0 { if len(i.List) != 0 {
retErr = errors.Join(retErr, errors.New("AWS map must be empty for request")) retErr = errors.Join(retErr, errors.New("list must be empty for request"))
}
if len(i.Azure) != 0 {
retErr = errors.Join(retErr, errors.New("Azure map must be empty for request"))
}
if len(i.GCP) != 0 {
retErr = errors.Join(retErr, errors.New("GCP map must be empty for request"))
}
if len(i.OpenStack) != 0 {
retErr = errors.Join(retErr, errors.New("OpenStack map must be empty for request"))
}
if len(i.QEMU) != 0 {
retErr = errors.Join(retErr, errors.New("QEMU map must be empty for request"))
} }
return retErr return retErr
@ -102,14 +90,8 @@ func (i ImageInfo) Validate() error {
if !semver.IsValid(i.Version) { if !semver.IsValid(i.Version) {
retErr = errors.Join(retErr, fmt.Errorf("version %q is not a valid semver", i.Version)) retErr = errors.Join(retErr, fmt.Errorf("version %q is not a valid semver", i.Version))
} }
var providers int if len(i.List) == 0 {
providers += len(i.AWS) retErr = errors.Join(retErr, errors.New("one or more image variants must be specified in the list"))
providers += len(i.Azure)
providers += len(i.GCP)
providers += len(i.OpenStack)
providers += len(i.QEMU)
if providers == 0 {
retErr = errors.Join(retErr, errors.New("one or more providers must be specified"))
} }
return retErr return retErr

View File

@ -24,7 +24,7 @@ func TestImageInfoJSONPath(t *testing.T) {
Stream: "nightly", Stream: "nightly",
Version: "v1.0.0", Version: "v1.0.0",
}, },
wantPath: constants.CDNAPIPrefix + "/ref/test-ref/stream/nightly/v1.0.0/image/info.json", wantPath: constants.CDNAPIPrefixV2 + "/ref/test-ref/stream/nightly/v1.0.0/image/info.json",
}, },
"image info release": { "image info release": {
info: ImageInfo{ info: ImageInfo{
@ -32,7 +32,7 @@ func TestImageInfoJSONPath(t *testing.T) {
Stream: "stable", Stream: "stable",
Version: "v1.0.0", Version: "v1.0.0",
}, },
wantPath: constants.CDNAPIPrefix + "/ref/-/stream/stable/v1.0.0/image/info.json", wantPath: constants.CDNAPIPrefixV2 + "/ref/-/stream/stable/v1.0.0/image/info.json",
}, },
} }
@ -55,7 +55,7 @@ func TestImageInfoURL(t *testing.T) {
Stream: "nightly", Stream: "nightly",
Version: "v1.0.0", Version: "v1.0.0",
}, },
wantURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/test-ref/stream/nightly/v1.0.0/image/info.json", wantURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefixV2 + "/ref/test-ref/stream/nightly/v1.0.0/image/info.json",
}, },
"image info release": { "image info release": {
info: ImageInfo{ info: ImageInfo{
@ -63,7 +63,7 @@ func TestImageInfoURL(t *testing.T) {
Stream: "stable", Stream: "stable",
Version: "v1.0.0", Version: "v1.0.0",
}, },
wantURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefix + "/ref/-/stream/stable/v1.0.0/image/info.json", wantURL: constants.CDNRepositoryURL + "/" + constants.CDNAPIPrefixV2 + "/ref/-/stream/stable/v1.0.0/image/info.json",
}, },
} }
@ -87,10 +87,35 @@ func TestImageInfoValidate(t *testing.T) {
Ref: "test-ref", Ref: "test-ref",
Stream: "nightly", Stream: "nightly",
Version: "v1.0.0", Version: "v1.0.0",
AWS: map[string]string{"key": "value", "key2": "value2"}, List: []ImageInfoEntry{
GCP: map[string]string{"key": "value", "key2": "value2"}, {
Azure: map[string]string{"key": "value", "key2": "value2"}, CSP: "aws",
QEMU: map[string]string{"key": "value", "key2": "value2"}, AttestationVariant: "aws-nitro-tpm",
Reference: "ami-123",
Region: "us-east-1",
},
{
CSP: "aws",
AttestationVariant: "aws-nitro-tpm",
Reference: "ami-123",
Region: "us-east-2",
},
{
CSP: "gcp",
AttestationVariant: "gcp-sev-es",
Reference: "gcp-123",
},
{
CSP: "azure",
AttestationVariant: "azure-sev-snp",
Reference: "azure-123",
},
{
CSP: "qemu",
AttestationVariant: "qemu-vtpm",
Reference: "https://example.com/qemu-123/image.raw",
},
},
}, },
}, },
"invalid ref": { "invalid ref": {
@ -98,10 +123,14 @@ func TestImageInfoValidate(t *testing.T) {
Ref: "", Ref: "",
Stream: "nightly", Stream: "nightly",
Version: "v1.0.0", Version: "v1.0.0",
AWS: map[string]string{"key": "value", "key2": "value2"}, List: []ImageInfoEntry{
GCP: map[string]string{"key": "value", "key2": "value2"}, {
Azure: map[string]string{"key": "value", "key2": "value2"}, CSP: "aws",
QEMU: map[string]string{"key": "value", "key2": "value2"}, AttestationVariant: "aws-nitro-tpm",
Reference: "ami-123",
Region: "us-east-1",
},
},
}, },
wantErr: true, wantErr: true,
}, },
@ -110,10 +139,14 @@ func TestImageInfoValidate(t *testing.T) {
Ref: "test-ref", Ref: "test-ref",
Stream: "", Stream: "",
Version: "v1.0.0", Version: "v1.0.0",
AWS: map[string]string{"key": "value", "key2": "value2"}, List: []ImageInfoEntry{
GCP: map[string]string{"key": "value", "key2": "value2"}, {
Azure: map[string]string{"key": "value", "key2": "value2"}, CSP: "aws",
QEMU: map[string]string{"key": "value", "key2": "value2"}, AttestationVariant: "aws-nitro-tpm",
Reference: "ami-123",
Region: "us-east-1",
},
},
}, },
wantErr: true, wantErr: true,
}, },
@ -122,14 +155,18 @@ func TestImageInfoValidate(t *testing.T) {
Ref: "test-ref", Ref: "test-ref",
Stream: "nightly", Stream: "nightly",
Version: "", Version: "",
AWS: map[string]string{"key": "value", "key2": "value2"}, List: []ImageInfoEntry{
GCP: map[string]string{"key": "value", "key2": "value2"}, {
Azure: map[string]string{"key": "value", "key2": "value2"}, CSP: "aws",
QEMU: map[string]string{"key": "value", "key2": "value2"}, AttestationVariant: "aws-nitro-tpm",
Reference: "ami-123",
Region: "us-east-1",
},
},
}, },
wantErr: true, wantErr: true,
}, },
"no provider": { "no entries in list": {
info: ImageInfo{ info: ImageInfo{
Ref: "test-ref", Ref: "test-ref",
Stream: "nightly", Stream: "nightly",
@ -197,48 +234,19 @@ func TestImageInfoValidateRequest(t *testing.T) {
}, },
wantErr: true, wantErr: true,
}, },
"invalid gcp": { "request contains entries": {
info: ImageInfo{ info: ImageInfo{
Ref: "test-ref", Ref: "test-ref",
Stream: "nightly", Stream: "nightly",
Version: "v1.0.0", Version: "v1.0.0",
GCP: map[string]string{"key": "value", "key2": "value2"}, List: []ImageInfoEntry{
}, {
wantErr: true, CSP: "aws",
}, AttestationVariant: "aws-nitro-tpm",
"invalid azure": { Reference: "ami-123",
info: ImageInfo{ Region: "us-east-1",
Ref: "test-ref", },
Stream: "nightly", },
Version: "v1.0.0",
Azure: map[string]string{"key": "value", "key2": "value2"},
},
wantErr: true,
},
"invalid aws": {
info: ImageInfo{
Ref: "test-ref",
Stream: "nightly",
Version: "v1.0.0",
AWS: map[string]string{"key": "value", "key2": "value2"},
},
wantErr: true,
},
"invalid qemu": {
info: ImageInfo{
Ref: "test-ref",
Stream: "nightly",
Version: "v1.0.0",
QEMU: map[string]string{"key": "value", "key2": "value2"},
},
wantErr: true,
},
"invalid openstack": {
info: ImageInfo{
Ref: "test-ref",
Stream: "nightly",
Version: "v1.0.0",
OpenStack: map[string]string{"key": "value", "key2": "value2"},
}, },
wantErr: true, wantErr: true,
}, },
@ -247,10 +255,14 @@ func TestImageInfoValidateRequest(t *testing.T) {
Ref: "", Ref: "",
Stream: "", Stream: "",
Version: "", Version: "",
AWS: map[string]string{"key": "value", "key2": "value2"}, List: []ImageInfoEntry{
GCP: map[string]string{"key": "value", "key2": "value2"}, {
Azure: map[string]string{"key": "value", "key2": "value2"}, CSP: "aws",
QEMU: map[string]string{"key": "value", "key2": "value2"}, AttestationVariant: "aws-nitro-tpm",
Reference: "ami-123",
Region: "us-east-1",
},
},
}, },
wantErr: true, wantErr: true,
}, },