Skip to content

Commit

Permalink
Merge branch 'main' into fix_documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
nesmabadr authored Oct 24, 2024
2 parents dcf1bef + 38f904d commit 2fbc4c0
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 46 deletions.
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
31 changes: 16 additions & 15 deletions internal/service/contentprovider/moduleconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,22 @@ 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"`
ResourceName string `yaml:"resourceName" comment:"optional, default={name}-{channel}, when channel is 'none', the default is {name}-{version}, the name for the ModuleTemplate that will be created"`
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"`
ResourceName string `yaml:"resourceName" comment:"optional, default={name}-{channel}, when channel is 'none', the default is {name}-{version}, the name for the ModuleTemplate that will be created"`
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 @@ -131,6 +131,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 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
62 changes: 62 additions & 0 deletions internal/service/moduleconfig/reader/moduleconfig_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ func Test_ParseModuleConfig_Returns_CorrectModuleConfig(t *testing.T) {
require.False(t, result.Beta)
require.Equal(t, map[string]string{"label1": "value1"}, result.Labels)
require.Equal(t, map[string]string{"annotation1": "value1"}, result.Annotations)
require.Equal(t, "networking.istio.io", result.AssociatedResources[0].Group)
require.Equal(t, "v1alpha3", result.AssociatedResources[0].Version)
require.Equal(t, "Gateway", result.AssociatedResources[0].Kind)
require.Equal(t, contentprovider.ResourcesMap{
"rawManifest": "https://github.com/kyma-project/template-operator/releases/download/1.0.1/template-operator.yaml",
}, result.Resources)
Expand Down Expand Up @@ -299,6 +302,58 @@ func Test_ValidateManager(t *testing.T) {
}
}

func Test_ValidateAssociatedResources(t *testing.T) {
tests := []struct {
name string
resources []*metav1.GroupVersionKind
wantErr bool
}{
{
name: "pass on empty resources",
resources: []*metav1.GroupVersionKind{},
wantErr: false,
},
{
name: "pass when all resources are valid",
resources: []*metav1.GroupVersionKind{
{
Group: "networking.istio.io",
Version: "v1alpha3",
Kind: "Gateway",
},
{
Group: "apps",
Version: "v1",
Kind: "Deployment",
},
},
wantErr: false,
},
{
name: "fail when even one resources is invalid",
resources: []*metav1.GroupVersionKind{
{
Group: "networking.istio.io",
Version: "v1alpha3",
Kind: "Gateway",
},
{
Group: "apps",
Kind: "Deployment",
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := moduleconfigreader.ValidateAssociatedResources(tt.resources); (err != nil) != tt.wantErr {
t.Errorf("ValidateAssociatedResources() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

// Test Stubs

type fileExistsStub struct{}
Expand All @@ -321,6 +376,13 @@ var expectedReturnedModuleConfig = contentprovider.ModuleConfig{
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",
},
},
Resources: contentprovider.ResourcesMap{
"rawManifest": "https://github.com/kyma-project/template-operator/releases/download/1.0.1/template-operator.yaml",
},
Expand Down
45 changes: 28 additions & 17 deletions internal/service/templategenerator/templategenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"text/template"

"github.com/kyma-project/lifecycle-manager/api/shared"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"ocm.software/ocm/api/oci"
"ocm.software/ocm/api/ocm/compdesc"
"sigs.k8s.io/yaml"
Expand Down Expand Up @@ -60,6 +61,14 @@ metadata:
spec:
channel: {{.Channel}}
mandatory: {{.Mandatory}}
{{- with .AssociatedResources}}
associatedResources:
{{- range .}}
- group: {{.Group}}
version: {{.Version}}
kind: {{.Kind}}
{{- end}}
{{- end}}
{{- with .Data}}
data:
{{. | indent 4}}
Expand Down Expand Up @@ -87,16 +96,17 @@ spec:
)

type moduleTemplateData struct {
ResourceName string
Namespace string
Descriptor compdesc.ComponentDescriptorVersion
Channel string
Labels map[string]string
Annotations map[string]string
Mandatory bool
Data string
Resources contentprovider.ResourcesMap
Manager *contentprovider.Manager
ResourceName string
Namespace string
Descriptor compdesc.ComponentDescriptorVersion
Channel string
Labels map[string]string
Annotations map[string]string
Mandatory bool
Data string
AssociatedResources []*metav1.GroupVersionKind
Resources contentprovider.ResourcesMap
Manager *contentprovider.Manager
}

func (s *Service) GenerateModuleTemplate(
Expand Down Expand Up @@ -140,13 +150,14 @@ func (s *Service) GenerateModuleTemplate(
}

mtData := moduleTemplateData{
ResourceName: moduleConfig.ResourceName,
Namespace: moduleConfig.Namespace,
Descriptor: cva,
Channel: moduleConfig.Channel,
Labels: labels,
Annotations: annotations,
Mandatory: moduleConfig.Mandatory,
ResourceName: moduleConfig.ResourceName,
Namespace: moduleConfig.Namespace,
Descriptor: cva,
Channel: moduleConfig.Channel,
Labels: labels,
Annotations: annotations,
Mandatory: moduleConfig.Mandatory,
AssociatedResources: moduleConfig.AssociatedResources,
Resources: contentprovider.ResourcesMap{
"rawManifest": moduleConfig.Manifest, // defaults rawManifest to Manifest; may be overwritten by explicitly provided entries
},
Expand Down
37 changes: 37 additions & 0 deletions internal/service/templategenerator/templategenerator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,43 @@ func TestGenerateModuleTemplate_Success_With_Overwritten_RawManifest(t *testing.
require.NotContains(t, mockFS.writtenTemplate, "https://github.com/kyma-project/template-operator/releases/download/1.0.1/template-operator.yaml")
}

func TestGenerateModuleTemplateWithAssociatedResources_Success(t *testing.T) {
mockFS := &mockFileSystem{}
svc, _ := templategenerator.NewService(mockFS)

moduleConfig := &contentprovider.ModuleConfig{
ResourceName: "test-resource",
Namespace: "default",
Channel: "stable",
Labels: map[string]string{"key": "value"},
Annotations: map[string]string{"annotation": "value"},
Mandatory: true,
AssociatedResources: []*metav1.GroupVersionKind{
{
Group: "networking.istio.io",
Version: "v1alpha3",
Kind: "Gateway",
},
},
}
descriptor := testutils.CreateComponentDescriptor("example.com/component", "1.0.0")
data := []byte("test-data")

err := svc.GenerateModuleTemplate(moduleConfig, descriptor, data, true, "output.yaml")

require.NoError(t, err)
require.Equal(t, "output.yaml", mockFS.path)
require.Contains(t, mockFS.writtenTemplate, "test-resource")
require.Contains(t, mockFS.writtenTemplate, "default")
require.Contains(t, mockFS.writtenTemplate, "stable")
require.Contains(t, mockFS.writtenTemplate, "test-data")
require.Contains(t, mockFS.writtenTemplate, "example.com/component")
require.Contains(t, mockFS.writtenTemplate, "associatedResources")
require.Contains(t, mockFS.writtenTemplate, "networking.istio.io")
require.Contains(t, mockFS.writtenTemplate, "v1alpha3")
require.Contains(t, mockFS.writtenTemplate, "Gateway")
}

func TestGenerateModuleTemplateWithManager_Success(t *testing.T) {
mockFS := &mockFileSystem{}
svc, _ := templategenerator.NewService(mockFS)
Expand Down
1 change: 1 addition & 0 deletions tests/e2e/create/create_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
withDefaultCrConfig = validConfigs + "with-defaultcr.yaml"
withSecurityConfig = validConfigs + "with-security.yaml"
withMandatoryConfig = validConfigs + "with-mandatory.yaml"
withAssociatedResourcesConfig = validConfigs + "with-associated-resources.yaml"
withResources = validConfigs + "with-resources.yaml"
withResourcesOverwrite = validConfigs + "with-resources-overwrite.yaml"
withManagerConfig = validConfigs + "with-manager.yaml"
Expand Down
Loading

0 comments on commit 2fbc4c0

Please sign in to comment.