diff --git a/api/filters/replacement/replacement.go b/api/filters/replacement/replacement.go index 0aacd074e6..0fec344973 100644 --- a/api/filters/replacement/replacement.go +++ b/api/filters/replacement/replacement.go @@ -8,6 +8,7 @@ import ( "strings" "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/resid" "sigs.k8s.io/kustomize/kyaml/yaml" @@ -48,6 +49,17 @@ func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targets []*types.T if err != nil { return nil, err } + + // filter targets by label and annotation selectors + selectByAnnoAndLabel, err := selectByAnnoAndLabel(n, t) + if err != nil { + return nil, err + } + if !selectByAnnoAndLabel { + continue + } + + // filter targets by matching resource IDs for _, id := range ids { if id.IsSelectedBy(t.Select.ResId) && !rejectId(t.Reject, &id) { err := applyToNode(n, value, t) @@ -62,9 +74,37 @@ func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targets []*types.T return nodes, nil } +func selectByAnnoAndLabel(n *yaml.RNode, t *types.TargetSelector) (bool, error) { + if matchesSelect, err := matchesAnnoAndLabelSelector(n, t.Select); !matchesSelect || err != nil { + return false, err + } + for _, reject := range t.Reject { + if reject.AnnotationSelector == "" && reject.LabelSelector == "" { + continue + } + if m, err := matchesAnnoAndLabelSelector(n, reject); m || err != nil { + return false, err + } + } + return true, nil +} + +func matchesAnnoAndLabelSelector(n *yaml.RNode, selector *types.Selector) (bool, error) { + r := resource.Resource{RNode: *n} + annoMatch, err := r.MatchesAnnotationSelector(selector.AnnotationSelector) + if err != nil { + return false, err + } + labelMatch, err := r.MatchesLabelSelector(selector.LabelSelector) + if err != nil { + return false, err + } + return annoMatch && labelMatch, nil +} + func rejectId(rejects []*types.Selector, id *resid.ResId) bool { for _, r := range rejects { - if id.IsSelectedBy(r.ResId) { + if !r.ResId.IsEmpty() && id.IsSelectedBy(r.ResId) { return true } } diff --git a/api/filters/replacement/replacement_test.go b/api/filters/replacement/replacement_test.go index 9380865e20..bae167c577 100644 --- a/api/filters/replacement/replacement_test.go +++ b/api/filters/replacement/replacement_test.go @@ -1588,6 +1588,230 @@ data: `, expectedErr: "fieldPath `data.httpPort` is missing for replacement source ~G_~V_ConfigMap|~X|ports-from:data.httpPort", }, + "annotationSelector": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + annotations: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + annotations: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb + +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy-1 + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + annotationSelector: foo=bar-1 + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + annotations: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + annotations: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, + "labelSelector": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + labels: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + labels: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb + +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy-1 + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + labelSelector: foo=bar-1 + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + labels: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + labels: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, + "reject via labelSelector": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + labels: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + labels: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb + +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy-1 + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + kind: Deployment + reject: + - labelSelector: foo=bar-2 + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + labels: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + labels: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, } for tn, tc := range testCases { diff --git a/kyaml/resid/resid.go b/kyaml/resid/resid.go index 1700531a2f..9a51c3716e 100644 --- a/kyaml/resid/resid.go +++ b/kyaml/resid/resid.go @@ -4,6 +4,7 @@ package resid import ( + "reflect" "strings" ) @@ -129,3 +130,9 @@ func (id ResId) EffectiveNamespace() string { } return id.Namespace } + +// IsEmpty returns true of all of the id's fields are +// empty strings +func (id ResId) IsEmpty() bool { + return reflect.DeepEqual(id, ResId{}) +}