diff --git a/pkg/document/plugin/replacement/v1alpha1/errors.go b/pkg/document/plugin/replacement/v1alpha1/errors.go index 296d9db98..70a5afff5 100644 --- a/pkg/document/plugin/replacement/v1alpha1/errors.go +++ b/pkg/document/plugin/replacement/v1alpha1/errors.go @@ -91,3 +91,13 @@ type ErrIndexOutOfBound struct { func (e ErrIndexOutOfBound) Error() string { return fmt.Sprintf("index %v is out of bound", e.Index) } + +// ErrMapNotFound returned if map specified in fieldRef option was not found in a list +type ErrMapNotFound struct { + Key, Value, ListKey string +} + +func (e ErrMapNotFound) Error() string { + return fmt.Sprintf("unable to find map key '%s' with the value '%s' in list under '%s' key", + e.Key, e.Value, e.ListKey) +} diff --git a/pkg/document/plugin/replacement/v1alpha1/transformer.go b/pkg/document/plugin/replacement/v1alpha1/transformer.go index 15aba543b..b5729aaba 100644 --- a/pkg/document/plugin/replacement/v1alpha1/transformer.go +++ b/pkg/document/plugin/replacement/v1alpha1/transformer.go @@ -28,6 +28,10 @@ var ( substringPatternRegex = regexp.MustCompile(`(\S+)%(\S+)%$`) ) +const ( + dotReplacer = "$$$$" +) + // GetGVK returns group, version, kind object used to register version // of the plugin func GetGVK() schema.GroupVersionKind { @@ -151,7 +155,23 @@ func substitute(m resmap.ResMap, to *types.ReplTarget, replacement interface{}) } for _, r := range resources { for _, p := range to.FieldRefs { + // TODO (dukov) rework this using k8s.io/client-go/util/jsonpath + parts := strings.Split(p, "[") + var tmp []string + for _, part := range parts { + if strings.Contains(part, "]") { + filter := strings.Split(part, "]") + filter[0] = strings.ReplaceAll(filter[0], ".", dotReplacer) + part = strings.Join(filter, "]") + } + tmp = append(tmp, part) + } + p = strings.Join(tmp, "[") + pathSlice := strings.Split(p, ".") + for i, part := range pathSlice { + pathSlice[i] = strings.ReplaceAll(part, dotReplacer, ".") + } if err := updateField(r.Map(), pathSlice, replacement); err != nil { return err } @@ -246,15 +266,15 @@ func updateMapField(m map[string]interface{}, pathToField []string, replacement } switch typedV := v.(type) { case []interface{}: - for _, item := range typedV { + for i, item := range typedV { typedItem, ok := item.(map[string]interface{}) if !ok { return ErrTypeMismatch{Actual: item, Expectation: fmt.Sprintf("is expected to be %T", typedItem)} } if actualValue, ok := typedItem[key]; ok { if value == actualValue { - // TODO (dukov) should not we do 'item = replacement' here? - typedItem[key] = value + typedV[i] = replacement + return nil } } } @@ -273,7 +293,7 @@ func updateSliceField(m []interface{}, pathToField []string, replacement interfa if len(pathToField) == 0 { return nil } - _, key, value, isArray := getFirstPathSegment(pathToField[0]) + path, key, value, isArray := getFirstPathSegment(pathToField[0]) if isArray { for _, item := range m { @@ -287,6 +307,7 @@ func updateSliceField(m []interface{}, pathToField []string, replacement interfa } } } + return ErrMapNotFound{Key: key, Value: value, ListKey: path} } index, err := strconv.Atoi(pathToField[0]) diff --git a/pkg/document/plugin/replacement/v1alpha1/transformer_test.go b/pkg/document/plugin/replacement/v1alpha1/transformer_test.go index 974950327..3ae93ba5f 100644 --- a/pkg/document/plugin/replacement/v1alpha1/transformer_test.go +++ b/pkg/document/plugin/replacement/v1alpha1/transformer_test.go @@ -89,7 +89,7 @@ replacements: objref: kind: Deployment fieldrefs: - - spec.template.spec.containers[name=nginx-latest].image + - spec.template.spec.containers[name=nginx.latest].image - source: value: postgres:latest target: @@ -112,7 +112,7 @@ spec: - image: nginx:1.7.9 name: nginx-tagged - image: nginx:latest - name: nginx-latest + name: nginx.latest - image: foobar:1 name: replaced-with-digest - image: postgres:1.8.0 @@ -137,7 +137,7 @@ spec: - image: nginx:1.7.9 name: nginx-tagged - image: nginx:newtag - name: nginx-latest + name: nginx.latest - image: foobar:1 name: replaced-with-digest - image: postgres:latest @@ -455,6 +455,65 @@ kind: ReplacementTransformer metadata: name: notImportantHere replacements: +- source: + objref: + kind: Pod + name: pod + fieldref: spec.containers[0] + target: + objref: + kind: Deployment + fieldrefs: + - spec.template.spec.containers[name=myapp-container]`, + in: ` +apiVersion: v1 +kind: Pod +metadata: + name: pod +spec: + containers: + - name: repl + image: repl +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: busybox + name: myapp-container +`, + expectedOut: `apiVersion: v1 +kind: Pod +metadata: + name: pod +spec: + containers: + - image: repl + name: repl +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: repl + name: repl +`, + }, + { + cfg: ` +apiVersion: airshipit.org/v1alpha1 +kind: ReplacementTransformer +metadata: + name: notImportantHere +replacements: - source: objref: kind: Pod @@ -782,6 +841,44 @@ spec: name: nginx-latest`, expectedErr: "pattern 'TAG' is defined in configuration but was not found in target value nginx:latest", }, + { + cfg: ` +apiVersion: airshipit.org/v1alpha1 +kind: ReplacementTransformer +metadata: + name: notImportantHere +replacements: +- source: + value: 12345678 + target: + objref: + kind: KubeadmControlPlane + fieldrefs: + - spec.kubeadmConfigSpec.files[path=konfigadm].content%{k8s-version}% +`, + + in: ` +kind: KubeadmControlPlane +metadata: + name: cluster-controlplane +spec: + infrastructureTemplate: + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 + kind: Metal3MachineTemplate + name: $(cluster-name) + kubeadmConfigSpec: + files: + - content: | + kubernetes: + version: {k8s-version} + container_runtime: + type: docker + owner: root:root + path: konfigadm_bug_ + permissions: "0640" +`, + expectedErr: "unable to find map key 'path' with the value 'konfigadm' in list under 'files' key", + }, } for _, tc := range testCases {