diff --git a/pkg/target/namespaces_test.go b/pkg/target/namespaces_test.go index 835d28fd1..d2ecf5d94 100644 --- a/pkg/target/namespaces_test.go +++ b/pkg/target/namespaces_test.go @@ -159,6 +159,9 @@ subjects: - kind: ServiceAccount name: sa3 namespace: random +- kind: ServiceAccount + name: default + namespace: irrelevant --- apiVersion: admissionregistration.k8s.io/v1beta1 kind: ValidatingWebhookConfiguration @@ -193,6 +196,10 @@ metadata: kind: ClusterRoleBinding metadata: name: crb1 +subjects: +- kind: ServiceAccount + name: default + namespace: irrelevant --- kind: PersistentVolume metadata: @@ -254,6 +261,9 @@ subjects: - kind: ServiceAccount name: sa3 namespace: random +- kind: ServiceAccount + name: default + namespace: newnamespace --- apiVersion: admissionregistration.k8s.io/v1beta1 kind: ValidatingWebhookConfiguration @@ -288,6 +298,10 @@ metadata: kind: ClusterRoleBinding metadata: name: p1-crb1-s1 +subjects: +- kind: ServiceAccount + name: default + namespace: newnamespace --- kind: PersistentVolume metadata: diff --git a/pkg/transformers/config/defaultconfig/namespace.go b/pkg/transformers/config/defaultconfig/namespace.go index 431eb0769..0cb24de9d 100644 --- a/pkg/transformers/config/defaultconfig/namespace.go +++ b/pkg/transformers/config/defaultconfig/namespace.go @@ -21,5 +21,9 @@ const ( namespace: - path: metadata/namespace create: true +- path: subjects + kind: RoleBinding +- path: subjects + kind: ClusterRoleBinding ` ) diff --git a/plugin/builtin/NamespaceTransformer.go b/plugin/builtin/NamespaceTransformer.go index 199a70bf7..6c6674c0e 100644 --- a/plugin/builtin/NamespaceTransformer.go +++ b/plugin/builtin/NamespaceTransformer.go @@ -35,17 +35,19 @@ func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error { 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 { + + id := r.OrgId() + applicableFs := p.applicableFieldSpecs(id) + + for _, fs := range applicableFs { + err := transformers.MutateField( + r.Map(), fs.PathSlice(), fs.CreateIfNotPresent, + p.changeNamespace(r)) + if err != nil { return err } } @@ -59,26 +61,64 @@ const metaNamespace = "metadata/namespace" // 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.IsNamespaceableKind()) -} - -func (p *NamespaceTransformerPlugin) 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 *NamespaceTransformerPlugin) isSelected( - id resid.ResId) (*config.FieldSpec, bool) { +func (p *NamespaceTransformerPlugin) applicableFieldSpecs(id resid.ResId) []config.FieldSpec { + res := []config.FieldSpec{} for _, fs := range p.FieldSpecs { - if id.IsSelected(&fs.Gvk) { - return &fs, true + if id.IsSelected(&fs.Gvk) && (fs.Path != metaNamespace || (fs.Path == metaNamespace && id.IsNamespaceableKind())) { + res = append(res, fs) + } + } + return res +} + +func (o *NamespaceTransformerPlugin) changeNamespace( + referrer *resource.Resource) func(in interface{}) (interface{}, error) { + return func(in interface{}) (interface{}, error) { + switch in.(type) { + case string: + // will happen when the metadata/namespace + // value is replaced + return o.Namespace, nil + case []interface{}: + l, _ := in.([]interface{}) + for idx, item := range l { + switch item.(type) { + case map[string]interface{}: + // Will happen when mutating the subjects + // field of ClusterRoleBinding and RoleBinding + inMap, _ := item.(map[string]interface{}) + if _, ok := inMap["name"]; !ok { + continue + } + name, ok := inMap["name"].(string) + if !ok { + continue + } + // The only case we need to force the namespace + // if for the "service account". "default" is + // kind of hardcoded here for right now. + if name != "default" { + continue + } + inMap["namespace"] = o.Namespace + l[idx] = inMap + default: + // nothing to do for right now + } + } + return in, nil + case map[string]interface{}: + // Will happen if the createField=true + // when the namespace is added to the + // object + inMap := in.(map[string]interface{}) + if len(inMap) == 0 { + return o.Namespace, nil + } else { + return in, nil + } + default: + return in, nil } } - return nil, false } diff --git a/plugin/builtin/namespacetransformer/NamespaceTransformer.go b/plugin/builtin/namespacetransformer/NamespaceTransformer.go index 906e6e564..1687958f6 100644 --- a/plugin/builtin/namespacetransformer/NamespaceTransformer.go +++ b/plugin/builtin/namespacetransformer/NamespaceTransformer.go @@ -36,17 +36,19 @@ func (p *plugin) Transform(m resmap.ResMap) error { 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 { + + id := r.OrgId() + applicableFs := p.applicableFieldSpecs(id) + + for _, fs := range applicableFs { + err := transformers.MutateField( + r.Map(), fs.PathSlice(), fs.CreateIfNotPresent, + p.changeNamespace(r)) + if err != nil { return err } } @@ -60,26 +62,64 @@ const metaNamespace = "metadata/namespace" // 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.IsNamespaceableKind()) -} - -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) { +func (p *plugin) applicableFieldSpecs(id resid.ResId) []config.FieldSpec { + res := []config.FieldSpec{} for _, fs := range p.FieldSpecs { - if id.IsSelected(&fs.Gvk) { - return &fs, true + if id.IsSelected(&fs.Gvk) && (fs.Path != metaNamespace || (fs.Path == metaNamespace && id.IsNamespaceableKind())) { + res = append(res, fs) + } + } + return res +} + +func (o *plugin) changeNamespace( + referrer *resource.Resource) func(in interface{}) (interface{}, error) { + return func(in interface{}) (interface{}, error) { + switch in.(type) { + case string: + // will happen when the metadata/namespace + // value is replaced + return o.Namespace, nil + case []interface{}: + l, _ := in.([]interface{}) + for idx, item := range l { + switch item.(type) { + case map[string]interface{}: + // Will happen when mutating the subjects + // field of ClusterRoleBinding and RoleBinding + inMap, _ := item.(map[string]interface{}) + if _, ok := inMap["name"]; !ok { + continue + } + name, ok := inMap["name"].(string) + if !ok { + continue + } + // The only case we need to force the namespace + // if for the "service account". "default" is + // kind of hardcoded here for right now. + if name != "default" { + continue + } + inMap["namespace"] = o.Namespace + l[idx] = inMap + default: + // nothing to do for right now + } + } + return in, nil + case map[string]interface{}: + // Will happen if the createField=true + // when the namespace is added to the + // object + inMap := in.(map[string]interface{}) + if len(inMap) == 0 { + return o.Namespace, nil + } else { + return in, nil + } + default: + return in, nil } } - return nil, false } diff --git a/plugin/builtin/namespacetransformer/NamespaceTransformer_test.go b/plugin/builtin/namespacetransformer/NamespaceTransformer_test.go index ce3043a0e..0dd5005c7 100644 --- a/plugin/builtin/namespacetransformer/NamespaceTransformer_test.go +++ b/plugin/builtin/namespacetransformer/NamespaceTransformer_test.go @@ -28,6 +28,12 @@ metadata: fieldSpecs: - path: metadata/namespace create: true +- path: subjects + kind: RoleBinding + group: rbac.authorization.k8s.io +- path: subjects + kind: ClusterRoleBinding + group: rbac.authorization.k8s.io `, ` apiVersion: v1 kind: ConfigMap @@ -54,7 +60,7 @@ apiVersion: v1 kind: ServiceAccount metadata: name: default - namespace: system + namespace: test --- apiVersion: v1 kind: ServiceAccount @@ -151,7 +157,7 @@ metadata: subjects: - kind: ServiceAccount name: default - namespace: system + namespace: test - kind: ServiceAccount name: service-account namespace: system @@ -222,6 +228,12 @@ metadata: fieldSpecs: - path: metadata/namespace create: true +- path: subjects + kind: RoleBinding + group: rbac.authorization.k8s.io +- path: subjects + kind: ClusterRoleBinding + group: rbac.authorization.k8s.io `, noChangeExpected) th.AssertActualEqualsExpected(rm, noChangeExpected)