Skip to content

Commit e086729

Browse files
authored
Add k8s custom policy tag handler for test (#1121)
* Add k8s custom policy tag handler for test * Add copyright and remove redundant attribute from go_library target
1 parent 1ef45b2 commit e086729

File tree

3 files changed

+102
-80
lines changed

3 files changed

+102
-80
lines changed

policy/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ go_library(
2828
"config.go",
2929
"parser.go",
3030
"source.go",
31+
"test_tag_handler_k8s.go",
3132
],
3233
importpath = "github.com/google/cel-go/policy",
3334
deps = [

policy/helper_test.go

Lines changed: 1 addition & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ var (
3939
{
4040
name: "k8s",
4141
parseOpts: []ParserOption{func(p *Parser) (*Parser, error) {
42-
p.TagVisitor = k8sTagHandler()
42+
p.TagVisitor = K8sTestTagHandler()
4343
return p, nil
4444
}},
4545
expr: `
@@ -268,85 +268,6 @@ ERROR: testdata/errors_unreachable/policy.yaml:36:13: match creates unreachable
268268
}
269269
)
270270

271-
func k8sTagHandler() TagVisitor {
272-
return k8sAdmissionTagHandler{TagVisitor: DefaultTagVisitor()}
273-
}
274-
275-
type k8sAdmissionTagHandler struct {
276-
TagVisitor
277-
}
278-
279-
func (k8sAdmissionTagHandler) PolicyTag(ctx ParserContext, id int64, tagName string, node *yaml.Node, policy *Policy) {
280-
switch tagName {
281-
case "kind":
282-
policy.SetMetadata("kind", ctx.NewString(node).Value)
283-
case "metadata":
284-
m := k8sMetadata{}
285-
if err := node.Decode(&m); err != nil {
286-
ctx.ReportErrorAtID(id, "invalid yaml metadata node: %v, error: %w", node, err)
287-
return
288-
}
289-
case "spec":
290-
spec := ctx.ParseRule(ctx, policy, node)
291-
policy.SetRule(spec)
292-
default:
293-
ctx.ReportErrorAtID(id, "unsupported policy tag: %s", tagName)
294-
}
295-
}
296-
297-
func (k8sAdmissionTagHandler) RuleTag(ctx ParserContext, id int64, tagName string, node *yaml.Node, policy *Policy, r *Rule) {
298-
switch tagName {
299-
case "failurePolicy":
300-
policy.SetMetadata(tagName, ctx.NewString(node).Value)
301-
case "matchConstraints":
302-
m := k8sMatchConstraints{}
303-
if err := node.Decode(&m); err != nil {
304-
ctx.ReportErrorAtID(id, "invalid yaml matchConstraints node: %v, error: %w", node, err)
305-
return
306-
}
307-
case "validations":
308-
id := ctx.CollectMetadata(node)
309-
if node.LongTag() != "tag:yaml.org,2002:seq" {
310-
ctx.ReportErrorAtID(id, "invalid 'validations' type, expected list got: %s", node.LongTag())
311-
return
312-
}
313-
for _, val := range node.Content {
314-
r.AddMatch(ctx.ParseMatch(ctx, policy, val))
315-
}
316-
default:
317-
ctx.ReportErrorAtID(id, "unsupported rule tag: %s", tagName)
318-
}
319-
}
320-
321-
func (k8sAdmissionTagHandler) MatchTag(ctx ParserContext, id int64, tagName string, node *yaml.Node, policy *Policy, m *Match) {
322-
if m.Output().Value == "" {
323-
m.SetOutput(ValueString{Value: "'invalid admission request'"})
324-
}
325-
switch tagName {
326-
case "expression":
327-
// The K8s expression to validate must return false in order to generate a violation message.
328-
condition := ctx.NewString(node)
329-
condition.Value = "!(" + condition.Value + ")"
330-
m.SetCondition(condition)
331-
case "messageExpression":
332-
m.SetOutput(ctx.NewString(node))
333-
}
334-
}
335-
336-
type k8sMetadata struct {
337-
Name string `yaml:"name"`
338-
}
339-
340-
type k8sMatchConstraints struct {
341-
ResourceRules []k8sResourceRule `yaml:"resourceRules"`
342-
}
343-
344-
type k8sResourceRule struct {
345-
APIGroups []string `yaml:"apiGroups"`
346-
APIVersions []string `yaml:"apiVersions"`
347-
Operations []string `yaml:"operations"`
348-
}
349-
350271
func readPolicy(t testing.TB, fileName string) *Source {
351272
t.Helper()
352273
policyBytes, err := os.ReadFile(fileName)

policy/test_tag_handler_k8s.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package policy
16+
17+
import (
18+
"gopkg.in/yaml.v3"
19+
)
20+
21+
// K8sTestTagHandler returns a TagVisitor which handles custom policy tags used in K8s policies. This is
22+
// a helper function to be used in tests.
23+
func K8sTestTagHandler() TagVisitor {
24+
return k8sAdmissionTagHandler{TagVisitor: DefaultTagVisitor()}
25+
}
26+
27+
type k8sAdmissionTagHandler struct {
28+
TagVisitor
29+
}
30+
31+
func (k8sAdmissionTagHandler) PolicyTag(ctx ParserContext, id int64, tagName string, node *yaml.Node, policy *Policy) {
32+
switch tagName {
33+
case "kind":
34+
policy.SetMetadata("kind", ctx.NewString(node).Value)
35+
case "metadata":
36+
m := k8sMetadata{}
37+
if err := node.Decode(&m); err != nil {
38+
ctx.ReportErrorAtID(id, "invalid yaml metadata node: %v, error: %w", node, err)
39+
return
40+
}
41+
case "spec":
42+
spec := ctx.ParseRule(ctx, policy, node)
43+
policy.SetRule(spec)
44+
default:
45+
ctx.ReportErrorAtID(id, "unsupported policy tag: %s", tagName)
46+
}
47+
}
48+
49+
func (k8sAdmissionTagHandler) RuleTag(ctx ParserContext, id int64, tagName string, node *yaml.Node, policy *Policy, r *Rule) {
50+
switch tagName {
51+
case "failurePolicy":
52+
policy.SetMetadata(tagName, ctx.NewString(node).Value)
53+
case "matchConstraints":
54+
m := k8sMatchConstraints{}
55+
if err := node.Decode(&m); err != nil {
56+
ctx.ReportErrorAtID(id, "invalid yaml matchConstraints node: %v, error: %w", node, err)
57+
return
58+
}
59+
case "validations":
60+
id := ctx.CollectMetadata(node)
61+
if node.LongTag() != "tag:yaml.org,2002:seq" {
62+
ctx.ReportErrorAtID(id, "invalid 'validations' type, expected list got: %s", node.LongTag())
63+
return
64+
}
65+
for _, val := range node.Content {
66+
r.AddMatch(ctx.ParseMatch(ctx, policy, val))
67+
}
68+
default:
69+
ctx.ReportErrorAtID(id, "unsupported rule tag: %s", tagName)
70+
}
71+
}
72+
73+
func (k8sAdmissionTagHandler) MatchTag(ctx ParserContext, id int64, tagName string, node *yaml.Node, policy *Policy, m *Match) {
74+
if m.Output().Value == "" {
75+
m.SetOutput(ValueString{Value: "'invalid admission request'"})
76+
}
77+
switch tagName {
78+
case "expression":
79+
// The K8s expression to validate must return false in order to generate a violation message.
80+
condition := ctx.NewString(node)
81+
condition.Value = "!(" + condition.Value + ")"
82+
m.SetCondition(condition)
83+
case "messageExpression":
84+
m.SetOutput(ctx.NewString(node))
85+
}
86+
}
87+
88+
type k8sMetadata struct {
89+
Name string `yaml:"name"`
90+
}
91+
92+
type k8sMatchConstraints struct {
93+
ResourceRules []k8sResourceRule `yaml:"resourceRules"`
94+
}
95+
96+
type k8sResourceRule struct {
97+
APIGroups []string `yaml:"apiGroups"`
98+
APIVersions []string `yaml:"apiVersions"`
99+
Operations []string `yaml:"operations"`
100+
}

0 commit comments

Comments
 (0)