From 0b0508442b3c1d142fa02a2bffed04005bda42e4 Mon Sep 17 00:00:00 2001 From: juliankatz Date: Tue, 1 Jun 2021 16:19:53 -0700 Subject: [PATCH] Add ConstraintTemplate v1 We recently upgraded Constraint Framework to produce v1 CRDs when creating Constraint kind CRDs. This was in preparation for the release of k8s 1.22, which removes the `v1beta1` CRD version. See open-policy-agent/gatekeeper#550 for more info. As v1beta1 ConstraintTemplate did _not_ required any user-entered schema information to be structural, transformation logic was implemented to "structuralize" the user-inputted schema information as needed. The new v1 ConstraintTemplate version purposefully does _no_ transformation, as it is meant to put the ConstraintTemplate creation experience in line with that of a v1 CRDs. Any schema information added by the user is expected to be structural. If non-structural schema info is added, an error should be returned. Signed-off-by: juliankatz --- constraint/Makefile | 3 +- .../pkg/apis/addtoscheme_templates_v1.go | 23 ++ .../v1/constrainttemplate_types_test.go | 215 +++++++----------- .../pkg/apis/templates/v1/conversion.go | 36 --- .../templates/v1/zz_generated.conversion.go | 27 ++- 5 files changed, 124 insertions(+), 180 deletions(-) create mode 100644 constraint/pkg/apis/addtoscheme_templates_v1.go delete mode 100644 constraint/pkg/apis/templates/v1/conversion.go diff --git a/constraint/Makefile b/constraint/Makefile index c529148d4..5fa8c63fe 100644 --- a/constraint/Makefile +++ b/constraint/Makefile @@ -60,4 +60,5 @@ generate: --input-dirs "./pkg/apis/templates/..." \ --go-header-file=./hack/boilerplate.go.txt \ --output-file-base=zz_generated.conversion \ - --extra-dirs=k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 + --extra-dirs=k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 \ + --extra-dirs=k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 diff --git a/constraint/pkg/apis/addtoscheme_templates_v1.go b/constraint/pkg/apis/addtoscheme_templates_v1.go new file mode 100644 index 000000000..bfdf6715b --- /dev/null +++ b/constraint/pkg/apis/addtoscheme_templates_v1.go @@ -0,0 +1,23 @@ +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apis + +import v1 "github.com/open-policy-agent/frameworks/constraint/pkg/apis/templates/v1" + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, v1.AddToScheme) +} diff --git a/constraint/pkg/apis/templates/v1/constrainttemplate_types_test.go b/constraint/pkg/apis/templates/v1/constrainttemplate_types_test.go index 122dfb0c0..6de2bc9e5 100644 --- a/constraint/pkg/apis/templates/v1/constrainttemplate_types_test.go +++ b/constraint/pkg/apis/templates/v1/constrainttemplate_types_test.go @@ -16,11 +16,16 @@ limitations under the License. package v1 import ( + "reflect" "testing" + "github.com/google/go-cmp/cmp" "github.com/onsi/gomega" + "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" "golang.org/x/net/context" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ) @@ -54,142 +59,74 @@ func TestStorageConstraintTemplate(t *testing.T) { g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) } -// func TestTypeConversion(t *testing.T) { -// scheme := runtime.NewScheme() -// if err := AddToScheme(scheme); err != nil { -// t.Fatalf("Could not add to scheme: %v", err) -// } -// versioned := &ConstraintTemplate{ -// TypeMeta: metav1.TypeMeta{ -// Kind: "ConstraintTemplate", -// APIVersion: "templates.gatekeeper.sh/v1beta1", -// }, -// ObjectMeta: metav1.ObjectMeta{ -// Name: "MustHaveMoreCats", -// }, -// Spec: ConstraintTemplateSpec{ -// CRD: CRD{ -// Spec: CRDSpec{ -// Names: Names{ -// Kind: "MustHaveMoreCats", -// ShortNames: []string{"mhmc"}, -// }, -// Validation: &Validation{ -// OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ -// Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ -// "message": { -// Type: "string", -// }, -// "labels": { -// Type: "array", -// Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{ -// Schema: &apiextensionsv1beta1.JSONSchemaProps{ -// Type: "object", -// Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ -// "key": {Type: "string"}, -// "allowedRegex": {Type: "string"}, -// }, -// }, -// }, -// }, -// }, -// }, -// }, -// }, -// }, -// Targets: []Target{ -// { -// Target: "sometarget", -// Rego: `package hello ; violation[{"msg": "msg"}] { true }`, -// }, -// }, -// }, -// } -// versionedCopy := versioned.DeepCopy() -// // Kind and API Version do not survive the conversion process -// versionedCopy.Kind = "" -// versionedCopy.APIVersion = "" -// -// unversioned := &templates.ConstraintTemplate{} -// if err := scheme.Convert(versioned, unversioned, nil); err != nil { -// t.Fatalf("Conversion error: %v", err) -// } -// recast := &ConstraintTemplate{} -// if err := scheme.Convert(unversioned, recast, nil); err != nil { -// t.Fatalf("Recast conversion error: %v", err) -// } -// } - -// // function works, and also that it adds in the x-kubernetes-preserve-unknown-fields information -// // that we require for v1 CRD support -// func TestValidationVersionConversionAndTransformation(t *testing.T) { -// trueBool := true -// testCases := []struct { -// name string -// v *Validation -// exp *templates.Validation -// error bool -// }{ -// { -// name: "Two deep properties", -// v: &Validation{ -// OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ -// Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ -// "message": { -// Type: "string", -// }, -// "labels": { -// Type: "array", -// Items: &apiextensionsv1beta1.JSONSchemaPropsOrArray{ -// Schema: &apiextensionsv1beta1.JSONSchemaProps{ -// Type: "object", -// Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ -// "key": {Type: "string"}, -// "allowedRegex": {Type: "string"}, -// }, -// }, -// }, -// }, -// }, -// }, -// }, -// exp: &templates.Validation{ -// OpenAPIV3Schema: &apiextensions.JSONSchemaProps{ -// XPreserveUnknownFields: &trueBool, -// Properties: map[string]apiextensions.JSONSchemaProps{ -// "message": { -// Type: "string", -// }, -// "labels": { -// Type: "array", -// Items: &apiextensions.JSONSchemaPropsOrArray{ -// Schema: &apiextensions.JSONSchemaProps{ -// Type: "object", -// XPreserveUnknownFields: &trueBool, -// Properties: map[string]apiextensions.JSONSchemaProps{ -// "key": {Type: "string"}, -// "allowedRegex": {Type: "string"}, -// }, -// }, -// }, -// }, -// }, -// }, -// }, -// error: false, -// }, -// } -// -// for _, tc := range testCases { -// t.Run(tc.name, func(t *testing.T) { -// out := &templates.Validation{} -// if err := Convert_v1beta1_Validation_To_templates_Validation(tc.v, out, nil); err != nil { -// t.Fatalf("Conversion error: %v", err) -// } -// -// if !reflect.DeepEqual(out, tc.exp) { -// t.Fatalf("Conversion does not match expected result: %v", cmp.Diff(out, tc.exp)) -// } -// }) -// } -// } +func TestTypeConversion(t *testing.T) { + scheme := runtime.NewScheme() + if err := AddToScheme(scheme); err != nil { + t.Fatalf("Could not add to scheme: %v", err) + } + versioned := &ConstraintTemplate{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConstraintTemplate", + APIVersion: "templates.gatekeeper.sh/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "MustHaveMoreCats", + }, + Spec: ConstraintTemplateSpec{ + CRD: CRD{ + Spec: CRDSpec{ + Names: Names{ + Kind: "MustHaveMoreCats", + ShortNames: []string{"mhmc"}, + }, + Validation: &Validation{ + OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ + Type: "object", + Properties: map[string]apiextensionsv1.JSONSchemaProps{ + "message": { + Type: "string", + }, + "labels": { + Type: "array", + Items: &apiextensionsv1.JSONSchemaPropsOrArray{ + Schema: &apiextensionsv1.JSONSchemaProps{ + Type: "object", + Properties: map[string]apiextensionsv1.JSONSchemaProps{ + "key": {Type: "string"}, + "allowedRegex": {Type: "string"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Targets: []Target{ + { + Target: "sometarget", + Rego: `package hello ; violation[{"msg": "msg"}] { true }`, + }, + }, + }, + } + versionedCopy := versioned.DeepCopy() + // Kind and API Version do not survive the conversion process + versionedCopy.Kind = "" + versionedCopy.APIVersion = "" + + unversioned := &templates.ConstraintTemplate{} + if err := scheme.Convert(versioned, unversioned, nil); err != nil { + t.Fatalf("Conversion error: %v", err) + } + + recast := &ConstraintTemplate{} + if err := scheme.Convert(unversioned, recast, nil); err != nil { + t.Fatalf("Recast conversion error: %v", err) + } + + if !reflect.DeepEqual(versionedCopy, recast) { + t.Fatalf("Unexpected template difference. Diff: %v", cmp.Diff(versionedCopy, recast)) + } +} diff --git a/constraint/pkg/apis/templates/v1/conversion.go b/constraint/pkg/apis/templates/v1/conversion.go deleted file mode 100644 index 0dee921e8..000000000 --- a/constraint/pkg/apis/templates/v1/conversion.go +++ /dev/null @@ -1,36 +0,0 @@ -/* - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1 - -import ( - coreTemplates "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - "k8s.io/apimachinery/pkg/conversion" -) - -func Convert_v1_Validation_To_templates_Validation(in *Validation, out *coreTemplates.Validation, s conversion.Scope) error { //nolint:golint - if in.OpenAPIV3Schema != nil { - inSchemaCopy := in.OpenAPIV3Schema.DeepCopy() - out.OpenAPIV3Schema = new(apiextensions.JSONSchemaProps) - if err := apiextensionsv1.Convert_v1_JSONSchemaProps_To_apiextensions_JSONSchemaProps(inSchemaCopy, out.OpenAPIV3Schema, s); err != nil { - return err - } - } else { - out.OpenAPIV3Schema = nil - } - return nil -} diff --git a/constraint/pkg/apis/templates/v1/zz_generated.conversion.go b/constraint/pkg/apis/templates/v1/zz_generated.conversion.go index 0ee5c878b..83456d233 100644 --- a/constraint/pkg/apis/templates/v1/zz_generated.conversion.go +++ b/constraint/pkg/apis/templates/v1/zz_generated.conversion.go @@ -22,6 +22,7 @@ import ( unsafe "unsafe" templates "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" @@ -134,13 +135,13 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*templates.Validation)(nil), (*Validation)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_templates_Validation_To_v1_Validation(a.(*templates.Validation), b.(*Validation), scope) + if err := s.AddGeneratedConversionFunc((*Validation)(nil), (*templates.Validation)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_Validation_To_templates_Validation(a.(*Validation), b.(*templates.Validation), scope) }); err != nil { return err } - if err := s.AddConversionFunc((*Validation)(nil), (*templates.Validation)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1_Validation_To_templates_Validation(a.(*Validation), b.(*templates.Validation), scope) + if err := s.AddGeneratedConversionFunc((*templates.Validation)(nil), (*Validation)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_templates_Validation_To_v1_Validation(a.(*templates.Validation), b.(*Validation), scope) }); err != nil { return err } @@ -429,6 +430,24 @@ func Convert_templates_Target_To_v1_Target(in *templates.Target, out *Target, s return autoConvert_templates_Target_To_v1_Target(in, out, s) } +func autoConvert_v1_Validation_To_templates_Validation(in *Validation, out *templates.Validation, s conversion.Scope) error { + if in.OpenAPIV3Schema != nil { + in, out := &in.OpenAPIV3Schema, &out.OpenAPIV3Schema + *out = new(apiextensions.JSONSchemaProps) + if err := apiextensionsv1.Convert_v1_JSONSchemaProps_To_apiextensions_JSONSchemaProps(*in, *out, s); err != nil { + return err + } + } else { + out.OpenAPIV3Schema = nil + } + return nil +} + +// Convert_v1_Validation_To_templates_Validation is an autogenerated conversion function. +func Convert_v1_Validation_To_templates_Validation(in *Validation, out *templates.Validation, s conversion.Scope) error { + return autoConvert_v1_Validation_To_templates_Validation(in, out, s) +} + func autoConvert_templates_Validation_To_v1_Validation(in *templates.Validation, out *Validation, s conversion.Scope) error { if in.OpenAPIV3Schema != nil { in, out := &in.OpenAPIV3Schema, &out.OpenAPIV3Schema