diff --git a/api/filters/replacement/replacement.go b/api/filters/replacement/replacement.go index 35858940d..7988c322b 100644 --- a/api/filters/replacement/replacement.go +++ b/api/filters/replacement/replacement.go @@ -44,11 +44,17 @@ func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targets []*types.T t.FieldPaths = []string{types.DefaultReplacementFieldPath} } for _, n := range nodes { - id := makeResId(n) - if id.IsSelectedBy(t.Select.ResId) && !rejectId(t.Reject, id) { - err := applyToNode(n, value, t) - if err != nil { - return nil, err + ids, err := utils.MakeResIds(n) + if err != nil { + return nil, err + } + for _, id := range ids { + if id.IsSelectedBy(t.Select.ResId) && !rejectId(t.Reject, &id) { + err := applyToNode(n, value, t) + if err != nil { + return nil, err + } + break } } } @@ -152,12 +158,19 @@ func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode, func selectSourceNode(nodes []*yaml.RNode, selector *types.SourceSelector) (*yaml.RNode, error) { var matches []*yaml.RNode for _, n := range nodes { - if makeResId(n).IsSelectedBy(selector.ResId) { - if len(matches) > 0 { - return nil, fmt.Errorf( - "multiple matches for selector %s", selector) + ids, err := utils.MakeResIds(n) + if err != nil { + return nil, err + } + for _, id := range ids { + if id.IsSelectedBy(selector.ResId) { + if len(matches) > 0 { + return nil, fmt.Errorf( + "multiple matches for selector %s", selector) + } + matches = append(matches, n) + break } - matches = append(matches, n) } } if len(matches) == 0 { @@ -165,17 +178,3 @@ func selectSourceNode(nodes []*yaml.RNode, selector *types.SourceSelector) (*yam } return matches[0], nil } - -// makeResId makes a ResId from an RNode. -func makeResId(n *yaml.RNode) *resid.ResId { - apiVersion := n.Field(yaml.APIVersionField) - var group, version string - if apiVersion != nil { - group, version = resid.ParseGroupVersion(yaml.GetValue(apiVersion.Value)) - } - return &resid.ResId{ - Gvk: resid.Gvk{Group: group, Version: version, Kind: n.GetKind()}, - Name: n.GetName(), - Namespace: n.GetNamespace(), - } -} diff --git a/api/filters/replacement/replacement_test.go b/api/filters/replacement/replacement_test.go index d32b7dfdc..76294ef24 100644 --- a/api/filters/replacement/replacement_test.go +++ b/api/filters/replacement/replacement_test.go @@ -1478,6 +1478,54 @@ spec: name: third version: latest property: third +`, + }, + "using a previous ID": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: pre-deploy + annotations: + config.kubernetes.io/previousNames: deploy,deploy + config.kubernetes.io/previousKinds: CronJob,Deployment + config.kubernetes.io/previousNamespaces: default,default +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + kind: CronJob + name: deploy + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + kind: Deployment + name: deploy + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: pre-deploy + annotations: + config.kubernetes.io/previousNames: deploy,deploy + config.kubernetes.io/previousKinds: CronJob,Deployment + config.kubernetes.io/previousNamespaces: default,default +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb `, }, } diff --git a/api/internal/utils/makeResIds.go b/api/internal/utils/makeResIds.go new file mode 100644 index 000000000..3f6b17a3f --- /dev/null +++ b/api/internal/utils/makeResIds.go @@ -0,0 +1,79 @@ +package utils + +import ( + "fmt" + "strings" + + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +const ( + BuildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds" + BuildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames" + BuildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes" + BuildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes" + BuildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces" + + // the following are only for patches, to specify whether they can change names + // and kinds of their targets + BuildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange" + BuildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange" + Allowed = "allowed" +) + +// MakeResIds returns all of an RNode's current and previous Ids +func MakeResIds(n *yaml.RNode) ([]resid.ResId, error) { + var result []resid.ResId + apiVersion := n.Field(yaml.APIVersionField) + var group, version string + if apiVersion != nil { + group, version = resid.ParseGroupVersion(yaml.GetValue(apiVersion.Value)) + } + result = append(result, resid.NewResIdWithNamespace( + resid.Gvk{Group: group, Version: version, Kind: n.GetKind()}, n.GetName(), n.GetNamespace()), + ) + prevIds, err := PrevIds(n) + if err != nil { + return nil, err + } + result = append(result, prevIds...) + return result, nil +} + +// PrevIds returns all of an RNode's previous Ids +func PrevIds(n *yaml.RNode) ([]resid.ResId, error) { + var ids []resid.ResId + // TODO: merge previous names and namespaces into one list of + // pairs on one annotation so there is no chance of error + annotations := n.GetAnnotations() + if _, ok := annotations[BuildAnnotationPreviousNames]; !ok { + return nil, nil + } + names := strings.Split(annotations[BuildAnnotationPreviousNames], ",") + ns := strings.Split(annotations[BuildAnnotationPreviousNamespaces], ",") + kinds := strings.Split(annotations[BuildAnnotationPreviousKinds], ",") + // This should never happen + if len(names) != len(ns) || len(names) != len(kinds) { + return nil, fmt.Errorf( + "number of previous names, " + + "number of previous namespaces, " + + "number of previous kinds not equal") + } + for i := range names { + meta, err := n.GetMeta() + if err != nil { + return nil, err + } + group, version := resid.ParseGroupVersion(meta.APIVersion) + gvk := resid.Gvk{ + Group: group, + Version: version, + Kind: kinds[i], + } + ids = append(ids, resid.NewResIdWithNamespace( + gvk, names[i], ns[i])) + } + return ids, nil +} diff --git a/api/krusty/replacementtransformer_test.go b/api/krusty/replacementtransformer_test.go index b52eea890..d48852513 100644 --- a/api/krusty/replacementtransformer_test.go +++ b/api/krusty/replacementtransformer_test.go @@ -261,3 +261,84 @@ spec: name: nginx `) } + +func TestReplacementTransformerWithOriginalName(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteF("base/deployments.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: target +spec: + template: + spec: + containers: + - image: nginx:oldtag + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: source +spec: + template: + spec: + containers: + - image: nginx:newtag + name: nginx +`) + th.WriteK("base", ` +resources: +- deployments.yaml +`) + th.WriteK("overlay", ` +namePrefix: prefix1- +resources: +- ../base +`) + + th.WriteK(".", ` +namePrefix: prefix2- +resources: +- overlay +replacements: +- path: replacement.yaml +`) + th.WriteF("replacement.yaml", ` +source: + name: source + fieldPath: spec.template.spec.containers.0.image +targets: +- select: + name: prefix1-target + fieldPaths: + - spec.template.spec.containers.[name=nginx].image +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prefix2-prefix1-target +spec: + template: + spec: + containers: + - image: nginx:newtag + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prefix2-prefix1-source +spec: + template: + spec: + containers: + - image: nginx:newtag + name: nginx +`) +} diff --git a/api/resource/resource.go b/api/resource/resource.go index afbe2d455..e93f54890 100644 --- a/api/resource/resource.go +++ b/api/resource/resource.go @@ -4,14 +4,13 @@ package resource import ( - "errors" "fmt" "log" "strings" "sigs.k8s.io/kustomize/api/filters/patchstrategicmerge" "sigs.k8s.io/kustomize/api/ifc" - "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/internal/utils" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/resid" @@ -28,28 +27,14 @@ type Resource struct { refVarNames []string } -const ( - buildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds" - buildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames" - buildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes" - buildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes" - buildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces" - - // the following are only for patches, to specify whether they can change names - // and kinds of their targets - buildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange" - buildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange" - allowed = "allowed" -) - -var buildAnnotations = []string{ - buildAnnotationPreviousKinds, - buildAnnotationPreviousNames, - buildAnnotationPrefixes, - buildAnnotationSuffixes, - buildAnnotationPreviousNamespaces, - buildAnnotationAllowNameChange, - buildAnnotationAllowKindChange, +var BuildAnnotations = []string{ + utils.BuildAnnotationPreviousKinds, + utils.BuildAnnotationPreviousNames, + utils.BuildAnnotationPrefixes, + utils.BuildAnnotationSuffixes, + utils.BuildAnnotationPreviousNamespaces, + utils.BuildAnnotationAllowNameChange, + utils.BuildAnnotationAllowKindChange, } func (r *Resource) ResetRNode(incoming *Resource) { @@ -191,12 +176,12 @@ func copyStringSlice(s []string) []string { // Implements ResCtx AddNamePrefix func (r *Resource) AddNamePrefix(p string) { - r.appendCsvAnnotation(buildAnnotationPrefixes, p) + r.appendCsvAnnotation(utils.BuildAnnotationPrefixes, p) } // Implements ResCtx AddNameSuffix func (r *Resource) AddNameSuffix(s string) { - r.appendCsvAnnotation(buildAnnotationSuffixes, s) + r.appendCsvAnnotation(utils.BuildAnnotationSuffixes, s) } func (r *Resource) appendCsvAnnotation(name, value string) { @@ -232,12 +217,12 @@ func SameEndingSubarray(shortest, longest []string) bool { // Implements ResCtx GetNamePrefixes func (r *Resource) GetNamePrefixes() []string { - return r.getCsvAnnotation(buildAnnotationPrefixes) + return r.getCsvAnnotation(utils.BuildAnnotationPrefixes) } // Implements ResCtx GetNameSuffixes func (r *Resource) GetNameSuffixes() []string { - return r.getCsvAnnotation(buildAnnotationSuffixes) + return r.getCsvAnnotation(utils.BuildAnnotationSuffixes) } func (r *Resource) getCsvAnnotation(name string) []string { @@ -263,7 +248,7 @@ func (r *Resource) RemoveBuildAnnotations() { if len(annotations) == 0 { return } - for _, a := range buildAnnotations { + for _, a := range BuildAnnotations { delete(annotations, a) } if err := r.SetAnnotations(annotations); err != nil { @@ -272,16 +257,16 @@ func (r *Resource) RemoveBuildAnnotations() { } func (r *Resource) setPreviousId(ns string, n string, k string) *Resource { - r.appendCsvAnnotation(buildAnnotationPreviousNames, n) - r.appendCsvAnnotation(buildAnnotationPreviousNamespaces, ns) - r.appendCsvAnnotation(buildAnnotationPreviousKinds, k) + r.appendCsvAnnotation(utils.BuildAnnotationPreviousNames, n) + r.appendCsvAnnotation(utils.BuildAnnotationPreviousNamespaces, ns) + r.appendCsvAnnotation(utils.BuildAnnotationPreviousKinds, k) return r } // AllowNameChange allows name changes to the resource. func (r *Resource) AllowNameChange() { annotations := r.GetAnnotations() - annotations[buildAnnotationAllowNameChange] = allowed + annotations[utils.BuildAnnotationAllowNameChange] = utils.Allowed if err := r.SetAnnotations(annotations); err != nil { panic(err) } @@ -289,14 +274,14 @@ func (r *Resource) AllowNameChange() { func (r *Resource) NameChangeAllowed() bool { annotations := r.GetAnnotations() - v, ok := annotations[buildAnnotationAllowNameChange] - return ok && v == allowed + v, ok := annotations[utils.BuildAnnotationAllowNameChange] + return ok && v == utils.Allowed } // AllowKindChange allows kind changes to the resource. func (r *Resource) AllowKindChange() { annotations := r.GetAnnotations() - annotations[buildAnnotationAllowKindChange] = allowed + annotations[utils.BuildAnnotationAllowKindChange] = utils.Allowed if err := r.SetAnnotations(annotations); err != nil { panic(err) } @@ -304,8 +289,8 @@ func (r *Resource) AllowKindChange() { func (r *Resource) KindChangeAllowed() bool { annotations := r.GetAnnotations() - v, ok := annotations[buildAnnotationAllowKindChange] - return ok && v == allowed + v, ok := annotations[utils.BuildAnnotationAllowKindChange] + return ok && v == utils.Allowed } // String returns resource as JSON. @@ -369,26 +354,12 @@ func (r *Resource) OrgId() resid.ResId { // The returned array does not include the resource's current // ID. If there are no previous IDs, this will return nil. func (r *Resource) PrevIds() []resid.ResId { - var ids []resid.ResId - // TODO: merge previous names and namespaces into one list of - // pairs on one annotation so there is no chance of error - names := r.getCsvAnnotation(buildAnnotationPreviousNames) - ns := r.getCsvAnnotation(buildAnnotationPreviousNamespaces) - kinds := r.getCsvAnnotation(buildAnnotationPreviousKinds) - if len(names) != len(ns) || len(names) != len(kinds) { - panic(errors.New( - "number of previous names, " + - "number of previous namespaces, " + - "number of previous kinds not equal")) + prevIds, err := utils.PrevIds(&r.RNode) + if err != nil { + // this should never happen + panic(err) } - for i := range names { - k := kinds[i] - gvk := r.GetGvk() - gvk.Kind = k - ids = append(ids, resid.NewResIdWithNamespace( - gvk, names[i], ns[i])) - } - return ids + return prevIds } // StorePreviousId stores the resource's current ID via build annotations.