Skip to content

Commit

Permalink
feat: update workspace type add secret store config (#724)
Browse files Browse the repository at this point in the history
  • Loading branch information
adohe committed Dec 26, 2023
1 parent c1f0455 commit 8dce43b
Show file tree
Hide file tree
Showing 3 changed files with 295 additions and 0 deletions.
3 changes: 3 additions & 0 deletions pkg/apis/core/v1/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type Workspace struct {

// Backends are the configs of a set of backends.
Backends *BackendConfigs `yaml:"backends,omitempty" json:"backends,omitempty"`

// SecretStore represents a secure external location for storing secrets.
SecretStore *SecretStoreSpec `yaml:"secretStore,omitempty" json:"secretStore,omitempty"`
}

// ModuleConfigs is a set of multiple ModuleConfig, whose key is the module name.
Expand Down
95 changes: 95 additions & 0 deletions pkg/workspace/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"errors"
"fmt"

utilerrors "k8s.io/apimachinery/pkg/util/errors"

"kusionstack.io/kusion/pkg/apis/core/v1"
)

Expand Down Expand Up @@ -40,6 +42,15 @@ var (
ErrEmptyAccessKeySecret = errors.New("empty access key secret")
ErrEmptyOssEndpoint = errors.New("empty oss endpoint")
ErrEmptyS3Region = errors.New("empty s3 region")

ErrMissingProvider = errors.New("invalid secret store spec, missing provider config")
ErrMultiSecretStoreProviders = errors.New("may not specify more than 1 secret store provider")
ErrEmptyAWSRegion = errors.New("region must be provided when using AWS Secrets Manager")
ErrEmptyVaultServer = errors.New("server address must be provided when using Hashicorp Vault")
ErrEmptyVaultURL = errors.New("vault url must be provided when using Azure KeyVault")
ErrEmptyTenantID = errors.New("azure tenant id must be provided when using Azure KeyVault")
ErrEmptyAlicloudRegion = errors.New("region must be provided when using Alicloud Secrets Manager")
ErrMissingProviderType = errors.New("must specify a provider type")
)

// ValidateWorkspace is used to validate the workspace get or set in the storage, and does not validate the
Expand All @@ -63,6 +74,11 @@ func ValidateWorkspace(ws *v1.Workspace) error {
return err
}
}
if ws.SecretStore != nil {
if allErrs := ValidateSecretStoreConfig(ws.SecretStore); allErrs != nil {
return utilerrors.NewAggregate(allErrs)
}
}
return nil
}

Expand Down Expand Up @@ -319,3 +335,82 @@ func validateWholeGenericObjectStorageConfig(config *v1.GenericObjectStorageConf
}
return nil
}

// ValidateSecretStoreConfig tests that the specified SecretStoreSpec has valid data.
func ValidateSecretStoreConfig(spec *v1.SecretStoreSpec) []error {
if spec.Provider == nil {
return []error{ErrMissingProvider}
}

numProviders := 0
var allErrs []error
if spec.Provider.AWS != nil {
numProviders++
allErrs = append(allErrs, validateAWSSecretStore(spec.Provider.AWS)...)
}
if spec.Provider.Vault != nil {
if numProviders > 0 {
allErrs = append(allErrs, ErrMultiSecretStoreProviders)
} else {
numProviders++
allErrs = append(allErrs, validateHashiVaultSecretStore(spec.Provider.Vault)...)
}
}
if spec.Provider.Azure != nil {
if numProviders > 0 {
allErrs = append(allErrs, ErrMultiSecretStoreProviders)
} else {
numProviders++
allErrs = append(allErrs, validateAzureKeyVaultSecretStore(spec.Provider.Azure)...)
}
}
if spec.Provider.Alicloud != nil {
if numProviders > 0 {
allErrs = append(allErrs, ErrMultiSecretStoreProviders)
} else {
numProviders++
allErrs = append(allErrs, validateAlicloudSecretStore(spec.Provider.Alicloud)...)
}
}

if numProviders == 0 {
allErrs = append(allErrs, ErrMissingProviderType)
}

return allErrs
}

func validateAWSSecretStore(ss *v1.AWSProvider) []error {
var allErrs []error
if len(ss.Region) == 0 {
allErrs = append(allErrs, ErrEmptyAWSRegion)
}
return allErrs
}

func validateHashiVaultSecretStore(vault *v1.VaultProvider) []error {
var allErrs []error
if len(vault.Server) == 0 {
allErrs = append(allErrs, ErrEmptyVaultServer)
}
return allErrs
}

