Skip to content

Commit

Permalink
feat: adds support for single secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgilman committed Sep 13, 2024
1 parent cf775cf commit 4f90912
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 29 deletions.
8 changes: 7 additions & 1 deletion blueprint/schema/_embed/schema.cue
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
8 changes: 7 additions & 1 deletion blueprint/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
8 changes: 7 additions & 1 deletion blueprint/schema/schema_go_gen.cue
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
59 changes: 35 additions & 24 deletions forge/cli/pkg/earthly/earthly.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}
}

Expand Down
51 changes: 49 additions & 2 deletions forge/cli/pkg/earthly/earthly_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit 4f90912

Please sign in to comment.