diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 94894ffee475..8716d57124e5 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -112,6 +112,7 @@ func Funcs() map[string]ast.Function { "split": interpolationFuncSplit(), "substr": interpolationFuncSubstr(), "timestamp": interpolationFuncTimestamp(), + "timeadd": interpolationFuncTimeAdd(), "title": interpolationFuncTitle(), "transpose": interpolationFuncTranspose(), "trimspace": interpolationFuncTrimSpace(), @@ -1504,6 +1505,29 @@ func interpolationFuncTimestamp() ast.Function { } } +func interpolationFuncTimeAdd() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ + ast.TypeString, // input timestamp string in RFC3339 format + ast.TypeString, // duration to add to input timestamp that should be parsable by time.ParseDuration + }, + ReturnType: ast.TypeString, + Callback: func(args []interface{}) (interface{}, error) { + + ts, err := time.Parse(time.RFC3339, args[0].(string)) + if err != nil { + return nil, err + } + duration, err := time.ParseDuration(args[1].(string)) + if err != nil { + return nil, err + } + + return ts.Add(duration).Format(time.RFC3339), nil + }, + } +} + // interpolationFuncTitle implements the "title" function that returns a copy of the // string in which first characters of all the words are capitalized. func interpolationFuncTitle() ast.Function { diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index fc7540e81453..1db1bbe04bd4 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -2426,6 +2426,38 @@ func TestInterpolateFuncTimestamp(t *testing.T) { } } +func TestInterpolateFuncTimeAdd(t *testing.T) { + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + { + `${timeadd("2017-11-22T00:00:00Z", "1s")}`, + "2017-11-22T00:00:01Z", + false, + }, + { + `${timeadd("2017-11-22T00:00:00Z", "10m1s")}`, + "2017-11-22T00:10:01Z", + false, + }, + { // also support subtraction + `${timeadd("2017-11-22T00:00:00Z", "-1h")}`, + "2017-11-21T23:00:00Z", + false, + }, + { // Invalid format timestamp + `${timeadd("2017-11-22", "-1h")}`, + nil, + true, + }, + { // Invalid format duration (day is not supported by ParseDuration) + `${timeadd("2017-11-22T00:00:00Z", "1d")}`, + nil, + true, + }, + }, + }) +} + type testFunctionConfig struct { Cases []testFunctionCase Vars map[string]ast.Variable diff --git a/website/docs/configuration/interpolation.html.md b/website/docs/configuration/interpolation.html.md index 8e1681ee81e6..f94547125bf6 100644 --- a/website/docs/configuration/interpolation.html.md +++ b/website/docs/configuration/interpolation.html.md @@ -395,6 +395,9 @@ The supported built-in functions are: invocation of the function, so in order to prevent diffs on every plan & apply, it must be used with the [`ignore_changes`](/docs/configuration/resources.html#ignore-changes) lifecycle attribute. + * `timeadd(time, duration)` - Returns a UTC timestamp string corresponding to adding a given `duration` to `time` in RFC 3339 format. + For example, `timeadd("2017-11-22T00:00:00Z", "10m")` produces a value `"2017-11-22T00:10:00Z"`. + * `title(string)` - Returns a copy of the string with the first characters of all the words capitalized. * `transpose(map)` - Swaps the keys and list values in a map of lists of strings. For example, transpose(map("a", list("1", "2"), "b", list("2", "3")) produces a value equivalent to map("1", list("a"), "2", list("a", "b"), "3", list("b")).