From 365f86663df304d22bcdcc10a1f9f8c43c1ca354 Mon Sep 17 00:00:00 2001 From: James Alseth Date: Sat, 13 Feb 2021 11:14:08 -0800 Subject: [PATCH] Add the @skip-constraint annotation (#130) This annotation allows policy authors to instruct Konstraint to skip generation of the Constraint resource for a given policy, allowing for external management of the matching and other configuration options for the Constraint. --- docs/cli/konstraint.md | 2 +- docs/cli/konstraint_create.md | 2 +- docs/cli/konstraint_doc.md | 2 +- docs/constraint_creation.md | 4 ++++ internal/commands/create.go | 4 ++++ internal/rego/rego.go | 18 ++++++++++++++++++ internal/rego/rego_test.go | 14 ++++++++++++++ 7 files changed, 43 insertions(+), 3 deletions(-) diff --git a/docs/cli/konstraint.md b/docs/cli/konstraint.md index 162a4589..0bdef546 100644 --- a/docs/cli/konstraint.md +++ b/docs/cli/konstraint.md @@ -17,4 +17,4 @@ A tool to create and manage Gatekeeper CRDs from Rego * [konstraint create](konstraint_create.md) - Create Gatekeeper constraints from Rego policies * [konstraint doc](konstraint_doc.md) - Generate documentation from Rego policies -###### Auto generated by spf13/cobra on 2-Feb-2021 +###### Auto generated by spf13/cobra on 13-Feb-2021 diff --git a/docs/cli/konstraint_create.md b/docs/cli/konstraint_create.md index 0239ca58..f9e9f802 100644 --- a/docs/cli/konstraint_create.md +++ b/docs/cli/konstraint_create.md @@ -32,4 +32,4 @@ Create constraints with the Gatekeeper enforcement action set to dryrun * [konstraint](konstraint.md) - Konstraint -###### Auto generated by spf13/cobra on 2-Feb-2021 +###### Auto generated by spf13/cobra on 13-Feb-2021 diff --git a/docs/cli/konstraint_doc.md b/docs/cli/konstraint_doc.md index 4d524da7..92e14c5d 100644 --- a/docs/cli/konstraint_doc.md +++ b/docs/cli/konstraint_doc.md @@ -31,4 +31,4 @@ Set the URL where the policies are hosted at * [konstraint](konstraint.md) - Konstraint -###### Auto generated by spf13/cobra on 2-Feb-2021 +###### Auto generated by spf13/cobra on 13-Feb-2021 diff --git a/docs/constraint_creation.md b/docs/constraint_creation.md index 8e927b16..5e5a3c78 100644 --- a/docs/constraint_creation.md +++ b/docs/constraint_creation.md @@ -87,6 +87,10 @@ The comment block is also what is used when generating documentation via the `do # @matchlabels app.kubernetes.io/name=mysql app.kubernetes.io/version=5.8 ``` +### Skipping generation of the Constraint resource + +In some scenarios, you may wish for Konstraint to skip the generation of the `Constraint` resource for a policy and manage that externally. To do so, add the `@skip-constraint` tag in the header comment block. + ## Using Input Parameters Gatekeeper has the ability for a single `ConstraintTemplate` resource to be used by multiple `Constraint`s. One of the reasons for this is that it allows for passing input parameters to the policy so a single policy to avoid duplication. Konstraint supports these input parameters via `@parameter` tags in the header comment block. **NOTE:** When input parameters are specified, Konstraint skips the generation of the `Constraint` resource. diff --git a/internal/commands/create.go b/internal/commands/create.go index 3a9a1af1..b4dedbb2 100644 --- a/internal/commands/create.go +++ b/internal/commands/create.go @@ -100,6 +100,10 @@ func runCreateCommand(path string) error { continue } + if violation.SkipConstraint() { + continue + } + constraint, err := getConstraint(violation) if err != nil { return fmt.Errorf("get constraint: %w", err) diff --git a/internal/rego/rego.go b/internal/rego/rego.go index abdf589e..58fd1cc2 100644 --- a/internal/rego/rego.go +++ b/internal/rego/rego.go @@ -35,6 +35,7 @@ type Rego struct { rules []string dependencies []string parameters []Parameter + skipConstraint bool } // Parameter represents a parameter that the policy uses @@ -199,6 +200,12 @@ func (r Rego) Dependencies() []string { return r.dependencies } +// SkipConstraint returns whether or not the generatin of the Constraint should be skipped +// It is only set to true when the @skip-constraint tag is present in the comment header block +func (r Rego) SkipConstraint() bool { + return r.skipConstraint +} + func parseDirectory(directory string) ([]Rego, error) { // Recursively find all rego files (ignoring test files), starting at the given directory. @@ -291,6 +298,7 @@ func parseDirectory(directory string) ([]Rego, error) { headerComments: headerComments, comments: comments, raw: raw, + skipConstraint: hasSkipConstraintTag(headerComments), } regos = append(regos, rego) @@ -348,6 +356,16 @@ func getHeaderParams(comments []string) ([]Parameter, error) { return parameters, nil } +func hasSkipConstraintTag(comments []string) bool { + for _, comment := range comments { + if strings.HasPrefix(comment, "@skip-constraint") { + return true + } + } + + return false +} + func removeComments(raw string) string { var regoWithoutComments string lines := strings.Split(raw, "\n") diff --git a/internal/rego/rego_test.go b/internal/rego/rego_test.go index 5bace0b6..09c16ffa 100644 --- a/internal/rego/rego_test.go +++ b/internal/rego/rego_test.go @@ -232,3 +232,17 @@ func TestGetHeaderParams(t *testing.T) { t.Errorf("unexpected headerParams. expected %+v, actual %+v", expected, actual) } } + +func TestHasSkipConstraintTag(t *testing.T) { + comments := []string{ + "@title Title", + "Description", + "@kinds another/thing", + "@skip-constraint", + } + + skip := hasSkipConstraintTag(comments) + if !skip { + t.Error("SkipConstraint is false when the @skip-constraint comment tag is present") + } +}