Ensure controller revision data is valid json
This commit is contained in:
		| @@ -22,6 +22,7 @@ import ( | |||||||
| 	fuzz "github.com/google/gofuzz" | 	fuzz "github.com/google/gofuzz" | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" | 	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" | ||||||
| 	"k8s.io/apimachinery/pkg/util/intstr" | 	"k8s.io/apimachinery/pkg/util/intstr" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/apps" | 	"k8s.io/kubernetes/pkg/apis/apps" | ||||||
| @@ -30,6 +31,11 @@ import ( | |||||||
| // Funcs returns the fuzzer functions for the apps api group. | // Funcs returns the fuzzer functions for the apps api group. | ||||||
| var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { | var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { | ||||||
| 	return []interface{}{ | 	return []interface{}{ | ||||||
|  | 		func(r *apps.ControllerRevision, c fuzz.Continue) { | ||||||
|  | 			c.FuzzNoCustom(r) | ||||||
|  | 			// match the fuzzer default content for runtime.Object | ||||||
|  | 			r.Data = runtime.RawExtension{Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`)} | ||||||
|  | 		}, | ||||||
| 		func(s *apps.StatefulSet, c fuzz.Continue) { | 		func(s *apps.StatefulSet, c fuzz.Continue) { | ||||||
| 			c.FuzzNoCustom(s) // fuzz self without calling this function again | 			c.FuzzNoCustom(s) // fuzz self without calling this function again | ||||||
|  |  | ||||||
|   | |||||||
| @@ -331,7 +331,7 @@ type ControllerRevision struct { | |||||||
| 	metav1.ObjectMeta | 	metav1.ObjectMeta | ||||||
|  |  | ||||||
| 	// Data is the Object representing the state. | 	// Data is the Object representing the state. | ||||||
| 	Data runtime.Object | 	Data runtime.RawExtension | ||||||
|  |  | ||||||
| 	// Revision indicates the revision of the state represented by Data. | 	// Revision indicates the revision of the state represented by Data. | ||||||
| 	Revision int64 | 	Revision int64 | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								pkg/apis/apps/v1/zz_generated.conversion.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								pkg/apis/apps/v1/zz_generated.conversion.go
									
									
									
										generated
									
									
									
								
							| @@ -347,9 +347,7 @@ func RegisterConversions(s *runtime.Scheme) error { | |||||||
|  |  | ||||||
| func autoConvert_v1_ControllerRevision_To_apps_ControllerRevision(in *v1.ControllerRevision, out *apps.ControllerRevision, s conversion.Scope) error { | func autoConvert_v1_ControllerRevision_To_apps_ControllerRevision(in *v1.ControllerRevision, out *apps.ControllerRevision, s conversion.Scope) error { | ||||||
| 	out.ObjectMeta = in.ObjectMeta | 	out.ObjectMeta = in.ObjectMeta | ||||||
| 	if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Data, &out.Data, s); err != nil { | 	out.Data = in.Data | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	out.Revision = in.Revision | 	out.Revision = in.Revision | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -361,9 +359,7 @@ func Convert_v1_ControllerRevision_To_apps_ControllerRevision(in *v1.ControllerR | |||||||
|  |  | ||||||
| func autoConvert_apps_ControllerRevision_To_v1_ControllerRevision(in *apps.ControllerRevision, out *v1.ControllerRevision, s conversion.Scope) error { | func autoConvert_apps_ControllerRevision_To_v1_ControllerRevision(in *apps.ControllerRevision, out *v1.ControllerRevision, s conversion.Scope) error { | ||||||
| 	out.ObjectMeta = in.ObjectMeta | 	out.ObjectMeta = in.ObjectMeta | ||||||
| 	if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Data, &out.Data, s); err != nil { | 	out.Data = in.Data | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	out.Revision = in.Revision | 	out.Revision = in.Revision | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -375,17 +371,7 @@ func Convert_apps_ControllerRevision_To_v1_ControllerRevision(in *apps.Controlle | |||||||
|  |  | ||||||
| func autoConvert_v1_ControllerRevisionList_To_apps_ControllerRevisionList(in *v1.ControllerRevisionList, out *apps.ControllerRevisionList, s conversion.Scope) error { | func autoConvert_v1_ControllerRevisionList_To_apps_ControllerRevisionList(in *v1.ControllerRevisionList, out *apps.ControllerRevisionList, s conversion.Scope) error { | ||||||
| 	out.ListMeta = in.ListMeta | 	out.ListMeta = in.ListMeta | ||||||
| 	if in.Items != nil { | 	out.Items = *(*[]apps.ControllerRevision)(unsafe.Pointer(&in.Items)) | ||||||
| 		in, out := &in.Items, &out.Items |  | ||||||
| 		*out = make([]apps.ControllerRevision, len(*in)) |  | ||||||
| 		for i := range *in { |  | ||||||
| 			if err := Convert_v1_ControllerRevision_To_apps_ControllerRevision(&(*in)[i], &(*out)[i], s); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		out.Items = nil |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -396,17 +382,7 @@ func Convert_v1_ControllerRevisionList_To_apps_ControllerRevisionList(in *v1.Con | |||||||
|  |  | ||||||
| func autoConvert_apps_ControllerRevisionList_To_v1_ControllerRevisionList(in *apps.ControllerRevisionList, out *v1.ControllerRevisionList, s conversion.Scope) error { | func autoConvert_apps_ControllerRevisionList_To_v1_ControllerRevisionList(in *apps.ControllerRevisionList, out *v1.ControllerRevisionList, s conversion.Scope) error { | ||||||
| 	out.ListMeta = in.ListMeta | 	out.ListMeta = in.ListMeta | ||||||
| 	if in.Items != nil { | 	out.Items = *(*[]v1.ControllerRevision)(unsafe.Pointer(&in.Items)) | ||||||
| 		in, out := &in.Items, &out.Items |  | ||||||
| 		*out = make([]v1.ControllerRevision, len(*in)) |  | ||||||
| 		for i := range *in { |  | ||||||
| 			if err := Convert_apps_ControllerRevision_To_v1_ControllerRevision(&(*in)[i], &(*out)[i], s); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		out.Items = nil |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								pkg/apis/apps/v1beta1/zz_generated.conversion.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								pkg/apis/apps/v1beta1/zz_generated.conversion.go
									
									
									
										generated
									
									
									
								
							| @@ -278,9 +278,7 @@ func RegisterConversions(s *runtime.Scheme) error { | |||||||
|  |  | ||||||
| func autoConvert_v1beta1_ControllerRevision_To_apps_ControllerRevision(in *v1beta1.ControllerRevision, out *apps.ControllerRevision, s conversion.Scope) error { | func autoConvert_v1beta1_ControllerRevision_To_apps_ControllerRevision(in *v1beta1.ControllerRevision, out *apps.ControllerRevision, s conversion.Scope) error { | ||||||
| 	out.ObjectMeta = in.ObjectMeta | 	out.ObjectMeta = in.ObjectMeta | ||||||
| 	if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Data, &out.Data, s); err != nil { | 	out.Data = in.Data | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	out.Revision = in.Revision | 	out.Revision = in.Revision | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -292,9 +290,7 @@ func Convert_v1beta1_ControllerRevision_To_apps_ControllerRevision(in *v1beta1.C | |||||||
|  |  | ||||||
| func autoConvert_apps_ControllerRevision_To_v1beta1_ControllerRevision(in *apps.ControllerRevision, out *v1beta1.ControllerRevision, s conversion.Scope) error { | func autoConvert_apps_ControllerRevision_To_v1beta1_ControllerRevision(in *apps.ControllerRevision, out *v1beta1.ControllerRevision, s conversion.Scope) error { | ||||||
| 	out.ObjectMeta = in.ObjectMeta | 	out.ObjectMeta = in.ObjectMeta | ||||||
| 	if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Data, &out.Data, s); err != nil { | 	out.Data = in.Data | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	out.Revision = in.Revision | 	out.Revision = in.Revision | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -306,17 +302,7 @@ func Convert_apps_ControllerRevision_To_v1beta1_ControllerRevision(in *apps.Cont | |||||||
|  |  | ||||||
| func autoConvert_v1beta1_ControllerRevisionList_To_apps_ControllerRevisionList(in *v1beta1.ControllerRevisionList, out *apps.ControllerRevisionList, s conversion.Scope) error { | func autoConvert_v1beta1_ControllerRevisionList_To_apps_ControllerRevisionList(in *v1beta1.ControllerRevisionList, out *apps.ControllerRevisionList, s conversion.Scope) error { | ||||||
| 	out.ListMeta = in.ListMeta | 	out.ListMeta = in.ListMeta | ||||||
| 	if in.Items != nil { | 	out.Items = *(*[]apps.ControllerRevision)(unsafe.Pointer(&in.Items)) | ||||||
| 		in, out := &in.Items, &out.Items |  | ||||||
| 		*out = make([]apps.ControllerRevision, len(*in)) |  | ||||||
| 		for i := range *in { |  | ||||||
| 			if err := Convert_v1beta1_ControllerRevision_To_apps_ControllerRevision(&(*in)[i], &(*out)[i], s); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		out.Items = nil |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -327,17 +313,7 @@ func Convert_v1beta1_ControllerRevisionList_To_apps_ControllerRevisionList(in *v | |||||||
|  |  | ||||||
| func autoConvert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList(in *apps.ControllerRevisionList, out *v1beta1.ControllerRevisionList, s conversion.Scope) error { | func autoConvert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList(in *apps.ControllerRevisionList, out *v1beta1.ControllerRevisionList, s conversion.Scope) error { | ||||||
| 	out.ListMeta = in.ListMeta | 	out.ListMeta = in.ListMeta | ||||||
| 	if in.Items != nil { | 	out.Items = *(*[]v1beta1.ControllerRevision)(unsafe.Pointer(&in.Items)) | ||||||
| 		in, out := &in.Items, &out.Items |  | ||||||
| 		*out = make([]v1beta1.ControllerRevision, len(*in)) |  | ||||||
| 		for i := range *in { |  | ||||||
| 			if err := Convert_apps_ControllerRevision_To_v1beta1_ControllerRevision(&(*in)[i], &(*out)[i], s); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		out.Items = nil |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								pkg/apis/apps/v1beta2/zz_generated.conversion.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								pkg/apis/apps/v1beta2/zz_generated.conversion.go
									
									
									
										generated
									
									
									
								
							| @@ -378,9 +378,7 @@ func RegisterConversions(s *runtime.Scheme) error { | |||||||
|  |  | ||||||
| func autoConvert_v1beta2_ControllerRevision_To_apps_ControllerRevision(in *v1beta2.ControllerRevision, out *apps.ControllerRevision, s conversion.Scope) error { | func autoConvert_v1beta2_ControllerRevision_To_apps_ControllerRevision(in *v1beta2.ControllerRevision, out *apps.ControllerRevision, s conversion.Scope) error { | ||||||
| 	out.ObjectMeta = in.ObjectMeta | 	out.ObjectMeta = in.ObjectMeta | ||||||
| 	if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Data, &out.Data, s); err != nil { | 	out.Data = in.Data | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	out.Revision = in.Revision | 	out.Revision = in.Revision | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -392,9 +390,7 @@ func Convert_v1beta2_ControllerRevision_To_apps_ControllerRevision(in *v1beta2.C | |||||||
|  |  | ||||||
| func autoConvert_apps_ControllerRevision_To_v1beta2_ControllerRevision(in *apps.ControllerRevision, out *v1beta2.ControllerRevision, s conversion.Scope) error { | func autoConvert_apps_ControllerRevision_To_v1beta2_ControllerRevision(in *apps.ControllerRevision, out *v1beta2.ControllerRevision, s conversion.Scope) error { | ||||||
| 	out.ObjectMeta = in.ObjectMeta | 	out.ObjectMeta = in.ObjectMeta | ||||||
| 	if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Data, &out.Data, s); err != nil { | 	out.Data = in.Data | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	out.Revision = in.Revision | 	out.Revision = in.Revision | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -406,17 +402,7 @@ func Convert_apps_ControllerRevision_To_v1beta2_ControllerRevision(in *apps.Cont | |||||||
|  |  | ||||||
| func autoConvert_v1beta2_ControllerRevisionList_To_apps_ControllerRevisionList(in *v1beta2.ControllerRevisionList, out *apps.ControllerRevisionList, s conversion.Scope) error { | func autoConvert_v1beta2_ControllerRevisionList_To_apps_ControllerRevisionList(in *v1beta2.ControllerRevisionList, out *apps.ControllerRevisionList, s conversion.Scope) error { | ||||||
| 	out.ListMeta = in.ListMeta | 	out.ListMeta = in.ListMeta | ||||||
| 	if in.Items != nil { | 	out.Items = *(*[]apps.ControllerRevision)(unsafe.Pointer(&in.Items)) | ||||||
| 		in, out := &in.Items, &out.Items |  | ||||||
| 		*out = make([]apps.ControllerRevision, len(*in)) |  | ||||||
| 		for i := range *in { |  | ||||||
| 			if err := Convert_v1beta2_ControllerRevision_To_apps_ControllerRevision(&(*in)[i], &(*out)[i], s); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		out.Items = nil |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -427,17 +413,7 @@ func Convert_v1beta2_ControllerRevisionList_To_apps_ControllerRevisionList(in *v | |||||||
|  |  | ||||||
| func autoConvert_apps_ControllerRevisionList_To_v1beta2_ControllerRevisionList(in *apps.ControllerRevisionList, out *v1beta2.ControllerRevisionList, s conversion.Scope) error { | func autoConvert_apps_ControllerRevisionList_To_v1beta2_ControllerRevisionList(in *apps.ControllerRevisionList, out *v1beta2.ControllerRevisionList, s conversion.Scope) error { | ||||||
| 	out.ListMeta = in.ListMeta | 	out.ListMeta = in.ListMeta | ||||||
| 	if in.Items != nil { | 	out.Items = *(*[]v1beta2.ControllerRevision)(unsafe.Pointer(&in.Items)) | ||||||
| 		in, out := &in.Items, &out.Items |  | ||||||
| 		*out = make([]v1beta2.ControllerRevision, len(*in)) |  | ||||||
| 		for i := range *in { |  | ||||||
| 			if err := Convert_apps_ControllerRevision_To_v1beta2_ControllerRevision(&(*in)[i], &(*out)[i], s); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		out.Items = nil |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ limitations under the License. | |||||||
| package validation | package validation | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strconv" | 	"strconv" | ||||||
|  |  | ||||||
| @@ -251,20 +252,34 @@ func ValidateStatefulSetStatusUpdate(statefulSet, oldStatefulSet *apps.StatefulS | |||||||
| // trailing dashes are allowed. | // trailing dashes are allowed. | ||||||
| var ValidateControllerRevisionName = apimachineryvalidation.NameIsDNSSubdomain | var ValidateControllerRevisionName = apimachineryvalidation.NameIsDNSSubdomain | ||||||
|  |  | ||||||
| // ValidateControllerRevision collects errors for the fields of state and returns those errors as an ErrorList. If the | // validateControllerRevision collects errors for the fields of state and returns those errors as an ErrorList. If the | ||||||
| // returned list is empty, state is valid. Validation is performed to ensure that state is a valid ObjectMeta, its name | // returned list is empty, state is valid. Validation is performed to ensure that state is a valid ObjectMeta, its name | ||||||
| // is valid, and that it doesn't exceed the MaxControllerRevisionSize. | // is valid, and that it doesn't exceed the MaxControllerRevisionSize. | ||||||
| func ValidateControllerRevision(revision *apps.ControllerRevision) field.ErrorList { | func validateControllerRevision(revision *apps.ControllerRevision) field.ErrorList { | ||||||
| 	errs := field.ErrorList{} | 	errs := field.ErrorList{} | ||||||
|  |  | ||||||
| 	errs = append(errs, apivalidation.ValidateObjectMeta(&revision.ObjectMeta, true, ValidateControllerRevisionName, field.NewPath("metadata"))...) | 	errs = append(errs, apivalidation.ValidateObjectMeta(&revision.ObjectMeta, true, ValidateControllerRevisionName, field.NewPath("metadata"))...) | ||||||
| 	if revision.Data == nil { |  | ||||||
| 		errs = append(errs, field.Required(field.NewPath("data"), "data is mandatory")) |  | ||||||
| 	} |  | ||||||
| 	errs = append(errs, apivalidation.ValidateNonnegativeField(revision.Revision, field.NewPath("revision"))...) | 	errs = append(errs, apivalidation.ValidateNonnegativeField(revision.Revision, field.NewPath("revision"))...) | ||||||
| 	return errs | 	return errs | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func ValidateControllerRevisionCreate(revision *apps.ControllerRevision) field.ErrorList { | ||||||
|  | 	errs := field.ErrorList{} | ||||||
|  | 	errs = append(errs, validateControllerRevision(revision)...) | ||||||
|  |  | ||||||
|  | 	var v any | ||||||
|  | 	if revision.Data.Raw == nil { | ||||||
|  | 		errs = append(errs, field.Required(field.NewPath("data"), "data is mandatory")) | ||||||
|  | 	} else if err := json.Unmarshal(revision.Data.Raw, &v); err != nil { | ||||||
|  | 		errs = append(errs, field.Invalid(field.NewPath("data"), "<value omitted>", fmt.Sprintf("error parsing data: %v", err.Error()))) | ||||||
|  | 	} else if v == nil { | ||||||
|  | 		errs = append(errs, field.Required(field.NewPath("data"), "data is mandatory")) | ||||||
|  | 	} else if _, isObject := v.(map[string]any); !isObject { | ||||||
|  | 		errs = append(errs, field.Required(field.NewPath("data"), "data must be a valid JSON object")) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return errs | ||||||
|  | } | ||||||
|  |  | ||||||
| // ValidateControllerRevisionUpdate collects errors pertaining to the mutation of an ControllerRevision Object. If the | // ValidateControllerRevisionUpdate collects errors pertaining to the mutation of an ControllerRevision Object. If the | ||||||
| // returned ErrorList is empty the update operation is valid. Any mutation to the ControllerRevision's Data or Revision | // returned ErrorList is empty the update operation is valid. Any mutation to the ControllerRevision's Data or Revision | ||||||
| // is considered to be invalid. | // is considered to be invalid. | ||||||
| @@ -272,7 +287,7 @@ func ValidateControllerRevisionUpdate(newHistory, oldHistory *apps.ControllerRev | |||||||
| 	errs := field.ErrorList{} | 	errs := field.ErrorList{} | ||||||
|  |  | ||||||
| 	errs = append(errs, apivalidation.ValidateObjectMetaUpdate(&newHistory.ObjectMeta, &oldHistory.ObjectMeta, field.NewPath("metadata"))...) | 	errs = append(errs, apivalidation.ValidateObjectMetaUpdate(&newHistory.ObjectMeta, &oldHistory.ObjectMeta, field.NewPath("metadata"))...) | ||||||
| 	errs = append(errs, ValidateControllerRevision(newHistory)...) | 	errs = append(errs, validateControllerRevision(newHistory)...) | ||||||
| 	errs = append(errs, apivalidation.ValidateImmutableField(newHistory.Data, oldHistory.Data, field.NewPath("data"))...) | 	errs = append(errs, apivalidation.ValidateImmutableField(newHistory.Data, oldHistory.Data, field.NewPath("data"))...) | ||||||
| 	return errs | 	return errs | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,12 +17,14 @@ limitations under the License. | |||||||
| package validation | package validation | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/google/go-cmp/cmp" | 	"github.com/google/go-cmp/cmp" | ||||||
| 	"github.com/google/go-cmp/cmp/cmpopts" | 	"github.com/google/go-cmp/cmp/cmpopts" | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/resource" | 	"k8s.io/apimachinery/pkg/api/resource" | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| @@ -969,7 +971,7 @@ func TestValidateStatefulSetUpdate(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestValidateControllerRevision(t *testing.T) { | func TestValidateControllerRevision(t *testing.T) { | ||||||
| 	newControllerRevision := func(name, namespace string, data runtime.Object, revision int64) apps.ControllerRevision { | 	newControllerRevision := func(name, namespace string, data runtime.RawExtension, revision int64) apps.ControllerRevision { | ||||||
| 		return apps.ControllerRevision{ | 		return apps.ControllerRevision{ | ||||||
| 			ObjectMeta: metav1.ObjectMeta{ | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
| 				Name:      name, | 				Name:      name, | ||||||
| @@ -993,15 +995,17 @@ func TestValidateControllerRevision(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ss := mkStatefulSet(&podTemplate) | 	ss := mkStatefulSet(&podTemplate) | ||||||
|  | 	ssJSON, _ := json.Marshal(ss) | ||||||
|  | 	raw := runtime.RawExtension{Raw: ssJSON} | ||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
| 		valid       = newControllerRevision("validname", "validns", &ss, 0) | 		valid       = newControllerRevision("validname", "validns", raw, 0) | ||||||
| 		badRevision = newControllerRevision("validname", "validns", &ss, -1) | 		badRevision = newControllerRevision("validname", "validns", raw, -1) | ||||||
| 		emptyName   = newControllerRevision("", "validns", &ss, 0) | 		emptyName   = newControllerRevision("", "validns", raw, 0) | ||||||
| 		invalidName = newControllerRevision("NoUppercaseOrSpecialCharsLike=Equals", "validns", &ss, 0) | 		invalidName = newControllerRevision("NoUppercaseOrSpecialCharsLike=Equals", "validns", raw, 0) | ||||||
| 		emptyNs     = newControllerRevision("validname", "", &ss, 100) | 		emptyNs     = newControllerRevision("validname", "", raw, 100) | ||||||
| 		invalidNs   = newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", &ss, 100) | 		invalidNs   = newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", raw, 100) | ||||||
| 		nilData     = newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", nil, 100) | 		nilData     = newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", runtime.RawExtension{}, 100) | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	tests := map[string]struct { | 	tests := map[string]struct { | ||||||
| @@ -1015,11 +1019,19 @@ func TestValidateControllerRevision(t *testing.T) { | |||||||
| 		"empty namespace":   {emptyNs, false}, | 		"empty namespace":   {emptyNs, false}, | ||||||
| 		"invalid namespace": {invalidNs, false}, | 		"invalid namespace": {invalidNs, false}, | ||||||
| 		"nil data":          {nilData, false}, | 		"nil data":          {nilData, false}, | ||||||
|  | 		"json error":        {newControllerRevision("validname", "validns", runtime.RawExtension{Raw: []byte(`{`)}, 0), false}, | ||||||
|  | 		"json bool":         {newControllerRevision("validname", "validns", runtime.RawExtension{Raw: []byte(`true`)}, 0), false}, | ||||||
|  | 		"json int":          {newControllerRevision("validname", "validns", runtime.RawExtension{Raw: []byte(`0`)}, 0), false}, | ||||||
|  | 		"json float":        {newControllerRevision("validname", "validns", runtime.RawExtension{Raw: []byte(`0.5`)}, 0), false}, | ||||||
|  | 		"json null":         {newControllerRevision("validname", "validns", runtime.RawExtension{Raw: []byte(`null`)}, 0), false}, | ||||||
|  | 		"json array":        {newControllerRevision("validname", "validns", runtime.RawExtension{Raw: []byte(`[]`)}, 0), false}, | ||||||
|  | 		"json string":       {newControllerRevision("validname", "validns", runtime.RawExtension{Raw: []byte(`"test"`)}, 0), false}, | ||||||
|  | 		"json object":       {newControllerRevision("validname", "validns", runtime.RawExtension{Raw: []byte(`{}`)}, 0), true}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for name, tc := range tests { | 	for name, tc := range tests { | ||||||
| 		t.Run(name, func(t *testing.T) { | 		t.Run(name, func(t *testing.T) { | ||||||
| 			errs := ValidateControllerRevision(&tc.history) | 			errs := ValidateControllerRevisionCreate(&tc.history) | ||||||
| 			if tc.isValid && len(errs) > 0 { | 			if tc.isValid && len(errs) > 0 { | ||||||
| 				t.Errorf("%v: unexpected error: %v", name, errs) | 				t.Errorf("%v: unexpected error: %v", name, errs) | ||||||
| 			} | 			} | ||||||
| @@ -1031,7 +1043,7 @@ func TestValidateControllerRevision(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestValidateControllerRevisionUpdate(t *testing.T) { | func TestValidateControllerRevisionUpdate(t *testing.T) { | ||||||
| 	newControllerRevision := func(version, name, namespace string, data runtime.Object, revision int64) apps.ControllerRevision { | 	newControllerRevision := func(version, name, namespace string, data runtime.RawExtension, revision int64) apps.ControllerRevision { | ||||||
| 		return apps.ControllerRevision{ | 		return apps.ControllerRevision{ | ||||||
| 			ObjectMeta: metav1.ObjectMeta{ | 			ObjectMeta: metav1.ObjectMeta{ | ||||||
| 				Name:            name, | 				Name:            name, | ||||||
| @@ -1058,11 +1070,16 @@ func TestValidateControllerRevisionUpdate(t *testing.T) { | |||||||
| 	ss := mkStatefulSet(&podTemplate, tweakName("abc")) | 	ss := mkStatefulSet(&podTemplate, tweakName("abc")) | ||||||
| 	modifiedss := mkStatefulSet(&podTemplate, tweakName("cdf")) | 	modifiedss := mkStatefulSet(&podTemplate, tweakName("cdf")) | ||||||
|  |  | ||||||
|  | 	ssJSON, _ := json.Marshal(ss) | ||||||
|  | 	modifiedSSJSON, _ := json.Marshal(modifiedss) | ||||||
|  | 	raw := runtime.RawExtension{Raw: ssJSON} | ||||||
|  | 	modifiedRaw := runtime.RawExtension{Raw: modifiedSSJSON} | ||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
| 		valid           = newControllerRevision("1", "validname", "validns", &ss, 0) | 		valid           = newControllerRevision("1", "validname", "validns", raw, 0) | ||||||
| 		noVersion       = newControllerRevision("", "validname", "validns", &ss, 0) | 		noVersion       = newControllerRevision("", "validname", "validns", raw, 0) | ||||||
| 		changedData     = newControllerRevision("1", "validname", "validns", &modifiedss, 0) | 		changedData     = newControllerRevision("1", "validname", "validns", modifiedRaw, 0) | ||||||
| 		changedRevision = newControllerRevision("1", "validname", "validns", &ss, 1) | 		changedRevision = newControllerRevision("1", "validname", "validns", raw, 1) | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	cases := []struct { | 	cases := []struct { | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								pkg/apis/apps/zz_generated.deepcopy.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								pkg/apis/apps/zz_generated.deepcopy.go
									
									
									
										generated
									
									
									
								
							| @@ -33,9 +33,7 @@ func (in *ControllerRevision) DeepCopyInto(out *ControllerRevision) { | |||||||
| 	*out = *in | 	*out = *in | ||||||
| 	out.TypeMeta = in.TypeMeta | 	out.TypeMeta = in.TypeMeta | ||||||
| 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) | 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) | ||||||
| 	if in.Data != nil { | 	in.Data.DeepCopyInto(&out.Data) | ||||||
| 		out.Data = in.Data.DeepCopyObject() |  | ||||||
| 	} |  | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ import ( | |||||||
| 	v1 "k8s.io/api/core/v1" | 	v1 "k8s.io/api/core/v1" | ||||||
| 	apinamingtest "k8s.io/apimachinery/pkg/api/apitesting/naming" | 	apinamingtest "k8s.io/apimachinery/pkg/api/apitesting/naming" | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/util/intstr" | 	"k8s.io/apimachinery/pkg/util/intstr" | ||||||
| 	"k8s.io/apimachinery/pkg/util/sets" | 	"k8s.io/apimachinery/pkg/util/sets" | ||||||
| 	"k8s.io/kubernetes/pkg/api/legacyscheme" | 	"k8s.io/kubernetes/pkg/api/legacyscheme" | ||||||
| @@ -70,6 +71,7 @@ var typesAllowedTags = map[reflect.Type]bool{ | |||||||
| 	reflect.TypeOf(metav1.GroupVersionResource{}): true, | 	reflect.TypeOf(metav1.GroupVersionResource{}): true, | ||||||
| 	reflect.TypeOf(metav1.Status{}):               true, | 	reflect.TypeOf(metav1.Status{}):               true, | ||||||
| 	reflect.TypeOf(metav1.Condition{}):            true, | 	reflect.TypeOf(metav1.Condition{}):            true, | ||||||
|  | 	reflect.TypeOf(runtime.RawExtension{}):        true, | ||||||
| } | } | ||||||
|  |  | ||||||
| // These fields are limited exceptions to the standard JSON naming structure. | // These fields are limited exceptions to the standard JSON naming structure. | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ limitations under the License. | |||||||
| package storage | package storage | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| @@ -43,7 +44,7 @@ func TestCreate(t *testing.T) { | |||||||
| 		invalidName = stripObjectMeta(newControllerRevision("NoUppercaseOrSpecialCharsLike=Equals", "validns", newObject(), 0)) | 		invalidName = stripObjectMeta(newControllerRevision("NoUppercaseOrSpecialCharsLike=Equals", "validns", newObject(), 0)) | ||||||
| 		emptyNs     = stripObjectMeta(newControllerRevision("validname", "", newObject(), 100)) | 		emptyNs     = stripObjectMeta(newControllerRevision("validname", "", newObject(), 100)) | ||||||
| 		invalidNs   = stripObjectMeta(newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", newObject(), 100)) | 		invalidNs   = stripObjectMeta(newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", newObject(), 100)) | ||||||
| 		nilData     = stripObjectMeta(newControllerRevision("validname", "validns", nil, 0)) | 		nilData     = stripObjectMeta(newControllerRevision("validname", "validns", runtime.RawExtension{Raw: nil}, 0)) | ||||||
| 	) | 	) | ||||||
| 	test.TestCreate( | 	test.TestCreate( | ||||||
| 		valid, | 		valid, | ||||||
| @@ -74,12 +75,11 @@ func TestUpdate(t *testing.T) { | |||||||
|  |  | ||||||
| 	updateData := func(obj runtime.Object) runtime.Object { | 	updateData := func(obj runtime.Object) runtime.Object { | ||||||
| 		rev := obj.(*apps.ControllerRevision) | 		rev := obj.(*apps.ControllerRevision) | ||||||
| 		modified := newObject() | 		ss := newStatefulSet() | ||||||
| 		ss := modified.(*apps.StatefulSet) |  | ||||||
| 		ss.Name = "cde" | 		ss.Name = "cde" | ||||||
| 		update := &apps.ControllerRevision{ | 		update := &apps.ControllerRevision{ | ||||||
| 			ObjectMeta: rev.ObjectMeta, | 			ObjectMeta: rev.ObjectMeta, | ||||||
| 			Data:       ss, | 			Data:       newRawExtensionFromObject(ss), | ||||||
| 			Revision:   rev.Revision + 1, | 			Revision:   rev.Revision + 1, | ||||||
| 		} | 		} | ||||||
| 		return update | 		return update | ||||||
| @@ -136,7 +136,7 @@ func TestWatch(t *testing.T) { | |||||||
| 	) | 	) | ||||||
| } | } | ||||||
|  |  | ||||||
| func newControllerRevision(name, namespace string, data runtime.Object, revision int64) *apps.ControllerRevision { | func newControllerRevision(name, namespace string, data runtime.RawExtension, revision int64) *apps.ControllerRevision { | ||||||
| 	return &apps.ControllerRevision{ | 	return &apps.ControllerRevision{ | ||||||
| 		ObjectMeta: metav1.ObjectMeta{ | 		ObjectMeta: metav1.ObjectMeta{ | ||||||
| 			Name:      name, | 			Name:      name, | ||||||
| @@ -167,7 +167,7 @@ func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) { | |||||||
| 	return storage, server | 	return storage, server | ||||||
| } | } | ||||||
|  |  | ||||||
| func newObject() runtime.Object { | func newStatefulSet() *apps.StatefulSet { | ||||||
| 	return &apps.StatefulSet{ | 	return &apps.StatefulSet{ | ||||||
| 		ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | 		ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | ||||||
| 		Spec: apps.StatefulSetSpec{ | 		Spec: apps.StatefulSetSpec{ | ||||||
| @@ -184,3 +184,10 @@ func newObject() runtime.Object { | |||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | func newRawExtensionFromObject(obj runtime.Object) runtime.RawExtension { | ||||||
|  | 	jsonData, _ := json.Marshal(obj) | ||||||
|  | 	return runtime.RawExtension{Raw: jsonData} | ||||||
|  | } | ||||||
|  | func newObject() runtime.RawExtension { | ||||||
|  | 	return newRawExtensionFromObject(newStatefulSet()) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -62,7 +62,7 @@ func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { | |||||||
| func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { | func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { | ||||||
| 	revision := obj.(*apps.ControllerRevision) | 	revision := obj.(*apps.ControllerRevision) | ||||||
|  |  | ||||||
| 	return validation.ValidateControllerRevision(revision) | 	return validation.ValidateControllerRevisionCreate(revision) | ||||||
| } | } | ||||||
|  |  | ||||||
| // WarningsOnCreate returns warnings for the creation of the given object. | // WarningsOnCreate returns warnings for the creation of the given object. | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ limitations under the License. | |||||||
| package controllerrevision | package controllerrevision | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| @@ -47,7 +48,7 @@ func TestStrategy_Validate(t *testing.T) { | |||||||
| 		invalidName = newControllerRevision("NoUppercaseOrSpecialCharsLike=Equals", "validns", newObject(), 0) | 		invalidName = newControllerRevision("NoUppercaseOrSpecialCharsLike=Equals", "validns", newObject(), 0) | ||||||
| 		emptyNs     = newControllerRevision("validname", "", newObject(), 100) | 		emptyNs     = newControllerRevision("validname", "", newObject(), 100) | ||||||
| 		invalidNs   = newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", newObject(), 100) | 		invalidNs   = newControllerRevision("validname", "NoUppercaseOrSpecialCharsLike=Equals", newObject(), 100) | ||||||
| 		nilData     = newControllerRevision("validname", "validns", nil, 0) | 		nilData     = newControllerRevision("validname", "validns", runtime.RawExtension{Raw: nil}, 0) | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	tests := map[string]struct { | 	tests := map[string]struct { | ||||||
| @@ -79,11 +80,10 @@ func TestStrategy_ValidateUpdate(t *testing.T) { | |||||||
| 	var ( | 	var ( | ||||||
| 		valid       = newControllerRevision("validname", "validns", newObject(), 0) | 		valid       = newControllerRevision("validname", "validns", newObject(), 0) | ||||||
| 		changedData = newControllerRevision("validname", "validns", | 		changedData = newControllerRevision("validname", "validns", | ||||||
| 			func() runtime.Object { | 			func() runtime.RawExtension { | ||||||
| 				modified := newObject() | 				ss := newStatefulSet() | ||||||
| 				ss := modified.(*apps.StatefulSet) |  | ||||||
| 				ss.Name = "cde" | 				ss.Name = "cde" | ||||||
| 				return modified | 				return newRawExtensionFromObject(ss) | ||||||
| 			}(), 0) | 			}(), 0) | ||||||
| 		changedRevision = newControllerRevision("validname", "validns", newObject(), 1) | 		changedRevision = newControllerRevision("validname", "validns", newObject(), 1) | ||||||
| 	) | 	) | ||||||
| @@ -125,7 +125,7 @@ func TestStrategy_ValidateUpdate(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func newControllerRevision(name, namespace string, data runtime.Object, revision int64) *apps.ControllerRevision { | func newControllerRevision(name, namespace string, data runtime.RawExtension, revision int64) *apps.ControllerRevision { | ||||||
| 	return &apps.ControllerRevision{ | 	return &apps.ControllerRevision{ | ||||||
| 		ObjectMeta: metav1.ObjectMeta{ | 		ObjectMeta: metav1.ObjectMeta{ | ||||||
| 			Name:            name, | 			Name:            name, | ||||||
| @@ -138,7 +138,7 @@ func newControllerRevision(name, namespace string, data runtime.Object, revision | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func newObject() runtime.Object { | func newStatefulSet() *apps.StatefulSet { | ||||||
| 	return &apps.StatefulSet{ | 	return &apps.StatefulSet{ | ||||||
| 		ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | 		ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | ||||||
| 		Spec: apps.StatefulSetSpec{ | 		Spec: apps.StatefulSetSpec{ | ||||||
| @@ -155,3 +155,10 @@ func newObject() runtime.Object { | |||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | func newRawExtensionFromObject(obj runtime.Object) runtime.RawExtension { | ||||||
|  | 	jsonData, _ := json.Marshal(obj) | ||||||
|  | 	return runtime.RawExtension{Raw: jsonData} | ||||||
|  | } | ||||||
|  | func newObject() runtime.RawExtension { | ||||||
|  | 	return newRawExtensionFromObject(newStatefulSet()) | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jordan Liggitt
					Jordan Liggitt