From cbcc2f68cf0275239aaac05173e8d6e7d6ab5790 Mon Sep 17 00:00:00 2001 From: Mason Cole Date: Thu, 3 Oct 2024 11:40:21 -0700 Subject: [PATCH] feat(replacements): add IgnoreMissingField option --- api/filters/replacement/replacement.go | 10 +- api/filters/replacement/replacement_test.go | 139 ++++++++++++++++++ api/types/replacement.go | 7 +- .../API/Kustomization File/replacements.md | 2 + 4 files changed, 153 insertions(+), 5 deletions(-) diff --git a/api/filters/replacement/replacement.go b/api/filters/replacement/replacement.go index a988b60e8d..e1cf1ea234 100644 --- a/api/filters/replacement/replacement.go +++ b/api/filters/replacement/replacement.go @@ -185,20 +185,24 @@ func containsRejectId(rejects []*types.Selector, ids []resid.ResId) bool { func copyValueToTarget(target *yaml.RNode, value *yaml.RNode, selector *types.TargetSelector) error { for _, fp := range selector.FieldPaths { createKind := yaml.Kind(0) // do not create + ignoreMissingField := false + if selector.Options != nil { + ignoreMissingField = selector.Options.IgnoreMissingField + } if selector.Options != nil && selector.Options.Create { createKind = value.YNode().Kind } targetFieldList, err := target.Pipe(&yaml.PathMatcher{ Path: kyaml_utils.SmarterPathSplitter(fp, "."), Create: createKind}) - if err != nil { + if err != nil && !ignoreMissingField { return errors.WrapPrefixf(err, fieldRetrievalError(fp, createKind != 0)) //nolint:govet } targetFields, err := targetFieldList.Elements() - if err != nil { + if err != nil && !ignoreMissingField { return errors.WrapPrefixf(err, fieldRetrievalError(fp, createKind != 0)) //nolint:govet } - if len(targetFields) == 0 { + if len(targetFields) == 0 && !ignoreMissingField { return errors.Errorf(fieldRetrievalError(fp, createKind != 0)) //nolint:govet } diff --git a/api/filters/replacement/replacement_test.go b/api/filters/replacement/replacement_test.go index 7619e5fc9d..6b042b6ea5 100644 --- a/api/filters/replacement/replacement_test.go +++ b/api/filters/replacement/replacement_test.go @@ -2830,6 +2830,145 @@ spec: `, expectedErr: "unable to find or create field \"spec.tls.5.hosts.5\" in replacement target: index 5 specified but only 0 elements found", }, + "replace target path and ignore missing fields": { + input: `apiVersion: v1 +kind: ConfigMap +metadata: + name: clusterConfig +data: + accountId: "123456789101" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-with-field + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::REPLACEME:role/super-admin +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-without-field +`, + replacements: `replacements: +- source: + kind: ConfigMap + name: clusterConfig + fieldPath: data.accountId + targets: + - select: + kind: ServiceAccount + fieldPaths: + - metadata.annotations.[eks.amazonaws.com/role-arn] + options: + # not all Service Accounts have irsa annotations + ignoreMissingField: true + delimiter: ":" + index: 4 +`, + expected: `apiVersion: v1 +kind: ConfigMap +metadata: + name: clusterConfig +data: + accountId: "123456789101" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-with-field + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::123456789101:role/super-admin +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-without-field`, + }, + "no replacement or error with ignore missing fields when there is no matching field": { + input: `apiVersion: v1 +kind: ConfigMap +metadata: + name: clusterConfig +data: + accountId: "123456789101" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-without-field +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-without-field-again +`, + replacements: `replacements: +- source: + kind: ConfigMap + name: clusterConfig + fieldPath: data.accountId + targets: + - select: + kind: ServiceAccount + fieldPaths: + - metadata.annotations.[eks.amazonaws.com/role-arn] + options: + # not all Service Accounts have irsa annotations + ignoreMissingField: true + delimiter: ":" + index: 4 +`, + expected: `apiVersion: v1 +kind: ConfigMap +metadata: + name: clusterConfig +data: + accountId: "123456789101" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-without-field +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-without-field-again`, + }, + "non matching field with no options": { + input: `apiVersion: v1 +kind: ConfigMap +metadata: + name: clusterConfig +data: + accountId: "123456789101" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-with-field + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::REPLACEME:role/super-admin +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: service-account-without-field-again +`, + replacements: `replacements: +- source: + kind: ConfigMap + name: clusterConfig + fieldPath: data.accountId + targets: + - select: + kind: ServiceAccount + fieldPaths: + - metadata.annotations.[eks.amazonaws.com/role-arn] +`, + expectedErr: `unable to find field "metadata.annotations.[eks.amazonaws.com/role-arn]" in replacement target`, + }, } for tn, tc := range testCases { diff --git a/api/types/replacement.go b/api/types/replacement.go index cb4163429a..ae02319a2f 100644 --- a/api/types/replacement.go +++ b/api/types/replacement.go @@ -77,11 +77,14 @@ type FieldOptions struct { // If field missing, add it. Create bool `json:"create,omitempty" yaml:"create,omitempty"` + + // If field missing, ignore it. + IgnoreMissingField bool `json:"ignoreMissingField,omitempty" yaml:"ignoreMissingField,omitempty"` } func (fo *FieldOptions) String() string { - if fo == nil || (fo.Delimiter == "" && !fo.Create) { + if fo == nil || (fo.Delimiter == "" && !fo.Create && !fo.IgnoreMissingField) { return "" } - return fmt.Sprintf("%s(%d), create=%t", fo.Delimiter, fo.Index, fo.Create) + return fmt.Sprintf("%s(%d), create=%t, ignoreMissingField=%t", fo.Delimiter, fo.Index, fo.Create, fo.IgnoreMissingField) } diff --git a/site/content/en/docs/Reference/API/Kustomization File/replacements.md b/site/content/en/docs/Reference/API/Kustomization File/replacements.md index bd51f2fcf0..4d2cc41c52 100644 --- a/site/content/en/docs/Reference/API/Kustomization File/replacements.md +++ b/site/content/en/docs/Reference/API/Kustomization File/replacements.md @@ -63,6 +63,7 @@ replacements: delimiter: string index: int create: bool + ignoreMissingField: bool targets: - select: group: string @@ -103,6 +104,7 @@ replacements: |`delimiter`| | Used to split/join the field |`index`| | Which position in the split to consider | `0` |`create`| | If target field is missing, add it | `false` +|`ignoreMissingField`| | If target field is missing, ignore it | `false` #### Source The source field is a selector that determines the source of the value by finding a