diff --git a/docs/rn/0.63.md b/docs/rn/0.63.md index becefb01f..7e60fe360 100644 --- a/docs/rn/0.63.md +++ b/docs/rn/0.63.md @@ -40,3 +40,7 @@ Given the sheer blast radius this change brings, we are eager to hear your feedb ### 0.63.2 * update podman dependency to not fail on a `~/.config` dir permissions when using sudo-less operation #2431 + +### 0.63.3 + +* added subtract template function that was used in SR OS and was removed with gomplate #2435 diff --git a/nodes/vr_sros/sshKey.go b/nodes/vr_sros/sshKey.go index 508587afc..05ce27795 100644 --- a/nodes/vr_sros/sshKey.go +++ b/nodes/vr_sros/sshKey.go @@ -8,6 +8,7 @@ import ( "text/template" log "github.com/sirupsen/logrus" + "github.com/srl-labs/containerlab/utils" "golang.org/x/crypto/ssh" ) @@ -23,7 +24,7 @@ func (s *vrSROS) generateSSHPublicKeysConfig() (io.Reader, error) { s.prepareSSHPubKeys(&tplData) - t, err := template.New("SSHKeys").Parse(SROSSSHKeysTemplate) + t, err := template.New("SSHKeys").Funcs(utils.TemplateFuncs).Parse(SROSSSHKeysTemplate) if err != nil { return nil, err } diff --git a/nodes/vr_sros/ssh_keys.go.tpl b/nodes/vr_sros/ssh_keys.go.tpl index b1cb03160..510e6de9e 100644 --- a/nodes/vr_sros/ssh_keys.go.tpl +++ b/nodes/vr_sros/ssh_keys.go.tpl @@ -4,9 +4,9 @@ /configure system security user-params attempts count 64 {{ range $index, $key := .SSHPubKeysRSA }} -/configure system security user-params local-user user "admin" public-keys rsa rsa-key {{ sub 32 $index }} key-value {{ $key }} +/configure system security user-params local-user user "admin" public-keys rsa rsa-key {{ subtract 32 $index }} key-value {{ $key }} {{ end }} {{ range $index, $key := .SSHPubKeysECDSA }} -/configure system security user-params local-user user "admin" public-keys ecdsa ecdsa-key {{ sub 32 $index }} key-value {{ $key }} +/configure system security user-params local-user user "admin" public-keys ecdsa ecdsa-key {{ subtract 32 $index }} key-value {{ $key }} {{ end }} \ No newline at end of file diff --git a/utils/template.go b/utils/template.go index d5f02a082..d008b41e6 100644 --- a/utils/template.go +++ b/utils/template.go @@ -2,6 +2,11 @@ package utils import ( "encoding/json" + "fmt" + "math" + "reflect" + "strconv" + "strings" "text/template" ) @@ -9,6 +14,7 @@ var TemplateFuncs = template.FuncMap{ "ToJSON": toJson, "ToJSONPretty": toJsonPretty, "add": add, + "subtract": subtract, } func toJson(v any) string { @@ -25,3 +31,171 @@ func toJsonPretty(v any, prefix string, indent string) string { func add(a, b int) int { return a + b } + +// subtract b from a +// copy from gomplate. +func subtract(a, b any) (any, error) { + if containsFloat(a, b) { + fa, err := ToFloat64(a) + if err != nil { + return nil, fmt.Errorf("expected a number: %w", err) + } + + fb, err := ToFloat64(b) + if err != nil { + return nil, fmt.Errorf("expected a number: %w", err) + } + + return fa - fb, nil + } + + ia, err := ToInt64(a) + if err != nil { + return nil, fmt.Errorf("expected a number: %w", err) + } + + ib, err := ToInt64(b) + if err != nil { + return nil, fmt.Errorf("expected a number: %w", err) + } + + return ia - ib, nil +} + +func containsFloat(n ...any) bool { + c := false + for _, v := range n { + if isFloat(v) { + return true + } + } + return c +} + +func isFloat(n any) bool { + switch i := n.(type) { + case float32, float64: + return true + case string: + _, err := strconv.ParseFloat(i, 64) + if err != nil { + return false + } + if isInt(i) { + return false + } + return true + } + return false +} + +func isInt(n any) bool { + switch i := n.(type) { + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return true + case string: + _, err := strconv.ParseInt(i, 0, 64) + return err == nil + } + return false +} + +func ToFloat64(v interface{}) (float64, error) { + if str, ok := v.(string); ok { + return strToFloat64(str) + } + + val := reflect.Indirect(reflect.ValueOf(v)) + switch val.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return float64(val.Int()), nil + case reflect.Uint8, reflect.Uint16, reflect.Uint32: + return float64(val.Uint()), nil + case reflect.Uint, reflect.Uint64: + return float64(val.Uint()), nil + case reflect.Float32, reflect.Float64: + return val.Float(), nil + case reflect.Bool: + if val.Bool() { + return 1, nil + } + return 0, nil + default: + return 0, fmt.Errorf("could not convert %v to float64", v) + } +} + +func strToInt64(str string) (int64, error) { + if strings.Contains(str, ",") { + str = strings.ReplaceAll(str, ",", "") + } + + iv, err := strconv.ParseInt(str, 0, 64) + if err != nil { + // maybe it's a float? + var fv float64 + fv, err = strconv.ParseFloat(str, 64) + if err != nil { + return 0, fmt.Errorf("could not convert %q to int64: %w", str, err) + } + + return ToInt64(fv) + } + + return iv, nil +} + +func ToInt64(v interface{}) (int64, error) { + if str, ok := v.(string); ok { + return strToInt64(str) + } + + val := reflect.Indirect(reflect.ValueOf(v)) + switch val.Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return val.Int(), nil + case reflect.Uint8, reflect.Uint16, reflect.Uint32: + //nolint:gosec // G115 isn't applicable, this is a Uint32 at most + return int64(val.Uint()), nil + case reflect.Uint, reflect.Uint64: + tv := val.Uint() + + if tv > math.MaxInt64 { + return 0, fmt.Errorf("could not convert %d to int64, would overflow", tv) + } + + return int64(tv), nil + case reflect.Float32, reflect.Float64: + return int64(val.Float()), nil + case reflect.Bool: + if val.Bool() { + return 1, nil + } + + return 0, nil + default: + return 0, fmt.Errorf("could not convert %v to int64", v) + } +} + +func strToFloat64(str string) (float64, error) { + if strings.Contains(str, ",") { + str = strings.ReplaceAll(str, ",", "") + } + + // this is inefficient, but it's the only way I can think of to + // properly convert octal integers to floats + iv, err := strconv.ParseInt(str, 0, 64) + if err != nil { + // ok maybe it's a float? + var fv float64 + fv, err = strconv.ParseFloat(str, 64) + if err != nil { + return 0, fmt.Errorf("could not convert %q to float64: %w", str, err) + } + + return fv, nil + } + + return float64(iv), nil +}