diff --git a/internal/controller/utils.go b/internal/controller/utils.go index 3092c379..072ace1f 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -7,6 +7,7 @@ import ( "log" "maps" "net" + "regexp" "slices" "strconv" "strings" @@ -576,13 +577,25 @@ func getPeerCertName(etcdClusterName string) string { return peerCertName } +// validityDurationDayPrefix matches a leading integer day segment, e.g. "365d" or the "100d" in "100d12h". +var validityDurationDayPrefix = regexp.MustCompile(`^(\d+)d`) + // parseValidityDuration parses a duration string and returns the parsed duration. // If the customizedDuration is empty, it returns the defaultDuration. +// A leading day segment (e.g. "365d", "100d12h") is expanded to hours, since +// time.ParseDuration does not support the "d" unit. // Returns an error if the duration string cannot be parsed. func parseValidityDuration(customizedDuration string, defaultDuration time.Duration) (time.Duration, error) { if customizedDuration == "" { return defaultDuration, nil } + if m := validityDurationDayPrefix.FindStringSubmatch(customizedDuration); m != nil { + days, err := strconv.Atoi(m[1]) + if err != nil { + return 0, fmt.Errorf("failed to parse ValidityDuration: %w", err) + } + customizedDuration = fmt.Sprintf("%dh%s", days*24, customizedDuration[len(m[0]):]) + } duration, err := time.ParseDuration(customizedDuration) if err != nil { return 0, fmt.Errorf("failed to parse ValidityDuration: %w", err) diff --git a/internal/controller/utils_test.go b/internal/controller/utils_test.go index 67592316..6260b7e9 100644 --- a/internal/controller/utils_test.go +++ b/internal/controller/utils_test.go @@ -1090,3 +1090,33 @@ func TestCreateCMCertificateConfig(t *testing.T) { }) } } + +func TestParseValidityDuration(t *testing.T) { + tests := []struct { + name string + input string + expected time.Duration + expectedErr bool + }{ + {name: "day suffix", input: "365d", expected: 365 * 24 * time.Hour}, + {name: "days and hours", input: "100d12h", expected: 100*24*time.Hour + 12*time.Hour}, + {name: "plain go duration", input: "24h", expected: 24 * time.Hour}, + {name: "minutes", input: "90m", expected: 90 * time.Minute}, + {name: "empty returns default", input: "", expected: certInterface.DefaultCertManagerValidity}, + {name: "invalid string", input: "abc", expectedErr: true}, + {name: "day unit without value", input: "d", expectedErr: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + duration, err := parseValidityDuration(tt.input, certInterface.DefaultCertManagerValidity) + if tt.expectedErr { + require.Error(t, err) + assert.ErrorContains(t, err, "failed to parse ValidityDuration") + return + } + require.NoError(t, err) + assert.Equal(t, tt.expected, duration) + }) + } +}