Skip to content

Commit 6c8e947

Browse files
authored
feat: simplify string expressions management (#1839)
Signed-off-by: Charles-Edouard Brétéché <[email protected]>
1 parent 8e36f27 commit 6c8e947

File tree

15 files changed

+103
-81
lines changed

15 files changed

+103
-81
lines changed

pkg/engine/bindings/bindings.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/jmespath-community/go-jmespath/pkg/binding"
99
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
1010
"github.com/kyverno/chainsaw/pkg/engine/templating"
11+
"github.com/kyverno/chainsaw/pkg/expressions"
1112
)
1213

1314
var identifier = regexp.MustCompile(`^\w+$`)
@@ -24,7 +25,7 @@ func RegisterBinding(ctx context.Context, bindings binding.Bindings, name string
2425
}
2526

2627
func ResolveBinding(ctx context.Context, bindings binding.Bindings, input any, variable v1alpha1.Binding) (string, any, error) {
27-
name, err := templating.String(ctx, variable.Name, bindings)
28+
name, err := expressions.String(ctx, variable.Name, bindings)
2829
if err != nil {
2930
return "", nil, err
3031
}

pkg/engine/kubectl/describe.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,22 @@ import (
88
"github.com/jmespath-community/go-jmespath/pkg/binding"
99
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
1010
"github.com/kyverno/chainsaw/pkg/client"
11-
"github.com/kyverno/chainsaw/pkg/engine/templating"
11+
"github.com/kyverno/chainsaw/pkg/expressions"
1212
)
1313

1414
func Describe(ctx context.Context, client client.Client, tc binding.Bindings, collector *v1alpha1.Describe) (string, []string, error) {
1515
if collector == nil {
1616
return "", nil, errors.New("collector is null")
1717
}
18-
name, err := templating.String(ctx, collector.Name, tc)
18+
name, err := expressions.String(ctx, collector.Name, tc)
1919
if err != nil {
2020
return "", nil, err
2121
}
22-
namespace, err := templating.String(ctx, collector.Namespace, tc)
22+
namespace, err := expressions.String(ctx, collector.Namespace, tc)
2323
if err != nil {
2424
return "", nil, err
2525
}
26-
selector, err := templating.String(ctx, collector.Selector, tc)
26+
selector, err := expressions.String(ctx, collector.Selector, tc)
2727
if err != nil {
2828
return "", nil, err
2929
}

pkg/engine/kubectl/get.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,26 @@ import (
77
"github.com/jmespath-community/go-jmespath/pkg/binding"
88
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
99
"github.com/kyverno/chainsaw/pkg/client"
10-
"github.com/kyverno/chainsaw/pkg/engine/templating"
10+
"github.com/kyverno/chainsaw/pkg/expressions"
1111
)
1212

1313
func Get(ctx context.Context, client client.Client, tc binding.Bindings, collector *v1alpha1.Get) (string, []string, error) {
1414
if collector == nil {
1515
return "", nil, errors.New("collector is null")
1616
}
17-
name, err := templating.String(ctx, collector.Name, tc)
17+
name, err := expressions.String(ctx, collector.Name, tc)
1818
if err != nil {
1919
return "", nil, err
2020
}
21-
namespace, err := templating.String(ctx, collector.Namespace, tc)
21+
namespace, err := expressions.String(ctx, collector.Namespace, tc)
2222
if err != nil {
2323
return "", nil, err
2424
}
25-
selector, err := templating.String(ctx, collector.Selector, tc)
25+
selector, err := expressions.String(ctx, collector.Selector, tc)
2626
if err != nil {
2727
return "", nil, err
2828
}
29-
format, err := templating.String(ctx, string(collector.Format), tc)
29+
format, err := expressions.String(ctx, string(collector.Format), tc)
3030
if err != nil {
3131
return "", nil, err
3232
}

pkg/engine/kubectl/logs.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,26 @@ import (
77

88
"github.com/jmespath-community/go-jmespath/pkg/binding"
99
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
10-
"github.com/kyverno/chainsaw/pkg/engine/templating"
10+
"github.com/kyverno/chainsaw/pkg/expressions"
1111
)
1212

1313
func Logs(ctx context.Context, tc binding.Bindings, collector *v1alpha1.PodLogs) (string, []string, error) {
1414
if collector == nil {
1515
return "", nil, errors.New("collector is null")
1616
}
17-
name, err := templating.String(ctx, collector.Name, tc)
17+
name, err := expressions.String(ctx, collector.Name, tc)
1818
if err != nil {
1919
return "", nil, err
2020
}
21-
namespace, err := templating.String(ctx, collector.Namespace, tc)
21+
namespace, err := expressions.String(ctx, collector.Namespace, tc)
2222
if err != nil {
2323
return "", nil, err
2424
}
25-
selector, err := templating.String(ctx, collector.Selector, tc)
25+
selector, err := expressions.String(ctx, collector.Selector, tc)
2626
if err != nil {
2727
return "", nil, err
2828
}
29-
container, err := templating.String(ctx, collector.Container, tc)
29+
container, err := expressions.String(ctx, collector.Container, tc)
3030
if err != nil {
3131
return "", nil, err
3232
}

pkg/engine/kubectl/mapping.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ import (
88
"github.com/jmespath-community/go-jmespath/pkg/binding"
99
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
1010
"github.com/kyverno/chainsaw/pkg/client"
11-
"github.com/kyverno/chainsaw/pkg/engine/templating"
11+
"github.com/kyverno/chainsaw/pkg/expressions"
1212
"k8s.io/apimachinery/pkg/api/meta"
1313
"k8s.io/apimachinery/pkg/runtime/schema"
1414
)
1515

1616
func mapResource(ctx context.Context, client client.Client, tc binding.Bindings, resource v1alpha1.ObjectType) (string, bool, error) {
1717
if resource.APIVersion != "" && resource.Kind != "" {
18-
if apiVersion, err := templating.String(ctx, resource.APIVersion, tc); err != nil {
18+
if apiVersion, err := expressions.String(ctx, resource.APIVersion, tc); err != nil {
1919
return "", false, err
20-
} else if kind, err := templating.String(ctx, resource.Kind, tc); err != nil {
20+
} else if kind, err := expressions.String(ctx, resource.Kind, tc); err != nil {
2121
return "", false, err
2222
} else {
2323
return mapResourceFromApiVersionAndKind(client, apiVersion, kind)

pkg/engine/kubectl/proxy.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,26 @@ import (
88
"github.com/jmespath-community/go-jmespath/pkg/binding"
99
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
1010
"github.com/kyverno/chainsaw/pkg/client"
11-
"github.com/kyverno/chainsaw/pkg/engine/templating"
11+
"github.com/kyverno/chainsaw/pkg/expressions"
1212
)
1313

1414
func Proxy(ctx context.Context, client client.Client, tc binding.Bindings, collector *v1alpha1.Proxy) (string, []string, error) {
1515
if collector == nil {
1616
return "", nil, errors.New("collector is null")
1717
}
18-
name, err := templating.String(ctx, collector.Name, tc)
18+
name, err := expressions.String(ctx, collector.Name, tc)
1919
if err != nil {
2020
return "", nil, err
2121
}
22-
namespace, err := templating.String(ctx, collector.Namespace, tc)
22+
namespace, err := expressions.String(ctx, collector.Namespace, tc)
2323
if err != nil {
2424
return "", nil, err
2525
}
26-
targetPath, err := templating.String(ctx, collector.TargetPath, tc)
26+
targetPath, err := expressions.String(ctx, collector.TargetPath, tc)
2727
if err != nil {
2828
return "", nil, err
2929
}
30-
targetPort, err := templating.String(ctx, collector.TargetPort, tc)
30+
targetPort, err := expressions.String(ctx, collector.TargetPort, tc)
3131
if err != nil {
3232
return "", nil, err
3333
}

pkg/engine/kubectl/wait.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,26 @@ import (
88
"github.com/jmespath-community/go-jmespath/pkg/binding"
99
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
1010
"github.com/kyverno/chainsaw/pkg/client"
11-
"github.com/kyverno/chainsaw/pkg/engine/templating"
11+
"github.com/kyverno/chainsaw/pkg/expressions"
1212
)
1313

1414
func Wait(ctx context.Context, client client.Client, tc binding.Bindings, collector *v1alpha1.Wait) (string, []string, error) {
1515
if collector == nil {
1616
return "", nil, errors.New("collector is null")
1717
}
18-
name, err := templating.String(ctx, collector.Name, tc)
18+
name, err := expressions.String(ctx, collector.Name, tc)
1919
if err != nil {
2020
return "", nil, err
2121
}
22-
namespace, err := templating.String(ctx, collector.Namespace, tc)
22+
namespace, err := expressions.String(ctx, collector.Namespace, tc)
2323
if err != nil {
2424
return "", nil, err
2525
}
26-
selector, err := templating.String(ctx, collector.Selector, tc)
26+
selector, err := expressions.String(ctx, collector.Selector, tc)
2727
if err != nil {
2828
return "", nil, err
2929
}
30-
format, err := templating.String(ctx, string(collector.Format), tc)
30+
format, err := expressions.String(ctx, string(collector.Format), tc)
3131
if err != nil {
3232
return "", nil, err
3333
}
@@ -42,15 +42,15 @@ func Wait(ctx context.Context, client client.Client, tc binding.Bindings, collec
4242
if collector.WaitFor.Deletion != nil {
4343
args = append(args, "--for=delete")
4444
} else if collector.WaitFor.Condition != nil {
45-
name, err := templating.String(ctx, collector.WaitFor.Condition.Name, tc)
45+
name, err := expressions.String(ctx, collector.WaitFor.Condition.Name, tc)
4646
if err != nil {
4747
return "", nil, err
4848
}
4949
if name == "" {
5050
return "", nil, errors.New("a condition name must be specified for condition wait type")
5151
}
5252
if collector.WaitFor.Condition.Value != nil {
53-
value, err := templating.String(ctx, *collector.WaitFor.Condition.Value, tc)
53+
value, err := expressions.String(ctx, *collector.WaitFor.Condition.Value, tc)
5454
if err != nil {
5555
return "", nil, err
5656
}
@@ -59,14 +59,14 @@ func Wait(ctx context.Context, client client.Client, tc binding.Bindings, collec
5959
args = append(args, fmt.Sprintf("--for=condition=%s", name))
6060
}
6161
} else if collector.WaitFor.JsonPath != nil {
62-
path, err := templating.String(ctx, collector.WaitFor.JsonPath.Path, tc)
62+
path, err := expressions.String(ctx, collector.WaitFor.JsonPath.Path, tc)
6363
if err != nil {
6464
return "", nil, err
6565
}
6666
if path == "" {
6767
return "", nil, errors.New("a path must be specified for jsonpath wait type")
6868
}
69-
value, err := templating.String(ctx, collector.WaitFor.JsonPath.Value, tc)
69+
value, err := expressions.String(ctx, collector.WaitFor.JsonPath.Value, tc)
7070
if err != nil {
7171
return "", nil, err
7272
}

pkg/expressions/parse.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package expressions
2+
3+
import (
4+
"context"
5+
"reflect"
6+
"regexp"
7+
)
8+
9+
var (
10+
escapeRegex = regexp.MustCompile(`^\\(.+)\\$`)
11+
engineRegex = regexp.MustCompile(`^\((?:(\w+):)?(.+)\)$`)
12+
)
13+
14+
type Expression struct {
15+
Statement string
16+
Engine string
17+
}
18+
19+
func Parse(ctx context.Context, value string) *Expression {
20+
return parseExpressionRegex(ctx, reflect.ValueOf(value).String())
21+
}
22+
23+
func parseExpressionRegex(_ context.Context, in string) *Expression {
24+
expression := &Expression{}
25+
// 1. match escape, if there's no escaping then match engine
26+
if match := escapeRegex.FindStringSubmatch(in); match != nil {
27+
in = match[1]
28+
} else {
29+
if match := engineRegex.FindStringSubmatch(in); match != nil {
30+
expression.Engine = match[1]
31+
// account for default engine
32+
if expression.Engine == "" {
33+
expression.Engine = "jp"
34+
}
35+
in = match[2]
36+
}
37+
}
38+
// parse statement
39+
expression.Statement = in
40+
if expression.Statement == "" {
41+
return nil
42+
}
43+
return expression
44+
}

pkg/engine/templating/string.go renamed to pkg/expressions/string.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1-
package templating
1+
package expressions
22

33
import (
44
"context"
55
"fmt"
66

77
"github.com/jmespath-community/go-jmespath/pkg/binding"
8-
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
8+
"github.com/kyverno/chainsaw/pkg/engine/functions"
9+
"github.com/kyverno/kyverno-json/pkg/engine/template"
910
)
1011

1112
func String(ctx context.Context, in string, bindings binding.Bindings) (string, error) {
1213
if in == "" {
1314
return "", nil
1415
}
15-
if converted, err := Template(ctx, v1alpha1.Any{Value: in}, nil, bindings); err != nil {
16+
expression := Parse(ctx, in)
17+
if expression == nil || expression.Engine == "" {
18+
return in, nil
19+
}
20+
if converted, err := template.Execute(ctx, expression.Statement, nil, bindings, template.WithFunctionCaller(functions.Caller())); err != nil {
1621
return "", err
1722
} else {
1823
if converted, ok := converted.(string); !ok {

pkg/engine/templating/string_pointer.go renamed to pkg/expressions/string_pointer.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
package templating
1+
package expressions
22

33
import (
44
"context"
55
"fmt"
66

77
"github.com/jmespath-community/go-jmespath/pkg/binding"
8-
"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
8+
"github.com/kyverno/chainsaw/pkg/engine/functions"
9+
"github.com/kyverno/kyverno-json/pkg/engine/template"
910
)
1011

1112
func StringPointer(ctx context.Context, in *string, bindings binding.Bindings) (*string, error) {
@@ -15,7 +16,11 @@ func StringPointer(ctx context.Context, in *string, bindings binding.Bindings) (
1516
if *in == "" {
1617
return in, nil
1718
}
18-
if converted, err := Template(ctx, v1alpha1.Any{Value: *in}, nil, bindings); err != nil {
19+
expression := Parse(ctx, *in)
20+
if expression == nil || expression.Engine == "" {
21+
return in, nil
22+
}
23+
if converted, err := template.Execute(ctx, expression.Statement, nil, bindings, template.WithFunctionCaller(functions.Caller())); err != nil {
1924
return nil, err
2025
} else if converted == nil {
2126
return nil, nil

pkg/engine/templating/string_pointer_test.go renamed to pkg/expressions/string_pointer_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package templating
1+
package expressions
22

33
import (
44
"context"

pkg/engine/templating/string_test.go renamed to pkg/expressions/string_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package templating
1+
package expressions
22

33
import (
44
"context"

pkg/mutate/expression.go

+3-36
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,14 @@ package mutate
33
import (
44
"context"
55
"reflect"
6-
"regexp"
76

7+
"github.com/kyverno/chainsaw/pkg/expressions"
88
reflectutils "github.com/kyverno/kyverno-json/pkg/utils/reflect"
99
)
1010

11-
var (
12-
escapeRegex = regexp.MustCompile(`^\\(.+)\\$`)
13-
engineRegex = regexp.MustCompile(`^\((?:(\w+):)?(.+)\)$`)
14-
)
15-
16-
type expression struct {
17-
statement string
18-
engine string
19-
}
20-
21-
func parseExpressionRegex(_ context.Context, in string) *expression {
22-
expression := &expression{}
23-
// 1. match escape, if there's no escaping then match engine
24-
if match := escapeRegex.FindStringSubmatch(in); match != nil {
25-
in = match[1]
26-
} else {
27-
if match := engineRegex.FindStringSubmatch(in); match != nil {
28-
expression.engine = match[1]
29-
// account for default engine
30-
if expression.engine == "" {
31-
expression.engine = "jp"
32-
}
33-
in = match[2]
34-
}
35-
}
36-
// parse statement
37-
expression.statement = in
38-
if expression.statement == "" {
39-
return nil
40-
}
41-
return expression
42-
}
43-
44-
func parseExpression(ctx context.Context, value any) *expression {
11+
func parseExpression(ctx context.Context, value any) *expressions.Expression {
4512
if reflectutils.GetKind(value) != reflect.String {
4613
return nil
4714
}
48-
return parseExpressionRegex(ctx, reflect.ValueOf(value).String())
15+
return expressions.Parse(ctx, reflect.ValueOf(value).String())
4916
}

0 commit comments

Comments
 (0)