From c302658d2758536c7d7f57605e45001b4f7fd866 Mon Sep 17 00:00:00 2001 From: Adrian Stobbe Date: Mon, 18 Sep 2023 16:58:52 +0200 Subject: [PATCH] fix e2e test --- .../attestationconfigapi/cli/e2e/test.sh.in | 48 +++++++++++++------ internal/api/attestationconfigapi/cli/main.go | 4 +- internal/api/attestationconfigapi/reporter.go | 43 +++++++++-------- .../api/attestationconfigapi/reporter_test.go | 16 ++++++- internal/staticupload/staticupload.go | 1 - 5 files changed, 75 insertions(+), 37 deletions(-) diff --git a/internal/api/attestationconfigapi/cli/e2e/test.sh.in b/internal/api/attestationconfigapi/cli/e2e/test.sh.in index d132780fef5..f5a2bcd17b2 100755 --- a/internal/api/attestationconfigapi/cli/e2e/test.sh.in +++ b/internal/api/attestationconfigapi/cli/e2e/test.sh.in @@ -28,29 +28,49 @@ tmpdir=$(mktemp -d) readonly tmpdir registerExitHandler "rm -rf $tmpdir" +# the high version numbers ensure that it's newer than the current latest value readonly claim_path="$tmpdir/maaClaim.json" cat << EOF > "$claim_path" { "x-ms-isolation-tee": { - "x-ms-sevsnpvm-tee-svn": 1, - "x-ms-sevsnpvm-snpfw-svn": 9, - "x-ms-sevsnpvm-microcode-svn": 116, - "x-ms-sevsnpvm-bootloader-svn": 4 + "x-ms-sevsnpvm-tee-svn": 255, + "x-ms-sevsnpvm-snpfw-svn": 255, + "x-ms-sevsnpvm-microcode-svn": 255, + "x-ms-sevsnpvm-bootloader-svn": 255 } } EOF -readonly date="2023-02-02-03-04" +# has an older version +readonly older_claim_path="$tmpdir/maaClaimOld.json" +cat << EOF > "$older_claim_path" +{ + "x-ms-isolation-tee": { + "x-ms-sevsnpvm-tee-svn": 255, + "x-ms-sevsnpvm-snpfw-svn": 255, + "x-ms-sevsnpvm-microcode-svn": 254, + "x-ms-sevsnpvm-bootloader-svn": 255 + } +} +EOF + +# report 3 versions with different dates to fill the reporter cache +readonly date_yet_older="2023-02-01-03-04" +${configapi_cli} --maa-claims-path "$older_claim_path" --upload-date "$date_yet_older" --region "$region" --bucket "$bucket" --distribution "$distribution" +readonly date_older="2023-02-02-03-04" +${configapi_cli} --maa-claims-path "$older_claim_path" --upload-date "$date_older" --region "$region" --bucket "$bucket" --distribution "$distribution" +readonly date="2023-02-03-03-04" ${configapi_cli} --maa-claims-path "$claim_path" --upload-date "$date" --region "$region" --bucket "$bucket" --distribution "$distribution" +# expect that the older version was expected as new latest version baseurl="https://d33dzgxuwsgbpw.cloudfront.net/constellation/v1/attestation/azure-sev-snp" -if ! curl -fsSL ${baseurl}/${date}.json > /dev/null; then - echo "Checking for uploaded version file constellation/v1/attestation/azure-sev-snp/${date}.json: request returned ${?}" +if ! curl -fsSL ${baseurl}/${date_yet_older}.json > /dev/null; then + echo "Checking for uploaded version file constellation/v1/attestation/azure-sev-snp/${date_yet_older}.json: request returned ${?}" exit 1 fi -if ! curl -fsSL ${baseurl}/${date}.json.sig > /dev/null; then - echo "Checking for uploaded version signature file constellation/v1/attestation/azure-sev-snp/${date}.json.sig: request returned ${?}" +if ! curl -fsSL ${baseurl}/${date_yet_older}.json.sig > /dev/null; then + echo "Checking for uploaded version signature file constellation/v1/attestation/azure-sev-snp/${date_yet_older}.json.sig: request returned ${?}" exit 1 fi @@ -58,17 +78,17 @@ if ! curl -fsSL ${baseurl}/list > /dev/null; then echo "Checking for uploaded list file constellation/v1/attestation/azure-sev-snp/list: request returned ${?}" exit 1 fi -${configapi_cli} delete --version "$date" --region "$region" --bucket "$bucket" --distribution "$distribution" +${configapi_cli} delete --version "$date_yet_older" --region "$region" --bucket "$bucket" --distribution "$distribution" # Omit -f to check for 404. We want to check that a file was deleted, therefore we expect the query to fail. -http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null ${baseurl}/${date}.json) +http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null ${baseurl}/${date_yet_older}.json) if [[ $http_code -ne 404 ]]; then - echo "Expected HTTP code 404 for: constellation/v1/attestation/azure-sev-snp/${date}.json, but got ${http_code}" + echo "Expected HTTP code 404 for: constellation/v1/attestation/azure-sev-snp/${date_yet_older}.json, but got ${http_code}" exit 1 fi # Omit -f to check for 404. We want to check that a file was deleted, therefore we expect the query to fail. -http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null ${baseurl}/${date}.json.sig) +http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null ${baseurl}/${date_yet_older}.json.sig) if [[ $http_code -ne 404 ]]; then - echo "Expected HTTP code 404 for: constellation/v1/attestation/azure-sev-snp/${date}.json, but got ${http_code}" + echo "Expected HTTP code 404 for: constellation/v1/attestation/azure-sev-snp/${date_yet_older}.json, but got ${http_code}" exit 1 fi diff --git a/internal/api/attestationconfigapi/cli/main.go b/internal/api/attestationconfigapi/cli/main.go index 6bb96e8553b..5487c05cb45 100644 --- a/internal/api/attestationconfigapi/cli/main.go +++ b/internal/api/attestationconfigapi/cli/main.go @@ -15,6 +15,8 @@ Notice that there is no synchronization on API operations. // TODO(elchead): wha */ package main +// TODO: separate reporter and upload CLI to ease manual upload or use force flag? + import ( "encoding/json" "errors" @@ -133,7 +135,7 @@ func runCmd(cmd *cobra.Command, _ []string) (retErr error) { if err := reporter.ReportAzureSEVSNPVersion(ctx, inputVersion, flags.uploadDate); err != nil { return fmt.Errorf("reporting version: %w", err) } - if err := reporter.UpdateLatestVersion(ctx, latestAPIVersion); err != nil { + if err := reporter.UpdateLatestVersion(ctx, latestAPIVersion, flags.uploadDate); err != nil { return fmt.Errorf("updating latest version: %w", err) } diff --git a/internal/api/attestationconfigapi/reporter.go b/internal/api/attestationconfigapi/reporter.go index 7afd6be087e..28931b0d265 100644 --- a/internal/api/attestationconfigapi/reporter.go +++ b/internal/api/attestationconfigapi/reporter.go @@ -22,6 +22,7 @@ import ( ) // cachedVersionsSubDir is the subdirectory in the bucket where the cached versions are stored. +// TODO(elchead): store in a different directory so that it is not mirrored to the CDN? const cachedVersionsSubDir = "cached-versions" // timeFrameForCachedVersions defines the time frame for reported versions which are considered to define the latest version. @@ -33,6 +34,7 @@ var reportVersionDir = path.Join(attestationURLPath, variant.AzureSEVSNP{}.Strin type Reporter struct { // Client is the client to the config api. *Client + // s3client: but no cache invalidation for upload -> new client } // ReportAzureSEVSNPVersion uploads the latest observed version numbers of the Azure SEVSNP. This version is used to later report the latest version numbers to the API. @@ -45,7 +47,7 @@ func (r Reporter) ReportAzureSEVSNPVersion(ctx context.Context, version AzureSEV return res.Execute(ctx, r.s3Client) } -func (r Reporter) listReportedVersions(ctx context.Context, timeFrame time.Duration) ([]string, error) { +func (r Reporter) listReportedVersions(ctx context.Context, timeFrame time.Duration, now time.Time) ([]string, error) { list, err := r.s3Client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{ Bucket: aws.String(r.bucketID), Prefix: aws.String(reportVersionDir), @@ -60,18 +62,18 @@ func (r Reporter) listReportedVersions(ctx context.Context, timeFrame time.Durat dates = append(dates, fileName[:len(fileName)-5]) } } - return filterDatesWithinTime(dates, time.Now(), timeFrame), nil + return filterDatesWithinTime(dates, now, timeFrame), nil } // UpdateLatestVersion checks the reported version values // and updates the latest version of the Azure SEVSNP in the API if there is an update . -func (r Reporter) UpdateLatestVersion(ctx context.Context, latestAPIVersion AzureSEVSNPVersion) error { +func (r Reporter) UpdateLatestVersion(ctx context.Context, latestAPIVersion AzureSEVSNPVersion, now time.Time) error { // get the reported version values of the last 3 weeks - versionDates, err := r.listReportedVersions(ctx, timeFrameForCachedVersions) + versionDates, err := r.listReportedVersions(ctx, timeFrameForCachedVersions, now) if err != nil { return fmt.Errorf("list reported versions: %w", err) } - r.s3Client.Logger.Infof("Found %d reported versions in the last %s", len(versionDates), timeFrameForCachedVersions.String()) + r.s3Client.Logger.Infof("Found %v reported versions in the last %s since %s", versionDates, timeFrameForCachedVersions.String(), now.String()) if len(versionDates) < 3 { r.s3Client.Logger.Infof("Skipping version update since only %s out of 3 expected versions are found in the cache within the last %d days", len(versionDates), int(timeFrameForCachedVersions.Hours()/24)) @@ -83,7 +85,7 @@ func (r Reporter) UpdateLatestVersion(ctx context.Context, latestAPIVersion Azur return fmt.Errorf("get minimal version: %w", err) } r.s3Client.Logger.Infof("Found minimal version: %+v with date: %s", *minVersion, minDate) - shouldUpdateAPI, err := isInputNewerThanLatestAPI(*minVersion, latestAPIVersion) + shouldUpdateAPI, err := isInputNewerThanOtherVersion(*minVersion, latestAPIVersion) if err == nil && shouldUpdateAPI { r.s3Client.Logger.Infof("Input version: %+v is newer than latest API version: %+v", *minVersion, latestAPIVersion) // upload minVersion to the API @@ -115,9 +117,9 @@ func (r Reporter) findMinVersion(ctx context.Context, versionDates []string) (*A minimalVersion = &obj.AzureSEVSNPVersion minimalDate = date } else { - shouldUpdateMinimal, err := isInputNewerThanLatestAPI(*minimalVersion, obj.AzureSEVSNPVersion) + shouldUpdateMinimal, err := isInputNewerThanOtherVersion(*minimalVersion, obj.AzureSEVSNPVersion) if err != nil { - return nil, "", fmt.Errorf("comparing versions: %w", err) + continue } if shouldUpdateMinimal { minimalVersion = &obj.AzureSEVSNPVersion @@ -135,29 +137,30 @@ func filterDatesWithinTime(dates []string, now time.Time, timeFrame time.Duratio if err != nil { continue } - if now.Sub(t) <= timeFrame { + fmt.Println(now, " t ", t, " sub ", now.Sub(t)) + if now.Sub(t) >= 0 && now.Sub(t) <= timeFrame { datesWithinTimeFrame = append(datesWithinTimeFrame, date) } } return datesWithinTimeFrame } -// isInputNewerThanLatestAPI compares all version fields with the latest API version and returns true if any input field is newer. -func isInputNewerThanLatestAPI(input, latest AzureSEVSNPVersion) (bool, error) { - if input == latest { +// isInputNewerThanOtherVersion compares all version fields and returns true if any input field is newer. +func isInputNewerThanOtherVersion(input, other AzureSEVSNPVersion) (bool, error) { + if input == other { return false, nil } - if input.TEE < latest.TEE { - return false, fmt.Errorf("input TEE version: %d is older than latest API version: %d", input.TEE, latest.TEE) + 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 < latest.SNP { - return false, fmt.Errorf("input SNP version: %d is older than latest API version: %d", input.SNP, latest.SNP) + 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 < latest.Microcode { - return false, fmt.Errorf("input Microcode version: %d is older than latest API version: %d", input.Microcode, latest.Microcode) + 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 < latest.Bootloader { - return false, fmt.Errorf("input Bootloader version: %d is older than latest API version: %d", input.Bootloader, latest.Bootloader) + 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 } diff --git a/internal/api/attestationconfigapi/reporter_test.go b/internal/api/attestationconfigapi/reporter_test.go index 2deab7bc549..17792dac8a0 100644 --- a/internal/api/attestationconfigapi/reporter_test.go +++ b/internal/api/attestationconfigapi/reporter_test.go @@ -26,11 +26,17 @@ func TestFilterDatesWithinTime(t *testing.T) { testCases := map[string]struct { timeFrame time.Duration expectedDates []string + customDates *[]string }{ "all dates within 3 days": { timeFrame: time.Hour * 24 * 3, expectedDates: []string{"2022-01-06-00-00", "2022-01-07-00-00", "2022-01-08-00-00"}, }, + "ignore dates newer than now": { + timeFrame: time.Hour * 24 * 3, + customDates: toPtr(append(dates, "2023-01-09-00-00")), + expectedDates: []string{"2022-01-06-00-00", "2022-01-07-00-00", "2022-01-08-00-00"}, + }, "no dates within time frame": { timeFrame: time.Hour, expectedDates: nil, @@ -43,12 +49,20 @@ func TestFilterDatesWithinTime(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { + dates := dates + if tc.customDates != nil { + dates = *tc.customDates + } filteredDates := filterDatesWithinTime(dates, now, tc.timeFrame) assert.Equal(t, tc.expectedDates, filteredDates) }) } } +func toPtr[T any](v T) *T { + return &v +} + func TestIsInputNewerThanLatestAPI(t *testing.T) { newTestCfg := func() AzureSEVSNPVersion { return AzureSEVSNPVersion{ @@ -100,7 +114,7 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) { } for name, tc := range testCases { t.Run(name, func(t *testing.T) { - isNewer, err := isInputNewerThanLatestAPI(tc.input, tc.latest) + isNewer, err := isInputNewerThanOtherVersion(tc.input, tc.latest) assert := assert.New(t) if tc.errMsg != "" { assert.EqualError(err, tc.errMsg) diff --git a/internal/staticupload/staticupload.go b/internal/staticupload/staticupload.go index 8b4e807bf5f..1872253b702 100644 --- a/internal/staticupload/staticupload.go +++ b/internal/staticupload/staticupload.go @@ -109,7 +109,6 @@ func New(ctx context.Context, config Config, log *logger.Logger) (*Client, Close } s3Client := s3.NewFromConfig(cfg) uploadClient := s3manager.NewUploader(s3Client) - cdnClient := cloudfront.NewFromConfig(cfg) client := &Client{