-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Verify Task definitions contain a certain result
- Loading branch information
Showing
5 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# | ||
# METADATA | ||
# title: Tekton Task result | ||
# description: Verify Tekton Task definitions provide expected results. | ||
# | ||
package policy.task.results | ||
|
||
import rego.v1 | ||
|
||
import data.lib | ||
|
||
# METADATA | ||
# title: Required result defined | ||
# description: >- | ||
# Verify if Task defines the required result. This is controlled by the `required_task_results` | ||
# rule data key. By default this is empty making this rule a no-op. | ||
# custom: | ||
# short_name: required | ||
# failure_msg: '%s' | ||
# | ||
deny contains result if { | ||
some err in errors | ||
result := lib.result_helper(rego.metadata.chain(), [err]) | ||
} | ||
|
||
# METADATA | ||
# title: Rule data provided | ||
# description: >- | ||
# Confirm the expected `required_task_results` rule data key has been provided in the expected | ||
# format. | ||
# custom: | ||
# short_name: rule_data_provided | ||
# failure_msg: '%s' | ||
# solution: If provided, ensure the rule data is in the expected format. | ||
# collections: | ||
# - redhat | ||
# - policy_data | ||
# | ||
deny contains result if { | ||
some error in _rule_data_errors | ||
result := lib.result_helper(rego.metadata.chain(), [error]) | ||
} | ||
|
||
errors contains err if { | ||
some required in lib.rule_data(_rule_data_key) | ||
input.metadata.name == required.task | ||
found := [result | | ||
some result in input.spec.results | ||
result.name == required.result | ||
] | ||
count(found) == 0 | ||
err := sprintf("%q result not found in %q Task", [required.result, required.task]) | ||
} | ||
|
||
_rule_data_errors contains err if { | ||
schema := { | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"task": {"type": "string"}, | ||
"result": {"type": "string"}, | ||
}, | ||
"additionalProperties": false, | ||
"required": ["task", "result"], | ||
}, | ||
"uniqueItems": true, | ||
} | ||
|
||
# match_schema expects either a marshaled JSON resource (String) or an Object. It doesn't | ||
# handle an Array directly. | ||
value := json.marshal(lib.rule_data(_rule_data_key)) | ||
some violation in json.match_schema(value, schema)[1] | ||
err := sprintf("Rule data %s has unexpected format: %s", [_rule_data_key, violation.error]) | ||
} | ||
|
||
_rule_data_key := "required_task_results" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package policy.task.results_test | ||
|
||
import rego.v1 | ||
|
||
import data.lib | ||
import data.policy.task.results | ||
|
||
test_all_good if { | ||
lib.assert_empty(results.deny) with input as _mock_task | ||
with data.rule_data as _rule_data | ||
} | ||
|
||
test_required_result_defined if { | ||
expected := {{ | ||
"code": "results.required", | ||
"msg": "\"GRILLED\" result not found in \"bacon\" Task", | ||
}} | ||
|
||
lib.assert_equal_results(results.deny, expected) with data.rule_data as _rule_data | ||
with input as json.patch(_mock_task, [{ | ||
"op": "add", | ||
"path": "spec/results", | ||
"value": [], | ||
}]) | ||
|
||
lib.assert_equal_results(results.deny, expected) with data.rule_data as _rule_data | ||
with input as json.remove(_mock_task, ["spec/results"]) | ||
|
||
lib.assert_equal_results(results.deny, expected) with data.rule_data as _rule_data | ||
with input as json.remove(_mock_task, ["spec/results/0"]) | ||
} | ||
|
||
test_rule_data_provided if { | ||
d := {"required_task_results": [ | ||
# Wrong type | ||
1, | ||
# Duplicated items | ||
{"task": "task1", "result": "result1"}, | ||
{"task": "task1", "result": "result1"}, | ||
# Additional properties | ||
{"task": "task2", "result": "result2", "foo": "bar"}, | ||
# Bad type for task | ||
{"task": 3, "result": "result3"}, | ||
# Bad type for result | ||
{"task": "task4", "result": 4}, | ||
]} | ||
|
||
expected := { | ||
{ | ||
"code": "results.rule_data_provided", | ||
"msg": "Rule data required_task_results has unexpected format: 0: Invalid type. Expected: object, given: integer", | ||
}, | ||
{ | ||
"code": "results.rule_data_provided", | ||
"msg": "Rule data required_task_results has unexpected format: (Root): array items[1,2] must be unique", | ||
}, | ||
{ | ||
"code": "results.rule_data_provided", | ||
"msg": "Rule data required_task_results has unexpected format: 3: Additional property foo is not allowed", | ||
}, | ||
{ | ||
"code": "results.rule_data_provided", | ||
# regal ignore:line-length | ||
"msg": "Rule data required_task_results has unexpected format: 4.task: Invalid type. Expected: string, given: integer", | ||
}, | ||
{ | ||
"code": "results.rule_data_provided", | ||
# regal ignore:line-length | ||
"msg": "Rule data required_task_results has unexpected format: 5.result: Invalid type. Expected: string, given: integer", | ||
}, | ||
} | ||
|
||
lib.assert_equal_results(results.deny, expected) with input as _mock_task | ||
with data.rule_data as d | ||
} | ||
|
||
_mock_task := { | ||
"apiVersion": "tekton.dev/v1", | ||
"kind": "Task", | ||
"metadata": {"name": "bacon"}, | ||
"spec": {"results": [ | ||
{"name": "GRILLED"}, | ||
{"name": "SCENT"}, | ||
]}, | ||
} | ||
|
||
_rule_data := {"required_task_results": [{"task": "bacon", "result": "GRILLED"}]} |