Skip to content

Commit

Permalink
Merge branch 'main' into remove_resourceName
Browse files Browse the repository at this point in the history
# Conflicts:
#	internal/service/contentprovider/moduleconfig.go
#	internal/service/moduleconfig/generator/moduleconfig_generator_test.go
#	internal/service/moduleconfig/reader/moduleconfig_reader_test.go
#	internal/service/templategenerator/templategenerator.go
  • Loading branch information
nesmabadr committed Oct 29, 2024
2 parents ba55b91 + 7d1a91d commit 21b8169
Show file tree
Hide file tree
Showing 19 changed files with 329 additions and 62 deletions.
15 changes: 13 additions & 2 deletions cmd/modulectl/create/long.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,31 @@ The module config file is a YAML file used to configure the following attributes
- name: a string, required, the name of the module
- version: a string, required, the version of the module
- channel: a string, required, channel that should be used in the ModuleTemplate CR
- manifest: a string, required, reference to the manifest, must be a relative file name or URL
- manifest: a string, required, reference to the manifest, must be a URL

- defaultCR: a string, optional, reference to a YAML file containing the default CR for the module, must be a relative file name or URL
- defaultCR: a string, optional, reference to a YAML file containing the default CR for the module, must be a URL
- mandatory: a boolean, optional, default=false, indicates whether the module is mandatory to be installed on all clusters
- internal: a boolean, optional, default=false, determines whether the ModuleTemplate CR should have the internal flag or not
- beta: a boolean, optional, default=false, determines whether the ModuleTemplate CR should have the beta flag or not
- security: a string, optional, name of the security scanners config file
- labels: a map with string keys and values, optional, additional labels for the generated ModuleTemplate CR
- annotations: a map with string keys and values, optional, additional annotations for the generated ModuleTemplate CR
- manager: # an object, optional, module resource that indicates the installation readiness of the module
name: a string, required, the name of the module resource
namespace: a string, optional, the namespace of the module resource
group: a string, required, the API group of the module resource
version: a string, required, the API version of the module resource
kind: a string, required, the API kind of the module resource
- associatedResources: a list of Group-Version-Kind(GVK), optional, resources that should be cleaned up with the module deletion
- resources: # a map with string keys and values, optional, additional resources of the module that may be fetched
- name: a string, required, the name of the resource
link: a URL, required, the link to the resource
```

The **manifest** file contains all the module's resources in a single, multi-document YAML file. These resources will be created in the Kyma cluster when the module is activated.
The **defaultCR** file contains a default custom resource for the module that is installed along with the module. It is additionally schema-validated against the Custom Resource Definition.
The CRD used for the validation must exist in the set of the module's resources.
The **resources** are copied to the ModuleTemplate **spec.resources**. If it does not have an entry named 'raw-manifest', the ModuleTemplate **spec.resources** populates this entry from the **manifest** field specified in the module config file.

### Modules as OCI artifacts
Modules are built and distributed as OCI artifacts.
Expand Down
5 changes: 4 additions & 1 deletion cmd/modulectl/scaffold/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func Test_Execute_ParsesOptions(t *testing.T) {
os.Args = []string{
"scaffold",
"--directory", directory,
"--module-config", moduleConfigFile,
"--config-file", moduleConfigFile,
"--overwrite",
"--gen-manifest", manifestFile,
"--gen-default-cr=" + defaultCRFile,
Expand Down Expand Up @@ -84,8 +84,10 @@ func Test_Execute_ParsesOptions(t *testing.T) {

func Test_Execute_ParsesShortOptions(t *testing.T) {
directory := testutils.RandomName(10)
configFile := testutils.RandomName(10)
os.Args = []string{
"scaffold",
"-c", configFile,
"-d", directory,
"-o",
}
Expand All @@ -95,6 +97,7 @@ func Test_Execute_ParsesShortOptions(t *testing.T) {
err := cmd.Execute()
require.NoError(t, err)

assert.Equal(t, configFile, svc.opts.ModuleConfigFileName)
assert.Equal(t, directory, svc.opts.Directory)
assert.True(t, svc.opts.ModuleConfigFileOverwrite)
}
Expand Down
5 changes: 3 additions & 2 deletions cmd/modulectl/scaffold/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const (
DirectoryFlagDefault = "./"
directoryFlagUsage = `Specifies the target directory where the scaffolding shall be generated (default "./").`

ModuleConfigFileFlagName = "module-config"
ModuleConfigFileFlagName = "config-file"
moduleConfigFileFlagShort = "c"
ModuleConfigFileFlagDefault = "scaffold-module-config.yaml"
moduleConfigFileFlagUsage = `Specifies the name of the generated module configuration file (default "scaffold-module-config.yaml").`

Expand Down Expand Up @@ -50,7 +51,7 @@ const (

func parseFlags(flags *pflag.FlagSet, opts *scaffold.Options) {
flags.StringVarP(&opts.Directory, DirectoryFlagName, directoryFlagShort, DirectoryFlagDefault, directoryFlagUsage)
flags.StringVar(&opts.ModuleConfigFileName, ModuleConfigFileFlagName, ModuleConfigFileFlagDefault, moduleConfigFileFlagUsage)
flags.StringVarP(&opts.ModuleConfigFileName, ModuleConfigFileFlagName, moduleConfigFileFlagShort, ModuleConfigFileFlagDefault, moduleConfigFileFlagUsage)
flags.BoolVarP(&opts.ModuleConfigFileOverwrite, ModuleConfigFileOverwriteFlagName, moduleConfigFileOverwriteFlagShort, ModuleConfigFileOverwriteFlagDefault, moduleConfigFileOverwriteFlagUsage)
flags.StringVar(&opts.ManifestFileName, ManifestFileFlagName, ManifestFileFlagDefault, manifestFileFlagUsage)
flags.StringVar(&opts.DefaultCRFileName, DefaultCRFlagName, DefaultCRFlagDefault, defaultCRFlagUsage)
Expand Down
2 changes: 1 addition & 1 deletion cmd/modulectl/scaffold/long.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ allowing for a tailored scaffolding experience according to the specific needs o
The command generates or uses the following files:
- Module Config:
Enabled: Always
Adjustable with flag: --module-config=VALUE
Adjustable with flag: --config-file=VALUE
Generated when: The file doesn't exist or the --overwrite=true flag is provided
Default file name: scaffold-module-config.yaml
- Manifest:
Expand Down
15 changes: 13 additions & 2 deletions docs/gen-docs/modulectl_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,31 @@ The module config file is a YAML file used to configure the following attributes
- name: a string, required, the name of the module
- version: a string, required, the version of the module
- channel: a string, required, channel that should be used in the ModuleTemplate CR
- manifest: a string, required, reference to the manifest, must be a relative file name or URL
- manifest: a string, required, reference to the manifest, must be a URL

- defaultCR: a string, optional, reference to a YAML file containing the default CR for the module, must be a relative file name or URL
- defaultCR: a string, optional, reference to a YAML file containing the default CR for the module, must be a URL
- mandatory: a boolean, optional, default=false, indicates whether the module is mandatory to be installed on all clusters
- internal: a boolean, optional, default=false, determines whether the ModuleTemplate CR should have the internal flag or not
- beta: a boolean, optional, default=false, determines whether the ModuleTemplate CR should have the beta flag or not
- security: a string, optional, name of the security scanners config file
- labels: a map with string keys and values, optional, additional labels for the generated ModuleTemplate CR
- annotations: a map with string keys and values, optional, additional annotations for the generated ModuleTemplate CR
- manager: # an object, optional, module resource that indicates the installation readiness of the module
name: a string, required, the name of the module resource
namespace: a string, optional, the namespace of the module resource
group: a string, required, the API group of the module resource
version: a string, required, the API version of the module resource
kind: a string, required, the API kind of the module resource
- associatedResources: a list of Group-Version-Kind(GVK), optional, resources that should be cleaned up with the module deletion
- resources: # a map with string keys and values, optional, additional resources of the module that may be fetched
- name: a string, required, the name of the resource
link: a URL, required, the link to the resource
```
The **manifest** file contains all the module's resources in a single, multi-document YAML file. These resources will be created in the Kyma cluster when the module is activated.
The **defaultCR** file contains a default custom resource for the module that is installed along with the module. It is additionally schema-validated against the Custom Resource Definition.
The CRD used for the validation must exist in the set of the module's resources.
The **resources** are copied to the ModuleTemplate **spec.resources**. If it does not have an entry named 'raw-manifest', the ModuleTemplate **spec.resources** populates this entry from the **manifest** field specified in the module config file.
### Modules as OCI artifacts
Modules are built and distributed as OCI artifacts.
Expand Down
4 changes: 2 additions & 2 deletions docs/gen-docs/modulectl_scaffold.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ allowing for a tailored scaffolding experience according to the specific needs o
The command generates or uses the following files:
- Module Config:
Enabled: Always
Adjustable with flag: --module-config=VALUE
Adjustable with flag: --config-file=VALUE
Generated when: The file doesn't exist or the --overwrite=true flag is provided
Default file name: scaffold-module-config.yaml
- Manifest:
Expand Down Expand Up @@ -69,13 +69,13 @@ Generate a scaffold with a manifest file, default CR and security-scanners confi
## Flags

