Repository: plexsystems/konstraint
Pull request #130 (2021-02-13 19:11:19)

Add the @skip-constraint annotation
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.


Patch:
diff --git a/docs/cli/konstraint.md b/docs/cli/konstraint.md
index 162a458..0bdef54 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 0239ca5..f9e9f80 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 4d524da..92e14c5 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 8e927b1..5e5a3c7 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 3a9a1af..b4dedbb 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 abdf589..58fd1cc 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")


Test patch:
diff --git a/internal/rego/rego_test.go b/internal/rego/rego_test.go
index 5bace0b..09c16ff 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")
+	}
+}