-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add secret store provider registry impl
- Loading branch information
Showing
3 changed files
with
145 additions
and
0 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package secrets | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"sync" | ||
|
||
"golang.org/x/exp/maps" | ||
|
||
"kusionstack.io/kusion/pkg/apis/secrets" | ||
"kusionstack.io/kusion/pkg/log" | ||
) | ||
|
||
type Providers struct { | ||
lock sync.RWMutex | ||
registry map[string]SecretStoreProvider | ||
} | ||
|
||
func NewProviders() *Providers { | ||
return &Providers{} | ||
} | ||
|
||
// Register registers a provider with associated spec. This | ||
// is expected to happen during app startup. | ||
func (ps *Providers) Register(sp SecretStoreProvider, spec *secrets.ProviderSpec) { | ||
providerName, err := getProviderName(spec) | ||
if err != nil { | ||
panic(fmt.Sprintf("provider registery failed to parse spec: %s", err.Error())) | ||
} | ||
|
||
ps.lock.Lock() | ||
defer ps.lock.Unlock() | ||
if ps.registry != nil { | ||
_, found := ps.registry[providerName] | ||
if found { | ||
log.Warnf("Provider %s was registered twice", providerName) | ||
} | ||
} else { | ||
ps.registry = map[string]SecretStoreProvider{} | ||
} | ||
|
||
log.Infof("Registered secret store provider %s", providerName) | ||
ps.registry[providerName] = sp | ||
} | ||
|
||
// GetProviderByName returns registered provider by name. | ||
func (ps *Providers) GetProviderByName(providerName string) (SecretStoreProvider, bool) { | ||
ps.lock.RLock() | ||
defer ps.lock.RUnlock() | ||
provider, found := ps.registry[providerName] | ||
return provider, found | ||
} | ||
|
||
func getProviderName(spec *secrets.ProviderSpec) (string, error) { | ||
specBytes, err := json.Marshal(spec) | ||
if err != nil || specBytes == nil { | ||
return "", fmt.Errorf("failed to marshal secret store provider spec: %w", err) | ||
} | ||
|
||
specMap := make(map[string]interface{}) | ||
err = json.Unmarshal(specBytes, &specMap) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to unmarshal secret store provider spec: %w", err) | ||
} | ||
|
||
if len(specMap) != 1 { | ||
return "", fmt.Errorf("secret stores must only have exactly one provider specified, found %d", len(specMap)) | ||
} | ||
|
||
return maps.Keys(specMap)[0], nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package secrets | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"kusionstack.io/kusion/pkg/apis/secrets" | ||
) | ||
|
||
// FakeSecretStore is the fake implementation of SecretStore. | ||
type FakeSecretStore struct{} | ||
|
||
// Fake implementation of SecretStore.GetSecret. | ||
func (fss *FakeSecretStore) GetSecret(_ context.Context, _ string) ([]byte, error) { | ||
return []byte("NOOP"), nil | ||
} | ||
|
||
// FakeSecretStoreProvider is the fake implementation of SecretStoreProvider. | ||
type FakeSecretStoreProvider struct{} | ||
|
||
// Fake implementation of SecretStoreProvider.Type. | ||
func (fsp *FakeSecretStoreProvider) Type() string { | ||
return "fake" | ||
} | ||
|
||
// Fake implementation of SecretStoreProvider.NewSecretStore. | ||
func (fsp *FakeSecretStoreProvider) NewSecretStore(_ *secrets.SecretStoreSpec) (SecretStore, error) { | ||
return &FakeSecretStore{}, nil | ||
} | ||
|
||
func TestRegister(t *testing.T) { | ||
testcases := []struct { | ||
name string | ||
providerName string | ||
shouldPanic bool | ||
expExists bool | ||
spec *secrets.ProviderSpec | ||
}{ | ||
{ | ||
name: "should panic when given an invalid provider spec", | ||
shouldPanic: true, | ||
spec: &secrets.ProviderSpec{}, | ||
}, | ||
{ | ||
name: "should register a valid provider", | ||
providerName: "aws", | ||
shouldPanic: false, | ||
expExists: true, | ||
spec: &secrets.ProviderSpec{ | ||
AWS: &secrets.AWSProvider{}, | ||
}, | ||
}, | ||
} | ||
|
||
providers := NewProviders() | ||
fsp := &FakeSecretStoreProvider{} | ||
for _, tc := range testcases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
if tc.shouldPanic { | ||
defer func() { | ||
if r := recover(); r == nil { | ||
t.Errorf("Register should panic") | ||
} | ||
}() | ||
} | ||
|
||
providers.Register(fsp, tc.spec) | ||
_, ok := providers.GetProviderByName(tc.providerName) | ||
assert.Equal(t, tc.expExists, ok, "provider should be registered") | ||
}) | ||
} | ||
} |