diff --git a/internal/resourcemodifiers/json_patch.go b/internal/resourcemodifiers/json_patch.go index b5af7c3622..d137d40be9 100644 --- a/internal/resourcemodifiers/json_patch.go +++ b/internal/resourcemodifiers/json_patch.go @@ -19,30 +19,37 @@ type JSONPatch struct { } func (p *JSONPatch) ToString() string { - if addQuotes(p.Value) { + if addQuotes(&p.Value) { return fmt.Sprintf(`{"op": "%s", "from": "%s", "path": "%s", "value": "%s"}`, p.Operation, p.From, p.Path, p.Value) } return fmt.Sprintf(`{"op": "%s", "from": "%s", "path": "%s", "value": %s}`, p.Operation, p.From, p.Path, p.Value) } -func addQuotes(value string) bool { - if value == "" { +func addQuotes(value *string) bool { + if *value == "" { + return true + } + // if value is escaped, remove escape and add quotes + // this is useful for scenarios where boolean, null and numbers are required to be set as string. + if strings.HasPrefix(*value, "\"") && strings.HasSuffix(*value, "\"") { + *value = strings.TrimPrefix(*value, "\"") + *value = strings.TrimSuffix(*value, "\"") return true } // if value is null, then don't add quotes - if value == "null" { + if *value == "null" { return false } // if value is a boolean, then don't add quotes - if _, err := strconv.ParseBool(value); err == nil { + if strings.ToLower(*value) == "true" || strings.ToLower(*value) == "false" { return false } // if value is a json object or array, then don't add quotes. - if strings.HasPrefix(value, "{") || strings.HasPrefix(value, "[") { + if strings.HasPrefix(*value, "{") || strings.HasPrefix(*value, "[") { return false } // if value is a number, then don't add quotes - if _, err := strconv.ParseFloat(value, 64); err == nil { + if _, err := strconv.ParseFloat(*value, 64); err == nil { return false } return true diff --git a/internal/resourcemodifiers/resource_modifiers_test.go b/internal/resourcemodifiers/resource_modifiers_test.go index b09b473d90..550eb4528d 100644 --- a/internal/resourcemodifiers/resource_modifiers_test.go +++ b/internal/resourcemodifiers/resource_modifiers_test.go @@ -256,6 +256,64 @@ func TestGetResourceModifiersFromConfig(t *testing.T) { }, }, } + cm9 := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-configmap", + Namespace: "test-namespace", + }, + Data: map[string]string{ + "sub.yml": "version: v1\nresourceModifierRules:\n- conditions:\n groupResource: deployments.apps\n resourceNameRegex: \"^test-.*$\"\n namespaces:\n - bar\n - foo\n patches:\n - operation: replace\n path: \"/value/bool\"\n value: \"\\\"true\\\"\"\n\n\n", + }, + } + + rules9 := &ResourceModifiers{ + Version: "v1", + ResourceModifierRules: []ResourceModifierRule{ + { + Conditions: Conditions{ + GroupResource: "deployments.apps", + ResourceNameRegex: "^test-.*$", + Namespaces: []string{"bar", "foo"}, + }, + Patches: []JSONPatch{ + { + Operation: "replace", + Path: "/value/bool", + Value: `"true"`, + }, + }, + }, + }, + } + cm10 := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-configmap", + Namespace: "test-namespace", + }, + Data: map[string]string{ + "sub.yml": "version: v1\nresourceModifierRules:\n- conditions:\n groupResource: deployments.apps\n resourceNameRegex: \"^test-.*$\"\n namespaces:\n - bar\n - foo\n patches:\n - operation: replace\n path: \"/value/bool\"\n value: \"true\"\n\n\n", + }, + } + + rules10 := &ResourceModifiers{ + Version: "v1", + ResourceModifierRules: []ResourceModifierRule{ + { + Conditions: Conditions{ + GroupResource: "deployments.apps", + ResourceNameRegex: "^test-.*$", + Namespaces: []string{"bar", "foo"}, + }, + Patches: []JSONPatch{ + { + Operation: "replace", + Path: "/value/bool", + Value: "true", + }, + }, + }, + }, + } type args struct { cm *v1.ConfigMap @@ -338,6 +396,22 @@ func TestGetResourceModifiersFromConfig(t *testing.T) { want: rules8, wantErr: false, }, + { + name: "bool value as string", + args: args{ + cm: cm9, + }, + want: rules9, + wantErr: false, + }, + { + name: "bool value as bool", + args: args{ + cm: cm10, + }, + want: rules10, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -480,7 +554,24 @@ func TestResourceModifiers_ApplyResourceModifierRules(t *testing.T) { }, }, } - + cmTrue := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "data": map[string]interface{}{ + "test": "true", + }, + }, + } + cmFalse := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "data": map[string]interface{}{ + "test": "false", + }, + }, + } type fields struct { Version string ResourceModifierRules []ResourceModifierRule @@ -496,6 +587,33 @@ func TestResourceModifiers_ApplyResourceModifierRules(t *testing.T) { wantErr bool wantObj *unstructured.Unstructured }{ + { + name: "configmap true false string", + fields: fields{ + Version: "v1", + ResourceModifierRules: []ResourceModifierRule{ + { + Conditions: Conditions{ + GroupResource: "configmaps", + ResourceNameRegex: ".*", + }, + Patches: []JSONPatch{ + { + Operation: "replace", + Path: "/data/test", + Value: `"false"`, + }, + }, + }, + }, + }, + args: args{ + obj: cmTrue.DeepCopy(), + groupResource: "configmaps", + }, + wantErr: false, + wantObj: cmFalse.DeepCopy(), + }, { name: "Invalid Regex throws error", fields: fields{