func validateAzureKeyVaultSecretStore(azureKv *v1.AzureKVProvider) []error {
var allErrs []error
if azureKv.VaultURL == nil || len(*azureKv.VaultURL) == 0 {
allErrs = append(allErrs, ErrEmptyVaultURL)
}
if azureKv.TenantID == nil || len(*azureKv.TenantID) == 0 {
allErrs = append(allErrs, ErrEmptyTenantID)
}
return allErrs
}

func validateAlicloudSecretStore(ac *v1.AlicloudProvider) []error {
var allErrs []error
if len(ac.Region) == 0 {
allErrs = append(allErrs, ErrEmptyAlicloudRegion)
}
return allErrs
}
197 changes: 197 additions & 0 deletions pkg/workspace/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -665,3 +665,200 @@ func TestValidateWholeS3Config(t *testing.T) {
})
}
}

func TestValidateAWSSecretStore(t *testing.T) {
type args struct {
ss *v1.AWSProvider
}
tests := []struct {
name string
args args
want []error
}{
{
name: "valid AWS provider spec",
args: args{
ss: &v1.AWSProvider{
Region: "eu-west-2",
},
},
want: nil,
},
{
name: "invalid AWS provider spec",
args: args{
ss: &v1.AWSProvider{},
},
want: []error{ErrEmptyAWSRegion},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, validateAWSSecretStore(tt.args.ss), "validateAWSSecretStore(%v)", tt.args.ss)
})
}
}

func TestValidateHashiVaultSecretStore(t *testing.T) {
type args struct {
vault *v1.VaultProvider
}
tests := []struct {
name string
args args
want []error
}{
{
name: "valid Hashi Vault provider spec",
args: args{
vault: &v1.VaultProvider{
Server: "https://vault.example.com:8200",
},
},
want: nil,
},
{
name: "invalid Hashi Vault provider spec",
args: args{
vault: &v1.VaultProvider{},
},
want: []error{ErrEmptyVaultServer},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, validateHashiVaultSecretStore(tt.args.vault), "validateHashiVaultSecretStore(%v)", tt.args.vault)
})
}
}

func TestValidateAzureKeyVaultSecretStore(t *testing.T) {
type args struct {
azureKv *v1.AzureKVProvider
}
vaultURL := "https://local.vault.url"
tenantID := "my-tenant-id"
tests := []struct {
name string
args args
want []error
}{
{
name: "valid Azure KV provider spec",
args: args{
azureKv: &v1.AzureKVProvider{
VaultURL: &vaultURL,
TenantID: &tenantID,
},
},
want: nil,
},
{
name: "invalid Azure KV provider spec",
args: args{
azureKv: &v1.AzureKVProvider{},
},
want: []error{ErrEmptyVaultURL, ErrEmptyTenantID},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, validateAzureKeyVaultSecretStore(tt.args.azureKv), "validateAzureKeyVaultSecretStore(%v)", tt.args.azureKv)
})
}
}

func TestValidateAlicloudSecretStore(t *testing.T) {
type args struct {
ac *v1.AlicloudProvider
}
tests := []struct {
name string
args args
want []error
}{
{
name: "valid Alicloud provider spec",
args: args{
ac: &v1.AlicloudProvider{
Region: "sh",
},
},
want: nil,
},
{
name: "invalid Alicloud provider spec",
args: args{
ac: &v1.AlicloudProvider{},
},
want: []error{ErrEmptyAlicloudRegion},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, validateAlicloudSecretStore(tt.args.ac), "validateAlicloudSecretStore(%v)", tt.args.ac)
})
}
}

func TestValidateSecretStoreConfig(t *testing.T) {
type args struct {
spec *v1.SecretStoreSpec
}
tests := []struct {
name string
args args
want []error
}{
{
name: "missing provider spec",
args: args{
spec: &v1.SecretStoreSpec{},
},
want: []error{ErrMissingProvider},
},
{
name: "missing provider type",
args: args{
spec: &v1.SecretStoreSpec{
Provider: &v1.ProviderSpec{},
},
},
want: []error{ErrMissingProviderType},
},
{
name: "multi secret store providers",
args: args{
spec: &v1.SecretStoreSpec{
Provider: &v1.ProviderSpec{
AWS: &v1.AWSProvider{
Region: "us-east-1",
},
Vault: &v1.VaultProvider{
Server: "https://vault.example.com:8200",
},
},
},
},
want: []error{ErrMultiSecretStoreProviders},
},
{
name: "valid secret store spec",
args: args{
spec: &v1.SecretStoreSpec{
Provider: &v1.ProviderSpec{
AWS: &v1.AWSProvider{
Region: "us-east-1",
},
},
},
},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, ValidateSecretStoreConfig(tt.args.spec), "validateAlicloudSecretStore(%v)", tt.args.spec)
})
}
}

0 comments on commit 8dce43b

Please sign in to comment.