Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .github/workflows/cron_e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,14 @@ jobs:
with:
name: konstraint

- name: generate resources
- name: generate resources (v0)
run: |
chmod +x ./konstraint
./konstraint create -o e2e-resources examples

- name: generate resources (v1)
run: ./konstraint create -o e2e-resources-v1 --rego-version v1 examples

- name: create kind cluster
run: kind create cluster

Expand All @@ -77,9 +80,23 @@ jobs:
kubectl create ns gatekeeper-system
helm install gatekeeper gk/gatekeeper -n gatekeeper-system --set replicas=1 --version ${GK_VERSION} --set psp.enabled=false

- name: apply resources
- name: apply resources (v0)
working-directory: e2e-resources
run: |
for ct in $(ls template*); do kubectl apply -f $ct; done
sleep 60 # gatekeeper takes some time to create the CRDs
for c in $(ls constraint*); do kubectl apply -f $c; done

- name: cleanup resources (v0)
working-directory: e2e-resources
run: |
for c in $(ls constraint*); do kubectl delete -f $c; done
for ct in $(ls template*); do kubectl delete -f $ct; done
sleep 60

- name: apply resources (v1)
working-directory: e2e-resources-v1
run: |
for ct in $(ls template*); do kubectl apply -f $ct; done
sleep 60 # gatekeeper takes some time to create the CRDs
for c in $(ls constraint*); do kubectl apply -f $c; done
27 changes: 24 additions & 3 deletions .github/workflows/pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,12 @@ jobs:
with:
version: latest

- name: opa check strict
- name: opa check strict (v0)
run: opa check --v0-compatible --strict --ignore "*.yaml" examples

- name: opa check strict (v1)
run: opa check --strict --ignore "*.yaml" examples

- name: setup regal
uses: styrainc/setup-regal@v1.0.0
with:
Expand Down Expand Up @@ -190,11 +193,15 @@ jobs:
with:
name: konstraint-ubuntu-latest

- name: generate resources
- name: generate resources (v0)
run: |
chmod +x ./konstraint
./konstraint create -o e2e-resources examples

- name: generate resources (v1)
run: |
./konstraint create -o e2e-resources-v1 --rego-version v1 examples

- name: create kind cluster
run: kind create cluster

Expand All @@ -206,9 +213,23 @@ jobs:
kubectl create ns gatekeeper-system
helm install gatekeeper gk/gatekeeper -n gatekeeper-system --set replicas=1 --version ${GK_VERSION} --set psp.enabled=false

- name: apply resources
- name: apply resources (v0)
working-directory: e2e-resources
run: |
for ct in $(ls template*); do kubectl apply -f $ct; done
sleep 60 # gatekeeper takes some time to create the CRDs
for c in $(ls constraint*); do kubectl apply -f $c; done

- name: cleanup resources (v0)
working-directory: e2e-resources
run: |
for c in $(ls constraint*); do kubectl delete -f $c --ignore-not-found; done
for ct in $(ls template*); do kubectl delete -f $ct --ignore-not-found; done
sleep 30 # wait for cleanup

- name: apply resources (v1)
working-directory: e2e-resources-v1
run: |
for ct in $(ls template*); do kubectl apply -f $ct; done
sleep 60 # gatekeeper takes some time to create the CRDs
for c in $(ls constraint*); do kubectl apply -f $c; done
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ To create the Gatekeeper resources, use `konstraint create <policy_dir>`.

To generate the accompanying documentation, use `konstraint doc <policy_dir>`.

Both commands support the `--output` flag to specify where to save the output. For more detailed usage documentation, see the [CLI Documentation](docs/cli/konstraint.md).
Both commands support the `--output` flag to specify where to save the output. Use `--rego-version v1` to generate OPA Rego v1 compatible ConstraintTemplates with the `code` field structure. For more detailed usage documentation, see the [CLI Documentation](docs/cli/konstraint.md).

## Why this tool exists

