Repository: kyverno/kyverno
Pull request #5946 (2023-01-09 14:23:26)

Added a time_add() filter to add duration and absolute time (cherry-pick #5817)
Added a time_add() filter to add duration and absolute time (cherry-pick #5817)

codecov[bot]: # [Codecov](https://codecov.io/gh/kyverno/kyverno/pull/5946?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) Report
> Merging [#5946](https://codecov.io/gh/kyverno/kyverno/pull/5946?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (1e037f7) into [release-1.9](https://codecov.io/gh/kyverno/kyverno/commit/5d42824f566034b124a19468178646571f410749?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (5d42824) will **increase** coverage by `0.10%`.
> The diff coverage is `65.71%`.

```diff
@@               Coverage Diff               @@
##           release-1.9    #5946      +/-   ##
===============================================
+ Coverage        35.40%   35.50%   +0.10%     
===============================================
  Files              187      187              
  Lines            20775    20845      +70     
===============================================
+ Hits              7355     7401      +46     
- Misses           12612    12628      +16     
- Partials           808      816       +8     
```


| [Impacted Files](https://codecov.io/gh/kyverno/kyverno/pull/5946?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) | Coverage Δ | |
|---|---|---|
| [pkg/engine/jmespath/functions.go](https://codecov.io/gh/kyverno/kyverno/pull/5946?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2VuZ2luZS9qbWVzcGF0aC9mdW5jdGlvbnMuZ28=) | `71.81% <65.71%> (-0.49%)` | :arrow_down: |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno)


Patch:
diff --git a/pkg/engine/jmespath/functions.go b/pkg/engine/jmespath/functions.go
index f513aaf76..91ae20508 100644
--- a/pkg/engine/jmespath/functions.go
+++ b/pkg/engine/jmespath/functions.go
@@ -81,6 +81,8 @@ var (
 	objectFromLists        = "object_from_lists"
 	random                 = "random"
 	x509_decode            = "x509_decode"
+	timeAdd                = "time_add"
+	timeParse              = "time_parse"
 	timeToCron             = "time_to_cron"
 )
 
@@ -502,6 +504,31 @@ func GetFunctions() []*FunctionEntry {
 			ReturnType: []JpType{JpString},
 			Note:       "converts an absolute time (RFC 3339) to a cron expression (string).",
 		},
+		{
+			Entry: &gojmespath.FunctionEntry{
+				Name: timeAdd,
+				Arguments: []ArgSpec{
+					{Types: []JpType{JpString}},
+					{Types: []JpType{JpString}},
+					{Types: []JpType{JpString}},
+				},
+				Handler: jpTimeAdd,
+			},
+			ReturnType: []JpType{JpString},
+			Note:       "adds duration (third string) to a time value (second string) of a specified layout (first string)",
+		},
+		{
+			Entry: &gojmespath.FunctionEntry{
+				Name: timeParse,
+				Arguments: []ArgSpec{
+					{Types: []JpType{JpString}},
+					{Types: []JpType{JpString}},
+				},
+				Handler: jpTimeParse,
+			},
+			ReturnType: []JpType{JpString},
+			Note:       "changes a time value of a given layout to RFC 3339",
+		},
 	}
 }
 
@@ -1131,3 +1158,60 @@ func jpTimeToCron(arguments []interface{}) (interface{}, error) {
 
 	return cron, nil
 }
+
+func jpTimeAdd(arguments []interface{}) (interface{}, error) {
+	var err error
+	layout, err := validateArg("", arguments, 0, reflect.String)
+	if err != nil {
+		return nil, err
+	}
+
+	ts, err := validateArg("", arguments, 1, reflect.String)
+	if err != nil {
+		return nil, err
+	}
+
+	dr, err := validateArg("", arguments, 2, reflect.String)
+	if err != nil {
+		return nil, err
+	}
+
+	var t time.Time
+	if layout.String() != "" {
+		t, err = time.Parse(layout.String(), ts.String())
+	} else {
+		t, err = time.Parse(time.RFC3339, ts.String())
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	var d time.Duration
+	d, err = time.ParseDuration(dr.String())
+	if err != nil {
+		return nil, err
+	}
+
+	return t.Add(d).Format(time.RFC3339), nil
+}
+
+func jpTimeParse(arguments []interface{}) (interface{}, error) {
+	var err error
+	layout, err := validateArg("", arguments, 0, reflect.String)
+	if err != nil {
+		return nil, err
+	}
+
+	ts, err := validateArg("", arguments, 1, reflect.String)
+	if err != nil {
+		return nil, err
+	}
+
+	var t time.Time
+	t, err = time.Parse(layout.String(), ts.String())
+	if err != nil {
+		return nil, err
+	}
+
+	return t.Format(time.RFC3339), nil
+}


Test patch:
diff --git a/pkg/engine/jmespath/functions_test.go b/pkg/engine/jmespath/functions_test.go
index cfbd73687..a20b3c536 100644
--- a/pkg/engine/jmespath/functions_test.go
+++ b/pkg/engine/jmespath/functions_test.go
@@ -1579,3 +1579,63 @@ func Test_TimeToCron(t *testing.T) {
 		})
 	}
 }
+
+func Test_TimeAdd(t *testing.T) {
+	testCases := []struct {
+		test           string
+		expectedResult string
+	}{
+		{
+			test:           "time_add('', '2021-01-02T15:04:05-07:00', '3h')",
+			expectedResult: "2021-01-02T18:04:05-07:00",
+		},
+		{
+			test:           "time_add('Mon Jan 02 15:04:05 MST 2006', 'Sat Jan 02 15:04:05 MST 2021', '5h30m40s')",
+			expectedResult: "2021-01-02T20:34:45Z",
+		},
+	}
+	for i, tc := range testCases {
+		t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
+			query, err := New(tc.test)
+			assert.NilError(t, err)
+
+			res, err := query.Search("")
+			assert.NilError(t, err)
+
+			result, ok := res.(string)
+			assert.Assert(t, ok)
+
+			assert.Equal(t, result, tc.expectedResult)
+		})
+	}
+}
+
+func Test_TimeParse(t *testing.T) {
+	testCases := []struct {
+		test           string
+		expectedResult string
+	}{
+		{
+			test:           "time_parse('2006-01-02T15:04:05Z07:00', '2021-01-02T15:04:05-07:00')",
+			expectedResult: "2021-01-02T15:04:05-07:00",
+		},
+		{
+			test:           "time_parse('Mon Jan 02 15:04:05 MST 2006', 'Sat Jan 02 15:04:05 MST 2021')",
+			expectedResult: "2021-01-02T15:04:05Z",
+		},
+	}
+	for i, tc := range testCases {
+		t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
+			query, err := New(tc.test)
+			assert.NilError(t, err)
+
+			res, err := query.Search("")
+			assert.NilError(t, err)
+
+			result, ok := res.(string)
+			assert.Assert(t, ok)
+
+			assert.Equal(t, result, tc.expectedResult)
+		})
+	}
+}