Move upload/delete code to its own package

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2024-06-11 14:50:38 +02:00 committed by Daniel Weiße
parent 5654e76f7e
commit 52a65c20ac
13 changed files with 129 additions and 100 deletions

View File

@ -5,31 +5,23 @@ go_library(
name = "attestationconfigapi", name = "attestationconfigapi",
srcs = [ srcs = [
"attestationconfigapi.go", "attestationconfigapi.go",
"client.go",
"fetcher.go", "fetcher.go",
"reporter.go",
"snp.go", "snp.go",
], ],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi", importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = [ deps = [
"//internal/api/client",
"//internal/api/fetcher", "//internal/api/fetcher",
"//internal/attestation/variant", "//internal/attestation/variant",
"//internal/constants", "//internal/constants",
"//internal/sigstore", "//internal/sigstore",
"//internal/staticupload",
"@com_github_aws_aws_sdk_go//aws",
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
], ],
) )
go_test( go_test(
name = "attestationconfigapi_test", name = "attestationconfigapi_test",
srcs = [ srcs = [
"client_test.go",
"fetcher_test.go", "fetcher_test.go",
"reporter_test.go",
"snp_test.go", "snp_test.go",
], ],
embed = [":attestationconfigapi"], embed = [":attestationconfigapi"],

View File

@ -19,6 +19,7 @@ go_library(
visibility = ["//visibility:private"], visibility = ["//visibility:private"],
deps = [ deps = [
"//internal/api/attestationconfigapi", "//internal/api/attestationconfigapi",
"//internal/api/attestationconfigapi/cli/client",
"//internal/attestation/variant", "//internal/attestation/variant",
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/constants", "//internal/constants",

View File

@ -0,0 +1,35 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//bazel/go:go_test.bzl", "go_test")
go_library(
name = "client",
srcs = [
"client.go",
"reporter.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli/client",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/attestationconfigapi",
"//internal/api/client",
"//internal/attestation/variant",
"//internal/sigstore",
"//internal/staticupload",
"@com_github_aws_aws_sdk_go//aws",
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
],
)
go_test(
name = "client_test",
srcs = [
"client_test.go",
"reporter_test.go",
],
embed = [":client"],
deps = [
"//internal/api/attestationconfigapi",
"//internal/attestation/variant",
"@com_github_stretchr_testify//assert",
],
)

View File

@ -3,7 +3,12 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
package attestationconfigapi
/*
package client contains code to manage CVM versions in Constellation's CDN API.
It is used to upload and delete "latest" versions for AMD SEV-SNP and Intel TDX.
*/
package client
import ( import (
"context" "context"
@ -12,6 +17,7 @@ import (
"log/slog" "log/slog"
"time" "time"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
apiclient "github.com/edgelesssys/constellation/v2/internal/api/client" apiclient "github.com/edgelesssys/constellation/v2/internal/api/client"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/sigstore" "github.com/edgelesssys/constellation/v2/internal/sigstore"
@ -31,8 +37,8 @@ type Client struct {
cacheWindowSize int cacheWindowSize int
} }
// NewClient returns a new Client. // New returns a new Client.
func NewClient(ctx context.Context, cfg staticupload.Config, cosignPwd, privateKey []byte, dryRun bool, versionWindowSize int, log *slog.Logger) (*Client, apiclient.CloseFunc, error) { func New(ctx context.Context, cfg staticupload.Config, cosignPwd, privateKey []byte, dryRun bool, versionWindowSize int, log *slog.Logger) (*Client, apiclient.CloseFunc, error) {
s3Client, clientClose, err := apiclient.NewClient(ctx, cfg.Region, cfg.Bucket, cfg.DistributionID, dryRun, log) s3Client, clientClose, err := apiclient.NewClient(ctx, cfg.Region, cfg.Bucket, cfg.DistributionID, dryRun, log)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to create s3 storage: %w", err) return nil, nil, fmt.Errorf("failed to create s3 storage: %w", err)
@ -49,7 +55,7 @@ func NewClient(ctx context.Context, cfg staticupload.Config, cosignPwd, privateK
} }
// uploadSEVSNPVersion uploads the latest version numbers of the SEVSNP. Then version name is the UTC timestamp of the date. The /list entry stores the version name + .json suffix. // uploadSEVSNPVersion uploads the latest version numbers of the SEVSNP. Then version name is the UTC timestamp of the date. The /list entry stores the version name + .json suffix.
func (a Client) uploadSEVSNPVersion(ctx context.Context, attestation variant.Variant, version SEVSNPVersion, date time.Time) error { func (a Client) uploadSEVSNPVersion(ctx context.Context, attestation variant.Variant, version attestationconfigapi.SEVSNPVersion, date time.Time) error {
versions, err := a.List(ctx, attestation) versions, err := a.List(ctx, attestation)
if err != nil { if err != nil {
return fmt.Errorf("fetch version list: %w", err) return fmt.Errorf("fetch version list: %w", err)
@ -74,32 +80,32 @@ func (a Client) DeleteSEVSNPVersion(ctx context.Context, attestation variant.Var
} }
// List returns the list of versions for the given attestation variant. // List returns the list of versions for the given attestation variant.
func (a Client) List(ctx context.Context, attestation variant.Variant) (SEVSNPVersionList, error) { func (a Client) List(ctx context.Context, attestation variant.Variant) (attestationconfigapi.SEVSNPVersionList, error) {
if !attestation.Equal(variant.AzureSEVSNP{}) && if !attestation.Equal(variant.AzureSEVSNP{}) &&
!attestation.Equal(variant.AWSSEVSNP{}) && !attestation.Equal(variant.AWSSEVSNP{}) &&
!attestation.Equal(variant.GCPSEVSNP{}) { !attestation.Equal(variant.GCPSEVSNP{}) {
return SEVSNPVersionList{}, fmt.Errorf("unsupported attestation variant: %s", attestation) return attestationconfigapi.SEVSNPVersionList{}, fmt.Errorf("unsupported attestation variant: %s", attestation)
} }
versions, err := apiclient.Fetch(ctx, a.s3Client, SEVSNPVersionList{variant: attestation}) versions, err := apiclient.Fetch(ctx, a.s3Client, attestationconfigapi.SEVSNPVersionList{Variant: attestation})
if err != nil { if err != nil {
var notFoundErr *apiclient.NotFoundError var notFoundErr *apiclient.NotFoundError
if errors.As(err, &notFoundErr) { if errors.As(err, &notFoundErr) {
return SEVSNPVersionList{variant: attestation}, nil return attestationconfigapi.SEVSNPVersionList{Variant: attestation}, nil
} }
return SEVSNPVersionList{}, err return attestationconfigapi.SEVSNPVersionList{}, err
} }
versions.variant = attestation versions.Variant = attestation
return versions, nil return versions, nil
} }
func (a Client) deleteSEVSNPVersion(versions SEVSNPVersionList, versionStr string) (ops []crudCmd, err error) { func (a Client) deleteSEVSNPVersion(versions attestationconfigapi.SEVSNPVersionList, versionStr string) (ops []crudCmd, err error) {
versionStr = versionStr + ".json" versionStr = versionStr + ".json"
ops = append(ops, deleteCmd{ ops = append(ops, deleteCmd{
apiObject: SEVSNPVersionAPI{ apiObject: attestationconfigapi.SEVSNPVersionAPI{
Variant: versions.variant, Variant: versions.Variant,
Version: versionStr, Version: versionStr,
}, },
}) })
@ -115,8 +121,8 @@ func (a Client) deleteSEVSNPVersion(versions SEVSNPVersionList, versionStr strin
return ops, nil return ops, nil
} }
func (a Client) constructUploadCmd(attestation variant.Variant, version SEVSNPVersion, versionNames SEVSNPVersionList, date time.Time) []crudCmd { func (a Client) constructUploadCmd(attestation variant.Variant, version attestationconfigapi.SEVSNPVersion, versionNames attestationconfigapi.SEVSNPVersionList, date time.Time) []crudCmd {
if !attestation.Equal(versionNames.variant) { if !attestation.Equal(versionNames.Variant) {
return nil return nil
} }
@ -124,11 +130,11 @@ func (a Client) constructUploadCmd(attestation variant.Variant, version SEVSNPVe
var res []crudCmd var res []crudCmd
res = append(res, putCmd{ res = append(res, putCmd{
apiObject: SEVSNPVersionAPI{Version: dateStr, Variant: attestation, SEVSNPVersion: version}, apiObject: attestationconfigapi.SEVSNPVersionAPI{Version: dateStr, Variant: attestation, SEVSNPVersion: version},
signer: a.signer, signer: a.signer,
}) })
versionNames.addVersion(dateStr) versionNames.AddVersion(dateStr)
res = append(res, putCmd{ res = append(res, putCmd{
apiObject: versionNames, apiObject: versionNames,
@ -138,19 +144,19 @@ func (a Client) constructUploadCmd(attestation variant.Variant, version SEVSNPVe
return res return res
} }
func removeVersion(list SEVSNPVersionList, versionStr string) (removedVersions SEVSNPVersionList, err error) { func removeVersion(list attestationconfigapi.SEVSNPVersionList, versionStr string) (removedVersions attestationconfigapi.SEVSNPVersionList, err error) {
versions := list.List() versions := list.List
for i, v := range versions { for i, v := range versions {
if v == versionStr { if v == versionStr {
if i == len(versions)-1 { if i == len(versions)-1 {
removedVersions = SEVSNPVersionList{list: versions[:i], variant: list.variant} removedVersions = attestationconfigapi.SEVSNPVersionList{List: versions[:i], Variant: list.Variant}
} else { } else {
removedVersions = SEVSNPVersionList{list: append(versions[:i], versions[i+1:]...), variant: list.variant} removedVersions = attestationconfigapi.SEVSNPVersionList{List: append(versions[:i], versions[i+1:]...), Variant: list.Variant}
} }
return removedVersions, nil return removedVersions, nil
} }
} }
return SEVSNPVersionList{}, fmt.Errorf("version %s not found in list %v", versionStr, versions) return attestationconfigapi.SEVSNPVersionList{}, fmt.Errorf("version %s not found in list %v", versionStr, versions)
} }
type crudCmd interface { type crudCmd interface {

View File

@ -3,12 +3,13 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
package attestationconfigapi package client
import ( import (
"testing" "testing"
"time" "time"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -18,13 +19,13 @@ func TestUploadAzureSEVSNP(t *testing.T) {
bucketID: "bucket", bucketID: "bucket",
signer: fakeSigner{}, signer: fakeSigner{},
} }
version := SEVSNPVersion{} version := attestationconfigapi.SEVSNPVersion{}
date := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC) date := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC)
ops := sut.constructUploadCmd(variant.AzureSEVSNP{}, version, SEVSNPVersionList{list: []string{"2021-01-01-01-01.json", "2019-01-01-01-01.json"}, variant: variant.AzureSEVSNP{}}, date) ops := sut.constructUploadCmd(variant.AzureSEVSNP{}, version, attestationconfigapi.SEVSNPVersionList{List: []string{"2021-01-01-01-01.json", "2019-01-01-01-01.json"}, Variant: variant.AzureSEVSNP{}}, date)
dateStr := "2023-01-01-01-01.json" dateStr := "2023-01-01-01-01.json"
assert := assert.New(t) assert := assert.New(t)
assert.Contains(ops, putCmd{ assert.Contains(ops, putCmd{
apiObject: SEVSNPVersionAPI{ apiObject: attestationconfigapi.SEVSNPVersionAPI{
Variant: variant.AzureSEVSNP{}, Variant: variant.AzureSEVSNP{},
Version: dateStr, Version: dateStr,
SEVSNPVersion: version, SEVSNPVersion: version,
@ -32,7 +33,7 @@ func TestUploadAzureSEVSNP(t *testing.T) {
signer: fakeSigner{}, signer: fakeSigner{},
}) })
assert.Contains(ops, putCmd{ assert.Contains(ops, putCmd{
apiObject: SEVSNPVersionList{variant: variant.AzureSEVSNP{}, list: []string{"2023-01-01-01-01.json", "2021-01-01-01-01.json", "2019-01-01-01-01.json"}}, apiObject: attestationconfigapi.SEVSNPVersionList{Variant: variant.AzureSEVSNP{}, List: []string{"2023-01-01-01-01.json", "2021-01-01-01-01.json", "2019-01-01-01-01.json"}},
signer: fakeSigner{}, signer: fakeSigner{},
}) })
} }
@ -41,20 +42,20 @@ func TestDeleteAzureSEVSNPVersions(t *testing.T) {
sut := Client{ sut := Client{
bucketID: "bucket", bucketID: "bucket",
} }
versions := SEVSNPVersionList{list: []string{"2023-01-01.json", "2021-01-01.json", "2019-01-01.json"}} versions := attestationconfigapi.SEVSNPVersionList{List: []string{"2023-01-01.json", "2021-01-01.json", "2019-01-01.json"}}
ops, err := sut.deleteSEVSNPVersion(versions, "2021-01-01") ops, err := sut.deleteSEVSNPVersion(versions, "2021-01-01")
assert := assert.New(t) assert := assert.New(t)
assert.NoError(err) assert.NoError(err)
assert.Contains(ops, deleteCmd{ assert.Contains(ops, deleteCmd{
apiObject: SEVSNPVersionAPI{ apiObject: attestationconfigapi.SEVSNPVersionAPI{
Version: "2021-01-01.json", Version: "2021-01-01.json",
}, },
}) })
assert.Contains(ops, putCmd{ assert.Contains(ops, putCmd{
apiObject: SEVSNPVersionList{list: []string{"2023-01-01.json", "2019-01-01.json"}}, apiObject: attestationconfigapi.SEVSNPVersionList{List: []string{"2023-01-01.json", "2019-01-01.json"}},
}) })
} }

View File

@ -4,12 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
/* package client
The reporter contains the logic to determine a latest version for SEVSNP based on cached version values observed on CVM instances.
Some code in this file (e.g. listing cached files) does not rely on dedicated API objects and instead uses the AWS SDK directly,
for no other reason than original development speed.
*/
package attestationconfigapi
import ( import (
"context" "context"
@ -23,6 +18,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/api/client" "github.com/edgelesssys/constellation/v2/internal/api/client"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
) )
@ -34,7 +30,7 @@ const cachedVersionsSubDir = "cached-versions"
var ErrNoNewerVersion = errors.New("input version is not newer than latest API version") var ErrNoNewerVersion = errors.New("input version is not newer than latest API version")
func reportVersionDir(attestation variant.Variant) string { func reportVersionDir(attestation variant.Variant) string {
return path.Join(AttestationURLPath, attestation.String(), cachedVersionsSubDir) return path.Join(attestationconfigapi.AttestationURLPath, attestation.String(), cachedVersionsSubDir)
} }
// UploadSEVSNPVersionLatest saves the given version to the cache, determines the smallest // UploadSEVSNPVersionLatest saves the given version to the cache, determines the smallest
@ -42,7 +38,7 @@ func reportVersionDir(attestation variant.Variant) string {
// the latest version in the API if there is an update. // the latest version in the API if there is an update.
// force can be used to bypass the validation logic against the cached versions. // force can be used to bypass the validation logic against the cached versions.
func (c Client) UploadSEVSNPVersionLatest(ctx context.Context, attestation variant.Variant, inputVersion, func (c Client) UploadSEVSNPVersionLatest(ctx context.Context, attestation variant.Variant, inputVersion,
latestAPIVersion SEVSNPVersion, now time.Time, force bool, latestAPIVersion attestationconfigapi.SEVSNPVersion, now time.Time, force bool,
) error { ) error {
if err := c.cacheSEVSNPVersion(ctx, attestation, inputVersion, now); err != nil { if err := c.cacheSEVSNPVersion(ctx, attestation, inputVersion, now); err != nil {
return fmt.Errorf("reporting version: %w", err) return fmt.Errorf("reporting version: %w", err)
@ -84,7 +80,7 @@ func (c Client) UploadSEVSNPVersionLatest(ctx context.Context, attestation varia
} }
// cacheSEVSNPVersion uploads the latest observed version numbers of the SEVSNP. This version is used to later report the latest version numbers to the API. // cacheSEVSNPVersion uploads the latest observed version numbers of the SEVSNP. This version is used to later report the latest version numbers to the API.
func (c Client) cacheSEVSNPVersion(ctx context.Context, attestation variant.Variant, version SEVSNPVersion, date time.Time) error { func (c Client) cacheSEVSNPVersion(ctx context.Context, attestation variant.Variant, version attestationconfigapi.SEVSNPVersion, date time.Time) error {
dateStr := date.Format(VersionFormat) + ".json" dateStr := date.Format(VersionFormat) + ".json"
res := putCmd{ res := putCmd{
apiObject: reportedSEVSNPVersionAPI{Version: dateStr, variant: attestation, SEVSNPVersion: version}, apiObject: reportedSEVSNPVersionAPI{Version: dateStr, variant: attestation, SEVSNPVersion: version},
@ -112,8 +108,8 @@ func (c Client) listCachedVersions(ctx context.Context, attestation variant.Vari
} }
// findMinVersion finds the minimal version of the given version dates among the latest values in the version window size. // findMinVersion finds the minimal version of the given version dates among the latest values in the version window size.
func (c Client) findMinVersion(ctx context.Context, attesation variant.Variant, versionDates []string) (SEVSNPVersion, string, error) { func (c Client) findMinVersion(ctx context.Context, attesation variant.Variant, versionDates []string) (attestationconfigapi.SEVSNPVersion, string, error) {
var minimalVersion *SEVSNPVersion var minimalVersion *attestationconfigapi.SEVSNPVersion
var minimalDate string var minimalDate string
sort.Sort(sort.Reverse(sort.StringSlice(versionDates))) // sort in reverse order to slice the latest versions sort.Sort(sort.Reverse(sort.StringSlice(versionDates))) // sort in reverse order to slice the latest versions
versionDates = versionDates[:c.cacheWindowSize] versionDates = versionDates[:c.cacheWindowSize]
@ -121,7 +117,7 @@ func (c Client) findMinVersion(ctx context.Context, attesation variant.Variant,
for _, date := range versionDates { for _, date := range versionDates {
obj, err := client.Fetch(ctx, c.s3Client, reportedSEVSNPVersionAPI{Version: date + ".json", variant: attesation}) obj, err := client.Fetch(ctx, c.s3Client, reportedSEVSNPVersionAPI{Version: date + ".json", variant: attesation})
if err != nil { if err != nil {
return SEVSNPVersion{}, "", fmt.Errorf("get object: %w", err) return attestationconfigapi.SEVSNPVersion{}, "", fmt.Errorf("get object: %w", err)
} }
// Need to set this explicitly as the variant is not part of the marshalled JSON. // Need to set this explicitly as the variant is not part of the marshalled JSON.
obj.variant = attesation obj.variant = attesation
@ -144,7 +140,7 @@ func (c Client) findMinVersion(ctx context.Context, attesation variant.Variant,
} }
// isInputNewerThanOtherVersion compares all version fields and returns true if any input field is newer. // isInputNewerThanOtherVersion compares all version fields and returns true if any input field is newer.
func isInputNewerThanOtherVersion(input, other SEVSNPVersion) (bool, error) { func isInputNewerThanOtherVersion(input, other attestationconfigapi.SEVSNPVersion) (bool, error) {
if input == other { if input == other {
return false, nil return false, nil
} }
@ -167,7 +163,7 @@ func isInputNewerThanOtherVersion(input, other SEVSNPVersion) (bool, error) {
type reportedSEVSNPVersionAPI struct { type reportedSEVSNPVersionAPI struct {
Version string `json:"-"` Version string `json:"-"`
variant variant.Variant `json:"-"` variant variant.Variant `json:"-"`
SEVSNPVersion attestationconfigapi.SEVSNPVersion
} }
// JSONPath returns the path to the JSON file for the request to the config api. // JSONPath returns the path to the JSON file for the request to the config api.

View File

@ -2,17 +2,18 @@
Copyright (c) Edgeless Systems GmbH Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
package attestationconfigapi package client
import ( import (
"testing" "testing"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestIsInputNewerThanLatestAPI(t *testing.T) { func TestIsInputNewerThanLatestAPI(t *testing.T) {
newTestCfg := func() SEVSNPVersion { newTestCfg := func() attestationconfigapi.SEVSNPVersion {
return SEVSNPVersion{ return attestationconfigapi.SEVSNPVersion{
Microcode: 93, Microcode: 93,
TEE: 0, TEE: 0,
SNP: 6, SNP: 6,
@ -21,13 +22,13 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
latest SEVSNPVersion latest attestationconfigapi.SEVSNPVersion
input SEVSNPVersion input attestationconfigapi.SEVSNPVersion
expect bool expect bool
errMsg string errMsg string
}{ }{
"input is older than latest": { "input is older than latest": {
input: func(c SEVSNPVersion) SEVSNPVersion { input: func(c attestationconfigapi.SEVSNPVersion) attestationconfigapi.SEVSNPVersion {
c.Microcode-- c.Microcode--
return c return c
}(newTestCfg()), }(newTestCfg()),
@ -36,7 +37,7 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
errMsg: "input Microcode version: 92 is older than latest API version: 93", errMsg: "input Microcode version: 92 is older than latest API version: 93",
}, },
"input has greater and smaller version field than latest": { "input has greater and smaller version field than latest": {
input: func(c SEVSNPVersion) SEVSNPVersion { input: func(c attestationconfigapi.SEVSNPVersion) attestationconfigapi.SEVSNPVersion {
c.Microcode++ c.Microcode++
c.Bootloader-- c.Bootloader--
return c return c
@ -46,7 +47,7 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
errMsg: "input Bootloader version: 1 is older than latest API version: 2", errMsg: "input Bootloader version: 1 is older than latest API version: 2",
}, },
"input is newer than latest": { "input is newer than latest": {
input: func(c SEVSNPVersion) SEVSNPVersion { input: func(c attestationconfigapi.SEVSNPVersion) attestationconfigapi.SEVSNPVersion {
c.TEE++ c.TEE++
return c return c
}(newTestCfg()), }(newTestCfg()),

View File

@ -16,6 +16,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3"
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli/client"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
@ -62,7 +63,7 @@ func runDelete(cmd *cobra.Command, args []string) (retErr error) {
Region: deleteCfg.region, Region: deleteCfg.region,
DistributionID: deleteCfg.distribution, DistributionID: deleteCfg.distribution,
} }
client, clientClose, err := attestationconfigapi.NewClient(cmd.Context(), cfg, client, clientClose, err := client.New(cmd.Context(), cfg,
[]byte(cosignPwd), []byte(privateKey), false, 1, log) []byte(cosignPwd), []byte(privateKey), false, 1, log)
if err != nil { if err != nil {
return fmt.Errorf("create attestation client: %w", err) return fmt.Errorf("create attestation client: %w", err)
@ -170,7 +171,7 @@ func newDeleteConfig(cmd *cobra.Command, args [3]string) (deleteConfig, error) {
}, nil }, nil
} }
func deleteEntry(ctx context.Context, attvar variant.Variant, client *attestationconfigapi.Client, cfg deleteConfig) error { func deleteEntry(ctx context.Context, attvar variant.Variant, client *client.Client, cfg deleteConfig) error {
if cfg.kind != snpReport { if cfg.kind != snpReport {
return fmt.Errorf("kind %s not supported", cfg.kind) return fmt.Errorf("kind %s not supported", cfg.kind)
} }

View File

@ -14,6 +14,7 @@ import (
"time" "time"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli/client"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
@ -68,18 +69,15 @@ func runUpload(cmd *cobra.Command, args []string) (retErr error) {
return fmt.Errorf("parsing cli flags: %w", err) return fmt.Errorf("parsing cli flags: %w", err)
} }
client, clientClose, err := attestationconfigapi.NewClient( client, clientClose, err := client.New(ctx,
ctx,
staticupload.Config{ staticupload.Config{
Bucket: uploadCfg.bucket, Bucket: uploadCfg.bucket,
Region: uploadCfg.region, Region: uploadCfg.region,
DistributionID: uploadCfg.distribution, DistributionID: uploadCfg.distribution,
}, },
[]byte(cosignPwd), []byte(cosignPwd), []byte(privateKey),
[]byte(privateKey), false, uploadCfg.cacheWindowSize, log,
false, )
uploadCfg.cacheWindowSize,
log)
defer func() { defer func() {
err := clientClose(cmd.Context()) err := clientClose(cmd.Context())
@ -109,7 +107,7 @@ func runUpload(cmd *cobra.Command, args []string) (retErr error) {
func uploadReport(ctx context.Context, func uploadReport(ctx context.Context,
attestation variant.Variant, attestation variant.Variant,
client *attestationconfigapi.Client, apiClient *client.Client,
cfg uploadConfig, cfg uploadConfig,
fs file.Handler, fs file.Handler,
log *slog.Logger, log *slog.Logger,
@ -137,8 +135,8 @@ func uploadReport(ctx context.Context,
} }
latestAPIVersion := latestAPIVersionAPI.SEVSNPVersion latestAPIVersion := latestAPIVersionAPI.SEVSNPVersion
if err := client.UploadSEVSNPVersionLatest(ctx, attestation, inputVersion, latestAPIVersion, cfg.uploadDate, cfg.force); err != nil { if err := apiClient.UploadSEVSNPVersionLatest(ctx, attestation, inputVersion, latestAPIVersion, cfg.uploadDate, cfg.force); err != nil {
if errors.Is(err, attestationconfigapi.ErrNoNewerVersion) { if errors.Is(err, client.ErrNoNewerVersion) {
log.Info(fmt.Sprintf("Input version: %+v is not newer than latest API version: %+v", inputVersion, latestAPIVersion)) log.Info(fmt.Sprintf("Input version: %+v is not newer than latest API version: %+v", inputVersion, latestAPIVersion))
return nil return nil
} }
@ -178,7 +176,7 @@ func newConfig(cmd *cobra.Command, args [3]string) (uploadConfig, error) {
} }
uploadDate := time.Now() uploadDate := time.Now()
if dateStr != "" { if dateStr != "" {
uploadDate, err = time.Parse(attestationconfigapi.VersionFormat, dateStr) uploadDate, err = time.Parse(client.VersionFormat, dateStr)
if err != nil { if err != nil {
return uploadConfig{}, fmt.Errorf("parsing date: %w", err) return uploadConfig{}, fmt.Errorf("parsing date: %w", err)
} }

View File

@ -39,13 +39,14 @@ const (
// unknown is the default objectKind and does nothing. // unknown is the default objectKind and does nothing.
unknown objectKind = "unknown-kind" unknown objectKind = "unknown-kind"
snpReport objectKind = "snp-report" snpReport objectKind = "snp-report"
tdxReport objectKind = "tdx-report"
guestFirmware objectKind = "guest-firmware" guestFirmware objectKind = "guest-firmware"
) )
func kindFromString(s string) objectKind { func kindFromString(s string) objectKind {
lower := strings.ToLower(s) lower := strings.ToLower(s)
switch objectKind(lower) { switch objectKind(lower) {
case snpReport, guestFirmware: case snpReport, guestFirmware, tdxReport:
return objectKind(lower) return objectKind(lower)
default: default:
return unknown return unknown

View File

@ -74,7 +74,7 @@ func (f *fetcher) FetchSEVSNPVersionList(ctx context.Context, list SEVSNPVersion
} }
// Need to set this explicitly as the variant is not part of the marshalled JSON. // Need to set this explicitly as the variant is not part of the marshalled JSON.
fetchedList.variant = list.variant fetchedList.Variant = list.Variant
return fetchedList, nil return fetchedList, nil
} }
@ -94,13 +94,13 @@ func (f *fetcher) FetchSEVSNPVersion(ctx context.Context, version SEVSNPVersionA
// FetchSEVSNPVersionLatest returns the latest versions of the given type. // FetchSEVSNPVersionLatest returns the latest versions of the given type.
func (f *fetcher) FetchSEVSNPVersionLatest(ctx context.Context, attesation variant.Variant) (res SEVSNPVersionAPI, err error) { func (f *fetcher) FetchSEVSNPVersionLatest(ctx context.Context, attesation variant.Variant) (res SEVSNPVersionAPI, err error) {
list, err := f.FetchSEVSNPVersionList(ctx, SEVSNPVersionList{variant: attesation}) list, err := f.FetchSEVSNPVersionList(ctx, SEVSNPVersionList{Variant: attesation})
if err != nil { if err != nil {
return res, ErrNoVersionsFound return res, ErrNoVersionsFound
} }
getVersionRequest := SEVSNPVersionAPI{ getVersionRequest := SEVSNPVersionAPI{
Version: list.List()[0], // latest version is first in list Version: list.List[0], // latest version is first in list
Variant: attesation, Variant: attesation,
} }
res, err = f.FetchSEVSNPVersion(ctx, getVersionRequest) res, err = f.FetchSEVSNPVersion(ctx, getVersionRequest)

View File

@ -64,26 +64,23 @@ func (i SEVSNPVersionAPI) Validate() error {
// Once we switch to v2 of the API we could embed the variant in the object and remove some code from fetcher & client. // Once we switch to v2 of the API we could embed the variant in the object and remove some code from fetcher & client.
// That would remove the possibility of some fetcher/client code forgetting to set the variant. // That would remove the possibility of some fetcher/client code forgetting to set the variant.
type SEVSNPVersionList struct { type SEVSNPVersionList struct {
variant variant.Variant Variant variant.Variant
list []string List []string
} }
// MarshalJSON marshals the i's list property to JSON. // MarshalJSON marshals the i's list property to JSON.
func (i SEVSNPVersionList) MarshalJSON() ([]byte, error) { func (i SEVSNPVersionList) MarshalJSON() ([]byte, error) {
return json.Marshal(i.list) return json.Marshal(i.List)
} }
// UnmarshalJSON unmarshals a list of strings into i's list property. // UnmarshalJSON unmarshals a list of strings into i's list property.
func (i *SEVSNPVersionList) UnmarshalJSON(data []byte) error { func (i *SEVSNPVersionList) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &i.list) return json.Unmarshal(data, &i.List)
} }
// List returns i's list property.
func (i SEVSNPVersionList) List() []string { return i.list }
// JSONPath returns the path to the JSON file for the request to the config api. // JSONPath returns the path to the JSON file for the request to the config api.
func (i SEVSNPVersionList) JSONPath() string { func (i SEVSNPVersionList) JSONPath() string {
return path.Join(AttestationURLPath, i.variant.String(), "list") return path.Join(AttestationURLPath, i.Variant.String(), "list")
} }
// ValidateRequest is a NoOp as there is no input. // ValidateRequest is a NoOp as there is no input.
@ -93,20 +90,20 @@ func (i SEVSNPVersionList) ValidateRequest() error {
// SortReverse sorts the list of versions in reverse order. // SortReverse sorts the list of versions in reverse order.
func (i *SEVSNPVersionList) SortReverse() { func (i *SEVSNPVersionList) SortReverse() {
sort.Sort(sort.Reverse(sort.StringSlice(i.list))) sort.Sort(sort.Reverse(sort.StringSlice(i.List)))
} }
// addVersion adds new to i's list and sorts the element in descending order. // AddVersion adds new to i's list and sorts the element in descending order.
func (i *SEVSNPVersionList) addVersion(new string) { func (i *SEVSNPVersionList) AddVersion(new string) {
i.list = append(i.list, new) i.List = append(i.List, new)
i.list = variant.RemoveDuplicate(i.list) i.List = variant.RemoveDuplicate(i.List)
i.SortReverse() i.SortReverse()
} }
// Validate validates the response. // Validate validates the response.
func (i SEVSNPVersionList) Validate() error { func (i SEVSNPVersionList) Validate() error {
if len(i.list) < 1 { if len(i.List) < 1 {
return fmt.Errorf("no versions found in /list") return fmt.Errorf("no versions found in /list")
} }
return nil return nil

View File

@ -21,16 +21,16 @@ func TestSEVSNPVersionListMarshalUnmarshalJSON(t *testing.T) {
wantDiff bool wantDiff bool
}{ }{
"success": { "success": {
input: SEVSNPVersionList{list: []string{"v1", "v2"}}, input: SEVSNPVersionList{List: []string{"v1", "v2"}},
output: SEVSNPVersionList{list: []string{"v1", "v2"}}, output: SEVSNPVersionList{List: []string{"v1", "v2"}},
}, },
"variant is lost": { "variant is lost": {
input: SEVSNPVersionList{list: []string{"v1", "v2"}, variant: variant.AzureSEVSNP{}}, input: SEVSNPVersionList{List: []string{"v1", "v2"}, Variant: variant.AzureSEVSNP{}},
output: SEVSNPVersionList{list: []string{"v1", "v2"}}, output: SEVSNPVersionList{List: []string{"v1", "v2"}},
}, },
"wrong order": { "wrong order": {
input: SEVSNPVersionList{list: []string{"v1", "v2"}}, input: SEVSNPVersionList{List: []string{"v1", "v2"}},
output: SEVSNPVersionList{list: []string{"v2", "v1"}}, output: SEVSNPVersionList{List: []string{"v2", "v1"}},
wantDiff: true, wantDiff: true,
}, },
} }
@ -68,10 +68,10 @@ func TestSEVSNPVersionListAddVersion(t *testing.T) {
for name, tc := range tests { for name, tc := range tests {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
v := SEVSNPVersionList{list: tc.versions} v := SEVSNPVersionList{List: tc.versions}
v.addVersion(tc.new) v.AddVersion(tc.new)
assert.Equal(t, tc.expected, v.list) assert.Equal(t, tc.expected, v.List)
}) })
} }
} }