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: validate module path and version in the workspace #1131

Merged
merged 1 commit into from
May 22, 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
11 changes: 4 additions & 7 deletions pkg/cmd/workspace/util/testdata/valid_ws.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,11 @@ modules:
- bar
instanceType: db.t3.small
network:
path: ghcr.io/kusionstack/mysql
version: 0.1.0
configs:
default:
type: aws
runtimes:
context:
kubernetes:
kubeConfig: /etc/kubeconfig.yaml
terraform:
aws:
source: hashicorp/aws
version: 1.0.4
region: us-east-1
kubeConfig: /etc/kubeconfig.yaml
4 changes: 2 additions & 2 deletions pkg/engine/api/generate/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1"
"kusionstack.io/kusion/pkg/engine/api/builders"
"kusionstack.io/kusion/pkg/engine/api/generate/run"
"kusionstack.io/kusion/pkg/modules/generators"
"kusionstack.io/kusion/pkg/modules"
"kusionstack.io/kusion/pkg/util/io"
"kusionstack.io/kusion/pkg/util/kfile"
)
Expand Down Expand Up @@ -101,7 +101,7 @@ func CopyDependentModules(workDir string) error {
for _, dep := range modFile.Deps {
if dep.Source.Oci != nil {
// ignore workload modules
if generators.IgnoreModules[dep.Name] {
if modules.IgnoreModules[dep.Name] {
continue
}

Expand Down
29 changes: 17 additions & 12 deletions pkg/modules/generators/app_configurations_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ type appConfigurationGenerator struct {
dependencies *pkg.Dependencies
}

// IgnoreModules todo@dayuan delete this condition after workload is changed into a module
var IgnoreModules = map[string]bool{
"service": true,
"job": true,
}

func NewAppConfigurationGenerator(
project string,
stack string,
Expand Down Expand Up @@ -357,7 +351,7 @@ func (g *appConfigurationGenerator) callModules(projectModuleConfigs map[string]
// generate customized module resources
for t, config := range indexModuleConfig {
// ignore workload modules
if IgnoreModules[t] {
if modules.IgnoreModules[t] {
continue
}

Expand Down Expand Up @@ -419,7 +413,10 @@ func (g *appConfigurationGenerator) buildModuleConfigIndex(platformModuleConfigs
return nil, err
}
log.Info("build module index of accessory:%s module key: %s", accName, key)
moduleName := getModuleName(accessory)
moduleName, err := getModuleName(accessory)
if err != nil {
return nil, err
}
indexModuleConfig[key] = moduleConfig{
devConfig: accessory,
platformConfig: platformModuleConfigs[moduleName],
Expand All @@ -432,7 +429,11 @@ func (g *appConfigurationGenerator) buildModuleConfigIndex(platformModuleConfigs
// parseModuleKey returns the module key of the accessory in format of "org/module@version"
// example: "kusionstack/mysql@v0.1.0"
func parseModuleKey(accessory v1.Accessory, dependencies *pkg.Dependencies) (string, error) {
moduleName := getModuleName(accessory)
moduleName, err := getModuleName(accessory)
if err != nil {
return "", err
}

// find module namespace and version
d, ok := dependencies.Deps[moduleName]
if !ok {
Expand All @@ -452,9 +453,13 @@ func parseModuleKey(accessory v1.Accessory, dependencies *pkg.Dependencies) (str
return key, nil
}

func getModuleName(accessory v1.Accessory) string {
split := strings.Split(accessory["_type"].(string), ".")
return split[0]
func getModuleName(accessory v1.Accessory) (string, error) {
t, ok := accessory["_type"]
if !ok {
return "", errors.New("can not find '_type' in module config")
}
split := strings.Split(t.(string), ".")
return split[0], nil
}

func (g *appConfigurationGenerator) initModuleRequest(config moduleConfig) (*proto.GeneratorRequest, error) {
Expand Down
21 changes: 16 additions & 5 deletions pkg/modules/generators/app_configurations_generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ func TestAppConfigurationGenerator_Generate_CustomNamespace(t *testing.T) {
ws := buildMockWorkspace("fakeNs")
dep := &pkg.Dependencies{
Deps: map[string]pkg.Dependency{
"fake": {
Name: "fakeName",
"port": {
Name: "port",
Version: "1.0.0",
},
},
}
Expand Down Expand Up @@ -176,6 +177,12 @@ func buildMockApp() (string, *v1.AppConfiguration) {
},
},
},
Accessories: map[string]v1.Accessory{
"port": map[string]interface{}{
"port": "2333",
"_type": "port.Port",
},
},
}
}

Expand Down Expand Up @@ -203,13 +210,17 @@ func buildMockWorkspace(namespace string) *v1.Workspace {
},
},
"port": &v1.ModuleConfig{
Path: "kusionstack.io/port",
Version: "v1.0.0",
Configs: v1.Configs{
Default: v1.GenericConfig{
"type": "aws",
},
},
},
"namespace": &v1.ModuleConfig{
Path: "kusionstack.io/namespace",
Version: "v1.0.0",
Configs: v1.Configs{
Default: v1.GenericConfig{
"name": namespace,
Expand Down Expand Up @@ -344,8 +355,8 @@ func TestAppConfigurationGenerator_CallModules(t *testing.T) {
// Mock dependencies
dependencies := &pkg.Dependencies{
Deps: map[string]pkg.Dependency{
"module1": {
Version: "v1.0.0",
"port": {
Version: "1.0.0",
Source: pkg.Source{
Oci: &pkg.Oci{
Repo: "kusionstack/module1",
Expand All @@ -357,7 +368,7 @@ func TestAppConfigurationGenerator_CallModules(t *testing.T) {

// Mock project module configs
projectModuleConfigs := map[string]v1.GenericConfig{
"module1": {
"port": {
"config1": "value1",
},
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/modules/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import (
v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1"
)

// IgnoreModules todo@dayuan delete this condition after workload is changed into a module
var IgnoreModules = map[string]bool{
"service": true,
"job": true,
}

// CallGeneratorFuncs calls each NewGeneratorFunc in the given slice
// and returns a slice of Generator instances.
func CallGeneratorFuncs(newGenerators ...NewGeneratorFunc) ([]Generator, error) {
Expand Down
26 changes: 21 additions & 5 deletions pkg/workspace/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
utilerrors "k8s.io/apimachinery/pkg/util/errors"

v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1"
"kusionstack.io/kusion/pkg/modules"
)

var (
Expand Down Expand Up @@ -60,7 +61,7 @@ func ValidateModuleConfigs(configs v1.ModuleConfigs) error {
if cfg == nil {
return fmt.Errorf("%w, module name: %s", ErrEmptyModuleConfig, name)
}
if err := ValidateModuleConfig(cfg); err != nil {
if err := ValidateModuleConfig(name, cfg); err != nil {
return fmt.Errorf("%w, module name: %s", err, name)
}
}
Expand All @@ -69,7 +70,10 @@ func ValidateModuleConfigs(configs v1.ModuleConfigs) error {
}

// ValidateModuleConfig is used to validate the moduleConfig is valid or not.
func ValidateModuleConfig(config *v1.ModuleConfig) error {
func ValidateModuleConfig(name string, config *v1.ModuleConfig) error {
if err := ValidateModuleMetadata(name, config); err != nil {
return err
}
if err := ValidateModuleDefaultConfig(config.Configs.Default); err != nil {
return err
}
Expand All @@ -79,10 +83,22 @@ func ValidateModuleConfig(config *v1.ModuleConfig) error {
return nil
}

func ValidateModuleMetadata(name string, config *v1.ModuleConfig) error {
if modules.IgnoreModules[name] {
return nil
}
if config.Version == "" {
return fmt.Errorf("empty version of module:%s in the workspacek config", name)
}
if config.Path == "" {
return fmt.Errorf("empty path of module:%s in the workspacek config", name)
}
return nil
}

func ValidateModuleDefaultConfig(config v1.GenericConfig) error {
// todo@dayuan validate path and version in the config when we have turned workload into a module
if len(config) == 0 {
return fmt.Errorf("%w, block name: %s", ErrEmptyModuleConfigBlock, v1.DefaultBlock)
if config == nil {
return nil
}
if _, ok := config[v1.ProjectSelectorField]; ok {
return ErrNotEmptyModuleConfigProjectSelector
Expand Down
26 changes: 25 additions & 1 deletion pkg/workspace/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func mockValidModuleConfigs() map[string]*v1.ModuleConfig {
},
},
"network": {
Path: "ghcr.io/kusionstack/network",
Version: "0.1.0",
Configs: v1.Configs{
Default: v1.GenericConfig{
"type": "aws",
Expand Down Expand Up @@ -274,12 +276,34 @@ func TestValidateModuleConfig(t *testing.T) {

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
err := ValidateModuleConfig(&tc.moduleConfig)
err := ValidateModuleConfig("mysql", &tc.moduleConfig)
assert.Equal(t, tc.success, err == nil)
})
}
}

func TestValidateModuleMetadata(t *testing.T) {
t.Run("ValidModuleMetadata", func(t *testing.T) {
err := ValidateModuleMetadata("testModule", &v1.ModuleConfig{Version: "1.0.0", Path: "/path/to/module"})
assert.NoError(t, err)
})

t.Run("IgnoreModule", func(t *testing.T) {
err := ValidateModuleMetadata("service", &v1.ModuleConfig{Version: "1.0.0", Path: "/path/to/module"})
assert.NoError(t, err)
})

t.Run("EmptyModuleVersion", func(t *testing.T) {
err := ValidateModuleMetadata("testModule", &v1.ModuleConfig{Version: "", Path: "/path/to/module"})
assert.Error(t, err)
})

t.Run("EmptyModulePath", func(t *testing.T) {
err := ValidateModuleMetadata("testModule", &v1.ModuleConfig{Version: "1.0.0", Path: ""})
assert.Error(t, err)
})
}

func TestValidateAWSSecretStore(t *testing.T) {
type args struct {
ss *v1.AWSProvider
Expand Down
Loading