Skip to content

Commit 59e7d59

Browse files
authored
Merge pull request #1429 from joejstuart/EC-1338
Add pipeline_intention field to rule metadata
2 parents acfaf0c + efa7f76 commit 59e7d59

File tree

10 files changed

+303
-160
lines changed

10 files changed

+303
-160
lines changed

antora/docs/modules/ROOT/pages/packages/release_olm.adoc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Each image referenced by the OLM bundle should match an entry in the list of pre
4343
* FAILURE message: `The %q CSV image reference is not from an allowed registry.`
4444
* Code: `olm.allowed_registries`
4545
* Effective from: `2024-09-01T00:00:00Z`
46-
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L288[Source, window="_blank"]
46+
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L304[Source, window="_blank"]
4747

4848
[#olm__olm_bundle_multi_arch]
4949
=== link:#olm__olm_bundle_multi_arch[OLM bundle images are not multi-arch]
@@ -56,7 +56,7 @@ OLM bundle images should be built for a single architecture. They should not be
5656
* FAILURE message: `The %q bundle image is a multi-arch reference.`
5757
* Code: `olm.olm_bundle_multi_arch`
5858
* Effective from: `2025-5-01T00:00:00Z`
59-
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L321[Source, window="_blank"]
59+
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L337[Source, window="_blank"]
6060

6161
[#olm__allowed_registries_related]
6262
=== link:#olm__allowed_registries_related[Related images references are from allowed registries]
@@ -69,7 +69,7 @@ Each image indicated as a related image should match an entry in the list of pre
6969
* FAILURE message: `The %q related image reference is not from an allowed registry.`
7070
* Code: `olm.allowed_registries_related`
7171
* Effective from: `2025-04-15T00:00:00Z`
72-
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L218[Source, window="_blank"]
72+
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L230[Source, window="_blank"]
7373

7474
[#olm__required_olm_features_annotations_provided]
7575
=== link:#olm__required_olm_features_annotations_provided[Required OLM feature annotations list provided]
@@ -105,7 +105,7 @@ Check the input image for the presence of related images. Ensure that all images
105105
* FAILURE message: `The %q related image reference is not accessible.`
106106
* Code: `olm.inaccessible_related_images`
107107
* Effective from: `2025-03-10T00:00:00Z`
108-
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L188[Source, window="_blank"]
108+
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L196[Source, window="_blank"]
109109

110110
[#olm__unmapped_references]
111111
=== link:#olm__unmapped_references[Unmapped images in OLM bundle]
@@ -118,7 +118,7 @@ Check the OLM bundle image for the presence of unmapped image references. Unmapp
118118
* FAILURE message: `The %q CSV image reference is not in the snapshot or accessible.`
119119
* Code: `olm.unmapped_references`
120120
* Effective from: `2024-08-15T00:00:00Z`
121-
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L248[Source, window="_blank"]
121+
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L260[Source, window="_blank"]
122122

123123
[#olm__unpinned_references]
124124
=== link:#olm__unpinned_references[Unpinned images in OLM bundle]
@@ -155,4 +155,4 @@ Check the input image for the presence of related images. Ensure all related ima
155155
* Rule type: [rule-type-indicator failure]#FAILURE#
156156
* FAILURE message: `%d related images are not pinned with a digest: %s.`
157157
* Code: `olm.unpinned_related_images`
158-
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L156[Source, window="_blank"]
158+
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/olm/olm.rego#L160[Source, window="_blank"]

antora/docs/modules/ROOT/pages/packages/release_schedule.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Check if the current date is not allowed based on the rule data value from the k
1818
* Rule type: [rule-type-indicator failure]#FAILURE#
1919
* FAILURE message: `%s is a disallowed date: %s`
2020
* Code: `schedule.date_restriction`
21-
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/schedule/schedule.rego#L38[Source, window="_blank"]
21+
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/schedule/schedule.rego#L41[Source, window="_blank"]
2222

2323
[#schedule__rule_data_provided]
2424
=== link:#schedule__rule_data_provided[Rule data provided]
@@ -30,7 +30,7 @@ Confirm the expected rule data keys have been provided in the expected format. T
3030
* Rule type: [rule-type-indicator failure]#FAILURE#
3131
* FAILURE message: `%s`
3232
* Code: `schedule.rule_data_provided`
33-
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/schedule/schedule.rego#L62[Source, window="_blank"]
33+
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/schedule/schedule.rego#L68[Source, window="_blank"]
3434

3535
[#schedule__weekday_restriction]
3636
=== link:#schedule__weekday_restriction[Weekday Restriction]
Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ import rego.v1
44

55
import data.lib.time as time_lib
66

7+
# The first entry in the chain always points to the active rule, even if it has
8+
# no declared annotations (in which case the annotations member is not present).
9+
# Thus, result_helper assumes every rule defines annotations. At the very least
10+
# custom.short_name must be present.
11+
_rule_annotations(chain) := chain[0].annotations
12+
13+
pipeline_intention_match(chain) if {
14+
rule_data("pipeline_intention") in _rule_annotations(chain).custom.pipeline_intention
15+
} else := false
16+
717
result_helper(chain, failure_sprintf_params) := result if {
818
with_collections := {"collections": _rule_annotations(chain).custom.collections}
919
result := object.union(_basic_result(chain, failure_sprintf_params), with_collections)
@@ -39,12 +49,6 @@ _code(chain) := code if {
3949
code := sprintf("%s.%s", [pkg_name, rule_name])
4050
}
4151

42-
# The first entry in the chain always points to the active rule, even if it has
43-
# no declared annotations (in which case the annotations member is not present).
44-
# Thus, result_helper assumes every rule defines annotations. At the very least
45-
# custom.short_name must be present.
46-
_rule_annotations(chain) := chain[0].annotations
47-
4852
_pkg_name(rule_path) := name if {
4953
# "data" is automatically added by rego.
5054
p1 := _left_strip_elements(["data"], rule_path)
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package lib_test
2+
3+
import rego.v1
4+
5+
import data.lib
6+
7+
test_rule_annotations_with_annotations if {
8+
rule_annotations := {"custom": {
9+
"short_name": "TestRule",
10+
"failure_msg": "Test failure message",
11+
"pipeline_intention": ["build", "test"],
12+
}}
13+
14+
chain := [
15+
{"annotations": rule_annotations, "path": ["data", "test", "deny"]},
16+
{"annotations": {}, "path": ["ignored", "path"]},
17+
]
18+
19+
lib.assert_equal(rule_annotations, lib._rule_annotations(chain))
20+
}
21+
22+
test_rule_annotations_empty_annotations if {
23+
empty_annotations := {}
24+
25+
chain := [
26+
{"annotations": empty_annotations, "path": ["data", "test", "deny"]},
27+
{"annotations": {"some": "other"}, "path": ["ignored", "path"]},
28+
]
29+
30+
lib.assert_equal(empty_annotations, lib._rule_annotations(chain))
31+
}
32+
33+
test_rule_annotations_only_first_entry if {
34+
first_rule_annotations := {"custom": {"short_name": "FirstRule"}}
35+
second_rule_annotations := {"custom": {"short_name": "SecondRule"}}
36+
37+
chain := [
38+
{"annotations": first_rule_annotations, "path": ["data", "test", "deny"]},
39+
{"annotations": second_rule_annotations, "path": ["other", "path"]},
40+
]
41+
42+
# Should only return annotations from the first entry
43+
lib.assert_equal(first_rule_annotations, lib._rule_annotations(chain))
44+
}
45+
46+
test_rule_annotations_single_entry_chain if {
47+
rule_annotations := {"custom": {"short_name": "SingleRule"}}
48+
49+
chain := [{"annotations": rule_annotations, "path": ["data", "single", "deny"]}]
50+
51+
lib.assert_equal(rule_annotations, lib._rule_annotations(chain))
52+
}
53+
54+
test_pipeline_intention_match_with_matching_intention if {
55+
rule_annotations := {"custom": {
56+
"short_name": "TestRule",
57+
"pipeline_intention": ["build", "release", "test"],
58+
}}
59+
60+
chain := [{"annotations": rule_annotations, "path": ["data", "test", "deny"]}]
61+
62+
# When rule_data("pipeline_intention") matches one of the pipeline_intention values
63+
lib.assert_equal(true, lib.pipeline_intention_match(chain)) with data.rule_data.pipeline_intention as "release"
64+
}
65+
66+
test_pipeline_intention_match_with_non_matching_intention if {
67+
rule_annotations := {"custom": {
68+
"short_name": "TestRule",
69+
"pipeline_intention": ["build", "test"],
70+
}}
71+
72+
chain := [{"annotations": rule_annotations, "path": ["data", "test", "deny"]}]
73+
74+
# When rule_data("pipeline_intention") doesn't match any of the pipeline_intention values
75+
lib.assert_equal(false, lib.pipeline_intention_match(chain)) with data.rule_data.pipeline_intention as "release"
76+
}
77+
78+
test_pipeline_intention_match_with_empty_pipeline_intention if {
79+
rule_annotations := {"custom": {
80+
"short_name": "TestRule",
81+
"pipeline_intention": [],
82+
}}
83+
84+
chain := [{"annotations": rule_annotations, "path": ["data", "test", "deny"]}]
85+
86+
# When pipeline_intention is an empty list, should return false
87+
lib.assert_equal(false, lib.pipeline_intention_match(chain)) with data.rule_data.pipeline_intention as "release"
88+
}
89+
90+
test_pipeline_intention_match_without_pipeline_intention_field if {
91+
rule_annotations := {"custom": {
92+
"short_name": "TestRule",
93+
"failure_msg": "Some failure message",
94+
}}
95+
96+
chain := [{"annotations": rule_annotations, "path": ["data", "test", "deny"]}]
97+
98+
# When pipeline_intention field is missing, should return false
99+
lib.assert_equal(false, lib.pipeline_intention_match(chain)) with data.rule_data.pipeline_intention as "release"
100+
}
101+
102+
test_pipeline_intention_match_without_custom_field if {
103+
rule_annotations := {"other": {"some_field": "value"}}
104+
105+
chain := [{"annotations": rule_annotations, "path": ["data", "test", "deny"]}]
106+
107+
# When custom field is missing, should return false
108+
lib.assert_equal(false, lib.pipeline_intention_match(chain)) with data.rule_data.pipeline_intention as "release"
109+
}
110+
111+
test_pipeline_intention_match_with_null_rule_data if {
112+
rule_annotations := {"custom": {
113+
"short_name": "TestRule",
114+
"pipeline_intention": ["build", "release", "test"],
115+
}}
116+
117+
chain := [{"annotations": rule_annotations, "path": ["data", "test", "deny"]}]
118+
119+
# When rule_data("pipeline_intention") is null, should return false
120+
lib.assert_equal(false, lib.pipeline_intention_match(chain)) with data.rule_data.pipeline_intention as null
121+
}
122+
123+
test_pipeline_intention_match_with_multiple_matching_intentions if {
124+
rule_annotations := {"custom": {
125+
"short_name": "TestRule",
126+
"pipeline_intention": ["build", "release", "production", "test"],
127+
}}
128+
129+
chain := [{"annotations": rule_annotations, "path": ["data", "test", "deny"]}]
130+
131+
# When rule_data("pipeline_intention") matches one of multiple pipeline_intention values
132+
lib.assert_equal(true, lib.pipeline_intention_match(chain)) with data.rule_data.pipeline_intention as "production"
133+
}
134+
135+
test_pipeline_intention_match_case_sensitivity if {
136+
rule_annotations := {"custom": {
137+
"short_name": "TestRule",
138+
"pipeline_intention": ["Build", "Release"],
139+
}}
140+
141+
chain := [{"annotations": rule_annotations, "path": ["data", "test", "deny"]}]
142+
143+
# Case sensitivity should be preserved
144+
lib.assert_equal(false, lib.pipeline_intention_match(chain)) with data.rule_data.pipeline_intention as "release"
145+
lib.assert_equal(true, lib.pipeline_intention_match(chain)) with data.rule_data.pipeline_intention as "Release"
146+
}
147+
148+
test_result_helper if {
149+
expected_result := {
150+
"code": "oh.Hey",
151+
"effective_on": "2022-01-01T00:00:00Z",
152+
"msg": "Bad thing foo",
153+
}
154+
155+
rule_annotations := {"custom": {
156+
"short_name": "Hey",
157+
"failure_msg": "Bad thing %s",
158+
}}
159+
160+
chain := [
161+
{"annotations": rule_annotations, "path": ["data", "oh", "deny"]},
162+
{"annotations": {}, "path": ["ignored", "ignored"]}, # Actually not needed any more
163+
]
164+
165+
lib.assert_equal(expected_result, lib.result_helper(chain, ["foo"]))
166+
}
167+
168+
test_result_helper_without_package_annotation if {
169+
expected_result := {
170+
"code": "package_name.Hey", # Fixme
171+
"effective_on": "2022-01-01T00:00:00Z",
172+
"msg": "Bad thing foo",
173+
}
174+
175+
rule_annotations := {"custom": {
176+
"short_name": "Hey",
177+
"failure_msg": "Bad thing %s",
178+
}}
179+
180+
chain := [{"annotations": rule_annotations, "path": ["package_name", "deny"]}]
181+
182+
lib.assert_equal(expected_result, lib.result_helper(chain, ["foo"]))
183+
}
184+
185+
test_result_helper_with_collections if {
186+
expected := {
187+
"code": "some.path.oh.Hey",
188+
"collections": ["spam"],
189+
"effective_on": "2022-01-01T00:00:00Z",
190+
"msg": "Bad thing foo",
191+
}
192+
193+
rule_annotations := {"custom": {
194+
"collections": ["spam"],
195+
"short_name": "Hey",
196+
"failure_msg": "Bad thing %s",
197+
}}
198+
199+
chain := [
200+
{"annotations": rule_annotations, "path": ["some", "path", "oh", "deny"]},
201+
{"annotations": {}, "path": ["ignored", "ignored"]}, # Actually not needed any more
202+
]
203+
204+
lib.assert_equal(expected, lib.result_helper(chain, ["foo"]))
205+
}
206+
207+
test_result_helper_with_term if {
208+
expected := {
209+
"code": "path.oh.Hey",
210+
"term": "ola",
211+
"effective_on": "2022-01-01T00:00:00Z",
212+
"msg": "Bad thing foo",
213+
}
214+
215+
rule_annotations := {"custom": {
216+
"short_name": "Hey",
217+
"failure_msg": "Bad thing %s",
218+
}}
219+
220+
chain := [
221+
{"annotations": rule_annotations, "path": ["data", "path", "oh", "deny"]},
222+
{"annotations": {}, "path": ["ignored", "also_ignored"]},
223+
]
224+
225+
lib.assert_equal(expected, lib.result_helper_with_term(chain, ["foo"], "ola"))
226+
}
227+
228+
test_result_helper_pkg_name if {
229+
# "Normal" for policy repo
230+
lib.assert_equal("foo", lib._pkg_name(["data", "foo", "deny"]))
231+
lib.assert_equal("foo", lib._pkg_name(["data", "foo", "warn"]))
232+
233+
# Long package paths are retained
234+
lib.assert_equal("another.foo.bar", lib._pkg_name(["data", "another", "foo", "bar", "deny"]))
235+
lib.assert_equal("another.foo.bar", lib._pkg_name(["data", "another", "foo", "bar", "warn"]))
236+
237+
# Unlikely edge case: No deny or warn
238+
lib.assert_equal("foo", lib._pkg_name(["data", "foo"]))
239+
lib.assert_equal("foo.bar", lib._pkg_name(["data", "foo", "bar"]))
240+
241+
# Unlikely edge case: No data
242+
lib.assert_equal("foo", lib._pkg_name(["foo", "deny"]))
243+
lib.assert_equal("foo.bar", lib._pkg_name(["foo", "bar", "warn"]))
244+
245+
# Very unlikely edge case: Just to illustrate how deny/warn/data are stripped once
246+
lib.assert_equal("foo", lib._pkg_name(["data", "foo", "warn", "deny"]))
247+
lib.assert_equal("foo.deny", lib._pkg_name(["data", "foo", "deny", "warn"]))
248+
lib.assert_equal("foo.warn", lib._pkg_name(["data", "foo", "warn", "warn"]))
249+
lib.assert_equal("data.foo.warn.deny", lib._pkg_name(["data", "data", "foo", "warn", "deny", "warn"]))
250+
}

0 commit comments

Comments
 (0)