From 949421038a68bca1b38e43a112f976c25aaa35f4 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Wed, 31 Jul 2024 12:10:04 +0200 Subject: [PATCH 1/3] operator: add built-in 'Random' label templates introduce built-in random templates: * ${Random/UUID} * ${Random/Hex/[0-32]} * ${Random/Int/[MAXINT]} examples: ${Random/UUID} --> e512d5ca-a765-42f2-82b7-264f37ffb329 ${Random/Hex/12} --> e512d5caa765 ${Random/Int/1000} --> 199 Signed-off-by: Francesco Giudici --- pkg/templater/random.go | 114 +++++++++++++++++++++++++++++++++++++ pkg/templater/templater.go | 29 ++++++++-- 2 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 pkg/templater/random.go diff --git a/pkg/templater/random.go b/pkg/templater/random.go new file mode 100644 index 00000000..9235cf0a --- /dev/null +++ b/pkg/templater/random.go @@ -0,0 +1,114 @@ +/* +Copyright © 2022 - 2024 SUSE LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package templater + +import ( + "encoding/hex" + "fmt" + "math/rand/v2" + "strconv" + "strings" + + "github.com/google/uuid" +) + +// Random template format examples: +// Random/UUID --> e511d5ca-a765-42f2-82b7-264f37ffb329 +// Random/Hex/3 --> e512 +// Random/Int/5 --> 75423 +const ( + tmplRandomKey = "Random" + tmplUUIDKey = "UUID" + tmplHexKey = "Hex" + tmplIntKey = "Int" +) + +func isRandomTemplate(tmplVal []string) bool { + if tmplVal[0] != tmplRandomKey { + return false + } + + switch tmplVal[1] { + case tmplUUIDKey: + case tmplHexKey: + case tmplIntKey: + default: + return false + } + + return true +} + +func randomTemplateToString(tmplVal []string) (string, error) { + // expected tamplates: + // Random/UUID + // Random/Hex/[1-32] + // Random/Int/[MAXINT] + // examples: + // [Random][UUID] --> e511d5ca-a765-42f2-82b7-264f37ffb329 + // [Random][Hex][3] --> e512 + // [Random][Hex][11] --> e512d5caa765 + // [Random][Int][100]--> 24 + + if !isRandomTemplate(tmplVal) { + return "", errValueNotFound + } + + tmplLen := len(tmplVal) + if tmplLen > 3 || tmplLen < 2 { + return "", fmt.Errorf("invalid template: %s", strings.Join(tmplVal, "/")) + } + + switch tmplVal[1] { + case tmplUUIDKey: + if len(tmplVal) != 2 { + return "", fmt.Errorf("invalid template: %s", strings.Join(tmplVal, "/")) + } + return uuid.NewString(), nil + case tmplHexKey: + if len(tmplVal) != 3 { + return "", fmt.Errorf("invalid template: %s", strings.Join(tmplVal, "/")) + } + rndLen, err := strconv.Atoi(tmplVal[2]) + if err != nil || rndLen < 1 { + return "", fmt.Errorf("unsupported %s/%s template: %s", + tmplRandomKey, tmplUUIDKey, strings.Join(tmplVal, "/")) + } + if rndLen > 32 { + return "", fmt.Errorf("unsupported %s/%s lenght: %s", + tmplRandomKey, tmplUUIDKey, strings.Join(tmplVal, "/")) + } + + rndHex := make([]byte, 32) + uuid := uuid.New() + hex.Encode(rndHex, uuid[:]) + return string(rndHex[:rndLen]), nil + case tmplIntKey: + if len(tmplVal) != 3 { + return "", fmt.Errorf("invalid template: %s", strings.Join(tmplVal, "/")) + } + intMax, err := strconv.Atoi(tmplVal[2]) + if err != nil || intMax < 1 { + return "", fmt.Errorf("unsupported %s/%s template: %s", + tmplRandomKey, tmplIntKey, strings.Join(tmplVal, "/")) + } + intVal := rand.IntN(intMax) + return strconv.Itoa(intVal), nil + } + + return "", fmt.Errorf("invalid template: %s", strings.Join(tmplVal, "/")) +} diff --git a/pkg/templater/templater.go b/pkg/templater/templater.go index cca29adb..44db09e2 100644 --- a/pkg/templater/templater.go +++ b/pkg/templater/templater.go @@ -18,6 +18,7 @@ package templater import ( "errors" + "fmt" "strings" values "github.com/rancher/wrangler/v2/pkg/data" @@ -72,14 +73,32 @@ func replaceStringData(data map[string]interface{}, name string) (string, error) } result.WriteString(str[:i]) - obj := values.GetValueN(data, strings.Split(str[i+2:j+i], "/")...) - if str, ok := obj.(string); ok { - result.WriteString(str) - } else { - return "", errValueNotFound + tmplVal := strings.Split(str[i+2:j+i], "/") + + strVal, err := templateToString(data, tmplVal) + fmt.Printf("TMPL CONVERSION: %q --> %q\n", tmplVal, strVal) + if err != nil { + return "", err } + + result.WriteString(strVal) str = str[j+i+1:] } return result.String(), nil } + +func templateToString(data map[string]interface{}, tmplVal []string) (string, error) { + var str string + var ok bool + + if isRandomTemplate(tmplVal) { + return randomTemplateToString(tmplVal) + } + + obj := values.GetValueN(data, tmplVal...) + if str, ok = obj.(string); !ok { + return "", errValueNotFound + } + return str, nil +} From 048e880aef57160d97c3ec448f0cdb546f7d9731 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Tue, 13 Aug 2024 11:40:21 +0200 Subject: [PATCH 2/3] tests: add 'Random' label templates coverage Signed-off-by: Francesco Giudici --- pkg/templater/templater_test.go | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/pkg/templater/templater_test.go b/pkg/templater/templater_test.go index f643a3da..5ee9f72e 100644 --- a/pkg/templater/templater_test.go +++ b/pkg/templater/templater_test.go @@ -18,6 +18,7 @@ package templater import ( "fmt" + "strings" "testing" "gotest.tools/v3/assert" @@ -130,3 +131,37 @@ func TestIsValueNotFoundError(t *testing.T) { assert.Equal(t, IsValueNotFoundError(errValueNotFound), true) assert.Equal(t, IsValueNotFoundError(errRandom), false) } + +func TestRandomTemplate(t *testing.T) { + testCase := []struct { + tmplVal []string + isRand bool + isValid bool + }{ + {[]string{"Custom"}, false, false}, + {[]string{tmplRandomKey, "Custom"}, false, false}, + {[]string{tmplRandomKey, tmplUUIDKey}, true, true}, + {[]string{tmplRandomKey, tmplUUIDKey, "ABC"}, true, false}, + {[]string{tmplRandomKey, tmplUUIDKey, "16"}, true, false}, + {[]string{tmplRandomKey, tmplHexKey}, true, false}, + {[]string{tmplRandomKey, tmplHexKey, "0"}, true, false}, + {[]string{tmplRandomKey, tmplHexKey, "33"}, true, false}, + {[]string{tmplRandomKey, tmplHexKey, "32"}, true, true}, + {[]string{tmplRandomKey, tmplIntKey}, true, false}, + {[]string{tmplRandomKey, tmplIntKey, "12123123"}, true, true}, + {[]string{tmplRandomKey, tmplIntKey, "0"}, true, false}, + {[]string{tmplRandomKey, tmplIntKey, "pippo"}, true, false}, + {[]string{tmplRandomKey, tmplIntKey, "1000", "extraarg"}, true, false}, + } + + for _, testCase := range testCase { + assert.Equal(t, isRandomTemplate(testCase.tmplVal), testCase.isRand, + "template: %s, expected ret: %t", strings.Join(testCase.tmplVal, "/"), testCase.isRand) + val, err := randomTemplateToString(testCase.tmplVal) + if testCase.isValid { + assert.NilError(t, err) + } else { + assert.Assert(t, err != nil, "template '%s' got converted to '%s'", strings.Join(testCase.tmplVal, "/"), val) + } + } +} From b83b16adc761f8473c056a080810fff08ce08a20 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Tue, 13 Aug 2024 12:36:35 +0200 Subject: [PATCH 3/3] fixup! operator: add built-in 'Random' label templates --- pkg/templater/random.go | 11 ++++++++--- pkg/templater/templater.go | 2 -- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/templater/random.go b/pkg/templater/random.go index 9235cf0a..b9788907 100644 --- a/pkg/templater/random.go +++ b/pkg/templater/random.go @@ -17,9 +17,10 @@ limitations under the License. package templater import ( + "crypto/rand" "encoding/hex" "fmt" - "math/rand/v2" + "math/big" "strconv" "strings" @@ -106,8 +107,12 @@ func randomTemplateToString(tmplVal []string) (string, error) { return "", fmt.Errorf("unsupported %s/%s template: %s", tmplRandomKey, tmplIntKey, strings.Join(tmplVal, "/")) } - intVal := rand.IntN(intMax) - return strconv.Itoa(intVal), nil + intBigVal, err := rand.Int(rand.Reader, big.NewInt(int64(intMax))) + if err != nil { + return "", fmt.Errorf("converting %s: %w", strings.Join(tmplVal, "/"), err) + } + strVal := fmt.Sprintf("%d", intBigVal) + return strVal, nil } return "", fmt.Errorf("invalid template: %s", strings.Join(tmplVal, "/")) diff --git a/pkg/templater/templater.go b/pkg/templater/templater.go index 44db09e2..8a6b243b 100644 --- a/pkg/templater/templater.go +++ b/pkg/templater/templater.go @@ -18,7 +18,6 @@ package templater import ( "errors" - "fmt" "strings" values "github.com/rancher/wrangler/v2/pkg/data" @@ -76,7 +75,6 @@ func replaceStringData(data map[string]interface{}, name string) (string, error) tmplVal := strings.Split(str[i+2:j+i], "/") strVal, err := templateToString(data, tmplVal) - fmt.Printf("TMPL CONVERSION: %q --> %q\n", tmplVal, strVal) if err != nil { return "", err }