Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Mutation: conversion functions from assign / assignmetadata to Mutator (
#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
Showing
4 changed files
with
600 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Oops, something went wrong.