Repository: plexsystems/konstraint
Pull request #104 (2020-12-11 05:05:03)

Treat comments before the package name as the header block
Previously, the @kinds tag was treated as the end of the comment block. This way
the comment block parsing is more stable as we improve the tool and add more
features using the comment block.

Addresses #88 


Patch:
diff --git a/docs/cli/konstraint.md b/docs/cli/konstraint.md
index 2083195..b431244 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 9-Dec-2020
+###### Auto generated by spf13/cobra on 14-Dec-2020
diff --git a/docs/cli/konstraint_create.md b/docs/cli/konstraint_create.md
index ae3c45e..ff6d788 100644
--- a/docs/cli/konstraint_create.md
+++ b/docs/cli/konstraint_create.md
@@ -2,10 +2,6 @@
 
 Create Gatekeeper constraints from Rego policies
 
-### Synopsis
-
-Create Gatekeeper constraints from Rego policies
-
 ```
 konstraint create <dir> [flags]
 ```
@@ -26,13 +22,14 @@ Create constraints with the Gatekeeper enforcement action set to dryrun
 ### Options
 
 ```
-  -d, --dryrun          Sets the enforcement action of the constraints to dryrun, overriding the @enforcement tag
-  -h, --help            help for create
-  -o, --output string   Specify an output directory for the Gatekeeper resources
+  -d, --dryrun             Sets the enforcement action of the constraints to dryrun, overriding the @enforcement tag
+  -h, --help               help for create
+  -o, --output string      Specify an output directory for the Gatekeeper resources
+      --skip-constraints   Skip generation of constraints
 ```
 
 ### SEE ALSO
 
 * [konstraint](konstraint.md)	 - Konstraint
 
-###### Auto generated by spf13/cobra on 9-Dec-2020
+###### Auto generated by spf13/cobra on 14-Dec-2020
diff --git a/docs/cli/konstraint_doc.md b/docs/cli/konstraint_doc.md
index e2fe9d3..525b702 100644
--- a/docs/cli/konstraint_doc.md
+++ b/docs/cli/konstraint_doc.md
@@ -2,10 +2,6 @@
 
 Generate documentation from Rego policies
 
-### Synopsis
-
-Generate documentation from Rego policies
-
 ```
 konstraint doc <dir> [flags]
 ```
@@ -35,4 +31,4 @@ Set the URL where the policies are hosted at
 
 * [konstraint](konstraint.md)	 - Konstraint
 
-###### Auto generated by spf13/cobra on 9-Dec-2020
+###### Auto generated by spf13/cobra on 14-Dec-2020
diff --git a/internal/commands/create.go b/internal/commands/create.go
index ce76362..d8cc06f 100644
--- a/internal/commands/create.go
+++ b/internal/commands/create.go
@@ -40,6 +40,10 @@ Create constraints with the Gatekeeper enforcement action set to dryrun
 				return fmt.Errorf("bind ouput flag: %w", err)
 			}
 
+			if err := viper.BindPFlag("skip-constraints", cmd.PersistentFlags().Lookup("skip-constraints")); err != nil {
+				return fmt.Errorf("bind skip-constraints flag: %w", err)
+			}
+
 			path := "."
 			if len(args) > 0 {
 				path = args[0]
@@ -51,6 +55,7 @@ Create constraints with the Gatekeeper enforcement action set to dryrun
 
 	cmd.PersistentFlags().StringP("output", "o", "", "Specify an output directory for the Gatekeeper resources")
 	cmd.PersistentFlags().BoolP("dryrun", "d", false, "Sets the enforcement action of the constraints to dryrun, overriding the @enforcement tag")
+	cmd.PersistentFlags().Bool("skip-constraints", false, "Skip generation of constraints")
 
 	return &cmd
 }
@@ -85,6 +90,10 @@ func runCreateCommand(path string) error {
 			return fmt.Errorf("writing template: %w", err)
 		}
 
