Skip to content

Commit e1d2685

Browse files
l46kokcopybara-github
authored andcommitted
Internal Changes
PiperOrigin-RevId: 910292784
1 parent 5fc1766 commit e1d2685

10 files changed

Lines changed: 167 additions & 112 deletions

File tree

conformance/src/test/java/dev/cel/conformance/policy/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ java_library(
1212
deps = [
1313
"//:auto_value",
1414
"//bundle:cel",
15+
"//policy:parser_factory",
16+
"//policy/testing:k8s_test_tag_handler",
1517
"//runtime:function_binding",
1618
"//testing/testrunner:cel_expression_source",
1719
"//testing/testrunner:cel_test_context",

conformance/src/test/java/dev/cel/conformance/policy/PolicyConformanceTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import dev.cel.bundle.Cel;
1919
import dev.cel.bundle.CelFactory;
2020
import dev.cel.expr.conformance.proto3.TestAllTypes;
21+
import dev.cel.policy.CelPolicyParserFactory;
22+
import dev.cel.policy.testing.K8sTagHandler;
2123
import dev.cel.runtime.CelFunctionBinding;
2224
import dev.cel.testing.testrunner.CelExpressionSource;
2325
import dev.cel.testing.testrunner.CelTestContext;
@@ -77,6 +79,13 @@ public void evaluate() throws Throwable {
7779
TestAllTypes.getDescriptor().getFile(),
7880
Struct.getDescriptor().getFile());
7981

82+
// Scopes the custom Kubernetes tag visitor exclusively to k8s tests to prevent non-standard
83+
// grammar leakage.
84+
if (name.startsWith("k8s/")) {
85+
contextBuilder.setCelPolicyParser(
86+
CelPolicyParserFactory.newYamlParserBuilder().addTagVisitor(new K8sTagHandler()).build());
87+
}
88+
8089
Path yamlConfigPath = Paths.get(dirPath, "config.yaml");
8190
Path textprotoConfigPath = Paths.get(dirPath, "config.textproto");
8291

policy/src/main/java/dev/cel/policy/CelPolicy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ public abstract static class Builder implements RequiredFieldsChecker {
252252

253253
abstract Optional<Long> id();
254254

255-
abstract Optional<Result> result();
255+
public abstract Optional<Result> result();
256256

257257
abstract Optional<ValueString> explanation();
258258

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
load("@rules_java//java:defs.bzl", "java_library")
2+
3+
package(
4+
default_applicable_licenses = [
5+
"//:license",
6+
],
7+
default_testonly = True,
8+
default_visibility = [
9+
"//policy/testing:__pkg__",
10+
],
11+
)
12+
13+
java_library(
14+
name = "k8s_tag_handler",
15+
srcs = ["K8sTagHandler.java"],
16+
tags = [
17+
],
18+
deps = [
19+
"//common/formats:value_string",
20+
"//common/formats:yaml_helper",
21+
"//policy",
22+
"//policy:parser",
23+
"//policy:policy_parser_context",
24+
"@maven//:com_google_guava_guava",
25+
"@maven//:org_yaml_snakeyaml",
26+
],
27+
)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2026 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 dev.cel.policy.testing;
16+
17+
import com.google.common.annotations.VisibleForTesting;
18+
import dev.cel.common.formats.ValueString;
19+
import dev.cel.common.formats.YamlHelper;
20+
import dev.cel.common.formats.YamlHelper.YamlNodeType;
21+
import dev.cel.policy.CelPolicy;
22+
import dev.cel.policy.CelPolicy.Match;
23+
import dev.cel.policy.CelPolicyParser.TagVisitor;
24+
import dev.cel.policy.PolicyParserContext;
25+
import org.yaml.snakeyaml.nodes.Node;
26+
import org.yaml.snakeyaml.nodes.SequenceNode;
27+
28+
/**
29+
* K8sTagHandler is a {@link TagVisitor} implementation to support parsing Kubernetes
30+
* ValidatingAdmissionPolicy structures in testing and conformance environments.
31+
*/
32+
@VisibleForTesting
33+
public final class K8sTagHandler implements TagVisitor<Node> {
34+
35+
@Override
36+
public void visitPolicyTag(
37+
PolicyParserContext<Node> ctx,
38+
long id,
39+
String tagName,
40+
Node node,
41+
CelPolicy.Builder policyBuilder) {
42+
switch (tagName) {
43+
case "kind":
44+
policyBuilder.putMetadata("kind", ctx.newYamlString(node).value());
45+
break;
46+
case "metadata":
47+
YamlHelper.assertYamlType(ctx, id, node, YamlNodeType.MAP);
48+
break;
49+
case "spec":
50+
CelPolicy.Rule spec = ctx.parseRule(ctx, policyBuilder, node);
51+
policyBuilder.setRule(spec);
52+
break;
53+
default:
54+
TagVisitor.super.visitPolicyTag(ctx, id, tagName, node, policyBuilder);
55+
break;
56+
}
57+
}
58+
59+
@Override
60+
public void visitRuleTag(
61+
PolicyParserContext<Node> ctx,
62+
long id,
63+
String tagName,
64+
Node node,
65+
CelPolicy.Builder policyBuilder,
66+
CelPolicy.Rule.Builder ruleBuilder) {
67+
switch (tagName) {
68+
case "failurePolicy":
69+
policyBuilder.putMetadata(tagName, ctx.newYamlString(node).value());
70+
break;
71+
case "matchConstraints":
72+
YamlHelper.assertYamlType(ctx, id, node, YamlNodeType.MAP);
73+
break;
74+
case "validations":
75+
if (!YamlHelper.assertYamlType(ctx, id, node, YamlNodeType.LIST)) {
76+
return;
77+
}
78+
SequenceNode seqNode = (SequenceNode) node;
79+
for (Node valNode : seqNode.getValue()) {
80+
ruleBuilder.addMatches(ctx.parseMatch(ctx, policyBuilder, valNode));
81+
}
82+
break;
83+
default:
84+
TagVisitor.super.visitRuleTag(ctx, id, tagName, node, policyBuilder, ruleBuilder);
85+
break;
86+
}
87+
}
88+
89+
@Override
90+
public void visitMatchTag(
91+
PolicyParserContext<Node> ctx,
92+
long id,
93+
String tagName,
94+
Node node,
95+
CelPolicy.Builder policyBuilder,
96+
CelPolicy.Match.Builder matchBuilder) {
97+
switch (tagName) {
98+
case "expression":
99+
// The K8s expression to validate must return false in order to generate a violation
100+
// message.
101+
ValueString condition = ctx.newSourceString(node);
102+
String invertedCondition = "!(" + condition.value() + ")";
103+
matchBuilder.setCondition(ValueString.of(condition.id(), invertedCondition));
104+
break;
105+
case "messageExpression":
106+
matchBuilder.setResult(Match.Result.ofOutput(ctx.newSourceString(node)));
107+
break;
108+
default:
109+
TagVisitor.super.visitMatchTag(ctx, id, tagName, node, policyBuilder, matchBuilder);
110+
break;
111+
}
112+
}
113+
}

policy/src/test/java/dev/cel/policy/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ java_library(
3030
"//policy:compiler_factory",
3131
"//policy:parser",
3232
"//policy:parser_factory",
33-
"//policy:policy_parser_context",
3433
"//policy:source",
3534
"//policy:validation_exception",
35+
"//policy/testing:k8s_test_tag_handler",
3636
"//runtime",
3737
"//runtime:function_binding",
3838
"//testing:cel_runtime_flavor",

policy/src/test/java/dev/cel/policy/CelPolicyCompilerImplTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@
3737
import dev.cel.extensions.CelOptionalLibrary;
3838
import dev.cel.parser.CelStandardMacro;
3939
import dev.cel.parser.CelUnparserFactory;
40-
import dev.cel.policy.PolicyTestHelper.K8sTagHandler;
4140
import dev.cel.policy.PolicyTestHelper.PolicyTestSuite;
4241
import dev.cel.policy.PolicyTestHelper.PolicyTestSuite.PolicyTestSection;
4342
import dev.cel.policy.PolicyTestHelper.PolicyTestSuite.PolicyTestSection.PolicyTestCase;
4443
import dev.cel.policy.PolicyTestHelper.PolicyTestSuite.PolicyTestSection.PolicyTestCase.PolicyTestInput;
4544
import dev.cel.policy.PolicyTestHelper.TestYamlPolicy;
45+
import dev.cel.policy.testing.K8sTagHandler;
4646
import dev.cel.runtime.CelFunctionBinding;
4747
import dev.cel.runtime.CelLateFunctionBindings;
4848
import dev.cel.testing.CelRuntimeFlavor;

policy/src/test/java/dev/cel/policy/CelPolicyYamlParserTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
2323
import dev.cel.common.formats.ValueString;
2424
import dev.cel.policy.CelPolicy.Import;
25-
import dev.cel.policy.PolicyTestHelper.K8sTagHandler;
2625
import dev.cel.policy.PolicyTestHelper.TestYamlPolicy;
26+
import dev.cel.policy.testing.K8sTagHandler;
2727
import org.junit.Test;
2828
import org.junit.runner.RunWith;
2929

policy/src/test/java/dev/cel/policy/PolicyTestHelper.java

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,13 @@
1919
import com.google.common.annotations.VisibleForTesting;
2020
import com.google.common.base.Ascii;
2121
import com.google.common.io.Resources;
22-
import dev.cel.common.formats.ValueString;
23-
import dev.cel.policy.CelPolicy.Match;
24-
import dev.cel.policy.CelPolicy.Match.Result;
25-
import dev.cel.policy.CelPolicy.Rule;
26-
import dev.cel.policy.CelPolicyParser.TagVisitor;
2722
import java.io.IOException;
2823
import java.net.URL;
2924
import java.util.List;
3025
import java.util.Map;
3126
import org.yaml.snakeyaml.LoaderOptions;
3227
import org.yaml.snakeyaml.Yaml;
3328
import org.yaml.snakeyaml.constructor.Constructor;
34-
import org.yaml.snakeyaml.nodes.Node;
35-
import org.yaml.snakeyaml.nodes.SequenceNode;
3629

3730
/** Package-private class to assist with policy testing. */
3831
final class PolicyTestHelper {
@@ -273,106 +266,5 @@ private static String readFile(String path) throws IOException {
273266
return Resources.toString(getResource(path), UTF_8);
274267
}
275268

276-
static class K8sTagHandler implements TagVisitor<Node> {
277-
278-
@Override
279-
public void visitPolicyTag(
280-
PolicyParserContext<Node> ctx,
281-
long id,
282-
String tagName,
283-
Node node,
284-
CelPolicy.Builder policyBuilder) {
285-
switch (tagName) {
286-
case "kind":
287-
policyBuilder.putMetadata("kind", ctx.newYamlString(node));
288-
break;
289-
case "metadata":
290-
long metadataId = ctx.collectMetadata(node);
291-
if (!node.getTag().getValue().equals("tag:yaml.org,2002:map")) {
292-
ctx.reportError(
293-
metadataId,
294-
String.format(
295-
"invalid 'metadata' type, expected map got: %s", node.getTag().getValue()));
296-
}
297-
break;
298-
case "spec":
299-
Rule rule = ctx.parseRule(ctx, policyBuilder, node);
300-
policyBuilder.setRule(rule);
301-
break;
302-
default:
303-
TagVisitor.super.visitPolicyTag(ctx, id, tagName, node, policyBuilder);
304-
break;
305-
}
306-
}
307-
308-
@Override
309-
public void visitRuleTag(
310-
PolicyParserContext<Node> ctx,
311-
long id,
312-
String tagName,
313-
Node node,
314-
CelPolicy.Builder policyBuilder,
315-
Rule.Builder ruleBuilder) {
316-
switch (tagName) {
317-
case "failurePolicy":
318-
policyBuilder.putMetadata(tagName, ctx.newYamlString(node));
319-
break;
320-
case "matchConstraints":
321-
long matchConstraintsId = ctx.collectMetadata(node);
322-
if (!node.getTag().getValue().equals("tag:yaml.org,2002:map")) {
323-
ctx.reportError(
324-
matchConstraintsId,
325-
String.format(
326-
"invalid 'matchConstraints' type, expected map got: %s",
327-
node.getTag().getValue()));
328-
}
329-
break;
330-
case "validations":
331-
long validationId = ctx.collectMetadata(node);
332-
if (!node.getTag().getValue().equals("tag:yaml.org,2002:seq")) {
333-
ctx.reportError(
334-
validationId,
335-
String.format(
336-
"invalid 'validations' type, expected list got: %s", node.getTag().getValue()));
337-
}
338-
339-
SequenceNode validationNodes = (SequenceNode) node;
340-
for (Node element : validationNodes.getValue()) {
341-
ruleBuilder.addMatches(ctx.parseMatch(ctx, policyBuilder, element));
342-
}
343-
break;
344-
default:
345-
TagVisitor.super.visitRuleTag(ctx, id, tagName, node, policyBuilder, ruleBuilder);
346-
break;
347-
}
348-
}
349-
350-
@Override
351-
public void visitMatchTag(
352-
PolicyParserContext<Node> ctx,
353-
long id,
354-
String tagName,
355-
Node node,
356-
CelPolicy.Builder policyBuilder,
357-
Match.Builder matchBuilder) {
358-
switch (tagName) {
359-
case "expression":
360-
// The K8s expression to validate must return false in order to generate a violation
361-
// message.
362-
ValueString conditionValue = ctx.newYamlString(node);
363-
conditionValue =
364-
conditionValue.toBuilder().setValue("!(" + conditionValue.value() + ")").build();
365-
matchBuilder.setCondition(conditionValue);
366-
break;
367-
case "messageExpression":
368-
matchBuilder.setResult(Result.ofOutput(ctx.newYamlString(node)));
369-
break;
370-
default:
371-
TagVisitor.super.visitMatchTag(ctx, id, tagName, node, policyBuilder, matchBuilder);
372-
break;
373-
}
374-
}
375-
}
376-
377269
private PolicyTestHelper() {}
378270
}

policy/testing/BUILD.bazel

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
load("@rules_java//java:defs.bzl", "java_library")
2+
3+
package(
4+
default_applicable_licenses = ["//:license"],
5+
default_testonly = True,
6+
default_visibility = ["//:internal"],
7+
)
8+
9+
java_library(
10+
name = "k8s_test_tag_handler",
11+
exports = ["//policy/src/main/java/dev/cel/policy/testing:k8s_tag_handler"],
12+
)

0 commit comments

Comments
 (0)