mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Fix validation and add many tests
This commit is contained in:
		
				
					committed by
					
						
						Jennifer Buckley
					
				
			
			
				
	
			
			
			
						parent
						
							c0617933d4
						
					
				
				
					commit
					cf24968309
				
			@@ -87,7 +87,7 @@ type JSONSchemaProps struct {
 | 
				
			|||||||
	//    - ... zero or more
 | 
						//    - ... zero or more
 | 
				
			||||||
	XIntOrString bool
 | 
						XIntOrString bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// x-kubernetes-list-map-keys annotates lists with the x-kubernetes-list-type `map` by specifying the keys used
 | 
						// x-kubernetes-list-map-keys annotates an array with the x-kubernetes-list-type `map` by specifying the keys used
 | 
				
			||||||
	// as the index of the map.
 | 
						// as the index of the map.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// This tag MUST only be used on lists that have the "x-kubernetes-list-type"
 | 
						// This tag MUST only be used on lists that have the "x-kubernetes-list-type"
 | 
				
			||||||
@@ -95,19 +95,19 @@ type JSONSchemaProps struct {
 | 
				
			|||||||
	// be a scalar typed field of the child structure (no nesting is supported).
 | 
						// be a scalar typed field of the child structure (no nesting is supported).
 | 
				
			||||||
	XListMapKeys []string
 | 
						XListMapKeys []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// x-kubernetes-list-type annotates a list to further describe its topology.
 | 
						// x-kubernetes-list-type annotates an array to further describe its topology.
 | 
				
			||||||
	// This extension must only be used on lists and may have 3 possible values:
 | 
						// This extension must only be used on lists and may have 3 possible values:
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// 1) `atomic`: the list is treated as a single entity, like a scalar.
 | 
						// 1) `atomic`: the list is treated as a single entity, like a scalar.
 | 
				
			||||||
	//      Atomic lists will be entirely replaced when updated. This extension
 | 
						//      Atomic lists will be entirely replaced when updated. This extension
 | 
				
			||||||
	//      may be used on any type of list (struct, scalar, ...).
 | 
						//      may be used on any type of list (struct, scalar, ...).
 | 
				
			||||||
	// 2) `set`:
 | 
						// 2) `set`:
 | 
				
			||||||
	//      Sets are lists that must not have multiple times the same value. Each
 | 
						//      Sets are lists that must not have multiple items with the same value. Each
 | 
				
			||||||
	//      value must be a scalar (or another atomic type).
 | 
						//      value must be a scalar (or another atomic type).
 | 
				
			||||||
	// 3) `map`:
 | 
						// 3) `map`:
 | 
				
			||||||
	//      These lists are like maps in that their elements have a non-index key
 | 
						//      These lists are like maps in that their elements have a non-index key
 | 
				
			||||||
	//      used to identify them. Order is preserved upon merge. The map tag
 | 
						//      used to identify them. Order is preserved upon merge. The map tag
 | 
				
			||||||
	//      must only be used on a list with struct elements.
 | 
						//      must only be used on a list with elements of type object.
 | 
				
			||||||
	XListType *string
 | 
						XListType *string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,3 +59,47 @@ func SetDefaults_ServiceReference(obj *ServiceReference) {
 | 
				
			|||||||
		obj.Port = utilpointer.Int32Ptr(443)
 | 
							obj.Port = utilpointer.Int32Ptr(443)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetDefaults_JSONSchemaProps sets the defaults for JSONSchemaProps
 | 
				
			||||||
 | 
					func SetDefaults_JSONSchemaProps(obj *JSONSchemaProps) {
 | 
				
			||||||
 | 
						if obj == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if obj.Type == "array" && obj.XListType == nil {
 | 
				
			||||||
 | 
							obj.XListType = utilpointer.StringPtr("atomic")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if obj.Items != nil {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(obj.Items.Schema)
 | 
				
			||||||
 | 
							defaultJSONSchemaPropsArray(obj.Items.JSONSchemas)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsArray(obj.AllOf)
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsArray(obj.OneOf)
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsArray(obj.AnyOf)
 | 
				
			||||||
 | 
						SetDefaults_JSONSchemaProps(obj.Not)
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsMap(obj.Properties)
 | 
				
			||||||
 | 
						if obj.AdditionalProperties != nil {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(obj.AdditionalProperties.Schema)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsMap(obj.PatternProperties)
 | 
				
			||||||
 | 
						for i := range obj.Dependencies {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(obj.Dependencies[i].Schema)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if obj.AdditionalItems != nil {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(obj.AdditionalItems.Schema)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsMap(map[string]JSONSchemaProps(obj.Definitions))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func defaultJSONSchemaPropsArray(obj []JSONSchemaProps) {
 | 
				
			||||||
 | 
						for i := range obj {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(&obj[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func defaultJSONSchemaPropsMap(obj map[string]JSONSchemaProps) {
 | 
				
			||||||
 | 
						for i := range obj {
 | 
				
			||||||
 | 
							props := obj[i]
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(&props)
 | 
				
			||||||
 | 
							obj[i] = props
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -124,6 +124,47 @@ func TestDefaults(t *testing.T) {
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "list type extension defaults",
 | 
				
			||||||
 | 
								original: &CustomResourceDefinition{
 | 
				
			||||||
 | 
									Spec: CustomResourceDefinitionSpec{
 | 
				
			||||||
 | 
										Scope:      NamespaceScoped,
 | 
				
			||||||
 | 
										Conversion: &CustomResourceConversion{Strategy: NoneConverter},
 | 
				
			||||||
 | 
										Versions: []CustomResourceDefinitionVersion{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												Name:    "v1",
 | 
				
			||||||
 | 
												Storage: true,
 | 
				
			||||||
 | 
												Schema: &CustomResourceValidation{
 | 
				
			||||||
 | 
													OpenAPIV3Schema: &JSONSchemaProps{
 | 
				
			||||||
 | 
														Type: "array",
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: &CustomResourceDefinition{
 | 
				
			||||||
 | 
									Spec: CustomResourceDefinitionSpec{
 | 
				
			||||||
 | 
										Scope:      NamespaceScoped,
 | 
				
			||||||
 | 
										Conversion: &CustomResourceConversion{Strategy: NoneConverter},
 | 
				
			||||||
 | 
										Versions: []CustomResourceDefinitionVersion{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												Name:    "v1",
 | 
				
			||||||
 | 
												Storage: true,
 | 
				
			||||||
 | 
												Schema: &CustomResourceValidation{
 | 
				
			||||||
 | 
													OpenAPIV3Schema: &JSONSchemaProps{
 | 
				
			||||||
 | 
														Type:      "array",
 | 
				
			||||||
 | 
														XListType: utilpointer.StringPtr("atomic"),
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Status: CustomResourceDefinitionStatus{
 | 
				
			||||||
 | 
										StoredVersions: []string{"v1"},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range tests {
 | 
						for _, test := range tests {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,27 +91,31 @@ type JSONSchemaProps struct {
 | 
				
			|||||||
	//    - ... zero or more
 | 
						//    - ... zero or more
 | 
				
			||||||
	XIntOrString bool `json:"x-kubernetes-int-or-string,omitempty" protobuf:"bytes,40,opt,name=xKubernetesIntOrString"`
 | 
						XIntOrString bool `json:"x-kubernetes-int-or-string,omitempty" protobuf:"bytes,40,opt,name=xKubernetesIntOrString"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// x-kubernetes-list-map-keys annotates lists with the x-kubernetes-list-type `map` by specifying the keys used
 | 
						// x-kubernetes-list-map-keys annotates an array with the x-kubernetes-list-type `map` by specifying the keys used
 | 
				
			||||||
	// as the index of the map.
 | 
						// as the index of the map.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// This tag MUST only be used on lists that have the "x-kubernetes-list-type"
 | 
						// This tag MUST only be used on lists that have the "x-kubernetes-list-type"
 | 
				
			||||||
	// extension set to "map". Also, the values specified for this attribute must
 | 
						// extension set to "map". Also, the values specified for this attribute must
 | 
				
			||||||
	// be a scalar typed field of the child structure (no nesting is supported).
 | 
						// be a scalar typed field of the child structure (no nesting is supported).
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// +optional
 | 
				
			||||||
	XListMapKeys []string `json:"x-kubernetes-list-map-keys,omitempty" protobuf:"bytes,41,rep,name=xKubernetesListMapKeys"`
 | 
						XListMapKeys []string `json:"x-kubernetes-list-map-keys,omitempty" protobuf:"bytes,41,rep,name=xKubernetesListMapKeys"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// x-kubernetes-list-type annotates a list to further describe its topology.
 | 
						// x-kubernetes-list-type annotates an array to further describe its topology.
 | 
				
			||||||
	// This extension must only be used on lists and may have 3 possible values:
 | 
						// This extension must only be used on lists and may have 3 possible values:
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// 1) `atomic`: the list is treated as a single entity, like a scalar.
 | 
						// 1) `atomic`: the list is treated as a single entity, like a scalar.
 | 
				
			||||||
	//      Atomic lists will be entirely replaced when updated. This extension
 | 
						//      Atomic lists will be entirely replaced when updated. This extension
 | 
				
			||||||
	//      may be used on any type of list (struct, scalar, ...).
 | 
						//      may be used on any type of list (struct, scalar, ...).
 | 
				
			||||||
	// 2) `set`:
 | 
						// 2) `set`:
 | 
				
			||||||
	//      Sets are lists that must not have multiple times the same value. Each
 | 
						//      Sets are lists that must not have multiple items with the same value. Each
 | 
				
			||||||
	//      value must be a scalar (or another atomic type).
 | 
						//      value must be a scalar (or another atomic type).
 | 
				
			||||||
	// 3) `map`:
 | 
						// 3) `map`:
 | 
				
			||||||
	//      These lists are like maps in that their elements have a non-index key
 | 
						//      These lists are like maps in that their elements have a non-index key
 | 
				
			||||||
	//      used to identify them. Order is preserved upon merge. The map tag
 | 
						//      used to identify them. Order is preserved upon merge. The map tag
 | 
				
			||||||
	//      must only be used on a list with struct elements.
 | 
						//      must only be used on a list with elements of type object.
 | 
				
			||||||
 | 
						// Defaults to atomic for arrays.
 | 
				
			||||||
 | 
						// +optional
 | 
				
			||||||
	XListType *string `json:"x-kubernetes-list-type,omitempty" protobuf:"bytes,42,opt,name=xKubernetesListType"`
 | 
						XListType *string `json:"x-kubernetes-list-type,omitempty" protobuf:"bytes,42,opt,name=xKubernetesListType"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,3 +80,47 @@ func SetDefaults_ServiceReference(obj *ServiceReference) {
 | 
				
			|||||||
		obj.Port = utilpointer.Int32Ptr(443)
 | 
							obj.Port = utilpointer.Int32Ptr(443)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetDefaults_JSONSchemaProps sets the defaults for JSONSchemaProps
 | 
				
			||||||
 | 
					func SetDefaults_JSONSchemaProps(obj *JSONSchemaProps) {
 | 
				
			||||||
 | 
						if obj == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if obj.Type == "array" && obj.XListType == nil {
 | 
				
			||||||
 | 
							obj.XListType = utilpointer.StringPtr("atomic")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if obj.Items != nil {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(obj.Items.Schema)
 | 
				
			||||||
 | 
							defaultJSONSchemaPropsArray(obj.Items.JSONSchemas)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsArray(obj.AllOf)
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsArray(obj.OneOf)
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsArray(obj.AnyOf)
 | 
				
			||||||
 | 
						SetDefaults_JSONSchemaProps(obj.Not)
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsMap(obj.Properties)
 | 
				
			||||||
 | 
						if obj.AdditionalProperties != nil {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(obj.AdditionalProperties.Schema)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsMap(obj.PatternProperties)
 | 
				
			||||||
 | 
						for i := range obj.Dependencies {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(obj.Dependencies[i].Schema)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if obj.AdditionalItems != nil {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(obj.AdditionalItems.Schema)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultJSONSchemaPropsMap(map[string]JSONSchemaProps(obj.Definitions))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func defaultJSONSchemaPropsArray(obj []JSONSchemaProps) {
 | 
				
			||||||
 | 
						for i := range obj {
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(&obj[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func defaultJSONSchemaPropsMap(obj map[string]JSONSchemaProps) {
 | 
				
			||||||
 | 
						for i := range obj {
 | 
				
			||||||
 | 
							props := obj[i]
 | 
				
			||||||
 | 
							SetDefaults_JSONSchemaProps(&props)
 | 
				
			||||||
 | 
							obj[i] = props
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,6 +129,34 @@ func TestDefaults(t *testing.T) {
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "list type extension defaults",
 | 
				
			||||||
 | 
								original: &CustomResourceDefinition{
 | 
				
			||||||
 | 
									Spec: CustomResourceDefinitionSpec{
 | 
				
			||||||
 | 
										Scope:                 NamespaceScoped,
 | 
				
			||||||
 | 
										Conversion:            &CustomResourceConversion{Strategy: NoneConverter},
 | 
				
			||||||
 | 
										PreserveUnknownFields: utilpointer.BoolPtr(true),
 | 
				
			||||||
 | 
										Validation: &CustomResourceValidation{
 | 
				
			||||||
 | 
											OpenAPIV3Schema: &JSONSchemaProps{
 | 
				
			||||||
 | 
												Type: "array",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: &CustomResourceDefinition{
 | 
				
			||||||
 | 
									Spec: CustomResourceDefinitionSpec{
 | 
				
			||||||
 | 
										Scope:                 NamespaceScoped,
 | 
				
			||||||
 | 
										Conversion:            &CustomResourceConversion{Strategy: NoneConverter},
 | 
				
			||||||
 | 
										PreserveUnknownFields: utilpointer.BoolPtr(true),
 | 
				
			||||||
 | 
										Validation: &CustomResourceValidation{
 | 
				
			||||||
 | 
											OpenAPIV3Schema: &JSONSchemaProps{
 | 
				
			||||||
 | 
												Type:      "array",
 | 
				
			||||||
 | 
												XListType: utilpointer.StringPtr("atomic"),
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range tests {
 | 
						for _, test := range tests {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,27 +91,31 @@ type JSONSchemaProps struct {
 | 
				
			|||||||
	//    - ... zero or more
 | 
						//    - ... zero or more
 | 
				
			||||||
	XIntOrString bool `json:"x-kubernetes-int-or-string,omitempty" protobuf:"bytes,40,opt,name=xKubernetesIntOrString"`
 | 
						XIntOrString bool `json:"x-kubernetes-int-or-string,omitempty" protobuf:"bytes,40,opt,name=xKubernetesIntOrString"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// x-kubernetes-list-map-keys annotates lists with the x-kubernetes-list-type `map` by specifying the keys used
 | 
						// x-kubernetes-list-map-keys annotates an array with the x-kubernetes-list-type `map` by specifying the keys used
 | 
				
			||||||
	// as the index of the map.
 | 
						// as the index of the map.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// This tag MUST only be used on lists that have the "x-kubernetes-list-type"
 | 
						// This tag MUST only be used on lists that have the "x-kubernetes-list-type"
 | 
				
			||||||
	// extension set to "map". Also, the values specified for this attribute must
 | 
						// extension set to "map". Also, the values specified for this attribute must
 | 
				
			||||||
	// be a scalar typed field of the child structure (no nesting is supported).
 | 
						// be a scalar typed field of the child structure (no nesting is supported).
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// +optional
 | 
				
			||||||
	XListMapKeys []string `json:"x-kubernetes-list-map-keys,omitempty" protobuf:"bytes,41,rep,name=xKubernetesListMapKeys"`
 | 
						XListMapKeys []string `json:"x-kubernetes-list-map-keys,omitempty" protobuf:"bytes,41,rep,name=xKubernetesListMapKeys"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// x-kubernetes-list-type annotates a list to further describe its topology.
 | 
						// x-kubernetes-list-type annotates an array to further describe its topology.
 | 
				
			||||||
	// This extension must only be used on lists and may have 3 possible values:
 | 
						// This extension must only be used on lists and may have 3 possible values:
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// 1) `atomic`: the list is treated as a single entity, like a scalar.
 | 
						// 1) `atomic`: the list is treated as a single entity, like a scalar.
 | 
				
			||||||
	//      Atomic lists will be entirely replaced when updated. This extension
 | 
						//      Atomic lists will be entirely replaced when updated. This extension
 | 
				
			||||||
	//      may be used on any type of list (struct, scalar, ...).
 | 
						//      may be used on any type of list (struct, scalar, ...).
 | 
				
			||||||
	// 2) `set`:
 | 
						// 2) `set`:
 | 
				
			||||||
	//      Sets are lists that must not have multiple times the same value. Each
 | 
						//      Sets are lists that must not have multiple items with the same value. Each
 | 
				
			||||||
	//      value must be a scalar (or another atomic type).
 | 
						//      value must be a scalar (or another atomic type).
 | 
				
			||||||
	// 3) `map`:
 | 
						// 3) `map`:
 | 
				
			||||||
	//      These lists are like maps in that their elements have a non-index key
 | 
						//      These lists are like maps in that their elements have a non-index key
 | 
				
			||||||
	//      used to identify them. Order is preserved upon merge. The map tag
 | 
						//      used to identify them. Order is preserved upon merge. The map tag
 | 
				
			||||||
	//      must only be used on a list with struct elements.
 | 
						//      must only be used on a list with elements of type object.
 | 
				
			||||||
 | 
						// Defaults to atomic for arrays.
 | 
				
			||||||
 | 
						// +optional
 | 
				
			||||||
	XListType *string `json:"x-kubernetes-list-type,omitempty" protobuf:"bytes,42,opt,name=xKubernetesListType"`
 | 
						XListType *string `json:"x-kubernetes-list-type,omitempty" protobuf:"bytes,42,opt,name=xKubernetesListType"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -796,8 +796,12 @@ func ValidateCustomResourceDefinitionOpenAPISchema(schema *apiextensions.JSONSch
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if schema.XListType == nil && schema.Type == "array" {
 | 
				
			||||||
 | 
							allErrs = append(allErrs, field.Required(fldPath.Child("x-kubernetes-list-type"), "must be set if type is array"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if schema.XListType != nil && *schema.XListType != "atomic" && *schema.XListType != "set" && *schema.XListType != "map" {
 | 
						if schema.XListType != nil && *schema.XListType != "atomic" && *schema.XListType != "set" && *schema.XListType != "map" {
 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("x-kubernetes-list-type"), *schema.XListType, "must be one of 'atomic', 'set', 'map', or unset"))
 | 
							allErrs = append(allErrs, field.NotSupported(fldPath.Child("x-kubernetes-list-type"), *schema.XListType, []string{"atomic", "set", "map"}))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(schema.XListMapKeys) > 0 && (schema.XListType == nil || *schema.XListType != "map") {
 | 
						if len(schema.XListMapKeys) > 0 && (schema.XListType == nil || *schema.XListType != "map") {
 | 
				
			||||||
@@ -808,6 +812,35 @@ func ValidateCustomResourceDefinitionOpenAPISchema(schema *apiextensions.JSONSch
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(schema.XListMapKeys) == 0 && schema.XListType != nil && *schema.XListType == "map" {
 | 
				
			||||||
 | 
							allErrs = append(allErrs, field.Required(fldPath.Child("x-kubernetes-list-map-keys"), "must not be empty if x-kubernetes-list-type is map"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if schema.Items != nil && schema.Items.Schema == nil && schema.XListType != nil && *schema.XListType == "map" {
 | 
				
			||||||
 | 
							allErrs = append(allErrs, field.Invalid(fldPath.Child("items"), schema.Items, "must only have a single schema if x-kubernetes-list-type is map"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if schema.Items != nil && schema.Items.Schema != nil && schema.Items.Schema.Type != "object" && schema.XListType != nil && *schema.XListType == "map" {
 | 
				
			||||||
 | 
							allErrs = append(allErrs, field.Invalid(fldPath.Child("items").Child("type"), schema.Items.Schema.Type, "must be object if parent array's x-kubernetes-list-type is map"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if schema.Items != nil && schema.Items.Schema != nil && schema.Items.Schema.Type == "object" && schema.XListType != nil && *schema.XListType == "map" {
 | 
				
			||||||
 | 
							keys := map[string]struct{}{}
 | 
				
			||||||
 | 
							for _, k := range schema.XListMapKeys {
 | 
				
			||||||
 | 
								if s, ok := schema.Items.Schema.Properties[k]; ok {
 | 
				
			||||||
 | 
									if s.Type == "array" || s.Type == "object" {
 | 
				
			||||||
 | 
										allErrs = append(allErrs, field.Invalid(fldPath.Child("items").Child("properties").Child(k).Child("type"), schema.Items.Schema.Type, "must be a scalar type if parent array's x-kubernetes-list-type is map"))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(fldPath.Child("x-kubernetes-list-map-keys"), schema.XListMapKeys, "entries must all be names of item properties"))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if _, ok := keys[k]; ok {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(fldPath.Child("x-kubernetes-list-map-keys"), schema.XListMapKeys, "must not contain duplicate entries"))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								keys[k] = struct{}{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6510,6 +6510,9 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
 | 
				
			|||||||
						"array": {
 | 
											"array": {
 | 
				
			||||||
							Type:     "array",
 | 
												Type:     "array",
 | 
				
			||||||
							Nullable: true,
 | 
												Nullable: true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												// This value is defaulted
 | 
				
			||||||
 | 
												XListType: strPtr("atomic"),
 | 
				
			||||||
						},
 | 
											},
 | 
				
			||||||
						"number": {
 | 
											"number": {
 | 
				
			||||||
							Type:     "number",
 | 
												Type:     "number",
 | 
				
			||||||
@@ -6577,7 +6580,7 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
 | 
				
			|||||||
			input: apiextensions.CustomResourceValidation{
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
				OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
					Type:      "object",
 | 
										Type:      "object",
 | 
				
			||||||
					XListType: strPtr("map"),
 | 
										XListType: strPtr("set"),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			wantError: true,
 | 
								wantError: true,
 | 
				
			||||||
@@ -6586,13 +6589,32 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
 | 
				
			|||||||
			name: "unset type with list type extension set",
 | 
								name: "unset type with list type extension set",
 | 
				
			||||||
			input: apiextensions.CustomResourceValidation{
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
				OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
					XListType: strPtr("map"),
 | 
										XListType: strPtr("set"),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			wantError: true,
 | 
								wantError: true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "invalid list type extension with list map keys extension set",
 | 
								name: "unset list type extension with type array",
 | 
				
			||||||
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
										Type: "array",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								wantError: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid list type extension",
 | 
				
			||||||
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
										Type:      "array",
 | 
				
			||||||
 | 
										XListType: strPtr("invalid"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								wantError: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid list type extension with list map keys extension non-empty",
 | 
				
			||||||
			input: apiextensions.CustomResourceValidation{
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
				OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
					Type:         "array",
 | 
										Type:         "array",
 | 
				
			||||||
@@ -6603,25 +6625,142 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
 | 
				
			|||||||
			wantError: true,
 | 
								wantError: true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "unset list type extension with list map keys extension set",
 | 
								name: "unset list type extension with list map keys extension non-empty",
 | 
				
			||||||
			input: apiextensions.CustomResourceValidation{
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
				OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
					Type:         "array",
 | 
					 | 
				
			||||||
					XListMapKeys: []string{"key"},
 | 
										XListMapKeys: []string{"key"},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			wantError: true,
 | 
								wantError: true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "invalid list type",
 | 
								name: "empty list map keys extension with list type extension map",
 | 
				
			||||||
			input: apiextensions.CustomResourceValidation{
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
				OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
					Type:      "array",
 | 
										Type:      "array",
 | 
				
			||||||
					XListType: strPtr("invalid"),
 | 
										XListType: strPtr("map"),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			wantError: true,
 | 
								wantError: true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "multiple schema items with list type extension map",
 | 
				
			||||||
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
										Type:         "array",
 | 
				
			||||||
 | 
										XListType:    strPtr("map"),
 | 
				
			||||||
 | 
										XListMapKeys: []string{"key"},
 | 
				
			||||||
 | 
										Items: &apiextensions.JSONSchemaPropsOrArray{
 | 
				
			||||||
 | 
											JSONSchemas: []apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													Type: "string",
 | 
				
			||||||
 | 
												}, {
 | 
				
			||||||
 | 
													Type: "integer",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								wantError: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "non object item with list type extension map",
 | 
				
			||||||
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
										Type:         "array",
 | 
				
			||||||
 | 
										XListType:    strPtr("map"),
 | 
				
			||||||
 | 
										XListMapKeys: []string{"key"},
 | 
				
			||||||
 | 
										Items: &apiextensions.JSONSchemaPropsOrArray{
 | 
				
			||||||
 | 
											Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
												Type: "string",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								wantError: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "items with key missing from properties with list type extension map",
 | 
				
			||||||
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
										Type:         "array",
 | 
				
			||||||
 | 
										XListType:    strPtr("map"),
 | 
				
			||||||
 | 
										XListMapKeys: []string{"key"},
 | 
				
			||||||
 | 
										Items: &apiextensions.JSONSchemaPropsOrArray{
 | 
				
			||||||
 | 
											Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
												Type: "object",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								wantError: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "items with non scalar key property type with list type extension map",
 | 
				
			||||||
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
										Type:         "array",
 | 
				
			||||||
 | 
										XListType:    strPtr("map"),
 | 
				
			||||||
 | 
										XListMapKeys: []string{"key"},
 | 
				
			||||||
 | 
										Items: &apiextensions.JSONSchemaPropsOrArray{
 | 
				
			||||||
 | 
											Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
												Type: "object",
 | 
				
			||||||
 | 
												Properties: map[string]apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
													"key": {
 | 
				
			||||||
 | 
														Type: "object",
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								wantError: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "duplicate map keys with list type extension map",
 | 
				
			||||||
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
										Type:         "array",
 | 
				
			||||||
 | 
										XListType:    strPtr("map"),
 | 
				
			||||||
 | 
										XListMapKeys: []string{"key", "key"},
 | 
				
			||||||
 | 
										Items: &apiextensions.JSONSchemaPropsOrArray{
 | 
				
			||||||
 | 
											Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
												Type: "object",
 | 
				
			||||||
 | 
												Properties: map[string]apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
													"key": {
 | 
				
			||||||
 | 
														Type: "string",
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								wantError: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "allowed schema with list type extension map",
 | 
				
			||||||
 | 
								input: apiextensions.CustomResourceValidation{
 | 
				
			||||||
 | 
									OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
										Type:         "array",
 | 
				
			||||||
 | 
										XListType:    strPtr("map"),
 | 
				
			||||||
 | 
										XListMapKeys: []string{"keyA", "keyB"},
 | 
				
			||||||
 | 
										Items: &apiextensions.JSONSchemaPropsOrArray{
 | 
				
			||||||
 | 
											Schema: &apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
												Type: "object",
 | 
				
			||||||
 | 
												Properties: map[string]apiextensions.JSONSchemaProps{
 | 
				
			||||||
 | 
													"keyA": {
 | 
				
			||||||
 | 
														Type: "string",
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
													"keyB": {
 | 
				
			||||||
 | 
														Type: "integer",
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								wantError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tt := range tests {
 | 
						for _, tt := range tests {
 | 
				
			||||||
		t.Run(tt.name, func(t *testing.T) {
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -655,7 +655,11 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			specs = append(specs, s)
 | 
								specs = append(specs, s)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		mergedOpenAPI := builder.MergeSpecs(r.staticOpenAPISpec, specs...)
 | 
							mergedOpenAPI, err := builder.MergeSpecs(r.staticOpenAPISpec, specs...)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								utilruntime.HandleError(err)
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("the server could not properly merge the CR schema")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		openAPIModels, err = utilopenapi.ToProtoModels(mergedOpenAPI)
 | 
							openAPIModels, err = utilopenapi.ToProtoModels(mergedOpenAPI)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			utilruntime.HandleError(err)
 | 
								utilruntime.HandleError(err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,7 +82,7 @@ func (x *Extensions) toGoOpenAPI(ret *spec.Schema) {
 | 
				
			|||||||
		ret.VendorExtensible.AddExtension("x-kubernetes-list-map-keys", x.XListMapKeys)
 | 
							ret.VendorExtensible.AddExtension("x-kubernetes-list-map-keys", x.XListMapKeys)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if x.XListType != nil {
 | 
						if x.XListType != nil {
 | 
				
			||||||
		ret.VendorExtensible.AddExtension("x-kubernetes-list-type", x.XListType)
 | 
							ret.VendorExtensible.AddExtension("x-kubernetes-list-type", *x.XListType)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -108,12 +108,12 @@ type Extensions struct {
 | 
				
			|||||||
	//      Atomic lists will be entirely replaced when updated. This extension
 | 
						//      Atomic lists will be entirely replaced when updated. This extension
 | 
				
			||||||
	//      may be used on any type of list (struct, scalar, ...).
 | 
						//      may be used on any type of list (struct, scalar, ...).
 | 
				
			||||||
	// 2) `set`:
 | 
						// 2) `set`:
 | 
				
			||||||
	//      Sets are lists that must not have multiple times the same value. Each
 | 
						//      Sets are lists that must not have multiple items with the same value. Each
 | 
				
			||||||
	//      value must be a scalar (or another atomic type).
 | 
						//      value must be a scalar (or another atomic type).
 | 
				
			||||||
	// 3) `map`:
 | 
						// 3) `map`:
 | 
				
			||||||
	//      These lists are like maps in that their elements have a non-index key
 | 
						//      These lists are like maps in that their elements have a non-index key
 | 
				
			||||||
	//      used to identify them. Order is preserved upon merge. The map tag
 | 
						//      used to identify them. Order is preserved upon merge. The map tag
 | 
				
			||||||
	//      must only be used on a list with struct elements.
 | 
						//      must only be used on a list with elements of type object.
 | 
				
			||||||
	XListType *string
 | 
						XListType *string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -250,7 +250,7 @@ func ConvertJSONSchemaPropsWithPostProcess(in *apiextensions.JSONSchemaProps, ou
 | 
				
			|||||||
		out.VendorExtensible.AddExtension("x-kubernetes-list-map-keys", in.XListMapKeys)
 | 
							out.VendorExtensible.AddExtension("x-kubernetes-list-map-keys", in.XListMapKeys)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if in.XListType != nil {
 | 
						if in.XListType != nil {
 | 
				
			||||||
		out.VendorExtensible.AddExtension("x-kubernetes-list-type", in.XListType)
 | 
							out.VendorExtensible.AddExtension("x-kubernetes-list-type", *in.XListType)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,7 @@ import (
 | 
				
			|||||||
	apiequality "k8s.io/apimachinery/pkg/api/equality"
 | 
						apiequality "k8s.io/apimachinery/pkg/api/equality"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
 | 
						utildiff "k8s.io/apimachinery/pkg/util/diff"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/wait"
 | 
						"k8s.io/apimachinery/pkg/util/wait"
 | 
				
			||||||
	k8sclientset "k8s.io/client-go/kubernetes"
 | 
						k8sclientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
	"k8s.io/client-go/rest"
 | 
						"k8s.io/client-go/rest"
 | 
				
			||||||
@@ -507,7 +508,8 @@ func waitForDefinition(c k8sclientset.Interface, name string, schema []byte) err
 | 
				
			|||||||
			// drop properties and extension that we added
 | 
								// drop properties and extension that we added
 | 
				
			||||||
			dropDefaults(&d)
 | 
								dropDefaults(&d)
 | 
				
			||||||
			if !apiequality.Semantic.DeepEqual(expect, d) {
 | 
								if !apiequality.Semantic.DeepEqual(expect, d) {
 | 
				
			||||||
				return false, fmt.Sprintf("spec.SwaggerProps.Definitions[\"%s\"] not match; expect: %v, actual: %v", name, expect, d)
 | 
									diff := utildiff.ObjectGoPrintSideBySide(expect, d)
 | 
				
			||||||
 | 
									return false, fmt.Sprintf("spec.SwaggerProps.Definitions[\"%s\"] not match; expect: %v, actual: %v\n%v", name, expect, d, diff)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return true, ""
 | 
							return true, ""
 | 
				
			||||||
@@ -627,6 +629,7 @@ properties:
 | 
				
			|||||||
      bars:
 | 
					      bars:
 | 
				
			||||||
        description: List of Bars and their specs.
 | 
					        description: List of Bars and their specs.
 | 
				
			||||||
        type: array
 | 
					        type: array
 | 
				
			||||||
 | 
					        x-kubernetes-list-type: atomic
 | 
				
			||||||
        items:
 | 
					        items:
 | 
				
			||||||
          type: object
 | 
					          type: object
 | 
				
			||||||
          required:
 | 
					          required:
 | 
				
			||||||
@@ -643,6 +646,7 @@ properties:
 | 
				
			|||||||
              items:
 | 
					              items:
 | 
				
			||||||
                type: string
 | 
					                type: string
 | 
				
			||||||
              type: array
 | 
					              type: array
 | 
				
			||||||
 | 
					              x-kubernetes-list-type: atomic
 | 
				
			||||||
  status:
 | 
					  status:
 | 
				
			||||||
    description: Status of Foo
 | 
					    description: Status of Foo
 | 
				
			||||||
    type: object
 | 
					    type: object
 | 
				
			||||||
@@ -650,6 +654,7 @@ properties:
 | 
				
			|||||||
      bars:
 | 
					      bars:
 | 
				
			||||||
        description: List of Bars and their statuses.
 | 
					        description: List of Bars and their statuses.
 | 
				
			||||||
        type: array
 | 
					        type: array
 | 
				
			||||||
 | 
					        x-kubernetes-list-type: atomic
 | 
				
			||||||
        items:
 | 
					        items:
 | 
				
			||||||
          type: object
 | 
					          type: object
 | 
				
			||||||
          properties:
 | 
					          properties:
 | 
				
			||||||
@@ -681,6 +686,7 @@ properties:
 | 
				
			|||||||
      bars:
 | 
					      bars:
 | 
				
			||||||
        description: List of Bars and their statuses.
 | 
					        description: List of Bars and their statuses.
 | 
				
			||||||
        type: array
 | 
					        type: array
 | 
				
			||||||
 | 
					        x-kubernetes-list-type: atomic
 | 
				
			||||||
        items:
 | 
					        items:
 | 
				
			||||||
          type: object`)
 | 
					          type: object`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -702,6 +708,7 @@ properties:
 | 
				
			|||||||
      bars:
 | 
					      bars:
 | 
				
			||||||
        description: List of Bars and their statuses.
 | 
					        description: List of Bars and their statuses.
 | 
				
			||||||
        type: array
 | 
					        type: array
 | 
				
			||||||
 | 
					        x-kubernetes-list-type: atomic
 | 
				
			||||||
        items:
 | 
					        items:
 | 
				
			||||||
          type: object`)
 | 
					          type: object`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -723,5 +730,6 @@ properties:
 | 
				
			|||||||
      bars:
 | 
					      bars:
 | 
				
			||||||
        description: List of Bars and their statuses.
 | 
					        description: List of Bars and their statuses.
 | 
				
			||||||
        type: array
 | 
					        type: array
 | 
				
			||||||
 | 
					        x-kubernetes-list-type: atomic
 | 
				
			||||||
        items:
 | 
					        items:
 | 
				
			||||||
          type: object`)
 | 
					          type: object`)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,12 +19,14 @@ package apiserver
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 | 
						apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 | 
				
			||||||
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
 | 
						"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
 | 
				
			||||||
	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
 | 
						"k8s.io/apiextensions-apiserver/test/integration/fixtures"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/errors"
 | 
						"k8s.io/apimachinery/pkg/api/errors"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
	genericfeatures "k8s.io/apiserver/pkg/features"
 | 
						genericfeatures "k8s.io/apiserver/pkg/features"
 | 
				
			||||||
	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
						utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
				
			||||||
@@ -83,6 +85,7 @@ spec:
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyReplicas(t, result, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Patch object to change the number of replicas
 | 
						// Patch object to change the number of replicas
 | 
				
			||||||
	result, err = rest.Patch(types.MergePatchType).
 | 
						result, err = rest.Patch(types.MergePatchType).
 | 
				
			||||||
@@ -93,6 +96,7 @@ spec:
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyReplicas(t, result, 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Re-apply, we should get conflicts now, since the number of replicas was changed.
 | 
						// Re-apply, we should get conflicts now, since the number of replicas was changed.
 | 
				
			||||||
	result, err = rest.Patch(types.ApplyPatchType).
 | 
						result, err = rest.Patch(types.ApplyPatchType).
 | 
				
			||||||
@@ -108,8 +112,8 @@ spec:
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		t.Fatalf("Expecting to get conflicts as API error")
 | 
							t.Fatalf("Expecting to get conflicts as API error")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(status.Status().Details.Causes) < 1 {
 | 
						if len(status.Status().Details.Causes) != 1 {
 | 
				
			||||||
		t.Fatalf("Expecting to get at least one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
 | 
							t.Fatalf("Expecting to get one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Re-apply with force, should work fine.
 | 
						// Re-apply with force, should work fine.
 | 
				
			||||||
@@ -123,6 +127,7 @@ spec:
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyReplicas(t, result, 1)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TestApplyCRDStructuralSchema tests that when a CRD has a structural schema in its validation field,
 | 
					// TestApplyCRDStructuralSchema tests that when a CRD has a structural schema in its validation field,
 | 
				
			||||||
@@ -239,27 +244,38 @@ spec:
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyNumFinalizers(t, result, 1)
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "test-finalizer")
 | 
				
			||||||
 | 
						verifyReplicas(t, result, 1)
 | 
				
			||||||
 | 
						verifyNumPorts(t, result, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Patch object to add another finalizer to the finalizers list
 | 
						// Patch object to add another finalizer to the finalizers list
 | 
				
			||||||
	result, err = rest.Patch(types.MergePatchType).
 | 
						result, err = rest.Patch(types.MergePatchType).
 | 
				
			||||||
		AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
 | 
							AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
 | 
				
			||||||
		Name(name).
 | 
							Name(name).
 | 
				
			||||||
		Body([]byte(`{"metadata":{"finalizers":["another-one"]}}`)).
 | 
							Body([]byte(`{"metadata":{"finalizers":["test-finalizer","another-one"]}}`)).
 | 
				
			||||||
		DoRaw()
 | 
							DoRaw()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to add finalizer with merge patch: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to add finalizer with merge patch: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyNumFinalizers(t, result, 2)
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "test-finalizer")
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "another-one")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Re-apply the same config, should work fine, since finalizers should have the list-type extension 'set'.
 | 
						// Re-apply the same config, should work fine, since finalizers should have the list-type extension 'set'.
 | 
				
			||||||
	result, err = rest.Patch(types.ApplyPatchType).
 | 
						result, err = rest.Patch(types.ApplyPatchType).
 | 
				
			||||||
		AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
 | 
							AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
 | 
				
			||||||
		Name(name).
 | 
							Name(name).
 | 
				
			||||||
		Param("fieldManager", "apply_test").
 | 
							Param("fieldManager", "apply_test").
 | 
				
			||||||
 | 
							SetHeader("Accept", "application/json").
 | 
				
			||||||
		Body(yamlBody).
 | 
							Body(yamlBody).
 | 
				
			||||||
		DoRaw()
 | 
							DoRaw()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to apply same config after adding a finalizer: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to apply same config after adding a finalizer: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyNumFinalizers(t, result, 2)
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "test-finalizer")
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "another-one")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Patch object to change the number of replicas
 | 
						// Patch object to change the number of replicas
 | 
				
			||||||
	result, err = rest.Patch(types.MergePatchType).
 | 
						result, err = rest.Patch(types.MergePatchType).
 | 
				
			||||||
@@ -270,6 +286,7 @@ spec:
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyReplicas(t, result, 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Re-apply, we should get conflicts now, since the number of replicas was changed.
 | 
						// Re-apply, we should get conflicts now, since the number of replicas was changed.
 | 
				
			||||||
	result, err = rest.Patch(types.ApplyPatchType).
 | 
						result, err = rest.Patch(types.ApplyPatchType).
 | 
				
			||||||
@@ -285,8 +302,8 @@ spec:
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		t.Fatalf("Expecting to get conflicts as API error")
 | 
							t.Fatalf("Expecting to get conflicts as API error")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(status.Status().Details.Causes) < 1 {
 | 
						if len(status.Status().Details.Causes) != 1 {
 | 
				
			||||||
		t.Fatalf("Expecting to get at least one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
 | 
							t.Fatalf("Expecting to get one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Re-apply with force, should work fine.
 | 
						// Re-apply with force, should work fine.
 | 
				
			||||||
@@ -300,6 +317,7 @@ spec:
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyReplicas(t, result, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// New applier tries to edit an existing list item, we should get conflicts.
 | 
						// New applier tries to edit an existing list item, we should get conflicts.
 | 
				
			||||||
	result, err = rest.Patch(types.ApplyPatchType).
 | 
						result, err = rest.Patch(types.ApplyPatchType).
 | 
				
			||||||
@@ -324,8 +342,8 @@ spec:
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		t.Fatalf("Expecting to get conflicts as API error")
 | 
							t.Fatalf("Expecting to get conflicts as API error")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(status.Status().Details.Causes) < 1 {
 | 
						if len(status.Status().Details.Causes) != 1 {
 | 
				
			||||||
		t.Fatalf("Expecting to get at least one conflict when a different applier updates existing list item, got: %v", status.Status().Details.Causes)
 | 
							t.Fatalf("Expecting to get one conflict when a different applier updates existing list item, got: %v", status.Status().Details.Causes)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// New applier tries to add a new list item, should work fine.
 | 
						// New applier tries to add a new list item, should work fine.
 | 
				
			||||||
@@ -343,10 +361,12 @@ spec:
 | 
				
			|||||||
  - name: "y"
 | 
					  - name: "y"
 | 
				
			||||||
    containerPort: 8080
 | 
					    containerPort: 8080
 | 
				
			||||||
    protocol: TCP`, apiVersion, kind, name))).
 | 
					    protocol: TCP`, apiVersion, kind, name))).
 | 
				
			||||||
 | 
							SetHeader("Accept", "application/json").
 | 
				
			||||||
		DoRaw()
 | 
							DoRaw()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to add a new list item to the object as a different applier: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to add a new list item to the object as a different applier: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyNumPorts(t, result, 2)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TestApplyCRDNonStructuralSchema tests that when a CRD has a non-structural schema in its validation field,
 | 
					// TestApplyCRDNonStructuralSchema tests that when a CRD has a non-structural schema in its validation field,
 | 
				
			||||||
@@ -430,27 +450,37 @@ spec:
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyNumFinalizers(t, result, 1)
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "test-finalizer")
 | 
				
			||||||
 | 
						verifyReplicas(t, result, 1.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Patch object to add another finalizer to the finalizers list
 | 
						// Patch object to add another finalizer to the finalizers list
 | 
				
			||||||
	result, err = rest.Patch(types.MergePatchType).
 | 
						result, err = rest.Patch(types.MergePatchType).
 | 
				
			||||||
		AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
 | 
							AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
 | 
				
			||||||
		Name(name).
 | 
							Name(name).
 | 
				
			||||||
		Body([]byte(`{"metadata":{"finalizers":["another-one"]}}`)).
 | 
							Body([]byte(`{"metadata":{"finalizers":["test-finalizer","another-one"]}}`)).
 | 
				
			||||||
		DoRaw()
 | 
							DoRaw()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to add finalizer with merge patch: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to add finalizer with merge patch: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyNumFinalizers(t, result, 2)
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "test-finalizer")
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "another-one")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Re-apply the same config, should work fine, since finalizers should have the list-type extension 'set'.
 | 
						// Re-apply the same config, should work fine, since finalizers should have the list-type extension 'set'.
 | 
				
			||||||
	result, err = rest.Patch(types.ApplyPatchType).
 | 
						result, err = rest.Patch(types.ApplyPatchType).
 | 
				
			||||||
		AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
 | 
							AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Version, noxuDefinition.Spec.Names.Plural).
 | 
				
			||||||
		Name(name).
 | 
							Name(name).
 | 
				
			||||||
		Param("fieldManager", "apply_test").
 | 
							Param("fieldManager", "apply_test").
 | 
				
			||||||
 | 
							SetHeader("Accept", "application/json").
 | 
				
			||||||
		Body(yamlBody).
 | 
							Body(yamlBody).
 | 
				
			||||||
		DoRaw()
 | 
							DoRaw()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to apply same config after adding a finalizer: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to apply same config after adding a finalizer: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyNumFinalizers(t, result, 2)
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "test-finalizer")
 | 
				
			||||||
 | 
						verifyFinalizersIncludes(t, result, "another-one")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Patch object to change the number of replicas
 | 
						// Patch object to change the number of replicas
 | 
				
			||||||
	result, err = rest.Patch(types.MergePatchType).
 | 
						result, err = rest.Patch(types.MergePatchType).
 | 
				
			||||||
@@ -461,6 +491,7 @@ spec:
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyReplicas(t, result, 5.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Re-apply, we should get conflicts now, since the number of replicas was changed.
 | 
						// Re-apply, we should get conflicts now, since the number of replicas was changed.
 | 
				
			||||||
	result, err = rest.Patch(types.ApplyPatchType).
 | 
						result, err = rest.Patch(types.ApplyPatchType).
 | 
				
			||||||
@@ -476,8 +507,8 @@ spec:
 | 
				
			|||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		t.Fatalf("Expecting to get conflicts as API error")
 | 
							t.Fatalf("Expecting to get conflicts as API error")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(status.Status().Details.Causes) < 1 {
 | 
						if len(status.Status().Details.Causes) != 1 {
 | 
				
			||||||
		t.Fatalf("Expecting to get at least one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
 | 
							t.Fatalf("Expecting to get one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Re-apply with force, should work fine.
 | 
						// Re-apply with force, should work fine.
 | 
				
			||||||
@@ -491,4 +522,88 @@ spec:
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
 | 
							t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						verifyReplicas(t, result, 1.0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// verifyNumFinalizers checks that len(.metadata.finalizers) == n
 | 
				
			||||||
 | 
					func verifyNumFinalizers(t *testing.T, b []byte, n int) {
 | 
				
			||||||
 | 
						obj := unstructured.Unstructured{}
 | 
				
			||||||
 | 
						err := obj.UnmarshalJSON(b)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to unmarshal response: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if actual, expected := len(obj.GetFinalizers()), n; actual != expected {
 | 
				
			||||||
 | 
							t.Fatalf("expected %v finalizers but got %v:\n%v", expected, actual, string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// verifyFinalizersIncludes checks that .metadata.finalizers includes e
 | 
				
			||||||
 | 
					func verifyFinalizersIncludes(t *testing.T, b []byte, e string) {
 | 
				
			||||||
 | 
						obj := unstructured.Unstructured{}
 | 
				
			||||||
 | 
						err := obj.UnmarshalJSON(b)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to unmarshal response: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, a := range obj.GetFinalizers() {
 | 
				
			||||||
 | 
							if a == e {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.Fatalf("expected finalizers to include %q but got: %v", e, obj.GetFinalizers())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// verifyReplicas checks that .spec.replicas == r
 | 
				
			||||||
 | 
					func verifyReplicas(t *testing.T, b []byte, r int) {
 | 
				
			||||||
 | 
						obj := unstructured.Unstructured{}
 | 
				
			||||||
 | 
						err := obj.UnmarshalJSON(b)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find replicas number in response: %v:\n%v", err, string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						spec, ok := obj.Object["spec"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find replicas number in response:\n%v", string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						specMap, ok := spec.(map[string]interface{})
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find replicas number in response:\n%v", string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						replicas, ok := specMap["replicas"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find replicas number in response:\n%v", string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						replicasNumber, ok := replicas.(int64)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find replicas number in response: expected int64 but got: %v", reflect.TypeOf(replicas))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if actual, expected := replicasNumber, int64(r); actual != expected {
 | 
				
			||||||
 | 
							t.Fatalf("expected %v ports but got %v:\n%v", expected, actual, string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// verifyNumPorts checks that len(.spec.ports) == n
 | 
				
			||||||
 | 
					func verifyNumPorts(t *testing.T, b []byte, n int) {
 | 
				
			||||||
 | 
						obj := unstructured.Unstructured{}
 | 
				
			||||||
 | 
						err := obj.UnmarshalJSON(b)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find ports list in response: %v:\n%v", err, string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						spec, ok := obj.Object["spec"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find ports list in response:\n%v", string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						specMap, ok := spec.(map[string]interface{})
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find ports list in response:\n%v", string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ports, ok := specMap["ports"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find ports list in response:\n%v", string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						portsList, ok := ports.([]interface{})
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find ports list in response: expected array but got: %v", reflect.TypeOf(ports))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if actual, expected := len(portsList), n; actual != expected {
 | 
				
			||||||
 | 
							t.Fatalf("expected %v ports but got %v:\n%v", expected, actual, string(b))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user