Skip to content

Commit

Permalink
Merge pull request #10 from 0xsequence/Add-test-for-mapstringConfig
Browse files Browse the repository at this point in the history
Support map[string]config{} by creating a ptr to the config value
  • Loading branch information
LukasJenicek authored Oct 10, 2024
2 parents a2c8fab + f76be65 commit ebefdc2
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 40 deletions.
14 changes: 13 additions & 1 deletion collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,19 @@ func (g *collector) collectSecretFields(v reflect.Value, path string) {
case reflect.Map:
for _, key := range v.MapKeys() {
item := v.MapIndex(key)
g.collectSecretFields(item, fmt.Sprintf("%v[%v]", path, key))

if item.Kind() == reflect.Struct {
// If the value is a struct, create a pointer to the map value and modify via pointer
ptr := reflect.New(item.Type())
ptr.Elem().Set(item)

g.collectSecretFields(ptr, fmt.Sprintf("%v[%v]", path, key))

// Set the modified struct back into the map
v.SetMapIndex(key, ptr.Elem())
} else {
g.collectSecretFields(item, fmt.Sprintf("%v[%v]", path, key))
}
}

case reflect.String:
Expand Down
133 changes: 94 additions & 39 deletions collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,10 @@ package cloudsecrets
import (
"fmt"
"reflect"
"sort"
"testing"
)

type config1 struct {
DB dbConfig
JWTSecrets []jwtSecret
Providers map[string]*providerConfig
DoublePtr **providerConfig
unexported dbConfig
}

type dbConfig struct {
User string
Password string
}

type providerConfig struct {
Name string
Secret string
}

type jwtSecret string

func TestCollectFields(t *testing.T) {
tt := []struct {
Name string
Expand All @@ -34,18 +15,26 @@ func TestCollectFields(t *testing.T) {
Error bool
}{
{
Name: "Basic DB config with no creds",
Input: &config1{
Name: "DB_config_with_no_creds",
Input: &cfg{
DB: dbConfig{
User: "db-user",
Password: "db-password",
},
DBPtr: &dbConfig{
User: "db-user",
Password: "db-password",
},
DBDoublePtr: ptr(&dbConfig{
User: "db-user",
Password: "db-password",
}),
},
Out: []string{},
},
{
Name: "Basic DB config with creds",
Input: &config1{
Name: "DB_config_with_creds",
Input: &cfg{
DB: dbConfig{
User: "db-user",
Password: "$SECRET:db-password",
Expand All @@ -54,8 +43,28 @@ func TestCollectFields(t *testing.T) {
Out: []string{"db-password"},
},
{
Name: "Slice of secrets",
Input: &config1{
Name: "DB config ptr with creds",
Input: &cfg{
DBPtr: &dbConfig{
User: "db-user",
Password: "$SECRET:db-password",
},
},
Out: []string{"db-password"},
},
{
Name: "DB_config_double_ptr_with_creds",
Input: &cfg{
DBDoublePtr: ptr(&dbConfig{
User: "db-user",
Password: "$SECRET:db-password",
}),
},
Out: []string{"db-password"},
},
{
Name: "Slice_of_secret_values",
Input: &cfg{
DB: dbConfig{
User: "db-user",
Password: "$SECRET:secretName",
Expand All @@ -65,9 +74,20 @@ func TestCollectFields(t *testing.T) {
Out: []string{"secretName", "jwtSecret1", "jwtSecret2"},
},
{
Name: "Map with secrets",
Input: &config1{
Providers: map[string]*providerConfig{
Name: "Slice_of_secret_pointer_values",
Input: &cfg{
DB: dbConfig{
User: "db-user",
Password: "$SECRET:secretName",
},
JWTSecretsPtr: []*jwtSecret{ptr(jwtSecret("$SECRET:jwtSecret1")), ptr(jwtSecret("$SECRET:jwtSecret2")), ptr(jwtSecret("nope"))},
},
Out: []string{"secretName", "jwtSecret1", "jwtSecret2"},
},
{
Name: "Map_with_values",
Input: &cfg{
Providers: map[string]providerConfig{
"provider1": {Name: "provider1", Secret: "$SECRET:secretProvider1"},
"provider2": {Name: "provider2", Secret: "$SECRET:secretProvider2"},
"provider3": {Name: "provider3", Secret: "$SECRET:secretProvider3"},
Expand All @@ -76,15 +96,19 @@ func TestCollectFields(t *testing.T) {
Out: []string{"secretProvider1", "secretProvider2", "secretProvider3"},
},
{
Name: "Double pointer",
Input: &config1{
DoublePtr: ptr(&providerConfig{Name: "double-pointer", Secret: "$SECRET:double-pointer-secret"}),
Name: "Map_with_ptr_values",
Input: &cfg{
ProvidersPtr: map[string]*providerConfig{
"provider1": {Name: "provider1", Secret: "$SECRET:secretProvider1"},
"provider2": {Name: "provider2", Secret: "$SECRET:secretProvider2"},
"provider3": {Name: "provider3", Secret: "$SECRET:secretProvider3"},
},
},
Out: []string{"double-pointer-secret"},
Out: []string{"secretProvider1", "secretProvider2", "secretProvider3"},
},
{
Name: "Unexported field should fail to hydrate",
Input: &config1{
Name: "Unexported_field_should_fail_to_hydrate",
Input: &cfg{
unexported: dbConfig{ // unexported fields can't be updated via reflect pkg
User: "db-user",
Password: "$SECRET:secretName", // match inside unexported field
Expand All @@ -97,7 +121,7 @@ func TestCollectFields(t *testing.T) {

for i, tc := range tt {
i, tc := i, tc
t.Run(fmt.Sprintf("tt[%v]: %v", i, tc.Name), func(t *testing.T) {
t.Run(tc.Name, func(t *testing.T) {
v := reflect.ValueOf(tc.Input)

c := &collector{}
Expand All @@ -106,23 +130,54 @@ func TestCollectFields(t *testing.T) {
if tc.Error {
if c.err == nil {
t.Error("expected error, got nil")
return
}
} else {
if c.err != nil {
t.Errorf("unexpected error: %v", c.err)
return
}
}

if len(c.fields) != len(tc.Out) {
t.Errorf("expected %v secrets, got %v", len(tc.Out), len(c.fields))
}
for i := 0; i < len(c.fields); i++ {
if c.fields[i].secretName != tc.Out[i] {
t.Errorf("collected field[%v].secretName=%v doesn't match tc.Out[%v]=%v", i, c.fields[i].secretName, i, tc.Out[i])

fields := c.fields
sort.Slice(fields, func(i, j int) bool {
return fields[i].fieldPath <= fields[j].fieldPath
})

for i := 0; i < len(fields); i++ {
if fields[i].secretName != tc.Out[i] {
t.Errorf("collected field[%v].secretName=%v doesn't match tc.Out[%v]=%v", i, fields[i].secretName, i, tc.Out[i])
}
}
})
}
}

type cfg struct {
DB dbConfig
DBPtr *dbConfig
DBDoublePtr **dbConfig
JWTSecrets []jwtSecret
JWTSecretsPtr []*jwtSecret
Providers map[string]providerConfig
ProvidersPtr map[string]*providerConfig
unexported dbConfig
}

type dbConfig struct {
User string
Password string
}

type providerConfig struct {
Name string
Secret string
}

type jwtSecret string

func ptr[T any](v T) *T { return &v }

0 comments on commit ebefdc2

Please sign in to comment.