diff --git a/pkg/resmap/resmap.go b/pkg/resmap/resmap.go index e293ec12d..f9b453871 100644 --- a/pkg/resmap/resmap.go +++ b/pkg/resmap/resmap.go @@ -19,15 +19,15 @@ import ( // ResMap is an interface describing operations on the // core kustomize data structure, a list of Resources. // -// Every Resource has two ResIds: OriginalId and CurId. -// -// A ResId is a tuple of {Namespace, Group, Version, Kind, Name}. +// Every Resource has two ResIds: OrgId and CurId. // // In a ResMap, no two resources may have the same CurId, -// but they may have the same OriginalId. The latter can happen +// but they may have the same OrgId. The latter can happen // when mixing two or more different overlays apply different -// transformations to a common base. -// +// transformations to a common base. When looking for a +// resource to transform, try the OrgId first, and if this +// fails or finds too many, it might make sense to then try +// the CurrId. Depends on the situation. type ResMap interface { // Size reports the number of resources. Size() int diff --git a/pkg/transformers/namespace.go b/pkg/transformers/namespace.go deleted file mode 100644 index 48a165e22..000000000 --- a/pkg/transformers/namespace.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package transformers - -import ( - "sigs.k8s.io/kustomize/pkg/gvk" - "sigs.k8s.io/kustomize/pkg/resid" - "sigs.k8s.io/kustomize/pkg/resmap" - "sigs.k8s.io/kustomize/pkg/resource" - "sigs.k8s.io/kustomize/pkg/transformers/config" -) - -type namespaceTransformer struct { - namespace string - fieldSpecsToUse []config.FieldSpec -} - -var _ Transformer = &namespaceTransformer{} - -// NewNamespaceTransformer construct a namespaceTransformer. -func NewNamespaceTransformer(ns string, cf []config.FieldSpec) Transformer { - return &namespaceTransformer{ - namespace: ns, - fieldSpecsToUse: cf, - } -} - -const metaNamespace = "metadata/namespace" - -// Transform adds the namespace. -func (o *namespaceTransformer) Transform(m resmap.ResMap) (err error) { - if len(o.namespace) == 0 { - return nil - } - for _, r := range m.Resources() { - id := r.OrgId() - fs, ok := o.isSelected(id) - if !ok { - continue - } - if len(r.Map()) == 0 { - // Don't mutate empty objects? - continue - } - if doIt(id, fs) { - err = o.changeNamespace(r, fs) - if err != nil { - return err - } - } - } - o.updateClusterRoleBinding(m) - return nil -} - -// Special casing metadata.namespace since -// all objects have it, even "ClusterKind" objects -// that don't exist in a namespace (the Namespace -// object itself doesn't live in a namespace). -func doIt(id resid.ResId, fs *config.FieldSpec) bool { - return fs.Path != metaNamespace || - (fs.Path == metaNamespace && !id.IsClusterKind()) -} - -func (o *namespaceTransformer) changeNamespace( - r *resource.Resource, fs *config.FieldSpec) error { - return MutateField( - r.Map(), fs.PathSlice(), fs.CreateIfNotPresent, - func(_ interface{}) (interface{}, error) { - return o.namespace, nil - }) -} - -func (o *namespaceTransformer) isSelected( - id resid.ResId) (*config.FieldSpec, bool) { - for _, fs := range o.fieldSpecsToUse { - if id.IsSelected(&fs.Gvk) { - return &fs, true - } - } - return nil, false -} - -func (o *namespaceTransformer) updateClusterRoleBinding(m resmap.ResMap) { - srvAccount := gvk.Gvk{Version: "v1", Kind: "ServiceAccount"} - saMap := map[string]bool{} - for _, id := range m.AllIds() { - if id.Gvk.Equals(srvAccount) { - saMap[id.Name] = true - } - } - - for _, res := range m.Resources() { - if res.OrgId().Kind != "ClusterRoleBinding" && - res.OrgId().Kind != "RoleBinding" { - continue - } - objMap := res.Map() - subjects, ok := objMap["subjects"].([]interface{}) - if subjects == nil || !ok { - continue - } - for i := range subjects { - subject := subjects[i].(map[string]interface{}) - kind, foundk := subject["kind"] - name, foundn := subject["name"] - if !foundk || !foundn || kind.(string) != srvAccount.Kind { - continue - } - // a ServiceAccount named “default” exists in every active namespace - if name.(string) == "default" || saMap[name.(string)] { - subject := subjects[i].(map[string]interface{}) - MutateField( - subject, []string{"namespace"}, - true, func(_ interface{}) (interface{}, error) { - return o.namespace, nil - }) - subjects[i] = subject - } - } - objMap["subjects"] = subjects - } -} diff --git a/pkg/transformers/namespace_test.go b/pkg/transformers/namespace_test.go deleted file mode 100644 index 92c28fb10..000000000 --- a/pkg/transformers/namespace_test.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package transformers - -import ( - "testing" - - "sigs.k8s.io/kustomize/k8sdeps/kunstruct" - "sigs.k8s.io/kustomize/pkg/resmaptest" - "sigs.k8s.io/kustomize/pkg/resource" -) - -func TestNamespaceRun(t *testing.T) { - rf := resource.NewFactory( - kunstruct.NewKunstructuredFactoryImpl()) - m := resmaptest_test.NewRmBuilder(t, rf). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{ - "name": "cm1", - }}). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{ - "name": "cm2", - "namespace": "foo", - }}). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "Namespace", - "metadata": map[string]interface{}{ - "name": "ns1", - }}). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ServiceAccount", - "metadata": map[string]interface{}{ - "name": "default", - "namespace": "system", - }}). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ServiceAccount", - "metadata": map[string]interface{}{ - "name": "service-account", - "namespace": "system", - }}). - Add(map[string]interface{}{ - "apiVersion": "rbac.authorization.k8s.io/v1", - "kind": "ClusterRoleBinding", - "metadata": map[string]interface{}{ - "name": "manager-rolebinding", - }, - "subjects": []interface{}{ - map[string]interface{}{ - "kind": "ServiceAccount", - "name": "default", - "namespace": "system", - }, - map[string]interface{}{ - "kind": "ServiceAccount", - "name": "service-account", - "namespace": "system", - }, - map[string]interface{}{ - "kind": "ServiceAccount", - "name": "another", - "namespace": "random", - }, - }}). - Add(map[string]interface{}{ - "apiVersion": "apiextensions.k8s.io/v1beta1", - "kind": "CustomResourceDefinition", - "metadata": map[string]interface{}{ - "name": "crd", - }}).ResMap() - - expected := resmaptest_test.NewRmBuilder(t, rf). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{ - "name": "cm1", - "namespace": "test", - }}). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{ - "name": "cm2", - "namespace": "test", - }}). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "Namespace", - "metadata": map[string]interface{}{ - "name": "ns1", - }}). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ServiceAccount", - "metadata": map[string]interface{}{ - "name": "default", - "namespace": "test", - }}). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ServiceAccount", - "metadata": map[string]interface{}{ - "name": "service-account", - "namespace": "test", - }}). - Add(map[string]interface{}{ - "apiVersion": "rbac.authorization.k8s.io/v1", - "kind": "ClusterRoleBinding", - "metadata": map[string]interface{}{ - "name": "manager-rolebinding", - }, - "subjects": []interface{}{ - map[string]interface{}{ - "kind": "ServiceAccount", - "name": "default", - "namespace": "test", - }, - map[string]interface{}{ - "kind": "ServiceAccount", - "name": "service-account", - "namespace": "test", - }, - map[string]interface{}{ - "kind": "ServiceAccount", - "name": "another", - "namespace": "random", - }, - }}). - Add(map[string]interface{}{ - "apiVersion": "apiextensions.k8s.io/v1beta1", - "kind": "CustomResourceDefinition", - "metadata": map[string]interface{}{ - "name": "crd", - }}).ResMap() - - nst := NewNamespaceTransformer("test", defaultTransformerConfig.NameSpace) - err := nst.Transform(m) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err = expected.ErrorIfNotEqualLists(m); err != nil { - t.Fatalf("actual doesn't match expected: %v", err) - } -} - -func TestNamespaceRunForClusterLevelKind(t *testing.T) { - rf := resource.NewFactory( - kunstruct.NewKunstructuredFactoryImpl()) - m := resmaptest_test.NewRmBuilder(t, rf). - Add(map[string]interface{}{ - "apiVersion": "v1", - "kind": "Namespace", - "metadata": map[string]interface{}{ - "name": "ns1", - }}). - Add(map[string]interface{}{ - "kind": "CustomResourceDefinition", - "metadata": map[string]interface{}{ - "name": "crd1", - }}). - Add(map[string]interface{}{ - "kind": "PersistentVolume", - "metadata": map[string]interface{}{ - "name": "pv1", - }}). - Add(map[string]interface{}{ - "kind": "ClusterRole", - "metadata": map[string]interface{}{ - "name": "cr1", - }}). - Add(map[string]interface{}{ - "kind": "ClusterRoleBinding", - "metadata": map[string]interface{}{ - "name": "crb1", - }, - "subjects": []interface{}{}}).ResMap() - - expected := m.DeepCopy() - - nst := NewNamespaceTransformer("test", defaultTransformerConfig.NameSpace) - - err := nst.Transform(m) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err = expected.ErrorIfNotEqualLists(m); err != nil { - t.Fatalf("actual doesn't match expected: %v", err) - } -} diff --git a/plugin/builtin/namespacetransformer/NamespaceTransformer.go b/plugin/builtin/namespacetransformer/NamespaceTransformer.go index 6cfa167b7..99806adad 100644 --- a/plugin/builtin/namespacetransformer/NamespaceTransformer.go +++ b/plugin/builtin/namespacetransformer/NamespaceTransformer.go @@ -5,8 +5,11 @@ package main import ( + "sigs.k8s.io/kustomize/pkg/gvk" "sigs.k8s.io/kustomize/pkg/ifc" + "sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resmap" + "sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/transformers" "sigs.k8s.io/kustomize/pkg/transformers/config" "sigs.k8s.io/yaml" @@ -29,6 +32,96 @@ func (p *plugin) Config( } func (p *plugin) Transform(m resmap.ResMap) error { - return transformers.NewNamespaceTransformer( - p.Namespace, p.FieldSpecs).Transform(m) + if len(p.Namespace) == 0 { + return nil + } + for _, r := range m.Resources() { + id := r.OrgId() + fs, ok := p.isSelected(id) + if !ok { + continue + } + if len(r.Map()) == 0 { + // Don't mutate empty objects? + continue + } + if doIt(id, fs) { + if err := p.changeNamespace(r, fs); err != nil { + return err + } + } + } + p.updateClusterRoleBinding(m) + return nil +} + +const metaNamespace = "metadata/namespace" + +// Special casing metadata.namespace since +// all objects have it, even "ClusterKind" objects +// that don't exist in a namespace (the Namespace +// object itself doesn't live in a namespace). +func doIt(id resid.ResId, fs *config.FieldSpec) bool { + return fs.Path != metaNamespace || + (fs.Path == metaNamespace && !id.IsClusterKind()) +} + +func (p *plugin) changeNamespace( + r *resource.Resource, fs *config.FieldSpec) error { + return transformers.MutateField( + r.Map(), fs.PathSlice(), fs.CreateIfNotPresent, + func(_ interface{}) (interface{}, error) { + return p.Namespace, nil + }) +} + +func (p *plugin) isSelected( + id resid.ResId) (*config.FieldSpec, bool) { + for _, fs := range p.FieldSpecs { + if id.IsSelected(&fs.Gvk) { + return &fs, true + } + } + return nil, false +} + +func (p *plugin) updateClusterRoleBinding(m resmap.ResMap) { + srvAccount := gvk.Gvk{Version: "v1", Kind: "ServiceAccount"} + saMap := map[string]bool{} + for _, id := range m.AllIds() { + if id.Gvk.Equals(srvAccount) { + saMap[id.Name] = true + } + } + + for _, res := range m.Resources() { + if res.OrgId().Kind != "ClusterRoleBinding" && + res.OrgId().Kind != "RoleBinding" { + continue + } + objMap := res.Map() + subjects, ok := objMap["subjects"].([]interface{}) + if subjects == nil || !ok { + continue + } + for i := range subjects { + subject := subjects[i].(map[string]interface{}) + kind, foundK := subject["kind"] + name, foundN := subject["name"] + if !foundK || !foundN || kind.(string) != srvAccount.Kind { + continue + } + // a ServiceAccount named “default” exists in every active namespace + if name.(string) == "default" || saMap[name.(string)] { + subject := subjects[i].(map[string]interface{}) + transformers.MutateField( + subject, []string{"namespace"}, + true, func(_ interface{}) (interface{}, error) { + return p.Namespace, nil + }) + subjects[i] = subject + } + } + objMap["subjects"] = subjects + } }