From 4f90912f05839b9a2cfda8caad131272b68eac1a Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Fri, 13 Sep 2024 07:57:13 -0700 Subject: [PATCH 1/2] feat: adds support for single secrets --- blueprint/schema/_embed/schema.cue | 8 +++- blueprint/schema/schema.go | 8 +++- blueprint/schema/schema_go_gen.cue | 8 +++- forge/cli/pkg/earthly/earthly.go | 59 ++++++++++++++++----------- forge/cli/pkg/earthly/earthly_test.go | 51 ++++++++++++++++++++++- 5 files changed, 105 insertions(+), 29 deletions(-) diff --git a/blueprint/schema/_embed/schema.cue b/blueprint/schema/_embed/schema.cue index 5c9fb9d..9cc37b6 100644 --- a/blueprint/schema/_embed/schema.cue +++ b/blueprint/schema/_embed/schema.cue @@ -140,11 +140,17 @@ package schema // Provider contains the provider to use for the secret. provider?: null | string @go(Provider,*string) - // Maps contains the mappings for the secret. + // Maps contains mappings for Earthly secret names to JSON keys in the secret. + // Mutually exclusive with Name. // +optional maps?: { [string]: string } @go(Maps,map[string]string) + + // Name contains the name of the Earthly secret to use. + // Mutually exclusive with Maps. + // +optional + name?: null | string @go(Name,*string) } version: "1.0" #Tagging: { diff --git a/blueprint/schema/schema.go b/blueprint/schema/schema.go index aad36a9..df26a42 100644 --- a/blueprint/schema/schema.go +++ b/blueprint/schema/schema.go @@ -147,9 +147,15 @@ type Secret struct { // Provider contains the provider to use for the secret. Provider *string `json:"provider"` - // Maps contains the mappings for the secret. + // Maps contains mappings for Earthly secret names to JSON keys in the secret. + // Mutually exclusive with Name. // +optional Maps map[string]string `json:"maps"` + + // Name contains the name of the Earthly secret to use. + // Mutually exclusive with Maps. + // +optional + Name *string `json:"name"` } type Tagging struct { diff --git a/blueprint/schema/schema_go_gen.cue b/blueprint/schema/schema_go_gen.cue index f3b5803..aac0207 100644 --- a/blueprint/schema/schema_go_gen.cue +++ b/blueprint/schema/schema_go_gen.cue @@ -142,9 +142,15 @@ package schema // Provider contains the provider to use for the secret. provider?: null | string @go(Provider,*string) - // Maps contains the mappings for the secret. + // Maps contains mappings for Earthly secret names to JSON keys in the secret. + // Mutually exclusive with Name. // +optional maps?: {[string]: string} @go(Maps,map[string]string) + + // Name contains the name of the Earthly secret to use. + // Mutually exclusive with Maps. + // +optional + name?: null | string @go(Name,*string) } #Tagging: { diff --git a/forge/cli/pkg/earthly/earthly.go b/forge/cli/pkg/earthly/earthly.go index 00fba67..3b80ec8 100644 --- a/forge/cli/pkg/earthly/earthly.go +++ b/forge/cli/pkg/earthly/earthly.go @@ -136,6 +136,11 @@ func (e *EarthlyExecutor) buildSecrets() ([]EarthlySecret, error) { var secrets []EarthlySecret for _, secret := range e.secrets { + if secret.Name != nil && len(secret.Maps) > 0 { + e.logger.Error("Secret contains both name and maps", "name", *secret.Name, "maps", secret.Maps) + return nil, fmt.Errorf("secret contains both name and maps: %s", *secret.Name) + } + secretClient, err := e.secretsStore.NewClient(e.logger, secretstore.Provider(*secret.Provider)) if err != nil { e.logger.Error("Unable to create new secret client", "provider", secret.Provider, "error", err) @@ -148,33 +153,39 @@ func (e *EarthlyExecutor) buildSecrets() ([]EarthlySecret, error) { return secrets, fmt.Errorf("unable to get secret %s from provider: %s", *secret.Path, *secret.Provider) } - var secretValues map[string]interface{} - - if err := json.Unmarshal([]byte(s), &secretValues); err != nil { - e.logger.Error("Unable to unmarshal secret value", "provider", secret.Provider, "path", secret.Path, "error", err) - return secrets, fmt.Errorf("unable to unmarshal secret value: %w", err) - } - - for sk, eid := range secret.Maps { - if _, ok := secretValues[sk]; !ok { - e.logger.Error("Secret key not found in secret values", "key", sk, "provider", secret.Provider, "path", secret.Path) - return nil, fmt.Errorf("secret key not found in secret values: %s", sk) - } - - s := EarthlySecret{ - Id: eid, + if len(secret.Maps) == 0 { + secrets = append(secrets, EarthlySecret{ + Id: *secret.Name, + Value: s, + }) + } else { + var secretValues map[string]interface{} + if err := json.Unmarshal([]byte(s), &secretValues); err != nil { + e.logger.Error("Failed to unmarshal secret values", "provider", secret.Provider, "path", secret.Path, "error", err) + return nil, fmt.Errorf("failed to unmarshal secret values from provider %s: %w", *secret.Provider, err) } - switch t := secretValues[sk].(type) { - case bool: - s.Value = strconv.FormatBool(t) - case int: - s.Value = strconv.FormatInt(int64(t), 10) - default: - s.Value = t.(string) + for sk, eid := range secret.Maps { + if _, ok := secretValues[sk]; !ok { + e.logger.Error("Secret key not found in secret values", "key", sk, "provider", secret.Provider, "path", secret.Path) + return nil, fmt.Errorf("secret key not found in secret values: %s", sk) + } + + s := EarthlySecret{ + Id: eid, + } + + switch t := secretValues[sk].(type) { + case bool: + s.Value = strconv.FormatBool(t) + case int: + s.Value = strconv.FormatInt(int64(t), 10) + default: + s.Value = t.(string) + } + + secrets = append(secrets, s) } - - secrets = append(secrets, s) } } diff --git a/forge/cli/pkg/earthly/earthly_test.go b/forge/cli/pkg/earthly/earthly_test.go index 0a00093..f5d48a9 100644 --- a/forge/cli/pkg/earthly/earthly_test.go +++ b/forge/cli/pkg/earthly/earthly_test.go @@ -229,6 +229,51 @@ func TestEarthlyExecutor_buildSecrets(t *testing.T) { expectErr: false, expectedErr: "", }, + { + name: "no JSON", + provider: &smocks.SecretProviderMock{ + GetFunc: func(path string) (string, error) { + return "secret", nil + }, + }, + secrets: []schema.Secret{ + { + Name: utils.StringPtr("name"), + Path: utils.StringPtr("path"), + Provider: utils.StringPtr("mock"), + Maps: map[string]string{}, + }, + }, + expect: []EarthlySecret{ + { + Id: "name", + Value: "secret", + }, + }, + expectErr: false, + expectedErr: "", + }, + { + name: "name and maps defined", + provider: &smocks.SecretProviderMock{ + GetFunc: func(path string) (string, error) { + return "", nil + }, + }, + secrets: []schema.Secret{ + { + Name: utils.StringPtr("name"), + Path: utils.StringPtr("path"), + Provider: utils.StringPtr("mock"), + Maps: map[string]string{ + "key": "id", + }, + }, + }, + expect: nil, + expectErr: true, + expectedErr: "secret contains both name and maps: name", + }, { name: "key does not exist", provider: &smocks.SecretProviderMock{ @@ -260,12 +305,14 @@ func TestEarthlyExecutor_buildSecrets(t *testing.T) { { Path: utils.StringPtr("path"), Provider: utils.StringPtr("mock"), - Maps: map[string]string{}, + Maps: map[string]string{ + "key1": "id1", + }, }, }, expect: nil, expectErr: true, - expectedErr: "unable to unmarshal secret value: invalid character 'i' looking for beginning of value", + expectedErr: "failed to unmarshal secret values from provider mock: invalid character 'i' looking for beginning of value", }, { name: "secret provider does not exist", From 4677b19050dda458dfa94eb6992a096e6a25e17b Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Fri, 13 Sep 2024 08:05:19 -0700 Subject: [PATCH 2/2] wip: fixes test --- forge/cli/pkg/scan/earthfile_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/forge/cli/pkg/scan/earthfile_test.go b/forge/cli/pkg/scan/earthfile_test.go index cdf427b..eddbc6f 100644 --- a/forge/cli/pkg/scan/earthfile_test.go +++ b/forge/cli/pkg/scan/earthfile_test.go @@ -9,7 +9,6 @@ import ( "github.com/input-output-hk/catalyst-forge/tools/pkg/walker" "github.com/spf13/afero" "github.com/stretchr/testify/assert" - "golang.org/x/exp/maps" ) type MockFileSeeker struct { @@ -76,7 +75,9 @@ func TestScanEarthfiles(t *testing.T) { return } - assert.Equal(t, maps.Keys(got), tt.expectedKeys) + for k := range got { + assert.Contains(t, tt.expectedKeys, k) + } }) } }