From 6e54814b6dd6fa8442c193512aa1c2b380fbbf52 Mon Sep 17 00:00:00 2001 From: Seth Pollack Date: Mon, 4 Jun 2018 22:30:03 -0400 Subject: [PATCH 1/3] kustomize namespace --- pkg/app/application.go | 2 + pkg/transformers/namespace.go | 43 +++++++++++++++ pkg/transformers/namespace_test.go | 86 ++++++++++++++++++++++++++++++ pkg/types/kustomization.go | 3 ++ 4 files changed, 134 insertions(+) create mode 100644 pkg/transformers/namespace.go create mode 100644 pkg/transformers/namespace_test.go diff --git a/pkg/app/application.go b/pkg/app/application.go index 13e0cb2ef..464a37bc6 100644 --- a/pkg/app/application.go +++ b/pkg/app/application.go @@ -208,6 +208,8 @@ func (a *applicationImpl) getTransformer(patches []*resource.Resource) (transfor } ts = append(ts, ot) + ts = append(ts, transformers.NewNamespaceTransformer(string(a.kustomization.Namespace))) + npt, err := transformers.NewDefaultingNamePrefixTransformer(string(a.kustomization.NamePrefix)) if err != nil { return nil, err diff --git a/pkg/transformers/namespace.go b/pkg/transformers/namespace.go new file mode 100644 index 000000000..3d118ce38 --- /dev/null +++ b/pkg/transformers/namespace.go @@ -0,0 +1,43 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package transformers + +import ( + "github.com/kubernetes-sigs/kustomize/pkg/resmap" +) + +type namespaceTransformer struct { + namespace string +} + +var _ Transformer = &namespaceTransformer{} + +// NewNamespaceTransformer construct a namespaceTransformer. +func NewNamespaceTransformer(ns string) Transformer { + if len(ns) == 0 { + return NewNoOpTransformer() + } + return &namespaceTransformer{namespace: ns} +} + +// Transform adds the namespace. +func (o *namespaceTransformer) Transform(m resmap.ResMap) error { + for _, obj := range m { + obj.Unstruct().SetNamespace(o.namespace) + } + return nil +} diff --git a/pkg/transformers/namespace_test.go b/pkg/transformers/namespace_test.go new file mode 100644 index 000000000..cb70474e3 --- /dev/null +++ b/pkg/transformers/namespace_test.go @@ -0,0 +1,86 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package transformers + +import ( + "reflect" + "testing" + + "github.com/kubernetes-sigs/kustomize/pkg/resmap" + "github.com/kubernetes-sigs/kustomize/pkg/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestNamespaceRun(t *testing.T) { + m := resmap.ResMap{ + resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource( + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + }, + }), + resource.NewResId(cmap, "cm2"): resource.NewBehaviorlessResource( + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm2", + "namespace": "foo", + }, + }, + }), + } + expected := resmap.ResMap{ + resource.NewResId(cmap, "cm1"): resource.NewBehaviorlessResource( + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + "namespace": "test", + }, + }, + }), + resource.NewResId(cmap, "cm2"): resource.NewBehaviorlessResource( + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm2", + "namespace": "test", + }, + }, + }), + } + + nst := NewNamespaceTransformer("test") + err := nst.Transform(m) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !reflect.DeepEqual(m, expected) { + err = expected.ErrorIfNotEqual(m) + t.Fatalf("actual doesn't match expected: %v", err) + } +} diff --git a/pkg/types/kustomization.go b/pkg/types/kustomization.go index 6cc23e6de..f389ee09a 100644 --- a/pkg/types/kustomization.go +++ b/pkg/types/kustomization.go @@ -22,6 +22,9 @@ type Kustomization struct { // file including generated configmaps and secrets. NamePrefix string `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"` + // Namespace to add to all objects. + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + // Labels to add to all objects and selectors. // These labels would also be used to form the selector for apply --prune // Named differently than “labels” to avoid confusion with metadata for From 501e1a406d8c175d2dcf34f4451e4a41be96fea1 Mon Sep 17 00:00:00 2001 From: Seth Pollack Date: Tue, 5 Jun 2018 15:32:22 -0400 Subject: [PATCH 2/3] add regression coverage --- pkg/app/application_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/app/application_test.go b/pkg/app/application_test.go index f123e439d..5809dca1b 100644 --- a/pkg/app/application_test.go +++ b/pkg/app/application_test.go @@ -34,6 +34,7 @@ import ( func setupTest(t *testing.T) loader.Loader { kustomizationContent := []byte(` namePrefix: foo- +namespace: foo commonLabels: app: nginx commonAnnotations: @@ -82,7 +83,8 @@ func TestResources(t *testing.T) { "apiVersion": "apps/v1", "kind": "Deployment", "metadata": map[string]interface{}{ - "name": "foo-dply1", + "name": "foo-dply1", + "namespace": "foo", "labels": map[string]interface{}{ "app": "nginx", }, @@ -115,7 +117,8 @@ func TestResources(t *testing.T) { "apiVersion": "v1", "kind": "ConfigMap", "metadata": map[string]interface{}{ - "name": "foo-literalConfigMap-mc92bgcbh5", + "name": "foo-literalConfigMap-mc92bgcbh5", + "namespace": "foo", "labels": map[string]interface{}{ "app": "nginx", }, @@ -136,7 +139,8 @@ func TestResources(t *testing.T) { "apiVersion": "v1", "kind": "Secret", "metadata": map[string]interface{}{ - "name": "foo-secret-877fcfhgt5", + "name": "foo-secret-877fcfhgt5", + "namespace": "foo", "labels": map[string]interface{}{ "app": "nginx", }, From 815db033cb35ca28f7982cfc4e1f266597626aa2 Mon Sep 17 00:00:00 2001 From: Seth Pollack Date: Tue, 5 Jun 2018 18:41:48 -0400 Subject: [PATCH 3/3] add namespace.yaml --- pkg/app/application_test.go | 46 +++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/pkg/app/application_test.go b/pkg/app/application_test.go index 5809dca1b..75745fed5 100644 --- a/pkg/app/application_test.go +++ b/pkg/app/application_test.go @@ -34,13 +34,14 @@ import ( func setupTest(t *testing.T) loader.Loader { kustomizationContent := []byte(` namePrefix: foo- -namespace: foo +namespace: ns1 commonLabels: app: nginx commonAnnotations: note: This is a test annotation resources: - deployment.yaml + - namespace.yaml configMapGenerator: - name: literalConfigMap literals: @@ -58,6 +59,11 @@ kind: Deployment metadata: name: dply1 `) + namespaceContent := []byte(`apiVersion: v1 +kind: Namespace +metadata: + name: ns1 + `) loader := loadertest.NewFakeLoader("/testpath") err := loader.AddFile("/testpath/"+constants.KustomizationFileName, kustomizationContent) @@ -68,12 +74,17 @@ metadata: if err != nil { t.Fatalf("Failed to setup fake loader.") } + err = loader.AddFile("/testpath/namespace.yaml", namespaceContent) + if err != nil { + t.Fatalf("Failed to setup fake loader.") + } return loader } var deploy = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"} var cmap = schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"} var secret = schema.GroupVersionKind{Version: "v1", Kind: "Secret"} +var ns = schema.GroupVersionKind{Version: "v1", Kind: "Namespace"} func TestResources(t *testing.T) { expected := resmap.ResMap{ @@ -84,7 +95,7 @@ func TestResources(t *testing.T) { "kind": "Deployment", "metadata": map[string]interface{}{ "name": "foo-dply1", - "namespace": "foo", + "namespace": "ns1", "labels": map[string]interface{}{ "app": "nginx", }, @@ -118,7 +129,7 @@ func TestResources(t *testing.T) { "kind": "ConfigMap", "metadata": map[string]interface{}{ "name": "foo-literalConfigMap-mc92bgcbh5", - "namespace": "foo", + "namespace": "ns1", "labels": map[string]interface{}{ "app": "nginx", }, @@ -140,7 +151,7 @@ func TestResources(t *testing.T) { "kind": "Secret", "metadata": map[string]interface{}{ "name": "foo-secret-877fcfhgt5", - "namespace": "foo", + "namespace": "ns1", "labels": map[string]interface{}{ "app": "nginx", }, @@ -156,6 +167,23 @@ func TestResources(t *testing.T) { }, }, }), + resource.NewResId(ns, "ns1"): resource.NewBehaviorlessResource( + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "Namespace", + "metadata": map[string]interface{}{ + "name": "foo-ns1", + "namespace": "ns1", + "labels": map[string]interface{}{ + "app": "nginx", + }, + "annotations": map[string]interface{}{ + "note": "This is a test annotation", + }, + }, + }, + }), } l := setupTest(t) app, err := New(l) @@ -185,6 +213,16 @@ func TestRawResources(t *testing.T) { }, }, }), + resource.NewResId(ns, "ns1"): resource.NewBehaviorlessResource( + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "Namespace", + "metadata": map[string]interface{}{ + "name": "ns1", + }, + }, + }), } l := setupTest(t) app, err := New(l)