Expand Down
1 change: 1 addition & 0 deletions docs/cli/konstraint_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Create constraints with the Gatekeeper enforcement action set to dryrun
--log-level string Set a log level. Options: error, info, debug, trace (default "info")
-o, --output string Specify an output directory for the Gatekeeper resources
--partial-constraints Generate partial Constraints for policies with parameters
--rego-version string Set the Rego version for parsing and template generation (v0, v1) (default "v0")
--skip-constraints Skip generation of constraints
```

Expand Down
1 change: 1 addition & 0 deletions docs/cli/konstraint_doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Set the URL where the policies are hosted at
--include-comments Include comments from the rego source in the documentation
--no-rego Do not include the Rego in the policy documentation
-o, --output string Output location (including filename) for the policy documentation (default "policies.md")
--rego-version string Rego version for parsing policies (v0, v1) (default "v0")
--template-file string File to read the template from (default: "")
--url string The URL where the policy files are hosted at (e.g. https://github.com/policies)
```
Expand Down
11 changes: 11 additions & 0 deletions internal/commands/constrainttemplate_template.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,19 @@ spec:
properties: {{- .AnnotationParameters | toJSON | fromJSON | toIndentYAML 2 | nindent 12 }}
{{- end }}
targets:
{{- if eq .Version.String "v1" }}
- code:
- engine: Rego
source:
libs: {{- range .Dependencies }}
- |- {{- stripV1Imports . | nindent 10 -}}
{{ end }}
rego: |- {{- .SourceV1 | nindent 10 }}
version: v1
{{- else }}
- libs: {{- range .Dependencies }}
- |- {{- . | nindent 6 -}}
{{ end }}
rego: |- {{- .Source | nindent 6 }}
{{- end }}
target: admission.k8s.gatekeeper.sh
83 changes: 66 additions & 17 deletions internal/commands/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ package commands

import (
"bytes"
"errors"
"fmt"
"os"
"path/filepath"
"slices"
"text/template"

"github.com/plexsystems/konstraint/internal/rego"

"github.com/go-sprout/sprout/sprigin"
v1 "github.com/open-policy-agent/frameworks/constraint/pkg/apis/templates/v1"
"github.com/open-policy-agent/frameworks/constraint/pkg/apis/templates/v1beta1"
"github.com/open-policy-agent/frameworks/constraint/pkg/core/templates"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -63,13 +66,17 @@ Create constraints with the Gatekeeper enforcement action set to dryrun
return fmt.Errorf("bind log-level flag: %w", err)
}

if err := viper.BindPFlag("rego-version", cmd.PersistentFlags().Lookup("rego-version")); err != nil {
return fmt.Errorf("bind rego-version flag: %w", err)
}

if cmd.PersistentFlags().Lookup("constraint-template-custom-template-file").Changed && cmd.PersistentFlags().Lookup("constraint-template-version").Changed {
return fmt.Errorf("need to set either constraint-template-custom-template-file or constraint-template-version")
return errors.New("need to set either constraint-template-custom-template-file or constraint-template-version")
}
if cmd.PersistentFlags().Lookup("log-level").Changed {
level, err := log.ParseLevel(viper.GetString("log-level"))
if err != nil {
return fmt.Errorf("unknown log level: Need to use either error, info, debug or trace")
return errors.New("unknown log level: Need to use either error, info, debug or trace")
}
log.SetLevel(level)
}
Expand All @@ -90,11 +97,17 @@ Create constraints with the Gatekeeper enforcement action set to dryrun
cmd.PersistentFlags().String("constraint-template-custom-template-file", "", "Path to a custom template file to generate constraint templates")
cmd.PersistentFlags().String("constraint-custom-template-file", "", "Path to a custom template file to generate constraints")
cmd.PersistentFlags().String("log-level", "info", "Set a log level. Options: error, info, debug, trace")
cmd.PersistentFlags().String("rego-version", "v0", "Set the Rego version for parsing and template generation (v0, v1)")
return &cmd
}