+		if viper.GetBool("skip-constraints") {
+			continue
+		}
+
 		// skip Constraint generation if there are parameters on the template
 		if len(violation.Parameters()) > 0 {
 			continue
diff --git a/internal/rego/matchers.go b/internal/rego/matchers.go
index 490a869..c7ce568 100644
--- a/internal/rego/matchers.go
+++ b/internal/rego/matchers.go
@@ -44,7 +44,7 @@ func (m MatchLabelsMatcher) String() string {
 // Matchers returns all of the matchers found in the rego file.
 func (r Rego) Matchers() (Matchers, error) {
 	var matchers Matchers
-	for _, comment := range r.comments {
+	for _, comment := range r.headerComments {
 		if strings.HasPrefix(comment, "@kinds") {
 			matchers.KindMatchers = getKindMatchers(comment)
 		}
diff --git a/internal/rego/rego.go b/internal/rego/rego.go
index 30774c2..2199a06 100644
--- a/internal/rego/rego.go
+++ b/internal/rego/rego.go
@@ -27,13 +27,14 @@ const (
 
 // Rego represents a parsed rego file.
 type Rego struct {
-	id           string
-	path         string
-	raw          string
-	comments     []string
-	rules        []string
-	dependencies []string
-	parameters   []Parameter
+	id             string
+	path           string
+	raw            string
+	comments       []string
+	headerComments []string
+	rules          []string
+	dependencies   []string
+	parameters     []Parameter
 }
 
 // Parameter represents a parameter that the policy uses
@@ -136,7 +137,7 @@ func (r Rego) Name() string {
 // Title returns the title found in the header comment of the rego file.
 func (r Rego) Title() string {
 	var title string
-	for _, comment := range r.comments {
+	for _, comment := range r.headerComments {
 		if !strings.Contains(comment, "@title") {
 			continue
 		}
@@ -152,7 +153,7 @@ func (r Rego) Title() string {
 // Defaults to deny if no enforcement action is specified
 func (r Rego) Enforcement() string {
 	enforcement := "deny"
-	for _, comment := range r.comments {
+	for _, comment := range r.headerComments {
 		if !strings.Contains(comment, "@enforcement") {
 			continue
 		}
@@ -174,14 +175,7 @@ func (r Rego) PolicyID() string {
 // found in the header comment of the rego file.
 func (r Rego) Description() string {
 	var description string
-	for _, comment := range r.comments {
-
-		// When the  token appears, we consider this point to
-		// be the end of the description.
-		if strings.HasPrefix(comment, "@kinds") {
-			break
-		}
-
+	for _, comment := range r.headerComments {
 		if strings.HasPrefix(comment, "@") {
 			continue
 		}
@@ -249,13 +243,17 @@ func parseDirectory(directory string) ([]Rego, error) {
 			rules = append(rules, file.Parsed.Rules[r].Head.Name.String())
 		}
 
-		var comments []string
-		for c := range file.Parsed.Comments {
-			comments = append(comments, trimString(string(file.Parsed.Comments[c].Text)))
+		var headerComments, comments []string
+		for _, c := range file.Parsed.Comments {
+			if c.Location.Row < file.Parsed.Package.Location.Row {
+				headerComments = append(headerComments, trimString(string(c.Text)))
+			} else {
+				comments = append(comments, trimString(string(c.Text)))
+			}
 		}
 
 		bodyParams := getBodyParamNames(file.Parsed.Rules)
-		headerParams, err := getHeaderParams(comments)
+		headerParams, err := getHeaderParams(headerComments)
 		if err != nil {
 			return nil, fmt.Errorf("parse header parameters: %w", err)
 		}
@@ -282,13 +280,14 @@ func parseDirectory(directory string) ([]Rego, error) {
 		raw := strings.ReplaceAll(string(file.Raw), "\r", "")
 
 		rego := Rego{
-			id:           getPolicyID(file.Parsed.Rules),
-			path:         file.Name,
-			dependencies: dependencies,
-			rules:        rules,
-			parameters:   headerParams,
-			comments:     comments,
-			raw:          raw,
+			id:             getPolicyID(file.Parsed.Rules),
+			path:           file.Name,
+			dependencies:   dependencies,
+			rules:          rules,
+			parameters:     headerParams,
+			headerComments: headerComments,
+			comments:       comments,
+			raw:            raw,
 		}
 
 		regos = append(regos, rego)


Test patch:
diff --git a/internal/rego/matchers_test.go b/internal/rego/matchers_test.go
index 2bf94d7..c172b72 100644
--- a/internal/rego/matchers_test.go
+++ b/internal/rego/matchers_test.go
@@ -10,7 +10,7 @@ func TestGetKindMatchers(t *testing.T) {
 		"@kinds core/Pod apps/Deployment",
 	}
 	rego := Rego{
-		comments: comments,
+		headerComments: comments,
 	}
 
 	expected := KindMatchers{
@@ -34,7 +34,7 @@ func TestGetMatchLabelsMatcher(t *testing.T) {
 		"@matchlabels team=a app.kubernetes.io/name=test",
 	}
 	rego := Rego{
-		comments: comments,
+		headerComments: comments,
 	}
 
 	expected := MatchLabelsMatcher{
diff --git a/internal/rego/rego_test.go b/internal/rego/rego_test.go
index 0ec8cbb..5bace0b 100644
--- a/internal/rego/rego_test.go
+++ b/internal/rego/rego_test.go
@@ -39,7 +39,7 @@ func TestTitle(t *testing.T) {
 	}
 
 	rego := Rego{
-		comments: comments,
+		headerComments: comments,
 	}
 
 	actual := rego.Title()
@@ -59,12 +59,12 @@ func TestDescription(t *testing.T) {
 	}
 
 	rego := Rego{
-		comments: comments,
+		headerComments: comments,
 	}
 
 	actual := rego.Description()
 
-	const expected = "The description"
+	const expected = "The description\nExtra comment"
 	if actual != expected {
 		t.Errorf("unexpected Description. expected %v, actual %v", expected, actual)
 	}
@@ -117,7 +117,7 @@ func TestEnforcement(t *testing.T) {
 		"@kinds apps/Deployment",
 	}
 	rego := Rego{
-		comments: comments,
+		headerComments: comments,
 	}
 
 	actual := rego.Enforcement()