```bash
-c, --config-file string Specifies the name of the generated module configuration file (default "scaffold-module-config.yaml").
-d, --directory string Specifies the target directory where the scaffolding shall be generated (default "./").
--gen-default-cr string Specifies the default CR in the generated module config. A blank default CR file is generated if it doesn't exist (default "default-cr.yaml").
--gen-manifest string Specifies the manifest in the generated module config. A blank manifest file is generated if it doesn't exist (default "manifest.yaml").
--gen-security-config string Specifies the security file in the generated module config. A scaffold security config file is generated if it doesn't exist (default "sec-scanners-config.yaml").
-h, --help Provides help for the scaffold command.
--module-channel string Specifies the module channel in the generated module config file (default "regular").
--module-config string Specifies the name of the generated module configuration file (default "scaffold-module-config.yaml").
--module-name string Specifies the module name in the generated config file (default "kyma-project.io/module/mymodule").
--module-version string Specifies the module version in the generated module config file (default "0.0.1").
-o, --overwrite Specifies if the command overwrites an existing module configuration file.
Expand Down
16 changes: 16 additions & 0 deletions internal/common/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,19 @@ func validateSemanticVersion(version string) error {

return nil
}

func ValidateGvk(group, version, kind string) error {
if kind == "" {
return fmt.Errorf("kind must not be empty: %w", commonerrors.ErrInvalidOption)
}

if group == "" {
return fmt.Errorf("group must not be empty: %w", commonerrors.ErrInvalidOption)
}

if version == "" {
return fmt.Errorf("version must not be empty: %w", commonerrors.ErrInvalidOption)
}

return nil
}
41 changes: 41 additions & 0 deletions internal/common/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,47 @@ func TestValidateNamespace(t *testing.T) {
}
}

func TestValidateGvk(t *testing.T) {
type args struct {
group string
version string
kind string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "valid GVK",
args: args{group: "kyma-project.io", version: "v1alpha1", kind: "Module"},
wantErr: false,
},
{
name: "invalid GVK when group empty",
args: args{version: "v1alpha1", kind: "Module"},
wantErr: true,
},
{
name: "invalid GVK when version empty",
args: args{group: "kyma-project.io", kind: "Module"},
wantErr: true,
},
{
name: "invalid GVK when kind empty",
args: args{group: "kyma-project.io", version: "v1alpha1"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := validation.ValidateGvk(tt.args.group, tt.args.version, tt.args.kind); (err != nil) != tt.wantErr {
t.Errorf("ValidateGvk() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func TestValidateResources(t *testing.T) {
tests := []struct {
name string
Expand Down
29 changes: 15 additions & 14 deletions internal/service/contentprovider/moduleconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,21 @@ type Manager struct {
}

type ModuleConfig struct {
Name string `yaml:"name" comment:"required, the name of the Module"`
Version string `yaml:"version" comment:"required, the version of the Module"`
Channel string `yaml:"channel" comment:"required, channel that should be used in the ModuleTemplate"`
Manifest string `yaml:"manifest" comment:"required, relative path or remote URL to the manifests"`
Mandatory bool `yaml:"mandatory" comment:"optional, default=false, indicates whether the module is mandatory to be installed on all clusters"`
DefaultCR string `yaml:"defaultCR" comment:"optional, relative path or remote URL to a YAML file containing the default CR for the module"`
Namespace string `yaml:"namespace" comment:"optional, default=kcp-system, the namespace where the ModuleTemplate will be deployed"`
Security string `yaml:"security" comment:"optional, name of the security scanners config file"`
Internal bool `yaml:"internal" comment:"optional, default=false, determines whether the ModuleTemplate should have the internal flag or not"`
Beta bool `yaml:"beta" comment:"optional, default=false, determines whether the ModuleTemplate should have the beta flag or not"`
Labels map[string]string `yaml:"labels" comment:"optional, additional labels for the ModuleTemplate"`
Annotations map[string]string `yaml:"annotations" comment:"optional, additional annotations for the ModuleTemplate"`
Manager *Manager `yaml:"manager" comment:"optional, the module resource that can be used to indicate the installation readiness of the module. This is typically the manager deployment of the module"`
Resources ResourcesMap `yaml:"resources,omitempty" comment:"optional, additional resources of the ModuleTemplate that may be fetched"`
Name string `yaml:"name" comment:"required, the name of the Module"`
Version string `yaml:"version" comment:"required, the version of the Module"`
Channel string `yaml:"channel" comment:"required, channel that should be used in the ModuleTemplate"`
Manifest string `yaml:"manifest" comment:"required, relative path or remote URL to the manifests"`
Mandatory bool `yaml:"mandatory" comment:"optional, default=false, indicates whether the module is mandatory to be installed on all clusters"`
DefaultCR string `yaml:"defaultCR" comment:"optional, relative path or remote URL to a YAML file containing the default CR for the module"`
Namespace string `yaml:"namespace" comment:"optional, default=kcp-system, the namespace where the ModuleTemplate will be deployed"`
Security string `yaml:"security" comment:"optional, name of the security scanners config file"`
Internal bool `yaml:"internal" comment:"optional, default=false, determines whether the ModuleTemplate should have the internal flag or not"`
Beta bool `yaml:"beta" comment:"optional, default=false, determines whether the ModuleTemplate should have the beta flag or not"`
Labels map[string]string `yaml:"labels" comment:"optional, additional labels for the ModuleTemplate"`
Annotations map[string]string `yaml:"annotations" comment:"optional, additional annotations for the ModuleTemplate"`
AssociatedResources []*metav1.GroupVersionKind `yaml:"associatedResources" comment:"optional, GVK of the resources which are associated with the module and have to be deleted with module deletion"`
Manager *Manager `yaml:"manager" comment:"optional, the module resource that can be used to indicate the installation readiness of the module. This is typically the manager deployment of the module"`
Resources ResourcesMap `yaml:"resources,omitempty" comment:"optional, additional resources of the ModuleTemplate that may be fetched"`
}

type resource struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ func (*fileExistsStub) ReadFile(_ string) ([]byte, error) {
Beta: false,
Labels: map[string]string{"label1": "value1"},
Annotations: map[string]string{"annotation1": "value1"},
AssociatedResources: []*metav1.GroupVersionKind{
{
Group: "networking.istio.io",
Version: "v1alpha3",
Kind: "Gateway",
},
},
Manager: &contentprovider.Manager{
Name: "manager-name",
Namespace: "manager-namespace",
Expand All @@ -139,6 +146,7 @@ func (*fileExistsStub) ReadFile(_ string) ([]byte, error) {
Kind: "Deployment",
},
},
Resources: contentprovider.ResourcesMap{},
}

return yaml.Marshal(moduleConfig)
Expand Down
29 changes: 16 additions & 13 deletions internal/service/moduleconfig/reader/moduleconfig_reader.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package moduleconfigreader

import (
"errors"
"fmt"

"gopkg.in/yaml.v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

commonerrors "github.com/kyma-project/modulectl/internal/common/errors"
"github.com/kyma-project/modulectl/internal/common/validation"
"github.com/kyma-project/modulectl/internal/service/contentprovider"
)

var ErrNoPathForDefaultCR = errors.New("no path for default CR given")

type FileSystem interface {
ReadFile(path string) ([]byte, error)
}
Expand Down Expand Up @@ -76,13 +74,26 @@ func ValidateModuleConfig(moduleConfig *contentprovider.ModuleConfig) error {
}
}

if err := ValidateAssociatedResources(moduleConfig.AssociatedResources); err != nil {
return fmt.Errorf("failed to validate associated resources: %w", err)
}

if err := ValidateManager(moduleConfig.Manager); err != nil {
return fmt.Errorf("failed to validate manager: %w", err)
}

return nil
}

func ValidateAssociatedResources(resources []*metav1.GroupVersionKind) error {
for _, resource := range resources {
if err := validation.ValidateGvk(resource.Group, resource.Version, resource.Kind); err != nil {
return fmt.Errorf("GVK is invalid: %w", err)
}
}
return nil
}

func ValidateManager(manager *contentprovider.Manager) error {
if manager == nil {
return nil
Expand All @@ -92,16 +103,8 @@ func ValidateManager(manager *contentprovider.Manager) error {
return fmt.Errorf("name must not be empty: %w", commonerrors.ErrInvalidOption)
}

if manager.Kind == "" {
return fmt.Errorf("kind must not be empty: %w", commonerrors.ErrInvalidOption)
}

if manager.Group == "" {
return fmt.Errorf("group must not be empty: %w", commonerrors.ErrInvalidOption)
}

if manager.Version == "" {
return fmt.Errorf("version must not be empty: %w", commonerrors.ErrInvalidOption)
if err := validation.ValidateGvk(manager.Group, manager.Version, manager.Kind); err != nil {
return fmt.Errorf("GVK is invalid: %w", err)
}

if manager.Namespace != "" {
Expand Down
Loading

0 comments on commit 21b8169

Please sign in to comment.