Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add backend config completion and validation funcs #891

Merged
merged 1 commit into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pkg/apis/core/v1/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ const (
BackendTypeMysql = "mysql"
BackendTypeOss = "oss"
BackendTypeS3 = "s3"

EnvBackendMysqlPassword = "KUSION_BACKEND_MYSQL_PASSWORD"
EnvOssAccessKeyID = "OSS_ACCESS_KEY_ID"
EnvOssAccessKeySecret = "OSS_ACCESS_KEY_SECRET"
EnvAwsAccessKeyID = "AWS_ACCESS_KEY_ID"
EnvAwsSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
EnvAwsDefaultRegion = "AWS_DEFAULT_REGION"
EnvAwsRegion = "AWS_REGION"

DefaultMysqlPort = 3306
)

// BackendConfigs contains the configuration of multiple backends and the current backend.
Expand Down
51 changes: 51 additions & 0 deletions pkg/backend/storages/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package storages

import (
"os"

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

// CompleteMysqlConfig sets default value of mysql config if not set.
func CompleteMysqlConfig(config *v1.BackendMysqlConfig) {
if config.Port == 0 {
config.Port = v1.DefaultMysqlPort
}
password := os.Getenv(v1.EnvBackendMysqlPassword)
if password != "" {
config.Password = password
}
}

// CompleteOssConfig constructs the whole oss config by environment variables if set.
func CompleteOssConfig(config *v1.BackendOssConfig) {
accessKeyID := os.Getenv(v1.EnvOssAccessKeyID)
accessKeySecret := os.Getenv(v1.EnvOssAccessKeySecret)

if accessKeyID != "" {
config.AccessKeyID = accessKeyID
}
if accessKeySecret != "" {
config.AccessKeySecret = accessKeySecret
}
}

// CompleteS3Config constructs the whole s3 config by environment variables if set.
func CompleteS3Config(config *v1.BackendS3Config) {
accessKeyID := os.Getenv(v1.EnvAwsAccessKeyID)
accessKeySecret := os.Getenv(v1.EnvAwsSecretAccessKey)
region := os.Getenv(v1.EnvAwsRegion)
if region == "" {
region = os.Getenv(v1.EnvAwsDefaultRegion)
}

if accessKeyID != "" {
config.AccessKeyID = accessKeyID
}
if accessKeySecret != "" {
config.AccessKeySecret = accessKeySecret
}
if region != "" {
config.Region = region
}
}
132 changes: 132 additions & 0 deletions pkg/backend/storages/completion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package storages

import (
"os"
"testing"

"github.com/stretchr/testify/assert"

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

func TestCompleteMysqlConfig(t *testing.T) {
testcases := []struct {
name string
config *v1.BackendMysqlConfig
envs map[string]string
completeConfig *v1.BackendMysqlConfig
}{
{
name: "complete mysql config",
config: &v1.BackendMysqlConfig{
DBName: "kusion",
User: "kk",
Host: "127.0.0.1",
},
envs: map[string]string{
v1.EnvBackendMysqlPassword: "fake-password",
},
completeConfig: &v1.BackendMysqlConfig{
DBName: "kusion",
User: "kk",
Host: "127.0.0.1",
Port: 3306,
Password: "fake-password",
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
for k, v := range tc.envs {
_ = os.Setenv(k, v)
}
CompleteMysqlConfig(tc.config)
assert.Equal(t, tc.completeConfig, tc.config)
})
}
}

func TestCompleteOssConfig(t *testing.T) {
testcases := []struct {
name string
config *v1.BackendOssConfig
envs map[string]string
completeConfig *v1.BackendOssConfig
}{
{
name: "complete oss config",
config: &v1.BackendOssConfig{
GenericBackendObjectStorageConfig: &v1.GenericBackendObjectStorageConfig{
Endpoint: "http://oss-cn-hangzhou.aliyuncs.com",
Bucket: "kusion",
},
},
envs: map[string]string{
v1.EnvOssAccessKeyID: "fake-ak",
v1.EnvOssAccessKeySecret: "fake-sk",
},
completeConfig: &v1.BackendOssConfig{
GenericBackendObjectStorageConfig: &v1.GenericBackendObjectStorageConfig{
Endpoint: "http://oss-cn-hangzhou.aliyuncs.com",
Bucket: "kusion",
AccessKeyID: "fake-ak",
AccessKeySecret: "fake-sk",
},
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
for k, v := range tc.envs {
_ = os.Setenv(k, v)
}
CompleteOssConfig(tc.config)
assert.Equal(t, tc.completeConfig, tc.config)
})
}
}

func TestCompleteS3Config(t *testing.T) {
testcases := []struct {
name string
config *v1.BackendS3Config
envs map[string]string
completeConfig *v1.BackendS3Config
}{
{
name: "complete s3 config",
config: &v1.BackendS3Config{
GenericBackendObjectStorageConfig: &v1.GenericBackendObjectStorageConfig{
Endpoint: "fake-endpoint",
Bucket: "kusion",
},
},
envs: map[string]string{
v1.EnvAwsRegion: "us-east-1",
v1.EnvAwsAccessKeyID: "fake-ak",
v1.EnvAwsSecretAccessKey: "fake-sk",
},
completeConfig: &v1.BackendS3Config{
GenericBackendObjectStorageConfig: &v1.GenericBackendObjectStorageConfig{
Endpoint: "fake-endpoint",
Bucket: "kusion",
AccessKeyID: "fake-ak",
AccessKeySecret: "fake-sk",
},
Region: "us-east-1",
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
for k, v := range tc.envs {
_ = os.Setenv(k, v)
}
CompleteS3Config(tc.config)
assert.Equal(t, tc.completeConfig, tc.config)
})
}
}
115 changes: 115 additions & 0 deletions pkg/backend/storages/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package storages

import (
"errors"
"fmt"

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

var (
ErrEmptyMysqlDBName = errors.New("empty db name")
ErrEmptyMysqlUser = errors.New("empty mysql db user")
ErrEmptyMysqlHost = errors.New("empty mysql host")
ErrInvalidMysqlPort = errors.New("mysql port must be between 1 and 65535")
ErrEmptyBucket = errors.New("empty bucket")
ErrEmptyAccessKeyID = errors.New("empty access key id")
ErrEmptyAccessKeySecret = errors.New("empty access key secret")
ErrEmptyOssEndpoint = errors.New("empty oss endpoint")
ErrEmptyS3Region = errors.New("empty s3 region")
)

// ValidateMysqlConfig is used to validate the v1.BackendMysqlConfig is valid or not.
// If valid, the config contains all valid items to new a mysql DB.
func ValidateMysqlConfig(config *v1.BackendMysqlConfig) error {
if err := ValidateMysqlConfigFromFile(config); err != nil {
return err
}
if config.Port < 1 || config.Port > 65535 {
return ErrInvalidMysqlPort
}
return nil
}

// ValidateMysqlConfigFromFile is used to validate the v1.BackendMysqlConfig parsed from config file is valid
// or not, where the sensitive data items set as environment variables are not included.
func ValidateMysqlConfigFromFile(config *v1.BackendMysqlConfig) error {
if config.DBName == "" {
return ErrEmptyMysqlDBName
}
if config.User == "" {
return ErrEmptyMysqlUser
}
if config.Host == "" {
return ErrEmptyMysqlHost
}
if config.Port != 0 && (config.Port < 1 || config.Port > 65535) {
return ErrInvalidMysqlPort
}
return nil
}

// ValidateOssConfig is used to validate v1.BackendOssConfig is valid or not, where all the items are included.
// If valid, the config contains all valid items to new an oss client.
func ValidateOssConfig(config *v1.BackendOssConfig) error {
if err := ValidateOssConfigFromFile(config); err != nil {
return err
}
if err := validateGenericObjectStorageSecret(config.AccessKeyID, config.AccessKeySecret); err != nil {
return fmt.Errorf("%w of %s", err, v1.BackendTypeOss)
}
return nil
}

// ValidateOssConfigFromFile is used to validate the v1.BackendOssConfig parsed from config file is valid or not,
// where the sensitive data items set as environment variables are not included.
func ValidateOssConfigFromFile(config *v1.BackendOssConfig) error {
if err := validateGenericObjectStorageBucket(config.Bucket); err != nil {
return fmt.Errorf("%w of %s", err, v1.BackendTypeOss)
}
if config.Endpoint == "" {
return ErrEmptyOssEndpoint
}
return nil
}

// ValidateS3Config is used to validate s3Config is valid or not, where all the items are included.
// If valid, the config contains all valid items to new a s3 client.
func ValidateS3Config(config *v1.BackendS3Config) error {
if err := ValidateS3ConfigFromFile(config); err != nil {
return err
}
if err := validateGenericObjectStorageSecret(config.AccessKeyID, config.AccessKeySecret); err != nil {
return fmt.Errorf("%w of %s", err, v1.BackendTypeS3)
}
if config.Region == "" {
return ErrEmptyS3Region
}
return nil
}

// ValidateS3ConfigFromFile is used to validate the v1.BackendS3Config parsed from config file is valid or not,
// where the sensitive data items set as environment variables are not included.
func ValidateS3ConfigFromFile(config *v1.BackendS3Config) error {
if err := validateGenericObjectStorageBucket(config.Bucket); err != nil {
return fmt.Errorf("%w of %s", err, v1.BackendTypeS3)
}
return nil
}

func validateGenericObjectStorageBucket(bucket string) error {
if bucket == "" {
return ErrEmptyBucket
}
return nil
}

func validateGenericObjectStorageSecret(ak, sk string) error {
if ak == "" {
return ErrEmptyAccessKeyID
}
if sk == "" {
return ErrEmptyAccessKeySecret
}
return nil
}
Loading
Loading