From a44ca578a1a9787c919ef1ce1d4872b0a0b307b8 Mon Sep 17 00:00:00 2001 From: Andreea Lupu <58118008+Andreea-Lupu@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:25:29 +0300 Subject: [PATCH] fix(tests): update imagetrust tests to use mock service (#1929) - use secretsManagerMock and secretsManagerCacheMock to avoid failing because of "already exists" error when running multiple times image_trust_test on the same localstack instance Signed-off-by: Andreea-Lupu --- THIRD-PARTY-LICENSES.md | 41 ++-- go.mod | 2 +- pkg/extensions/imagetrust/cosign.go | 6 +- pkg/extensions/imagetrust/image_trust.go | 17 ++ pkg/extensions/imagetrust/image_trust_test.go | 231 ++++++++++++------ pkg/extensions/imagetrust/notation.go | 6 +- 6 files changed, 204 insertions(+), 99 deletions(-) diff --git a/THIRD-PARTY-LICENSES.md b/THIRD-PARTY-LICENSES.md index e4704cb7..c7b16ad4 100644 --- a/THIRD-PARTY-LICENSES.md +++ b/THIRD-PARTY-LICENSES.md @@ -65,28 +65,28 @@ github.com/aquasecurity/tml|https://github.com/aquasecurity/tml/blob/v0.6.1/LICE github.com/asaskevich/govalidator|https://github.com/asaskevich/govalidator/blob/a9d515a09cc2/LICENSE|MIT github.com/aws/aws-sdk-go-v2/config|https://github.com/aws/aws-sdk-go-v2/blob/config/v1.18.44/config/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/credentials|https://github.com/aws/aws-sdk-go-v2/blob/credentials/v1.13.42/credentials/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue|https://github.com/aws/aws-sdk-go-v2/blob/feature/dynamodb/attributevalue/v1.10.41/feature/dynamodb/attributevalue/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue|https://github.com/aws/aws-sdk-go-v2/blob/feature/dynamodb/attributevalue/v1.10.42/feature/dynamodb/attributevalue/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/feature/ec2/imds|https://github.com/aws/aws-sdk-go-v2/blob/feature/ec2/imds/v1.13.12/feature/ec2/imds/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/internal/configsources|https://github.com/aws/aws-sdk-go-v2/blob/internal/configsources/v1.1.42/internal/configsources/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2|https://github.com/aws/aws-sdk-go-v2/blob/internal/endpoints/v2.4.36/internal/endpoints/v2/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/internal/configsources|https://github.com/aws/aws-sdk-go-v2/blob/internal/configsources/v1.1.43/internal/configsources/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2|https://github.com/aws/aws-sdk-go-v2/blob/internal/endpoints/v2.4.37/internal/endpoints/v2/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/internal/ini|https://github.com/aws/aws-sdk-go-v2/blob/internal/ini/v1.3.44/internal/ini/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/internal/sync/singleflight|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.1/internal/sync/singleflight/LICENSE|BSD-3-Clause -github.com/aws/aws-sdk-go-v2/service/dynamodb/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodb/v1.22.1/service/dynamodb/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/service/dynamodbstreams/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodbstreams/v1.15.6/service/dynamodbstreams/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/service/dynamodbstreams|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodbstreams/v1.15.6/service/dynamodbstreams/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/service/dynamodb|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodb/v1.22.1/service/dynamodb/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/internal/sync/singleflight|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.2/internal/sync/singleflight/LICENSE|BSD-3-Clause +github.com/aws/aws-sdk-go-v2/service/dynamodb/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodb/v1.22.2/service/dynamodb/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/service/dynamodbstreams/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodbstreams/v1.15.7/service/dynamodbstreams/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/service/dynamodbstreams|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodbstreams/v1.15.7/service/dynamodbstreams/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/service/dynamodb|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodb/v1.22.2/service/dynamodb/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/ebs|https://github.com/aws/aws-sdk-go-v2/blob/service/ebs/v1.18.1/service/ebs/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/ec2|https://github.com/aws/aws-sdk-go-v2/blob/service/ec2/v1.98.0/service/ec2/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/ecrpublic|https://github.com/aws/aws-sdk-go-v2/blob/service/ecrpublic/v1.12.0/service/ecrpublic/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/ecr|https://github.com/aws/aws-sdk-go-v2/blob/service/ecr/v1.17.18/service/ecr/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/accept-encoding/v1.9.15/service/internal/accept-encoding/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/endpoint-discovery/v1.7.36/service/internal/endpoint-discovery/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/endpoint-discovery/v1.7.37/service/internal/endpoint-discovery/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/presigned-url/v1.9.36/service/internal/presigned-url/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/service/secretsmanager|https://github.com/aws/aws-sdk-go-v2/blob/service/secretsmanager/v1.21.4/service/secretsmanager/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/service/secretsmanager|https://github.com/aws/aws-sdk-go-v2/blob/service/secretsmanager/v1.21.5/service/secretsmanager/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/ssooidc|https://github.com/aws/aws-sdk-go-v2/blob/service/ssooidc/v1.17.2/service/ssooidc/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/sso|https://github.com/aws/aws-sdk-go-v2/blob/service/sso/v1.15.1/service/sso/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/sts|https://github.com/aws/aws-sdk-go-v2/blob/service/sts/v1.23.1/service/sts/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.1/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.2/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go/internal/sync/singleflight|https://github.com/aws/aws-sdk-go/blob/v1.45.24/internal/sync/singleflight/LICENSE|BSD-3-Clause github.com/aws/aws-sdk-go|https://github.com/aws/aws-sdk-go/blob/v1.45.24/LICENSE.txt|Apache-2.0 github.com/aws/smithy-go/internal/sync/singleflight|https://github.com/aws/smithy-go/blob/v1.15.0/internal/sync/singleflight/LICENSE|BSD-3-Clause @@ -192,7 +192,7 @@ github.com/golang/protobuf|https://github.com/golang/protobuf/blob/v1.5.3/LICENS github.com/golang/snappy|https://github.com/golang/snappy/blob/v0.0.4/LICENSE|BSD-3-Clause github.com/google/btree|https://github.com/google/btree/blob/v1.1.2/LICENSE|Apache-2.0 github.com/google/certificate-transparency-go|https://github.com/google/certificate-transparency-go/blob/v1.1.6/LICENSE|Apache-2.0 -github.com/google/go-cmp/cmp|https://github.com/google/go-cmp/blob/v0.5.9/LICENSE|BSD-3-Clause +github.com/google/go-cmp/cmp|https://github.com/google/go-cmp/blob/v0.6.0/LICENSE|BSD-3-Clause github.com/google/go-querystring/query|https://github.com/google/go-querystring/blob/v1.1.0/LICENSE|BSD-3-Clause github.com/google/gofuzz|https://github.com/google/gofuzz/blob/v1.2.0/LICENSE|Apache-2.0 github.com/google/licenseclassifier/v2|https://github.com/google/licenseclassifier/blob/v2.0.0/v2/LICENSE|Apache-2.0 @@ -391,7 +391,7 @@ github.com/yashtewari/glob-intersection|https://github.com/yashtewari/glob-inter github.com/zclconf/go-cty-yaml|https://github.com/zclconf/go-cty-yaml/blob/v1.0.3/LICENSE|Apache-2.0 github.com/zclconf/go-cty/cty|https://github.com/zclconf/go-cty/blob/v1.13.0/LICENSE|MIT github.com/zeebo/errs|https://github.com/zeebo/errs/blob/v1.3.0/LICENSE|MIT -github.com/zitadel/oidc|https://github.com/zitadel/oidc/blob/v1.13.4/LICENSE|Apache-2.0 +github.com/zitadel/oidc|https://github.com/zitadel/oidc/blob/v1.13.5/LICENSE|Apache-2.0 go.etcd.io/bbolt|https://github.com/etcd-io/bbolt/blob/v1.3.7/LICENSE|MIT go.mongodb.org/mongo-driver|https://github.com/mongodb/mongo-go-driver/blob/v1.11.3/LICENSE|Apache-2.0 go.mozilla.org/pkcs7|https://github.com/mozilla-services/pkcs7/blob/33d05740a352/LICENSE|MIT @@ -412,13 +412,12 @@ golang.org/x/crypto/sha3|https://cs.opensource.google/go/x/crypto/+/v0.14.0:LICE golang.org/x/crypto|https://cs.opensource.google/go/x/crypto/+/v0.14.0:LICENSE|BSD-3-Clause golang.org/x/exp/constraints|https://cs.opensource.google/go/x/exp/+/92128663:LICENSE|BSD-3-Clause golang.org/x/exp|https://cs.opensource.google/go/x/exp/+/92128663:LICENSE|BSD-3-Clause -golang.org/x/mod/semver|https://cs.opensource.google/go/x/mod/+/v0.12.0:LICENSE|BSD-3-Clause -golang.org/x/mod|https://cs.opensource.google/go/x/mod/+/v0.12.0:LICENSE|BSD-3-Clause -golang.org/x/net/context|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause -golang.org/x/net/html|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause -golang.org/x/net/publicsuffix|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause -golang.org/x/net/webdav|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause -golang.org/x/net|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause +golang.org/x/mod/semver|https://cs.opensource.google/go/x/mod/+/v0.13.0:LICENSE|BSD-3-Clause +golang.org/x/mod|https://cs.opensource.google/go/x/mod/+/v0.13.0:LICENSE|BSD-3-Clause +golang.org/x/net/html|https://cs.opensource.google/go/x/net/+/v0.17.0:LICENSE|BSD-3-Clause +golang.org/x/net/publicsuffix|https://cs.opensource.google/go/x/net/+/v0.17.0:LICENSE|BSD-3-Clause +golang.org/x/net/webdav|https://cs.opensource.google/go/x/net/+/v0.17.0:LICENSE|BSD-3-Clause +golang.org/x/net|https://cs.opensource.google/go/x/net/+/v0.17.0:LICENSE|BSD-3-Clause golang.org/x/oauth2|https://cs.opensource.google/go/x/oauth2/+/v0.13.0:LICENSE|BSD-3-Clause golang.org/x/sync/errgroup|https://cs.opensource.google/go/x/sync/+/v0.3.0:LICENSE|BSD-3-Clause golang.org/x/sync/semaphore|https://cs.opensource.google/go/x/sync/+/v0.3.0:LICENSE|BSD-3-Clause @@ -435,7 +434,7 @@ golang.org/x/xerrors|https://cs.opensource.google/go/x/xerrors/+/04be3eba:LICENS google.golang.org/api/googleapi|https://github.com/googleapis/google-api-go-client/blob/v0.143.0/LICENSE|BSD-3-Clause google.golang.org/api/internal/third_party/uritemplates|https://github.com/googleapis/google-api-go-client/blob/v0.143.0/internal/third_party/uritemplates/LICENSE|BSD-3-Clause google.golang.org/api|https://github.com/googleapis/google-api-go-client/blob/v0.143.0/LICENSE|BSD-3-Clause -google.golang.org/appengine|https://github.com/golang/appengine/blob/v1.6.7/LICENSE|Apache-2.0 +google.golang.org/appengine|https://github.com/golang/appengine/blob/v1.6.8/LICENSE|Apache-2.0 google.golang.org/genproto/googleapis/api|https://github.com/googleapis/go-genproto/blob/007df8e322eb/googleapis/api/LICENSE|Apache-2.0 google.golang.org/genproto/googleapis/rpc/status|https://github.com/googleapis/go-genproto/blob/e6e6cdab5c13/googleapis/rpc/LICENSE|Apache-2.0 google.golang.org/genproto/googleapis/rpc|https://github.com/googleapis/go-genproto/blob/e6e6cdab5c13/googleapis/rpc/LICENSE|Apache-2.0 diff --git a/go.mod b/go.mod index df8a5407..b773fc47 100644 --- a/go.mod +++ b/go.mod @@ -306,7 +306,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.15.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.2 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.23.1 // indirect - github.com/aws/smithy-go v1.15.0 // indirect + github.com/aws/smithy-go v1.15.0 github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect diff --git a/pkg/extensions/imagetrust/cosign.go b/pkg/extensions/imagetrust/cosign.go index 41b9935c..1d584afd 100644 --- a/pkg/extensions/imagetrust/cosign.go +++ b/pkg/extensions/imagetrust/cosign.go @@ -256,11 +256,11 @@ func (cloud *PublicKeyAWSStorage) StorePublicKey(name godigest.Digest, publicKey } _, err := cloud.secretsManagerClient.CreateSecret(context.Background(), secretInputParam) - if err != nil { - return err + if err != nil && IsResourceExistsException(err) { + return nil } - return nil + return err } func validatePublicKey(publicKeyContent []byte) (bool, error) { diff --git a/pkg/extensions/imagetrust/image_trust.go b/pkg/extensions/imagetrust/image_trust.go index 974418a2..273f2d90 100644 --- a/pkg/extensions/imagetrust/image_trust.go +++ b/pkg/extensions/imagetrust/image_trust.go @@ -9,13 +9,16 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/transport/http" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" aws1 "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/aws/session" smanager "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-secretsmanager-caching-go/secretcache" + smithy "github.com/aws/smithy-go" godigest "github.com/opencontainers/go-digest" ispec "github.com/opencontainers/image-spec/specs-go/v1" @@ -140,6 +143,20 @@ func GetSecretsManagerRetrieval(region, endpoint string) *secretcache.Cache { return cache } +func IsResourceExistsException(err error) bool { + if opErr, ok := err.(*smithy.OperationError); ok { //nolint: errorlint + if resErr, ok := opErr.Err.(*http.ResponseError); ok { //nolint: errorlint + if _, ok := resErr.Err.(*types.ResourceExistsException); ok { //nolint: errorlint + return true + } + } + + return false + } + + return false +} + func (imgTrustStore *ImageTrustStore) VerifySignature( signatureType string, rawSignature []byte, sigKey string, manifestDigest godigest.Digest, manifestContent []byte, repo string, diff --git a/pkg/extensions/imagetrust/image_trust_test.go b/pkg/extensions/imagetrust/image_trust_test.go index d7bd53a2..892b21e6 100644 --- a/pkg/extensions/imagetrust/image_trust_test.go +++ b/pkg/extensions/imagetrust/image_trust_test.go @@ -16,8 +16,11 @@ import ( "testing" "time" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" + smithy "github.com/aws/smithy-go" + smithyhttp "github.com/aws/smithy-go/transport/http" guuid "github.com/gofrs/uuid" "github.com/notaryproject/notation-go" notreg "github.com/notaryproject/notation-go/registry" @@ -661,7 +664,7 @@ func TestLocalTrustStore(t *testing.T) { func TestAWSTrustStore(t *testing.T) { tskip.SkipDynamo(t) - trustpolicy := "trustpolicy" + trustpolicyDoc := "trustpolicy" Convey("NewAWSImageTrustStore error", t, func() { _, err := imagetrust.NewAWSImageTrustStore("us-east-2", "wrong;endpoint") @@ -669,22 +672,7 @@ func TestAWSTrustStore(t *testing.T) { }) Convey("InitTrustpolicy retry", t, func() { - smanager, err := imagetrust.GetSecretsManagerClient("us-east-2", os.Getenv("DYNAMODBMOCK_ENDPOINT")) - So(err, ShouldBeNil) - - smCache := imagetrust.GetSecretsManagerRetrieval("us-east-2", os.Getenv("DYNAMODBMOCK_ENDPOINT")) - - description := "notation trustpolicy file" content := "trustpolicy content" - - _, err = smanager.CreateSecret(context.Background(), - &secretsmanager.CreateSecretInput{ - Name: &trustpolicy, - Description: &description, - SecretString: &content, - }) - So(err, ShouldBeNil) - secretsManagerMock := mocks.SecretsManagerMock{ DeleteSecretFn: func(ctx context.Context, params *secretsmanager.DeleteSecretInput, optFns ...func(*secretsmanager.Options), @@ -694,20 +682,25 @@ func TestAWSTrustStore(t *testing.T) { CreateSecretFn: func(ctx context.Context, params *secretsmanager.CreateSecretInput, optFns ...func(*secretsmanager.Options), ) (*secretsmanager.CreateSecretOutput, error) { - return smanager.CreateSecret(ctx, params, optFns...) + return &secretsmanager.CreateSecretOutput{}, getResourceExistsException() }, } - secretsManagerCacheMock := mocks.SecretsManagerCacheMock{ GetSecretStringFn: func(secretID string) (string, error) { return "", errUnexpectedError }, } - _, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, secretsManagerCacheMock) + _, err := imagetrust.NewCertificateAWSStorage(secretsManagerMock, secretsManagerCacheMock) So(err, ShouldNotBeNil) - _, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, smCache) + secretsManagerCacheMock = mocks.SecretsManagerCacheMock{ + GetSecretStringFn: func(secretID string) (string, error) { + return content, nil + }, + } + + _, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, secretsManagerCacheMock) So(err, ShouldNotBeNil) secretsManagerMock = mocks.SecretsManagerMock{ @@ -719,13 +712,15 @@ func TestAWSTrustStore(t *testing.T) { CreateSecretFn: func(ctx context.Context, params *secretsmanager.CreateSecretInput, optFns ...func(*secretsmanager.Options), ) (*secretsmanager.CreateSecretOutput, error) { - return smanager.CreateSecret(ctx, params, optFns...) + return &secretsmanager.CreateSecretOutput{}, getResourceExistsException() }, } - _, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, smCache) + _, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, secretsManagerCacheMock) So(err, ShouldNotBeNil) + errVal := make(chan bool) + secretsManagerMock = mocks.SecretsManagerMock{ DeleteSecretFn: func(ctx context.Context, params *secretsmanager.DeleteSecretInput, optFns ...func(*secretsmanager.Options), @@ -733,7 +728,7 @@ func TestAWSTrustStore(t *testing.T) { go func() { time.Sleep(3 * time.Second) - smanager.DeleteSecret(ctx, params, optFns...) //nolint:errcheck + errVal <- true }() return &secretsmanager.DeleteSecretOutput{}, nil @@ -741,21 +736,53 @@ func TestAWSTrustStore(t *testing.T) { CreateSecretFn: func(ctx context.Context, params *secretsmanager.CreateSecretInput, optFns ...func(*secretsmanager.Options), ) (*secretsmanager.CreateSecretOutput, error) { - return smanager.CreateSecret(ctx, params, optFns...) + select { + case <-errVal: + return &secretsmanager.CreateSecretOutput{}, nil + default: + return &secretsmanager.CreateSecretOutput{}, getResourceExistsException() + } }, } - _, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, smCache) + _, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, secretsManagerCacheMock) So(err, ShouldBeNil) }) Convey("GetCertificates errors", t, func() { - smanager, err := imagetrust.GetSecretsManagerClient("us-east-2", os.Getenv("DYNAMODBMOCK_ENDPOINT")) - So(err, ShouldBeNil) + name := "ca/test/digest" + content := "invalid certificate content" - smCache := imagetrust.GetSecretsManagerRetrieval("us-east-2", os.Getenv("DYNAMODBMOCK_ENDPOINT")) + secretsManagerMock := mocks.SecretsManagerMock{ + DeleteSecretFn: func(ctx context.Context, params *secretsmanager.DeleteSecretInput, + optFns ...func(*secretsmanager.Options), + ) (*secretsmanager.DeleteSecretOutput, error) { + return &secretsmanager.DeleteSecretOutput{}, nil + }, + CreateSecretFn: func(ctx context.Context, params *secretsmanager.CreateSecretInput, + optFns ...func(*secretsmanager.Options), + ) (*secretsmanager.CreateSecretOutput, error) { + if *params.Name == trustpolicyDoc { + return &secretsmanager.CreateSecretOutput{}, nil + } - notationStorage, err := imagetrust.NewCertificateAWSStorage(smanager, smCache) + return &secretsmanager.CreateSecretOutput{}, errUnexpectedError + }, + ListSecretsFn: func(ctx context.Context, params *secretsmanager.ListSecretsInput, + optFns ...func(*secretsmanager.Options), + ) (*secretsmanager.ListSecretsOutput, error) { + return &secretsmanager.ListSecretsOutput{ + SecretList: []types.SecretListEntry{{Name: &name}}, + }, nil + }, + } + secretsManagerCacheMock := mocks.SecretsManagerCacheMock{ + GetSecretStringFn: func(secretID string) (string, error) { + return content, nil + }, + } + + notationStorage, err := imagetrust.NewCertificateAWSStorage(secretsManagerMock, secretsManagerCacheMock) So(err, ShouldBeNil) _, err = notationStorage.GetCertificates(context.Background(), "wrongType", "") @@ -766,36 +793,48 @@ func TestAWSTrustStore(t *testing.T) { So(err, ShouldNotBeNil) So(err, ShouldEqual, zerr.ErrInvalidTruststoreName) - name := "ca/test/digest" - description := "notation certificate" - content := "invalid certificate content" - - _, err = smanager.CreateSecret(context.Background(), - &secretsmanager.CreateSecretInput{ - Name: &name, - Description: &description, - SecretString: &content, - }) - So(err, ShouldBeNil) - _, err = notationStorage.GetCertificates(context.Background(), "ca", "test") So(err, ShouldNotBeNil) newName := "ca/newtest/digest" newSecret := base64.StdEncoding.EncodeToString([]byte(content)) - _, err = smanager.CreateSecret(context.Background(), - &secretsmanager.CreateSecretInput{ - Name: &newName, - Description: &description, - SecretString: &newSecret, - }) + secretsManagerMock = mocks.SecretsManagerMock{ + DeleteSecretFn: func(ctx context.Context, params *secretsmanager.DeleteSecretInput, + optFns ...func(*secretsmanager.Options), + ) (*secretsmanager.DeleteSecretOutput, error) { + return &secretsmanager.DeleteSecretOutput{}, nil + }, + CreateSecretFn: func(ctx context.Context, params *secretsmanager.CreateSecretInput, + optFns ...func(*secretsmanager.Options), + ) (*secretsmanager.CreateSecretOutput, error) { + if *params.Name == trustpolicyDoc { + return &secretsmanager.CreateSecretOutput{}, nil + } + + return &secretsmanager.CreateSecretOutput{}, errUnexpectedError + }, + ListSecretsFn: func(ctx context.Context, params *secretsmanager.ListSecretsInput, + optFns ...func(*secretsmanager.Options), + ) (*secretsmanager.ListSecretsOutput, error) { + return &secretsmanager.ListSecretsOutput{ + SecretList: []types.SecretListEntry{{Name: &newName}}, + }, nil + }, + } + secretsManagerCacheMock = mocks.SecretsManagerCacheMock{ + GetSecretStringFn: func(secretID string) (string, error) { + return newSecret, nil + }, + } + + notationStorage, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, secretsManagerCacheMock) So(err, ShouldBeNil) _, err = notationStorage.GetCertificates(context.Background(), "ca", "newtest") So(err, ShouldNotBeNil) - secretsManagerMock := mocks.SecretsManagerMock{ + secretsManagerMock = mocks.SecretsManagerMock{ ListSecretsFn: func(ctx context.Context, params *secretsmanager.ListSecretsInput, optFns ...func(*secretsmanager.Options), ) (*secretsmanager.ListSecretsOutput, error) { @@ -803,7 +842,7 @@ func TestAWSTrustStore(t *testing.T) { }, } - notationStorage, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, smCache) + notationStorage, err = imagetrust.NewCertificateAWSStorage(secretsManagerMock, secretsManagerCacheMock) So(err, ShouldBeNil) _, err = notationStorage.GetCertificates(context.Background(), "ca", "newtest") @@ -817,7 +856,7 @@ func TestAWSTrustStore(t *testing.T) { }, } - secretsManagerCacheMock := mocks.SecretsManagerCacheMock{ + secretsManagerCacheMock = mocks.SecretsManagerCacheMock{ GetSecretStringFn: func(secretID string) (string, error) { return "", errUnexpectedError }, @@ -831,42 +870,41 @@ func TestAWSTrustStore(t *testing.T) { }) Convey("GetPublicKeyVerifier errors", t, func() { - smanager, err := imagetrust.GetSecretsManagerClient("us-east-2", os.Getenv("DYNAMODBMOCK_ENDPOINT")) - So(err, ShouldBeNil) + secretsManagerMock := mocks.SecretsManagerMock{} + secretsManagerCacheMock := mocks.SecretsManagerCacheMock{ + GetSecretStringFn: func(secretID string) (string, error) { + return "", errUnexpectedError + }, + } - smCache := imagetrust.GetSecretsManagerRetrieval("us-east-2", os.Getenv("DYNAMODBMOCK_ENDPOINT")) + cosignStorage := imagetrust.NewPublicKeyAWSStorage(secretsManagerMock, secretsManagerCacheMock) - cosignStorage := imagetrust.NewPublicKeyAWSStorage(smanager, smCache) - - _, _, err = cosignStorage.GetPublicKeyVerifier("badsecret") + _, _, err := cosignStorage.GetPublicKeyVerifier("badsecret") So(err, ShouldNotBeNil) secretName := "digest" - description := "cosign public key" secret := "invalid public key content" - _, err = smanager.CreateSecret(context.Background(), - &secretsmanager.CreateSecretInput{ - Name: &secretName, - Description: &description, - SecretString: &secret, - }) - So(err, ShouldBeNil) + secretsManagerCacheMock = mocks.SecretsManagerCacheMock{ + GetSecretStringFn: func(secretID string) (string, error) { + return secret, nil + }, + } + + cosignStorage = imagetrust.NewPublicKeyAWSStorage(secretsManagerMock, secretsManagerCacheMock) _, _, err = cosignStorage.GetPublicKeyVerifier(secretName) So(err, ShouldNotBeNil) - secretName = "newdigest" - newSecret := base64.StdEncoding.EncodeToString([]byte(secret)) - _, err = smanager.CreateSecret(context.Background(), - &secretsmanager.CreateSecretInput{ - Name: &secretName, - Description: &description, - SecretString: &newSecret, - }) - So(err, ShouldBeNil) + secretsManagerCacheMock = mocks.SecretsManagerCacheMock{ + GetSecretStringFn: func(secretID string) (string, error) { + return newSecret, nil + }, + } + + cosignStorage = imagetrust.NewPublicKeyAWSStorage(secretsManagerMock, secretsManagerCacheMock) _, _, err = cosignStorage.GetPublicKeyVerifier(secretName) So(err, ShouldNotBeNil) @@ -900,6 +938,39 @@ func TestAWSTrustStore(t *testing.T) { err := cosignStorage.StorePublicKey(digest.FromString("dig"), []byte("content")) So(err, ShouldNotBeNil) + + secretsManagerMock = mocks.SecretsManagerMock{ + CreateSecretFn: func(ctx context.Context, params *secretsmanager.CreateSecretInput, + optFns ...func(*secretsmanager.Options), + ) (*secretsmanager.CreateSecretOutput, error) { + return &secretsmanager.CreateSecretOutput{}, getResourceExistsException() + }, + } + + cosignStorage = imagetrust.NewPublicKeyAWSStorage(secretsManagerMock, nil) + + err = cosignStorage.StorePublicKey(digest.FromString("dig"), []byte("content")) + So(err, ShouldBeNil) + }) + + Convey("StoreCertificate error", t, func() { + secretsManagerMock := mocks.SecretsManagerMock{ + CreateSecretFn: func(ctx context.Context, params *secretsmanager.CreateSecretInput, + optFns ...func(*secretsmanager.Options), + ) (*secretsmanager.CreateSecretOutput, error) { + if *params.Name != trustpolicyDoc { + return &secretsmanager.CreateSecretOutput{}, getResourceExistsException() + } + + return &secretsmanager.CreateSecretOutput{}, nil + }, + } + + notationStorage, err := imagetrust.NewCertificateAWSStorage(secretsManagerMock, nil) + So(err, ShouldBeNil) + + err = notationStorage.StoreCertificate([]byte("content"), "ca") + So(err, ShouldBeNil) }) Convey("VerifySignature - trustpolicy.json does not exist", t, func() { @@ -1337,3 +1408,17 @@ func RunVerificationTests(t *testing.T, dbDriverParams map[string]interface{}) { }) }) } + +func getResourceExistsException() error { + errAlreadyExists := "the secret already exists" + + return &smithy.OperationError{ + Err: &awshttp.ResponseError{ + ResponseError: &smithyhttp.ResponseError{ + Err: &types.ResourceExistsException{ + Message: &errAlreadyExists, + }, + }, + }, + } +} diff --git a/pkg/extensions/imagetrust/notation.go b/pkg/extensions/imagetrust/notation.go index 22de1eb2..4152d154 100644 --- a/pkg/extensions/imagetrust/notation.go +++ b/pkg/extensions/imagetrust/notation.go @@ -170,7 +170,7 @@ func (cloud *CertificateAWSStorage) InitTrustpolicy(trustpolicy []byte) error { } _, err := cloud.secretsManagerClient.CreateSecret(context.Background(), secretInputParam) - if err != nil && strings.Contains(err.Error(), "the secret trustpolicy already exists.") { + if err != nil && IsResourceExistsException(err) { trustpolicyContent, err := cloud.secretsManagerCache.GetSecretString(name) if err != nil { return err @@ -495,6 +495,10 @@ func (cloud *CertificateAWSStorage) StoreCertificate( _, err := cloud.secretsManagerClient.CreateSecret(context.Background(), secretInputParam) + if err != nil && IsResourceExistsException(err) { + return nil + } + return err }