mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-12-15 08:05:19 -05:00
Move upload/delete code to its own package
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
parent
5654e76f7e
commit
52a65c20ac
13 changed files with 129 additions and 100 deletions
35
internal/api/attestationconfigapi/cli/client/BUILD.bazel
Normal file
35
internal/api/attestationconfigapi/cli/client/BUILD.bazel
Normal 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",
|
||||
],
|
||||
)
|
||||
190
internal/api/attestationconfigapi/cli/client/client.go
Normal file
190
internal/api/attestationconfigapi/cli/client/client.go
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/*
|
||||
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 (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
||||
apiclient "github.com/edgelesssys/constellation/v2/internal/api/client"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/sigstore"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/staticupload"
|
||||
)
|
||||
|
||||
// VersionFormat is the format of the version name in the S3 bucket.
|
||||
const VersionFormat = "2006-01-02-15-04"
|
||||
|
||||
// Client manages (modifies) the version information for the attestation variants.
|
||||
type Client struct {
|
||||
s3Client *apiclient.Client
|
||||
s3ClientClose func(ctx context.Context) error
|
||||
bucketID string
|
||||
signer sigstore.Signer
|
||||
cacheWindowSize int
|
||||
}
|
||||
|
||||
// New returns a new Client.
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create s3 storage: %w", err)
|
||||
}
|
||||
|
||||
repo := &Client{
|
||||
s3Client: s3Client,
|
||||
s3ClientClose: clientClose,
|
||||
signer: sigstore.NewSigner(cosignPwd, privateKey),
|
||||
bucketID: cfg.Bucket,
|
||||
cacheWindowSize: versionWindowSize,
|
||||
}
|
||||
return repo, clientClose, nil
|
||||
}
|
||||
|
||||
// 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 attestationconfigapi.SEVSNPVersion, date time.Time) error {
|
||||
versions, err := a.List(ctx, attestation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetch version list: %w", err)
|
||||
}
|
||||
ops := a.constructUploadCmd(attestation, version, versions, date)
|
||||
|
||||
return executeAllCmds(ctx, a.s3Client, ops)
|
||||
}
|
||||
|
||||
// DeleteSEVSNPVersion deletes the given version (without .json suffix) from the API.
|
||||
func (a Client) DeleteSEVSNPVersion(ctx context.Context, attestation variant.Variant, versionStr string) error {
|
||||
versions, err := a.List(ctx, attestation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetch version list: %w", err)
|
||||
}
|
||||
|
||||
ops, err := a.deleteSEVSNPVersion(versions, versionStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return executeAllCmds(ctx, a.s3Client, ops)
|
||||
}
|
||||
|
||||
// List returns the list of versions for the given attestation variant.
|
||||
func (a Client) List(ctx context.Context, attestation variant.Variant) (attestationconfigapi.SEVSNPVersionList, error) {
|
||||
if !attestation.Equal(variant.AzureSEVSNP{}) &&
|
||||
!attestation.Equal(variant.AWSSEVSNP{}) &&
|
||||
!attestation.Equal(variant.GCPSEVSNP{}) {
|
||||
return attestationconfigapi.SEVSNPVersionList{}, fmt.Errorf("unsupported attestation variant: %s", attestation)
|
||||
}
|
||||
|
||||
versions, err := apiclient.Fetch(ctx, a.s3Client, attestationconfigapi.SEVSNPVersionList{Variant: attestation})
|
||||
if err != nil {
|
||||
var notFoundErr *apiclient.NotFoundError
|
||||
if errors.As(err, ¬FoundErr) {
|
||||
return attestationconfigapi.SEVSNPVersionList{Variant: attestation}, nil
|
||||
}
|
||||
return attestationconfigapi.SEVSNPVersionList{}, err
|
||||
}
|
||||
|
||||
versions.Variant = attestation
|
||||
|
||||
return versions, nil
|
||||
}
|
||||
|
||||
func (a Client) deleteSEVSNPVersion(versions attestationconfigapi.SEVSNPVersionList, versionStr string) (ops []crudCmd, err error) {
|
||||
versionStr = versionStr + ".json"
|
||||
ops = append(ops, deleteCmd{
|
||||
apiObject: attestationconfigapi.SEVSNPVersionAPI{
|
||||
Variant: versions.Variant,
|
||||
Version: versionStr,
|
||||
},
|
||||
})
|
||||
|
||||
removedVersions, err := removeVersion(versions, versionStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ops = append(ops, putCmd{
|
||||
apiObject: removedVersions,
|
||||
signer: a.signer,
|
||||
})
|
||||
return ops, nil
|
||||
}
|
||||
|
||||
func (a Client) constructUploadCmd(attestation variant.Variant, version attestationconfigapi.SEVSNPVersion, versionNames attestationconfigapi.SEVSNPVersionList, date time.Time) []crudCmd {
|
||||
if !attestation.Equal(versionNames.Variant) {
|
||||
return nil
|
||||
}
|
||||
|
||||
dateStr := date.Format(VersionFormat) + ".json"
|
||||
var res []crudCmd
|
||||
|
||||
res = append(res, putCmd{
|
||||
apiObject: attestationconfigapi.SEVSNPVersionAPI{Version: dateStr, Variant: attestation, SEVSNPVersion: version},
|
||||
signer: a.signer,
|
||||
})
|
||||
|
||||
versionNames.AddVersion(dateStr)
|
||||
|
||||
res = append(res, putCmd{
|
||||
apiObject: versionNames,
|
||||
signer: a.signer,
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func removeVersion(list attestationconfigapi.SEVSNPVersionList, versionStr string) (removedVersions attestationconfigapi.SEVSNPVersionList, err error) {
|
||||
versions := list.List
|
||||
for i, v := range versions {
|
||||
if v == versionStr {
|
||||
if i == len(versions)-1 {
|
||||
removedVersions = attestationconfigapi.SEVSNPVersionList{List: versions[:i], Variant: list.Variant}
|
||||
} else {
|
||||
removedVersions = attestationconfigapi.SEVSNPVersionList{List: append(versions[:i], versions[i+1:]...), Variant: list.Variant}
|
||||
}
|
||||
return removedVersions, nil
|
||||
}
|
||||
}
|
||||
return attestationconfigapi.SEVSNPVersionList{}, fmt.Errorf("version %s not found in list %v", versionStr, versions)
|
||||
}
|
||||
|
||||
type crudCmd interface {
|
||||
Execute(ctx context.Context, c *apiclient.Client) error
|
||||
}
|
||||
|
||||
type deleteCmd struct {
|
||||
apiObject apiclient.APIObject
|
||||
}
|
||||
|
||||
func (d deleteCmd) Execute(ctx context.Context, c *apiclient.Client) error {
|
||||
return apiclient.DeleteWithSignature(ctx, c, d.apiObject)
|
||||
}
|
||||
|
||||
type putCmd struct {
|
||||
apiObject apiclient.APIObject
|
||||
signer sigstore.Signer
|
||||
}
|
||||
|
||||
func (p putCmd) Execute(ctx context.Context, c *apiclient.Client) error {
|
||||
return apiclient.SignAndUpdate(ctx, c, p.apiObject, p.signer)
|
||||
}
|
||||
|
||||
func executeAllCmds(ctx context.Context, client *apiclient.Client, cmds []crudCmd) error {
|
||||
for _, cmd := range cmds {
|
||||
if err := cmd.Execute(ctx, client); err != nil {
|
||||
return fmt.Errorf("execute operation %+v: %w", cmd, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
66
internal/api/attestationconfigapi/cli/client/client_test.go
Normal file
66
internal/api/attestationconfigapi/cli/client/client_test.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUploadAzureSEVSNP(t *testing.T) {
|
||||
sut := Client{
|
||||
bucketID: "bucket",
|
||||
signer: fakeSigner{},
|
||||
}
|
||||
version := attestationconfigapi.SEVSNPVersion{}
|
||||
date := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC)
|
||||
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"
|
||||
assert := assert.New(t)
|
||||
assert.Contains(ops, putCmd{
|
||||
apiObject: attestationconfigapi.SEVSNPVersionAPI{
|
||||
Variant: variant.AzureSEVSNP{},
|
||||
Version: dateStr,
|
||||
SEVSNPVersion: version,
|
||||
},
|
||||
signer: fakeSigner{},
|
||||
})
|
||||
assert.Contains(ops, putCmd{
|
||||
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{},
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteAzureSEVSNPVersions(t *testing.T) {
|
||||
sut := Client{
|
||||
bucketID: "bucket",
|
||||
}
|
||||
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")
|
||||
|
||||
assert := assert.New(t)
|
||||
assert.NoError(err)
|
||||
assert.Contains(ops, deleteCmd{
|
||||
apiObject: attestationconfigapi.SEVSNPVersionAPI{
|
||||
Version: "2021-01-01.json",
|
||||
},
|
||||
})
|
||||
|
||||
assert.Contains(ops, putCmd{
|
||||
apiObject: attestationconfigapi.SEVSNPVersionList{List: []string{"2023-01-01.json", "2019-01-01.json"}},
|
||||
})
|
||||
}
|
||||
|
||||
type fakeSigner struct{}
|
||||
|
||||
func (fakeSigner) Sign(_ []byte) ([]byte, error) {
|
||||
return []byte("signature"), nil
|
||||
}
|
||||
185
internal/api/attestationconfigapi/cli/client/reporter.go
Normal file
185
internal/api/attestationconfigapi/cli/client/reporter.go
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"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/attestation/variant"
|
||||
)
|
||||
|
||||
// cachedVersionsSubDir is the subdirectory in the bucket where the cached versions are stored.
|
||||
const cachedVersionsSubDir = "cached-versions"
|
||||
|
||||
// ErrNoNewerVersion is returned if the input version is not newer than the latest API version.
|
||||
var ErrNoNewerVersion = errors.New("input version is not newer than latest API version")
|
||||
|
||||
func reportVersionDir(attestation variant.Variant) string {
|
||||
return path.Join(attestationconfigapi.AttestationURLPath, attestation.String(), cachedVersionsSubDir)
|
||||
}
|
||||
|
||||
// UploadSEVSNPVersionLatest saves the given version to the cache, determines the smallest
|
||||
// TCB version in the cache among the last cacheWindowSize versions and updates
|
||||
// the latest version in the API if there is an update.
|
||||
// force can be used to bypass the validation logic against the cached versions.
|
||||
func (c Client) UploadSEVSNPVersionLatest(ctx context.Context, attestation variant.Variant, inputVersion,
|
||||
latestAPIVersion attestationconfigapi.SEVSNPVersion, now time.Time, force bool,
|
||||
) error {
|
||||
if err := c.cacheSEVSNPVersion(ctx, attestation, inputVersion, now); err != nil {
|
||||
return fmt.Errorf("reporting version: %w", err)
|
||||
}
|
||||
if force {
|
||||
return c.uploadSEVSNPVersion(ctx, attestation, inputVersion, now)
|
||||
}
|
||||
versionDates, err := c.listCachedVersions(ctx, attestation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("list reported versions: %w", err)
|
||||
}
|
||||
if len(versionDates) < c.cacheWindowSize {
|
||||
c.s3Client.Logger.Warn(fmt.Sprintf("Skipping version update, found %d, expected %d reported versions.", len(versionDates), c.cacheWindowSize))
|
||||
return nil
|
||||
}
|
||||
minVersion, minDate, err := c.findMinVersion(ctx, attestation, versionDates)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get minimal version: %w", err)
|
||||
}
|
||||
c.s3Client.Logger.Info(fmt.Sprintf("Found minimal version: %+v with date: %s", minVersion, minDate))
|
||||
shouldUpdateAPI, err := isInputNewerThanOtherVersion(minVersion, latestAPIVersion)
|
||||
if err != nil {
|
||||
return ErrNoNewerVersion
|
||||
}
|
||||
if !shouldUpdateAPI {
|
||||
c.s3Client.Logger.Info(fmt.Sprintf("Input version: %+v is not newer than latest API version: %+v", minVersion, latestAPIVersion))
|
||||
return nil
|
||||
}
|
||||
c.s3Client.Logger.Info(fmt.Sprintf("Input version: %+v is newer than latest API version: %+v", minVersion, latestAPIVersion))
|
||||
t, err := time.Parse(VersionFormat, minDate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing date: %w", err)
|
||||
}
|
||||
if err := c.uploadSEVSNPVersion(ctx, attestation, minVersion, t); err != nil {
|
||||
return fmt.Errorf("uploading version: %w", err)
|
||||
}
|
||||
c.s3Client.Logger.Info(fmt.Sprintf("Successfully uploaded new SEV-SNP version: %+v", minVersion))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 attestationconfigapi.SEVSNPVersion, date time.Time) error {
|
||||
dateStr := date.Format(VersionFormat) + ".json"
|
||||
res := putCmd{
|
||||
apiObject: reportedSEVSNPVersionAPI{Version: dateStr, variant: attestation, SEVSNPVersion: version},
|
||||
signer: c.signer,
|
||||
}
|
||||
return res.Execute(ctx, c.s3Client)
|
||||
}
|
||||
|
||||
func (c Client) listCachedVersions(ctx context.Context, attestation variant.Variant) ([]string, error) {
|
||||
list, err := c.s3Client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
||||
Bucket: aws.String(c.bucketID),
|
||||
Prefix: aws.String(reportVersionDir(attestation)),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list objects: %w", err)
|
||||
}
|
||||
var dates []string
|
||||
for _, obj := range list.Contents {
|
||||
fileName := path.Base(*obj.Key)
|
||||
if strings.HasSuffix(fileName, ".json") {
|
||||
dates = append(dates, fileName[:len(fileName)-5])
|
||||
}
|
||||
}
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
// 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) (attestationconfigapi.SEVSNPVersion, string, error) {
|
||||
var minimalVersion *attestationconfigapi.SEVSNPVersion
|
||||
var minimalDate string
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(versionDates))) // sort in reverse order to slice the latest versions
|
||||
versionDates = versionDates[:c.cacheWindowSize]
|
||||
sort.Strings(versionDates) // sort with oldest first to to take the minimal version with the oldest date
|
||||
for _, date := range versionDates {
|
||||
obj, err := client.Fetch(ctx, c.s3Client, reportedSEVSNPVersionAPI{Version: date + ".json", variant: attesation})
|
||||
if err != nil {
|
||||
return attestationconfigapi.SEVSNPVersion{}, "", fmt.Errorf("get object: %w", err)
|
||||
}
|
||||
// Need to set this explicitly as the variant is not part of the marshalled JSON.
|
||||
obj.variant = attesation
|
||||
|
||||
if minimalVersion == nil {
|
||||
minimalVersion = &obj.SEVSNPVersion
|
||||
minimalDate = date
|
||||
} else {
|
||||
shouldUpdateMinimal, err := isInputNewerThanOtherVersion(*minimalVersion, obj.SEVSNPVersion)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if shouldUpdateMinimal {
|
||||
minimalVersion = &obj.SEVSNPVersion
|
||||
minimalDate = date
|
||||
}
|
||||
}
|
||||
}
|
||||
return *minimalVersion, minimalDate, nil
|
||||
}
|
||||
|
||||
// isInputNewerThanOtherVersion compares all version fields and returns true if any input field is newer.
|
||||
func isInputNewerThanOtherVersion(input, other attestationconfigapi.SEVSNPVersion) (bool, error) {
|
||||
if input == other {
|
||||
return false, nil
|
||||
}
|
||||
if input.TEE < other.TEE {
|
||||
return false, fmt.Errorf("input TEE version: %d is older than latest API version: %d", input.TEE, other.TEE)
|
||||
}
|
||||
if input.SNP < other.SNP {
|
||||
return false, fmt.Errorf("input SNP version: %d is older than latest API version: %d", input.SNP, other.SNP)
|
||||
}
|
||||
if input.Microcode < other.Microcode {
|
||||
return false, fmt.Errorf("input Microcode version: %d is older than latest API version: %d", input.Microcode, other.Microcode)
|
||||
}
|
||||
if input.Bootloader < other.Bootloader {
|
||||
return false, fmt.Errorf("input Bootloader version: %d is older than latest API version: %d", input.Bootloader, other.Bootloader)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// reportedSEVSNPVersionAPI is the request to get the version information of the specific version in the config api.
|
||||
type reportedSEVSNPVersionAPI struct {
|
||||
Version string `json:"-"`
|
||||
variant variant.Variant `json:"-"`
|
||||
attestationconfigapi.SEVSNPVersion
|
||||
}
|
||||
|
||||
// JSONPath returns the path to the JSON file for the request to the config api.
|
||||
func (i reportedSEVSNPVersionAPI) JSONPath() string {
|
||||
return path.Join(reportVersionDir(i.variant), i.Version)
|
||||
}
|
||||
|
||||
// ValidateRequest validates the request.
|
||||
func (i reportedSEVSNPVersionAPI) ValidateRequest() error {
|
||||
if !strings.HasSuffix(i.Version, ".json") {
|
||||
return fmt.Errorf("version has no .json suffix")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate is a No-Op at the moment.
|
||||
func (i reportedSEVSNPVersionAPI) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||
newTestCfg := func() attestationconfigapi.SEVSNPVersion {
|
||||
return attestationconfigapi.SEVSNPVersion{
|
||||
Microcode: 93,
|
||||
TEE: 0,
|
||||
SNP: 6,
|
||||
Bootloader: 2,
|
||||
}
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
latest attestationconfigapi.SEVSNPVersion
|
||||
input attestationconfigapi.SEVSNPVersion
|
||||
expect bool
|
||||
errMsg string
|
||||
}{
|
||||
"input is older than latest": {
|
||||
input: func(c attestationconfigapi.SEVSNPVersion) attestationconfigapi.SEVSNPVersion {
|
||||
c.Microcode--
|
||||
return c
|
||||
}(newTestCfg()),
|
||||
latest: newTestCfg(),
|
||||
expect: false,
|
||||
errMsg: "input Microcode version: 92 is older than latest API version: 93",
|
||||
},
|
||||
"input has greater and smaller version field than latest": {
|
||||
input: func(c attestationconfigapi.SEVSNPVersion) attestationconfigapi.SEVSNPVersion {
|
||||
c.Microcode++
|
||||
c.Bootloader--
|
||||
return c
|
||||
}(newTestCfg()),
|
||||
latest: newTestCfg(),
|
||||
expect: false,
|
||||
errMsg: "input Bootloader version: 1 is older than latest API version: 2",
|
||||
},
|
||||
"input is newer than latest": {
|
||||
input: func(c attestationconfigapi.SEVSNPVersion) attestationconfigapi.SEVSNPVersion {
|
||||
c.TEE++
|
||||
return c
|
||||
}(newTestCfg()),
|
||||
latest: newTestCfg(),
|
||||
expect: true,
|
||||
},
|
||||
"input is equal to latest": {
|
||||
input: newTestCfg(),
|
||||
latest: newTestCfg(),
|
||||
expect: false,
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
isNewer, err := isInputNewerThanOtherVersion(tc.input, tc.latest)
|
||||
assert := assert.New(t)
|
||||
if tc.errMsg != "" {
|
||||
assert.EqualError(err, tc.errMsg)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.expect, isNewer)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue