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

Fix namespace management #579

Merged
merged 1 commit into from
Apr 25, 2022
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
46 changes: 4 additions & 42 deletions pkg/course/course.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ type FileV2 struct {
// Hooks is a set of scripts to be run before or after the release is installed.
Hooks Hooks `yaml:"hooks,omitempty" json:"hooks,omitempty"`
// NamespaceMgmt contains the default namespace config for all namespaces managed by this course.
NamespaceMgmt NamespaceMgmt `yaml:"namespace_management,omitempty" json:"namespace_management,omitempty"`
Secrets SecretsList `yaml:"secrets,omitempty" json:"secrets,omitempty"`
NamespaceMgmt *NamespaceMgmt `yaml:"namespace_management,omitempty" json:"namespace_management,omitempty"`
Secrets SecretsList `yaml:"secrets,omitempty" json:"secrets,omitempty"`
// Releases is the list of releases that should be maintained by this course file.
Releases []*Release `yaml:"releases,omitempty" json:"releases,omitempty"`
// HelmArgs is a list of arguments to pass to helm commands
Expand Down Expand Up @@ -192,11 +192,8 @@ type FileV1 struct {
// Hooks is a set of scripts to be run before or after the release is installed.
Hooks Hooks `yaml:"hooks" json:"hooks"`
// NamespaceMgmt contains the default namespace config for all namespaces managed by this course.
NamespaceMgmt struct {
// Default is the default namespace config for this course
Default *NamespaceConfig `yaml:"default" json:"default"`
} `yaml:"namespace_management" json:"namespace_management"`
Secrets SecretsList `yaml:"secrets,omitempty" json:"secrets,omitempty"`
NamespaceMgmt *NamespaceMgmt `yaml:"namespace_management" json:"namespace_management"`
Secrets SecretsList `yaml:"secrets,omitempty" json:"secrets,omitempty"`
// Charts is the list of releases. In the actual file this will be a map, but we must convert to a list to preserve order.
// This conversion is done in the ChartsListV1 UnmarshalYAML function.
Charts ChartsListV1 `yaml:"charts" json:"charts"`
Expand Down Expand Up @@ -549,41 +546,6 @@ func (f *FileV2) populateEmptyChartNames() {
}
}

// populateNamespaceManagement populates each release with the default namespace management settings if they are not set
func (f *FileV2) populateNamespaceManagement() {
var emptyNamespaceMgmt NamespaceConfig
if f.NamespaceMgmt.Default == nil {
f.NamespaceMgmt.Default = &emptyNamespaceMgmt
f.NamespaceMgmt.Default.Settings.Overwrite = boolPtr(false)
} else if f.NamespaceMgmt.Default.Settings.Overwrite == nil {
f.NamespaceMgmt.Default.Settings.Overwrite = boolPtr(false)
}
for releaseIndex, release := range f.Releases {
if release.NamespaceMgmt == nil {
klog.V(5).Infof("using default namespace management for release: %s", release.Name)
release.NamespaceMgmt = f.NamespaceMgmt.Default
f.Releases[releaseIndex] = release
} else {
release.NamespaceMgmt = mergeNamespaceManagement(f.NamespaceMgmt.Default, release.NamespaceMgmt)
}
}
}

func mergeNamespaceManagement(defaults *NamespaceConfig, mergeInto *NamespaceConfig) *NamespaceConfig {
d := defaults
for k, v := range mergeInto.Metadata.Annotations {
d.Metadata.Annotations[k] = v
}
for k, v := range mergeInto.Metadata.Labels {
d.Metadata.Labels[k] = v
}
if mergeInto.Settings.Overwrite != nil {
d.Settings.Overwrite = mergeInto.Settings.Overwrite
}
mergeInto = d
return mergeInto
}

func (f *FileV2) validateJsonSchema(schemaData []byte) error {
klog.V(10).Infof("validating course file against schema: \n%s", string(schemaData))
schema, err := gojsonschema.NewSchema(gojsonschema.NewBytesLoader(schemaData))
Expand Down
58 changes: 58 additions & 0 deletions pkg/course/namespace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package course

import (
"k8s.io/klog/v2"
)

// populateNamespaceManagement populates each release with the default namespace management settings if they are not set
func (f *FileV2) populateNamespaceManagement() {
var emptyNamespaceMgmt NamespaceConfig
if f.NamespaceMgmt == nil {
f.NamespaceMgmt = &NamespaceMgmt{}
}
if f.NamespaceMgmt.Default == nil {
f.NamespaceMgmt.Default = &emptyNamespaceMgmt
f.NamespaceMgmt.Default.Settings.Overwrite = boolPtr(false)
} else if f.NamespaceMgmt.Default.Settings.Overwrite == nil {
f.NamespaceMgmt.Default.Settings.Overwrite = boolPtr(false)
}

for releaseIndex, release := range f.Releases {
newRelease := *release
if newRelease.NamespaceMgmt == nil {
klog.V(5).Infof("using default namespace management for release: %s", release.Name)
newRelease.NamespaceMgmt = f.NamespaceMgmt.Default
} else {
newRelease.NamespaceMgmt = mergeNamespaceManagement(*f.NamespaceMgmt.Default, *newRelease.NamespaceMgmt)

}
f.Releases[releaseIndex] = &newRelease
}
}

// mergeNamespaceManagement merges the default namespace management settings with the release specific settings
func mergeNamespaceManagement(defaults NamespaceConfig, mergeInto NamespaceConfig) *NamespaceConfig {
for k, v := range defaults.Metadata.Annotations {
if mergeInto.Metadata.Annotations == nil {
mergeInto.Metadata.Annotations = map[string]string{}
}
if mergeInto.Metadata.Annotations[k] == "" {
mergeInto.Metadata.Annotations[k] = v
}
}

for k, v := range defaults.Metadata.Labels {
if mergeInto.Metadata.Labels == nil {
mergeInto.Metadata.Labels = map[string]string{}
}
if mergeInto.Metadata.Labels[k] == "" {
mergeInto.Metadata.Labels[k] = v
}
}

if mergeInto.Settings.Overwrite == nil {
mergeInto.Settings.Overwrite = defaults.Settings.Overwrite
}

return &mergeInto
}
252 changes: 252 additions & 0 deletions pkg/course/namespace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
package course

import (
"testing"

"github.com/stretchr/testify/assert"
)

func Test_mergeNamespaceManagement(t *testing.T) {
type args struct {
defaults NamespaceConfig
mergeInto NamespaceConfig
}
tests := []struct {
name string
args args
want *NamespaceConfig
}{
{
name: "basic merge",
args: args{
defaults: NamespaceConfig{
Metadata: NSMetadata{
Annotations: map[string]string{
"default-annotation": "default-value",
},
Labels: map[string]string{
"default-label": "default-value",
},
},
Settings: NSSettings{
Overwrite: boolPtr(false),
},
},
mergeInto: NamespaceConfig{
Metadata: NSMetadata{
Annotations: map[string]string{
"merge-annotation": "merge-value",
},
Labels: map[string]string{
"merge-label": "merge-value",
},
},
Settings: NSSettings{
Overwrite: boolPtr(false),
},
},
},
want: &NamespaceConfig{
Metadata: NSMetadata{
Annotations: map[string]string{
"default-annotation": "default-value",
"merge-annotation": "merge-value",
},
Labels: map[string]string{
"default-label": "default-value",
"merge-label": "merge-value",
},
},
Settings: NSSettings{
Overwrite: boolPtr(false),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := mergeNamespaceManagement(tt.args.defaults, tt.args.mergeInto)
assert.EqualValues(t, tt.want, got)
})
}
}

func TestFileV2_populateNamespaceManagement(t *testing.T) {
tests := []struct {
name string
file *FileV2
want *FileV2
}{
{
name: "empty default",
file: &FileV2{
Releases: []*Release{},
NamespaceMgmt: &NamespaceMgmt{},
},
want: &FileV2{
NamespaceMgmt: &NamespaceMgmt{
Default: &NamespaceConfig{
Settings: NSSettings{
Overwrite: boolPtr(false),
},
},
},
Releases: []*Release{},
},
},
{
name: "empty default overwrite",
file: &FileV2{
Releases: []*Release{},
NamespaceMgmt: &NamespaceMgmt{
Default: &NamespaceConfig{
Settings: NSSettings{
Overwrite: nil,
},
},
},
},
want: &FileV2{
NamespaceMgmt: &NamespaceMgmt{
Default: &NamespaceConfig{
Settings: NSSettings{
Overwrite: boolPtr(false),
},
},
},
Releases: []*Release{},
},
},
{
name: "release default",
file: &FileV2{
Releases: []*Release{
{
Name: "default",
},
},
NamespaceMgmt: &NamespaceMgmt{
Default: nil,
},
},
want: &FileV2{
NamespaceMgmt: &NamespaceMgmt{
Default: &NamespaceConfig{
Settings: NSSettings{
Overwrite: boolPtr(false),
},
},
},
Releases: []*Release{
{
Name: "default",
NamespaceMgmt: &NamespaceConfig{
Settings: NSSettings{
Overwrite: boolPtr(false),
},
},
},
},
},
},
{
name: "release specific",
file: &FileV2{
Releases: []*Release{
{
Name: "default",
NamespaceMgmt: &NamespaceConfig{
Settings: NSSettings{
Overwrite: boolPtr(false),
},
Metadata: NSMetadata{
Annotations: map[string]string{
"release-annotation": "release-value",
},
Labels: map[string]string{
"release-label": "release-value",
},
},
},
},
{
Name: "release2",
NamespaceMgmt: &NamespaceConfig{},
},
},
NamespaceMgmt: &NamespaceMgmt{
Default: &NamespaceConfig{
Settings: NSSettings{},
Metadata: NSMetadata{
Annotations: map[string]string{
"course-annotation": "course-value",
},
Labels: map[string]string{
"course-label": "course-value",
},
},
},
},
},
want: &FileV2{
NamespaceMgmt: &NamespaceMgmt{
Default: &NamespaceConfig{
Settings: NSSettings{
Overwrite: boolPtr(false),
},
Metadata: NSMetadata{
Annotations: map[string]string{
"course-annotation": "course-value",
},
Labels: map[string]string{
"course-label": "course-value",
},
},
},
},
Releases: []*Release{
{
Name: "default",
NamespaceMgmt: &NamespaceConfig{
Settings: NSSettings{
Overwrite: boolPtr(false),
},
Metadata: NSMetadata{
Annotations: map[string]string{
"course-annotation": "course-value",
"release-annotation": "release-value",
},
Labels: map[string]string{
"course-label": "course-value",
"release-label": "release-value",
},
},
},
},
{
Name: "release2",
NamespaceMgmt: &NamespaceConfig{
Settings: NSSettings{
Overwrite: boolPtr(false),
},
Metadata: NSMetadata{
Annotations: map[string]string{
"course-annotation": "course-value",
},
Labels: map[string]string{
"course-label": "course-value",
},
},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.file.populateNamespaceManagement()
assert.EqualValues(t, tt.want, tt.file)
})
}
}
Loading