Skip to content

Commit

Permalink
fix: preserve metadata annotations on OCI registry push (#750)
Browse files Browse the repository at this point in the history
* fix: preserve metadata annotations on OCI registry push

Annotations can be accessed with the rego.metadata built-in
functions. They can be used in policies to populate messages.
Adding annotation support, as exposed by the FileLoader
interface of open-policy-agent/opa/loader, preserves this
functionality for policies pushed to an OCI registry.

Signed-off-by: Kevin Swiber <[email protected]>

* Setting policyContents to the raw Rego source file on OCI push.

Signed-off-by: Kevin Swiber <[email protected]>

* Moving OCI push annotations tests to acceptance and e2e.

Signed-off-by: Kevin Swiber <[email protected]>

Signed-off-by: Kevin Swiber <[email protected]>
  • Loading branch information
kevinswiber authored Nov 11, 2022
1 parent df0b7b1 commit f18b7bb
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 4 deletions.
10 changes: 7 additions & 3 deletions policy/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/open-policy-agent/conftest/parser"

"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/bundle"
"github.com/open-policy-agent/opa/loader"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/storage"
Expand All @@ -34,7 +35,10 @@ type Engine struct {

// Load returns an Engine after loading all of the specified policies.
func Load(ctx context.Context, policyPaths []string, c *ast.Capabilities) (*Engine, error) {
policies, err := loader.AllRegos(policyPaths)
policies, err := loader.NewFileLoader().WithProcessAnnotation(true).Filtered(policyPaths, func(_ string, info os.FileInfo, depth int) bool {
return !info.IsDir() && !strings.HasSuffix(info.Name(), bundle.RegoExt)
})

if err != nil {
return nil, fmt.Errorf("load: %w", err)
} else if len(policies.Modules) == 0 {
Expand All @@ -49,11 +53,11 @@ func Load(ctx context.Context, policyPaths []string, c *ast.Capabilities) (*Engi
}

policyContents := make(map[string]string, len(modules))
for path, module := range modules {
for path, module := range policies.Modules {
path = filepath.Clean(path)
path = filepath.ToSlash(path)

policyContents[path] = module.String()
policyContents[path] = string(module.Raw)
}

engine := Engine{
Expand Down
20 changes: 19 additions & 1 deletion scripts/push-pull-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ CONFTEST="./conftest"
CONTAINER_NAME="conftest-push-pull-e2e"

function cleanup() {
docker rm $CONTAINER_NAME -f > /dev/null 2>&1
docker rm $CONTAINER_NAME -f >/dev/null 2>&1
rm -rf tmp
}

Expand Down Expand Up @@ -63,5 +63,23 @@ if [ $? != 0 ]; then
exit 1
fi

$CONFTEST push localhost:5000/test-annotations -p tests/annotations
if [ $? != 0 ]; then
echo "ERROR PUSHING ANNOTATIONS BUNDLE"
exit 1
fi

$CONFTEST pull localhost:5000/test-annotations -p tmp
if [ $? != 0 ]; then
echo "ERROR PULLING ANNOTATIONS BUNDLE"
exit 1
fi

$CONFTEST verify -p tmp/tests/annotations/policy -d tmp/tests/annotations/exclusions tmp/tests/annotations/service.yaml
if [ $? != 0 ]; then
echo "POLICIES WITH ANNOTATIONS WERE NOT SUCCESSFULLY VERIFIED"
exit 1
fi

cleanup
exit 0
4 changes: 4 additions & 0 deletions tests/annotations/exclusions/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
ports:
- 22
- 21
26 changes: 26 additions & 0 deletions tests/annotations/policy/base.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import data.services

name := input.metadata.name

kind := input.kind

type := input.spec.type

# METADATA
# title: Example using annotations
# custom:
# template: 'Cannot expose port %v on LoadBalancer. Denied ports: %v'
deny[msg] {
kind == "Service"
type == "LoadBalancer"

some p
input.spec.ports[p].port

input.spec.ports[p].port == services.ports[_]

metadata := rego.metadata.rule()
msg := sprintf(metadata.custom.template, [input.spec.ports[p].port, services.ports])
}
14 changes: 14 additions & 0 deletions tests/annotations/policy/base_test.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package main

test_service_denied {
input := {
"kind": "Service",
"metadata": {"name": "sample"},
"spec": {
"type": "LoadBalancer",
"ports": [{"port": 22}],
},
}

deny["Cannot expose port 22 on LoadBalancer. Denied ports: [22, 21]"] with input as input
}
11 changes: 11 additions & 0 deletions tests/annotations/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: hello-kubernetes
spec:
type: LoadBalancer
ports:
- port: 22
targetPort: 22
selector:
app: hello-kubernetes
9 changes: 9 additions & 0 deletions tests/annotations/test.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bats

@test "Can verify policies that rely on annotations" {
run $CONFTEST verify --data exclusions service.yaml

[ "$status" -eq 0 ]
echo $output
[[ "$output" =~ "1 test, 1 passed, 0 warnings, 0 failures" ]]
}

0 comments on commit f18b7bb

Please sign in to comment.