func runCreateCommand(path string) error {
violations, err := rego.GetViolations(path)
regoVersion, err := rego.ParseVersion(viper.GetString("rego-version"))
if err != nil {
return fmt.Errorf("parse rego-version flag: %w", err)
}

violations, err := rego.GetViolations(path, regoVersion)
if err != nil {
return fmt.Errorf("get violations: %w", err)
}
Expand Down Expand Up @@ -200,7 +213,6 @@ func renderConstraintTemplate(violation rego.Rego, constraintTemplateVersion str
}

return constraintTemplateBytes, nil

}
func renderConstraint(violation rego.Rego, constraintCustomTemplateFile string, logger *log.Entry) ([]byte, error) {
var constraintBytes []byte
Expand All @@ -225,11 +237,12 @@ func renderConstraint(violation rego.Rego, constraintCustomTemplateFile string,
}
}
return constraintBytes, nil

}

func renderTemplate(violation rego.Rego, appliedTemplate []byte) ([]byte, error) {
t, err := template.New("template").Funcs(sprigin.FuncMap()).Parse(string(appliedTemplate))
funcMap := sprigin.FuncMap()
funcMap["stripV1Imports"] = rego.StripV1Imports
t, err := template.New("template").Funcs(funcMap).Parse(string(appliedTemplate))
if err != nil {
return nil, fmt.Errorf("parsing template: %w", err)
}
Expand Down Expand Up @@ -262,13 +275,34 @@ func getConstraintTemplatev1(violation rego.Rego, _ *log.Entry) *v1.ConstraintTe
Targets: []v1.Target{
{
Target: "admission.k8s.gatekeeper.sh",
Libs: violation.Dependencies(),
Rego: violation.Source(),
},
},
},
}

if violation.Version() == rego.V1 {
source := map[string]any{
"version": "v1",
"rego": violation.SourceV1(),
}
if len(violation.Dependencies()) > 0 {
var libs []string
for _, lib := range violation.Dependencies() {
libs = append(libs, rego.StripV1Imports(lib))
}
source["libs"] = libs
}
constraintTemplate.Spec.Targets[0].Code = []v1.Code{
{
Engine: "Rego",
Source: &templates.Anything{Value: source},
},
}
} else {
constraintTemplate.Spec.Targets[0].Rego = violation.Source()
constraintTemplate.Spec.Targets[0].Libs = violation.Dependencies()
}

if len(violation.AnnotationParameters()) > 0 {
constraintTemplate.Spec.CRD.Spec.Validation = &v1.Validation{
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
Expand Down Expand Up @@ -301,13 +335,34 @@ func getConstraintTemplatev1beta1(violation rego.Rego, _ *log.Entry) *v1beta1.Co
Targets: []v1beta1.Target{
{
Target: "admission.k8s.gatekeeper.sh",
Libs: violation.Dependencies(),
Rego: violation.Source(),
},
},
},
}

if violation.Version() == rego.V1 {
source := map[string]any{
"version": "v1",
"rego": violation.SourceV1(),
}
if len(violation.Dependencies()) > 0 {
var libs []string
for _, lib := range violation.Dependencies() {
libs = append(libs, rego.StripV1Imports(lib))
}
source["libs"] = libs
}
constraintTemplate.Spec.Targets[0].Code = []v1beta1.Code{
{
Engine: "Rego",
Source: &templates.Anything{Value: source},
},
}
} else {
constraintTemplate.Spec.Targets[0].Rego = violation.Source()
constraintTemplate.Spec.Targets[0].Libs = violation.Dependencies()
}

if len(violation.AnnotationParameters()) > 0 {
constraintTemplate.Spec.CRD.Spec.Validation = &v1beta1.Validation{
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
Expand Down Expand Up @@ -383,11 +438,5 @@ func addParametersToConstraint(constraint *unstructured.Unstructured, parameters
}

func isValidEnforcementAction(action string) bool {
for _, a := range []string{"deny", "dryrun", "warn"} {
if a == action {
return true
}
}

return false
return slices.Contains([]string{"deny", "dryrun", "warn"}, action)
}
Loading