Skip to content

Commit

Permalink
Mutation: conversion functions from assign / assignmetadata to Mutator (
Browse files Browse the repository at this point in the history
#938)

* Conversion functions from assign / assignmetadata to Mutator.

We expose a generic MutatorFor(o Object) to build a mutator instance
that wraps the cr.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>

* Update to location parser updates.

Location parser is merged. Here we fill the path field for both the mutators.
We also add a new test validating that the conversion fails if an invalid location is specified.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>

* Implement assignmetadata path validation.

This checks that the only valid location for assignmetadata is related to labels or annotations.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>

Co-authored-by: Max Smythe <smythe@google.com>
  • Loading branch information
fedepaol and maxsmythe committed Nov 25, 2020
1 parent e47d95b commit 964864d
Show file tree
Hide file tree
Showing 4 changed files with 600 additions and 3 deletions.
125 changes: 125 additions & 0 deletions pkg/mutation/assign_mutator.go
@@ -0,0 +1,125 @@
package mutation

import (
"github.com/google/go-cmp/cmp"
mutationsv1alpha1 "github.com/open-policy-agent/gatekeeper/apis/mutations/v1alpha1"
"github.com/open-policy-agent/gatekeeper/pkg/mutation/path/parser"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

// AssignMutator is a mutator object built out of a
// Assign instance.
type AssignMutator struct {
id ID
assign *mutationsv1alpha1.Assign
path *parser.Path
bindings []SchemaBinding
}

// AssignMutator implements mutatorWithSchema
var _ MutatorWithSchema = &AssignMutator{}

func (m *AssignMutator) Matches(obj metav1.Object, ns *corev1.Namespace) bool {
// TODO implement using matches function
return false
}

func (m *AssignMutator) Mutate(obj *unstructured.Unstructured) error {
// TODO implement
return nil
}
func (m *AssignMutator) ID() ID {
return m.id
}

func (m *AssignMutator) SchemaBindings() []SchemaBinding {
return m.bindings
}

func (m *AssignMutator) HasDiff(mutator Mutator) bool {
toCheck, ok := mutator.(*AssignMutator)
if !ok { // different types, different
return true
}

if !cmp.Equal(toCheck.id, m.id) {
return true
}
if !cmp.Equal(toCheck.path, m.path) {
return true
}
if !cmp.Equal(toCheck.bindings, m.bindings) {
return true
}

// any difference in spec may be enough
if !cmp.Equal(toCheck.assign.Spec, m.assign.Spec) {
return true
}

return false
}

func (m *AssignMutator) Path() *parser.Path {
return m.path
}

func (m *AssignMutator) DeepCopy() Mutator {
res := &AssignMutator{
id: m.id,
assign: m.assign.DeepCopy(),
path: &parser.Path{
Nodes: make([]parser.Node, len(m.path.Nodes)),
},
bindings: make([]SchemaBinding, len(m.bindings)),
}
copy(res.path.Nodes, m.path.Nodes)
copy(res.bindings, m.bindings)
return res
}

// MutatorForAssign returns an AssignMutator built from
// the given assign instance.
func MutatorForAssign(assign *mutationsv1alpha1.Assign) (*AssignMutator, error) {
id, err := MakeID(assign)
if err != nil {
return nil, errors.Wrapf(err, "Failed to retrieve id for assign type")
}

path, err := parser.Parse(assign.Spec.Location)
if err != nil {
return nil, errors.Wrapf(err, "Failed to parse the location specified")
}

return &AssignMutator{
id: id,
assign: assign.DeepCopy(),
bindings: applyToToBindings(assign.Spec.ApplyTo),
path: path,
}, nil
}

func applyToToBindings(applyTos []mutationsv1alpha1.ApplyTo) []SchemaBinding {
res := []SchemaBinding{}
for _, applyTo := range applyTos {
binding := SchemaBinding{
Groups: make([]string, len(applyTo.Groups)),
Kinds: make([]string, len(applyTo.Kinds)),
Versions: make([]string, len(applyTo.Versions)),
}
for i, g := range applyTo.Groups {
binding.Groups[i] = g
}
for i, k := range applyTo.Kinds {
binding.Kinds[i] = k
}
for i, v := range applyTo.Versions {
binding.Versions[i] = v
}
res = append(res, binding)
}
return res
}
125 changes: 125 additions & 0 deletions pkg/mutation/assignmeta_mutator.go
@@ -0,0 +1,125 @@
package mutation

import (
"fmt"
"reflect"

"github.com/google/go-cmp/cmp"
mutationsv1alpha1 "github.com/open-policy-agent/gatekeeper/apis/mutations/v1alpha1"
"github.com/open-policy-agent/gatekeeper/pkg/mutation/path/parser"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

var (
labelsValidSubPath = []parser.Node{
&parser.Object{
Reference: "metadata",
},
&parser.Object{
Reference: "labels",
},
}

annotationValidSubPath = []parser.Node{
&parser.Object{
Reference: "metadata",
},
&parser.Object{
Reference: "annotations",
},
}
)

//AssignMetadataMutator is a mutator built out of an
// AssignMeta instance.
type AssignMetadataMutator struct {
id ID
assignMetadata *mutationsv1alpha1.AssignMetadata
path *parser.Path
}

// assignMetadataMutator implements mutator
var _ Mutator = &AssignMetadataMutator{}

func (m *AssignMetadataMutator) Matches(obj metav1.Object, ns *corev1.Namespace) bool {
// TODO implement using matches function
return false
}

func (m *AssignMetadataMutator) Mutate(obj *unstructured.Unstructured) error {
// TODO implement
return nil
}
func (m *AssignMetadataMutator) ID() ID {
return m.id
}

func (m *AssignMetadataMutator) HasDiff(mutator Mutator) bool {
toCheck, ok := mutator.(*AssignMetadataMutator)
if !ok { // different types, different
return true
}

if !cmp.Equal(toCheck.id, m.id) {
return true
}
// any difference in spec may be enough
if !cmp.Equal(toCheck.assignMetadata.Spec, m.assignMetadata.Spec) {
return true
}

return false
}

func (m *AssignMetadataMutator) DeepCopy() Mutator {
res := &AssignMetadataMutator{
id: m.id,
assignMetadata: m.assignMetadata.DeepCopy(),
}
return res
}

// MutatorForAssignMetadata builds an AssignMetadataMutator from the given AssignMetadata object.
func MutatorForAssignMetadata(assignMeta *mutationsv1alpha1.AssignMetadata) (*AssignMetadataMutator, error) {
id, err := MakeID(assignMeta)
if err != nil {
return nil, errors.Wrapf(err, "Failed to retrieve id for assignMetadata type")
}

path, err := parser.Parse(assignMeta.Spec.Location)
if err != nil {
return nil, errors.Wrapf(err, "Failed to parse location for assign metadata")
}

if !isMetadataPath(path) {
return nil, fmt.Errorf("Invalid location for assignmetadata: %s", assignMeta.Spec.Location)
}
return &AssignMetadataMutator{
id: id,
assignMetadata: assignMeta.DeepCopy(),
path: path,
}, nil
}

// Verifies that the given path is valid for metadata
func isMetadataPath(path *parser.Path) bool {
// Path must be metadata.annotations.something or metadata.labels.something
if len(path.Nodes) != 3 ||
path.Nodes[0].Type() != parser.ObjectNode ||
path.Nodes[1].Type() != parser.ObjectNode ||
path.Nodes[2].Type() != parser.ObjectNode {

return false
}

if reflect.DeepEqual(path.Nodes[0:2], labelsValidSubPath) {
return true
}
if reflect.DeepEqual(path.Nodes[0:2], annotationValidSubPath) {
return true
}
return false
}

0 comments on commit 964864d

Please sign in to comment.