Skip to content

Commit

Permalink
Add driver.AddTemplate and driver.RemoveTemplate (#179)
Browse files Browse the repository at this point in the history
* Add AddTemplate and RemoveTemplate interface to driver

Signed-off-by: Becky Huang <beckyhd@google.com>

* Move parsing module logic from local.AddTemplate to MapModules, sharing with client.CreateCRD

Signed-off-by: Becky Huang <beckyhd@google.com>

* refactor local.driver (#171)

export local.Driver
set Externs for local.Driver within client.init
move local.Driver PutModules and DeleteModules to private, deleted from drivers interface
remove rego validation in client.CreateCRD

Signed-off-by: Becky Huang <beckyhd@google.com>

* Add driver.AddTemplate unit tests (#171)

Signed-off-by: Becky Huang <beckyhd@google.com>

* Add client.ValidateConstraintTemplate interface to replace validation logic from client.CeateCRD (#171)

Signed-off-by: Becky Huang <beckyhd@google.com>
  • Loading branch information
becky-hd authored Jan 14, 2022
1 parent 4b0a3de commit b0196c3
Show file tree
Hide file tree
Showing 9 changed files with 723 additions and 228 deletions.
169 changes: 50 additions & 119 deletions constraint/pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import (
"strings"
"sync"

"github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers"
"github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/local"

"github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers"
"github.com/open-policy-agent/frameworks/constraint/pkg/client/regolib"
constraintlib "github.com/open-policy-agent/frameworks/constraint/pkg/core/constraints"
"github.com/open-policy-agent/frameworks/constraint/pkg/core/templates"
"github.com/open-policy-agent/frameworks/constraint/pkg/regorewriter"
"github.com/open-policy-agent/frameworks/constraint/pkg/types"
"github.com/open-policy-agent/opa/format"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
Expand Down Expand Up @@ -111,11 +111,6 @@ func createTemplatePath(target, name string) string {
return fmt.Sprintf(`templates["%s"]["%s"]`, target, name)
}

// templateLibPrefix returns the new lib prefix for the libs that are specified in the CT.
func templateLibPrefix(target, name string) string {
return fmt.Sprintf("libs.%s.%s", target, name)
}

// validateTargets handles validating the targets section of the CT.
func (c *Client) validateTargets(templ *templates.ConstraintTemplate) (*templates.Target, TargetHandler, error) {
if err := validateTargets(templ); err != nil {
Expand Down Expand Up @@ -195,38 +190,17 @@ func (a *basicCTArtifacts) CRD() *apiextensions.CustomResourceDefinition {
return a.crd
}

// ctArtifacts are all artifacts created by processing a constraint template.
type ctArtifacts struct {
basicCTArtifacts

// modules is the rewritten set of modules that the constraint template declares in Rego and Libs
modules []string
}

// createBasicTemplateArtifacts creates the low-cost artifacts for a template, avoiding more
// complex tasks like rewriting Rego.
func (c *Client) createBasicTemplateArtifacts(templ *templates.ConstraintTemplate) (*basicCTArtifacts, error) {
rawArtifacts, err := c.createRawTemplateArtifacts(templ)
if err != nil {
return nil, err
}

kind := templ.Spec.CRD.Spec.Names.Kind
if kind == "" {
return nil, fmt.Errorf("%w: ConstraintTemplate %q does not specify CRD Kind",
ErrInvalidConstraintTemplate, templ.GetName())
}

if !strings.EqualFold(templ.ObjectMeta.Name, kind) {
return nil, fmt.Errorf("%w: the ConstraintTemplate's name %q is not equal to the lowercase of CRD's Kind: %q",
ErrInvalidConstraintTemplate, templ.ObjectMeta.Name, strings.ToLower(kind))
}

targetSpec, targetHandler, err := c.validateTargets(templ)
targetSpec, targetHandler, err := c.ValidateConstraintTemplateBasic(templ)
if err != nil {
return nil, fmt.Errorf("failed to validate targets for template %s: %w", templ.Name, err)
return nil, err
}

sch := c.backend.crd.createSchema(templ, targetHandler)

crd, err := c.backend.crd.createCRD(templ, sch)
Expand All @@ -250,97 +224,52 @@ func (c *Client) createBasicTemplateArtifacts(templ *templates.ConstraintTemplat
}, nil
}

// createTemplateArtifacts will validate the CT, create the CRD for the CT's constraints, then
// validate and rewrite the rego sources specified in the CT.
func (c *Client) createTemplateArtifacts(templ *templates.ConstraintTemplate) (*ctArtifacts, error) {
artifacts, err := c.createBasicTemplateArtifacts(templ)
if err != nil {
return nil, err
}

var externs []string
for _, field := range c.allowedDataFields {
externs = append(externs, fmt.Sprintf("data.%s", field))
}

libPrefix := templateLibPrefix(artifacts.targetHandler.GetName(), artifacts.crd.Spec.Names.Kind)

rr, err := regorewriter.New(
regorewriter.NewPackagePrefixer(libPrefix),
[]string{"data.lib"},
externs)
if err != nil {
return nil, fmt.Errorf("creating rego rewriter: %w", err)
}

entryPoint, err := parseModule(artifacts.namePrefix, artifacts.targetSpec.Rego)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrInvalidConstraintTemplate, err)
}

if entryPoint == nil {
return nil, fmt.Errorf("%w: failed to parse module for unknown reason",
// CreateCRD creates a CRD from template.
func (c *Client) CreateCRD(templ *templates.ConstraintTemplate) (*apiextensions.CustomResourceDefinition, error) {
if templ == nil {
return nil, fmt.Errorf("%w: got nil ConstraintTemplate",
ErrInvalidConstraintTemplate)
}

if err = rewriteModulePackage(artifacts.namePrefix, entryPoint); err != nil {
artifacts, err := c.createBasicTemplateArtifacts(templ)
if err != nil {
return nil, err
}
return artifacts.crd, nil
}

req := map[string]struct{}{"violation": {}}

if err = requireModuleRules(entryPoint, req); err != nil {
return nil, fmt.Errorf("%w: invalid rego: %v",
ErrInvalidConstraintTemplate, err)
}

rr.AddEntryPointModule(artifacts.namePrefix, entryPoint)
for idx, libSrc := range artifacts.targetSpec.Libs {
libPath := fmt.Sprintf(`%s["lib_%d"]`, libPrefix, idx)
if err = rr.AddLib(libPath, libSrc); err != nil {
return nil, fmt.Errorf("%w: %v",
ErrInvalidConstraintTemplate, err)
}
func (c *Client) ValidateConstraintTemplateBasic(templ *templates.ConstraintTemplate) (*templates.Target, TargetHandler, error) {
kind := templ.Spec.CRD.Spec.Names.Kind
if kind == "" {
return nil, nil, fmt.Errorf("%w: ConstraintTemplate %q does not specify CRD Kind",
ErrInvalidConstraintTemplate, templ.GetName())
}

sources, err := rr.Rewrite()
if err != nil {
return nil, fmt.Errorf("%w: %v",
ErrInvalidConstraintTemplate, err)
if !strings.EqualFold(templ.ObjectMeta.Name, kind) {
return nil, nil, fmt.Errorf("%w: the ConstraintTemplate's name %q is not equal to the lowercase of CRD's Kind: %q",
ErrInvalidConstraintTemplate, templ.ObjectMeta.Name, strings.ToLower(kind))
}

var mods []string
err = sources.ForEachModule(func(m *regorewriter.Module) error {
content, err2 := m.Content()
if err2 != nil {
return err2
}
mods = append(mods, string(content))
return nil
})
targetSpec, targetHandler, err := c.validateTargets(templ)
if err != nil {
return nil, fmt.Errorf("%w: %v",
ErrInvalidConstraintTemplate, err)
return nil, nil, fmt.Errorf("failed to validate targets for template %s: %w", templ.Name, err)
}

return &ctArtifacts{
basicCTArtifacts: *artifacts,
modules: mods,
}, nil
return targetSpec, targetHandler, nil
}

// CreateCRD creates a CRD from template.
func (c *Client) CreateCRD(templ *templates.ConstraintTemplate) (*apiextensions.CustomResourceDefinition, error) {
func (c *Client) ValidateConstraintTemplate(templ *templates.ConstraintTemplate) error {
if templ == nil {
return nil, fmt.Errorf("%w: got nil ConstraintTemplate",
return fmt.Errorf(`%w: ConstraintTemplate is nil`,
ErrInvalidConstraintTemplate)
}

artifacts, err := c.createTemplateArtifacts(templ)
if err != nil {
return nil, err
if _, _, err := c.ValidateConstraintTemplateBasic(templ); err != nil {
return err
}
return artifacts.crd, nil
if dr, ok := c.backend.driver.(*local.Driver); ok {
_, _, err := dr.ValidateConstraintTemplate(templ)
return err
}
return fmt.Errorf("driver %T is not supported", c.backend.driver)
}

// AddTemplate adds the template source code to OPA and registers the CRD with the client for
Expand All @@ -360,29 +289,23 @@ func (c *Client) AddTemplate(templ *templates.ConstraintTemplate) (*types.Respon
return resp, nil
}

artifacts, err := c.createTemplateArtifacts(templ)
if err != nil {
return resp, err
}

c.mtx.Lock()
defer c.mtx.Unlock()

if err = c.backend.driver.PutModules(artifacts.namePrefix, artifacts.modules); err != nil {
return resp, fmt.Errorf("%w: %v", local.ErrCompile, err)
if err = c.backend.driver.AddTemplate(templ); err != nil {
return resp, err
}

cpy := templ.DeepCopy()
cpy.Status = templates.ConstraintTemplateStatus{}
c.templates[artifacts.Key()] = &templateEntry{
c.templates[basicArtifacts.Key()] = &templateEntry{
template: cpy,
CRD: artifacts.crd,
Targets: []string{artifacts.targetHandler.GetName()},
CRD: basicArtifacts.crd,
Targets: []string{basicArtifacts.targetHandler.GetName()},
}
if _, ok := c.constraints[artifacts.gk]; !ok {
c.constraints[artifacts.gk] = make(map[string]*unstructured.Unstructured)
if _, ok := c.constraints[basicArtifacts.gk]; !ok {
c.constraints[basicArtifacts.gk] = make(map[string]*unstructured.Unstructured)
}
resp.Handled[artifacts.targetHandler.GetName()] = true
resp.Handled[basicArtifacts.targetHandler.GetName()] = true
return resp, nil
}

Expand Down Expand Up @@ -414,7 +337,7 @@ func (c *Client) RemoveTemplate(ctx context.Context, templ *templates.Constraint
return resp, err
}

if _, err := c.backend.driver.DeleteModules(artifacts.namePrefix); err != nil {
if err := c.backend.driver.RemoveTemplate(ctx, templ); err != nil {
return resp, err
}

Expand Down Expand Up @@ -747,7 +670,15 @@ func (c *Client) init() error {
ErrCreatingClient, err, src)
}
}

if d, ok := c.backend.driver.(*local.Driver); ok {
var externs []string
for _, field := range c.allowedDataFields {
externs = append(externs, fmt.Sprintf("data.%s", field))
}
d.SetExterns(externs)
} else {
return fmt.Errorf("%w: driver %T is not supported", ErrCreatingClient, c.backend.driver)
}
return nil
}

Expand Down
Loading

0 comments on commit b0196c3

Please sign in to comment.