RM support for Escaped bool, float, null (#7118)

* RM support for Escaped bool, float, null

Signed-off-by: Anshul Ahuja <anshul.ahu@gmail.com>

* fix ci

Signed-off-by: Anshul Ahuja <anshul.ahu@gmail.com>

---------

Signed-off-by: Anshul Ahuja <anshul.ahu@gmail.com>
This commit is contained in:
Anshul Ahuja
2023-11-21 09:18:34 +05:30
committed by GitHub
parent e58a7808e0
commit 0e53cd0916
2 changed files with 133 additions and 8 deletions

View File

@@ -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

View File

@@ -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{