diff --git a/PROJECT b/PROJECT index 91986d824..2251fee9d 100644 --- a/PROJECT +++ b/PROJECT @@ -10,8 +10,8 @@ resources: domain: kubevirt.io group: ssp kind: SSP - path: kubevirt.io/ssp-operator/api/v1beta2 - version: v1beta2 + path: kubevirt.io/ssp-operator/api/v1beta3 + version: v1beta3 webhooks: # conversion: true # defaulting: true @@ -24,8 +24,8 @@ resources: domain: kubevirt.io group: ssp kind: SSP - path: kubevirt.io/ssp-operator/api/v1beta1 - version: v1beta1 + path: kubevirt.io/ssp-operator/api/v1beta2 + version: v1beta2 webhooks: # conversion: true # defaulting: true diff --git a/api/v1beta1/groupversion_info.go b/api/v1beta3/groupversion_info.go similarity index 90% rename from api/v1beta1/groupversion_info.go rename to api/v1beta3/groupversion_info.go index 6a35a63df..131ba91cd 100644 --- a/api/v1beta1/groupversion_info.go +++ b/api/v1beta3/groupversion_info.go @@ -14,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1beta1 contains API Schema definitions for the ssp v1beta1 API group +// Package v1beta3 contains API Schema definitions for the ssp v1beta3 API group // +kubebuilder:object:generate=true // +groupName=ssp.kubevirt.io -package v1beta1 +package v1beta3 import ( "k8s.io/apimachinery/pkg/runtime/schema" @@ -26,7 +26,7 @@ import ( var ( // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "ssp.kubevirt.io", Version: "v1beta1"} + GroupVersion = schema.GroupVersion{Group: "ssp.kubevirt.io", Version: "v1beta3"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/vendor/kubevirt.io/ssp-operator/api/v1beta1/ssp_types.go b/api/v1beta3/ssp_types.go similarity index 60% rename from vendor/kubevirt.io/ssp-operator/api/v1beta1/ssp_types.go rename to api/v1beta3/ssp_types.go index 1f962e17d..c2af6b9bb 100644 --- a/vendor/kubevirt.io/ssp-operator/api/v1beta1/ssp_types.go +++ b/api/v1beta3/ssp_types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1 +package v1beta3 import ( ocpv1 "github.com/openshift/api/config/v1" @@ -43,38 +43,10 @@ type CommonTemplates struct { //+kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ Namespace string `json:"namespace"` - // DataImportCronTemplates defines a list of DataImportCrons managed by the SSP - // Operator. This is intended for images used by CommonTemplates. + // DataImportCronTemplates defines a list of DataImportCrons managed by the SSP Operator. DataImportCronTemplates []DataImportCronTemplate `json:"dataImportCronTemplates,omitempty"` } -type NodeLabeller struct { - // Placement describes the node scheduling configuration - Placement *lifecycleapi.NodePlacement `json:"placement,omitempty"` -} - -type CommonInstancetypes struct { - // URL of a remote Kustomize target from which to generate and deploy resources. - // - // The following caveats apply to the provided URL: - // - // * Only 'https://' and 'git://' URLs are supported. - // - // * The URL must include '?ref=$ref' or '?version=$ref' pinning it to a specific - // reference. It is recommended that the reference be a specific commit or tag - // to ensure the generated contents does not change over time. As such it is - // recommended not to use branches as the ref for the time being. - // - // * Only VirtualMachineClusterPreference and VirtualMachineClusterInstancetype - // resources generated from the URL are deployed by the operand. - // - // See the following Kustomize documentation for more details: - // - // remote targets - // https://github.com/kubernetes-sigs/kustomize/blob/master/examples/remoteBuild.md - URL *string `json:"url,omitempty"` -} - // SSPSpec defines the desired state of SSP type SSPSpec struct { // TemplateValidator is configuration of the template validator operand @@ -83,38 +55,11 @@ type SSPSpec struct { // CommonTemplates is the configuration of the common templates operand CommonTemplates CommonTemplates `json:"commonTemplates"` - // NodeLabeller is configuration of the node-labeller operand - NodeLabeller *NodeLabeller `json:"nodeLabeller,omitempty"` - // TLSSecurityProfile is a configuration for the TLS. TLSSecurityProfile *ocpv1.TLSSecurityProfile `json:"tlsSecurityProfile,omitempty"` - // CommonInstancetypes is the configuration of the common-instancetypes operand - CommonInstancetypes *CommonInstancetypes `json:"commonInstancetypes,omitempty"` - - // TektonPipelines is the configuration of the tekton-pipelines operand - TektonPipelines *TektonPipelines `json:"tektonPipelines,omitempty"` - - // TektonTasks is the configuration of the tekton-tasks operand - TektonTasks *TektonTasks `json:"tektonTasks,omitempty"` - - // FeatureGates is the configuration of the tekton operands - FeatureGates *FeatureGates `json:"featureGates,omitempty"` -} - -// TektonPipelines defines the desired state of pipelines -type TektonPipelines struct { - Namespace string `json:"namespace,omitempty"` -} - -// TektonTasks defines variables for configuration of tasks -type TektonTasks struct { - Namespace string `json:"namespace,omitempty"` -} - -// FeatureGates defines feature gate for tto operator -type FeatureGates struct { - DeployTektonTaskResources bool `json:"deployTektonTaskResources,omitempty"` + // TokenGenerationService configures the service for generating tokens to access VNC for a VM. + TokenGenerationService *TokenGenerationService `json:"tokenGenerationService,omitempty"` } // DataImportCronTemplate defines the template type for DataImportCrons. @@ -133,6 +78,11 @@ func (t *DataImportCronTemplate) AsDataImportCron() cdiv1beta1.DataImportCron { } } +// TokenGenerationService configures the service for generating tokens to access VNC for a VM. +type TokenGenerationService struct { + Enabled bool `json:"enabled,omitempty"` +} + // SSPStatus defines the observed state of SSP type SSPStatus struct { lifecycleapi.Status `json:",inline"` @@ -146,9 +96,9 @@ type SSPStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status -// +kubebuilder:deprecatedversion:warning="ssp.kubevirt.io/v1beta1 ssp is deprecated" -// SSP is the Schema for the ssps API // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" + +// SSP is the Schema for the ssps API type SSP struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta3/zz_generated.deepcopy.go similarity index 67% rename from api/v1beta1/zz_generated.deepcopy.go rename to api/v1beta3/zz_generated.deepcopy.go index 92c3e9d9c..b572d7c1f 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta3/zz_generated.deepcopy.go @@ -18,33 +18,13 @@ limitations under the License. // Code generated by controller-gen. DO NOT EDIT. -package v1beta1 +package v1beta3 import ( "github.com/openshift/api/config/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonInstancetypes) DeepCopyInto(out *CommonInstancetypes) { - *out = *in - if in.URL != nil { - in, out := &in.URL, &out.URL - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonInstancetypes. -func (in *CommonInstancetypes) DeepCopy() *CommonInstancetypes { - if in == nil { - return nil - } - out := new(CommonInstancetypes) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CommonTemplates) DeepCopyInto(out *CommonTemplates) { *out = *in @@ -84,40 +64,6 @@ func (in *DataImportCronTemplate) DeepCopy() *DataImportCronTemplate { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FeatureGates) DeepCopyInto(out *FeatureGates) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureGates. -func (in *FeatureGates) DeepCopy() *FeatureGates { - if in == nil { - return nil - } - out := new(FeatureGates) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NodeLabeller) DeepCopyInto(out *NodeLabeller) { - *out = *in - if in.Placement != nil { - in, out := &in.Placement, &out.Placement - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeLabeller. -func (in *NodeLabeller) DeepCopy() *NodeLabeller { - if in == nil { - return nil - } - out := new(NodeLabeller) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SSP) DeepCopyInto(out *SSP) { *out = *in @@ -186,34 +132,14 @@ func (in *SSPSpec) DeepCopyInto(out *SSPSpec) { (*in).DeepCopyInto(*out) } in.CommonTemplates.DeepCopyInto(&out.CommonTemplates) - if in.NodeLabeller != nil { - in, out := &in.NodeLabeller, &out.NodeLabeller - *out = new(NodeLabeller) - (*in).DeepCopyInto(*out) - } if in.TLSSecurityProfile != nil { in, out := &in.TLSSecurityProfile, &out.TLSSecurityProfile *out = new(v1.TLSSecurityProfile) (*in).DeepCopyInto(*out) } - if in.CommonInstancetypes != nil { - in, out := &in.CommonInstancetypes, &out.CommonInstancetypes - *out = new(CommonInstancetypes) - (*in).DeepCopyInto(*out) - } - if in.TektonPipelines != nil { - in, out := &in.TektonPipelines, &out.TektonPipelines - *out = new(TektonPipelines) - **out = **in - } - if in.TektonTasks != nil { - in, out := &in.TektonTasks, &out.TektonTasks - *out = new(TektonTasks) - **out = **in - } - if in.FeatureGates != nil { - in, out := &in.FeatureGates, &out.FeatureGates - *out = new(FeatureGates) + if in.TokenGenerationService != nil { + in, out := &in.TokenGenerationService, &out.TokenGenerationService + *out = new(TokenGenerationService) **out = **in } } @@ -244,36 +170,6 @@ func (in *SSPStatus) DeepCopy() *SSPStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TektonPipelines) DeepCopyInto(out *TektonPipelines) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TektonPipelines. -func (in *TektonPipelines) DeepCopy() *TektonPipelines { - if in == nil { - return nil - } - out := new(TektonPipelines) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TektonTasks) DeepCopyInto(out *TektonTasks) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TektonTasks. -func (in *TektonTasks) DeepCopy() *TektonTasks { - if in == nil { - return nil - } - out := new(TektonTasks) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TemplateValidator) DeepCopyInto(out *TemplateValidator) { *out = *in @@ -297,3 +193,18 @@ func (in *TemplateValidator) DeepCopy() *TemplateValidator { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TokenGenerationService) DeepCopyInto(out *TokenGenerationService) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TokenGenerationService. +func (in *TokenGenerationService) DeepCopy() *TokenGenerationService { + if in == nil { + return nil + } + out := new(TokenGenerationService) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/ssp.kubevirt.io_ssps.yaml b/config/crd/bases/ssp.kubevirt.io_ssps.yaml index 11447e337..3295aec69 100644 --- a/config/crd/bases/ssp.kubevirt.io_ssps.yaml +++ b/config/crd/bases/ssp.kubevirt.io_ssps.yaml @@ -18,9 +18,7 @@ spec: - jsonPath: .status.phase name: Phase type: string - deprecated: true - deprecationWarning: ssp.kubevirt.io/v1beta1 ssp is deprecated - name: v1beta1 + name: v1beta2 schema: openAPIV3Schema: description: SSP is the Schema for the ssps API @@ -46,8 +44,9 @@ spec: description: SSPSpec defines the desired state of SSP properties: commonInstancetypes: - description: CommonInstancetypes is the configuration of the common-instancetypes - operand + description: |- + CommonInstancetypes is ignored. + Deprecated: This field is ignored. properties: url: description: |- @@ -835,1110 +834,123 @@ spec: description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: |- - Name of the StorageClass required by the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 - type: string - volumeMode: - description: |- - volumeMode defines what type of volume is required by the claim. - Value of Filesystem is implied when not included in claim spec. - type: string - volumeName: - description: VolumeName is the binding reference - to the PersistentVolume backing this claim. - type: string - type: object - type: object - status: - description: DataVolumeStatus contains the current - status of the DataVolume - properties: - claimName: - description: ClaimName is the name of the underlying - PVC used by the DataVolume. - type: string - conditions: - items: - description: DataVolumeCondition represents - the state of a data volume condition. - properties: - lastHeartbeatTime: - format: date-time - type: string - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - description: DataVolumeConditionType is - the string representation of known condition - types - type: string - required: - - status - - type - type: object - type: array - phase: - description: Phase is the current phase of the - data volume - type: string - progress: - description: DataVolumeProgress is the current - progress of the DataVolume transfer operation. - Value between 0 and 100 inclusive, N/A if - not available - type: string - restartCount: - description: RestartCount is the number of times - the pod populating the DataVolume has restarted - format: int32 - type: integer - type: object - required: - - spec - type: object - required: - - managedDataSource - - schedule - - template - type: object - required: - - spec - type: object - type: array - namespace: - description: Namespace is the k8s namespace where CommonTemplates - should be installed - maxLength: 63 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - namespace - type: object - featureGates: - description: FeatureGates is the configuration of the tekton operands - properties: - deployTektonTaskResources: - type: boolean - type: object - nodeLabeller: - description: NodeLabeller is configuration of the node-labeller operand - properties: - placement: - description: Placement describes the node scheduling configuration - properties: - affinity: - description: |- - affinity enables pod affinity/anti-affinity placement expanding the types of constraints - that can be expressed with nodeSelector. - affinity is going to be applied to the relevant kind of pods in parallel with nodeSelector - See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity - properties: - nodeAffinity: - description: Describes node affinity scheduling rules - for the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated - with the corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - type: object - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching - the corresponding nodeSelectorTerm, in the - range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector - terms. The terms are ORed. - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - type: object - x-kubernetes-map-type: atomic - type: array - x-kubernetes-list-type: atomic - required: - - nodeSelectorTerms - type: object - x-kubernetes-map-type: atomic - type: object - podAffinity: - description: Describes pod affinity scheduling rules (e.g. - co-locate this pod in the same node, zone, etc. as some - other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - x-kubernetes-list-type: atomic - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules - (e.g. avoid putting this pod in the same node, zone, - etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object type: object + x-kubernetes-map-type: atomic + storageClassName: + description: |- + Name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeMode: + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference + to the PersistentVolume backing this claim. + type: string type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". + type: object + status: + description: DataVolumeStatus contains the current + status of the DataVolume + properties: + claimName: + description: ClaimName is the name of the underlying + PVC used by the DataVolume. + type: string + conditions: items: - type: string + description: DataVolumeCondition represents + the state of a data volume condition. + properties: + lastHeartbeatTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: DataVolumeConditionType is + the string representation of known condition + types + type: string + required: + - status + - type + type: object type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. + phase: + description: Phase is the current phase of the + data volume type: string - required: - - topologyKey + progress: + description: DataVolumeProgress is the current + progress of the DataVolume transfer operation. + Value between 0 and 100 inclusive, N/A if + not available + type: string + restartCount: + description: RestartCount is the number of times + the pod populating the DataVolume has restarted + format: int32 + type: integer type: object - type: array - x-kubernetes-list-type: atomic - type: object - type: object - nodeSelector: - additionalProperties: - type: string - description: |- - nodeSelector is the node selector applied to the relevant kind of pods - It specifies a map of key-value pairs: for the pod to be eligible to run on a node, - the node must have each of the indicated key-value pairs as labels - (it can have additional labels as well). - See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - type: object - tolerations: - description: |- - tolerations is a list of tolerations applied to the relevant kind of pods - See https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ for more info. - These are additional tolerations other than default ones. - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string + required: + - spec + type: object + required: + - managedDataSource + - schedule + - template type: object - type: array - type: object + required: + - spec + type: object + type: array + namespace: + description: Namespace is the k8s namespace where CommonTemplates + should be installed + maxLength: 63 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - namespace + type: object + featureGates: + description: FeatureGates for SSP + properties: + deployCommonInstancetypes: + description: 'Deprecated: This field is ignored.' + type: boolean + deployTektonTaskResources: + description: 'Deprecated: This field is ignored.' + type: boolean + deployVmConsoleProxy: + description: 'Deprecated: This field is ignored.' + type: boolean type: object tektonPipelines: - description: TektonPipelines is the configuration of the tekton-pipelines - operand + description: |- + TektonPipelines is the configuration of the tekton-pipelines operand + Deprecated: This field is ignored. properties: namespace: type: string type: object tektonTasks: - description: TektonTasks is the configuration of the tekton-tasks - operand + description: |- + TektonTasks is the configuration of the tekton-tasks operand + Deprecated: This field is ignored. properties: namespace: type: string @@ -3209,6 +2221,13 @@ spec: - Custom type: string type: object + tokenGenerationService: + description: TokenGenerationService configures the service for generating + tokens to access VNC for a VM. + properties: + enabled: + type: boolean + type: object required: - commonTemplates type: object @@ -3266,14 +2285,14 @@ spec: type: object type: object served: true - storage: false + storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .status.phase name: Phase type: string - name: v1beta2 + name: v1beta3 schema: openAPIV3Schema: description: SSP is the Schema for the ssps API @@ -3298,47 +2317,13 @@ spec: spec: description: SSPSpec defines the desired state of SSP properties: - commonInstancetypes: - description: |- - CommonInstancetypes is ignored. - Deprecated: This field is ignored. - properties: - url: - description: |- - URL of a remote Kustomize target from which to generate and deploy resources. - - - The following caveats apply to the provided URL: - - - * Only 'https://' and 'git://' URLs are supported. - - - * The URL must include '?ref=$ref' or '?version=$ref' pinning it to a specific - reference. It is recommended that the reference be a specific commit or tag - to ensure the generated contents does not change over time. As such it is - recommended not to use branches as the ref for the time being. - - - * Only VirtualMachineClusterPreference and VirtualMachineClusterInstancetype - resources generated from the URL are deployed by the operand. - - - See the following Kustomize documentation for more details: - - - remote targets - https://github.com/kubernetes-sigs/kustomize/blob/master/examples/remoteBuild.md - type: string - type: object commonTemplates: description: CommonTemplates is the configuration of the common templates operand properties: dataImportCronTemplates: - description: |- - DataImportCronTemplates defines a list of DataImportCrons managed by the SSP - Operator. This is intended for images used by CommonTemplates. + description: DataImportCronTemplates defines a list of DataImportCrons + managed by the SSP Operator. items: description: |- DataImportCronTemplate defines the template type for DataImportCrons. @@ -4181,35 +3166,6 @@ spec: required: - namespace type: object - featureGates: - description: FeatureGates for SSP - properties: - deployCommonInstancetypes: - description: 'Deprecated: This field is ignored.' - type: boolean - deployTektonTaskResources: - description: 'Deprecated: This field is ignored.' - type: boolean - deployVmConsoleProxy: - description: 'Deprecated: This field is ignored.' - type: boolean - type: object - tektonPipelines: - description: |- - TektonPipelines is the configuration of the tekton-pipelines operand - Deprecated: This field is ignored. - properties: - namespace: - type: string - type: object - tektonTasks: - description: |- - TektonTasks is the configuration of the tekton-tasks operand - Deprecated: This field is ignored. - properties: - namespace: - type: string - type: object templateValidator: description: TemplateValidator is configuration of the template validator operand @@ -5540,6 +4496,6 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: status: {} diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index db771cb95..aa76002fa 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,4 +1,5 @@ ## Append samples you want in your CSV to this file as resources ## resources: - ssp_v1beta2_ssp.yaml +- ssp_v1beta3_ssp.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/ssp_v1beta3_ssp.yaml b/config/samples/ssp_v1beta3_ssp.yaml new file mode 100644 index 000000000..d937d94cb --- /dev/null +++ b/config/samples/ssp_v1beta3_ssp.yaml @@ -0,0 +1,10 @@ +apiVersion: ssp.kubevirt.io/v1beta3 +kind: SSP +metadata: + name: ssp-sample + namespace: kubevirt +spec: + commonTemplates: + namespace: kubevirt + templateValidator: + replicas: 2 diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 58a25566b..0d0e58494 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -12,12 +12,11 @@ webhooks: namespace: system path: /validate-ssp-kubevirt-io-v1beta2-ssp failurePolicy: Fail - name: validation.ssp.kubevirt.io + name: validation.v1beta2.ssp.kubevirt.io rules: - apiGroups: - ssp.kubevirt.io apiVersions: - - v1beta1 - v1beta2 operations: - CREATE @@ -25,3 +24,23 @@ webhooks: resources: - ssps sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-ssp-kubevirt-io-v1beta3-ssp + failurePolicy: Fail + name: validation.v1beta3.ssp.kubevirt.io + rules: + - apiGroups: + - ssp.kubevirt.io + apiVersions: + - v1beta3 + operations: + - CREATE + - UPDATE + resources: + - ssps + sideEffects: None diff --git a/data/crd/ssp.kubevirt.io_ssps.yaml b/data/crd/ssp.kubevirt.io_ssps.yaml index ab935b711..f8dbf8449 100644 --- a/data/crd/ssp.kubevirt.io_ssps.yaml +++ b/data/crd/ssp.kubevirt.io_ssps.yaml @@ -20,9 +20,7 @@ spec: - jsonPath: .status.phase name: Phase type: string - deprecated: true - deprecationWarning: ssp.kubevirt.io/v1beta1 ssp is deprecated - name: v1beta1 + name: v1beta2 schema: openAPIV3Schema: description: SSP is the Schema for the ssps API @@ -48,8 +46,9 @@ spec: description: SSPSpec defines the desired state of SSP properties: commonInstancetypes: - description: CommonInstancetypes is the configuration of the common-instancetypes - operand + description: |- + CommonInstancetypes is ignored. + Deprecated: This field is ignored. properties: url: description: |- @@ -837,1110 +836,123 @@ spec: description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: |- - Name of the StorageClass required by the claim. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 - type: string - volumeMode: - description: |- - volumeMode defines what type of volume is required by the claim. - Value of Filesystem is implied when not included in claim spec. - type: string - volumeName: - description: VolumeName is the binding reference - to the PersistentVolume backing this claim. - type: string - type: object - type: object - status: - description: DataVolumeStatus contains the current - status of the DataVolume - properties: - claimName: - description: ClaimName is the name of the underlying - PVC used by the DataVolume. - type: string - conditions: - items: - description: DataVolumeCondition represents - the state of a data volume condition. - properties: - lastHeartbeatTime: - format: date-time - type: string - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - description: DataVolumeConditionType is - the string representation of known condition - types - type: string - required: - - status - - type - type: object - type: array - phase: - description: Phase is the current phase of the - data volume - type: string - progress: - description: DataVolumeProgress is the current - progress of the DataVolume transfer operation. - Value between 0 and 100 inclusive, N/A if - not available - type: string - restartCount: - description: RestartCount is the number of times - the pod populating the DataVolume has restarted - format: int32 - type: integer - type: object - required: - - spec - type: object - required: - - managedDataSource - - schedule - - template - type: object - required: - - spec - type: object - type: array - namespace: - description: Namespace is the k8s namespace where CommonTemplates - should be installed - maxLength: 63 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - namespace - type: object - featureGates: - description: FeatureGates is the configuration of the tekton operands - properties: - deployTektonTaskResources: - type: boolean - type: object - nodeLabeller: - description: NodeLabeller is configuration of the node-labeller operand - properties: - placement: - description: Placement describes the node scheduling configuration - properties: - affinity: - description: |- - affinity enables pod affinity/anti-affinity placement expanding the types of constraints - that can be expressed with nodeSelector. - affinity is going to be applied to the relevant kind of pods in parallel with nodeSelector - See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity - properties: - nodeAffinity: - description: Describes node affinity scheduling rules - for the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated - with the corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - type: object - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching - the corresponding nodeSelectorTerm, in the - range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector - terms. The terms are ORed. - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the - selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - type: object - x-kubernetes-map-type: atomic - type: array - x-kubernetes-list-type: atomic - required: - - nodeSelectorTerms - type: object - x-kubernetes-map-type: atomic - type: object - podAffinity: - description: Describes pod affinity scheduling rules (e.g. - co-locate this pod in the same node, zone, etc. as some - other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - x-kubernetes-list-type: atomic - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules - (e.g. avoid putting this pod in the same node, zone, - etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object type: object + x-kubernetes-map-type: atomic + storageClassName: + description: |- + Name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1 + type: string + volumeMode: + description: |- + volumeMode defines what type of volume is required by the claim. + Value of Filesystem is implied when not included in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference + to the PersistentVolume backing this claim. + type: string type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". + type: object + status: + description: DataVolumeStatus contains the current + status of the DataVolume + properties: + claimName: + description: ClaimName is the name of the underlying + PVC used by the DataVolume. + type: string + conditions: items: - type: string + description: DataVolumeCondition represents + the state of a data volume condition. + properties: + lastHeartbeatTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + description: DataVolumeConditionType is + the string representation of known condition + types + type: string + required: + - status + - type + type: object type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. + phase: + description: Phase is the current phase of the + data volume type: string - required: - - topologyKey + progress: + description: DataVolumeProgress is the current + progress of the DataVolume transfer operation. + Value between 0 and 100 inclusive, N/A if + not available + type: string + restartCount: + description: RestartCount is the number of times + the pod populating the DataVolume has restarted + format: int32 + type: integer type: object - type: array - x-kubernetes-list-type: atomic - type: object - type: object - nodeSelector: - additionalProperties: - type: string - description: |- - nodeSelector is the node selector applied to the relevant kind of pods - It specifies a map of key-value pairs: for the pod to be eligible to run on a node, - the node must have each of the indicated key-value pairs as labels - (it can have additional labels as well). - See https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector - type: object - tolerations: - description: |- - tolerations is a list of tolerations applied to the relevant kind of pods - See https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ for more info. - These are additional tolerations other than default ones. - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string + required: + - spec + type: object + required: + - managedDataSource + - schedule + - template type: object - type: array - type: object + required: + - spec + type: object + type: array + namespace: + description: Namespace is the k8s namespace where CommonTemplates + should be installed + maxLength: 63 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - namespace + type: object + featureGates: + description: FeatureGates for SSP + properties: + deployCommonInstancetypes: + description: 'Deprecated: This field is ignored.' + type: boolean + deployTektonTaskResources: + description: 'Deprecated: This field is ignored.' + type: boolean + deployVmConsoleProxy: + description: 'Deprecated: This field is ignored.' + type: boolean type: object tektonPipelines: - description: TektonPipelines is the configuration of the tekton-pipelines - operand + description: |- + TektonPipelines is the configuration of the tekton-pipelines operand + Deprecated: This field is ignored. properties: namespace: type: string type: object tektonTasks: - description: TektonTasks is the configuration of the tekton-tasks - operand + description: |- + TektonTasks is the configuration of the tekton-tasks operand + Deprecated: This field is ignored. properties: namespace: type: string @@ -3211,6 +2223,13 @@ spec: - Custom type: string type: object + tokenGenerationService: + description: TokenGenerationService configures the service for generating + tokens to access VNC for a VM. + properties: + enabled: + type: boolean + type: object required: - commonTemplates type: object @@ -3268,14 +2287,14 @@ spec: type: object type: object served: true - storage: false + storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .status.phase name: Phase type: string - name: v1beta2 + name: v1beta3 schema: openAPIV3Schema: description: SSP is the Schema for the ssps API @@ -3300,47 +2319,13 @@ spec: spec: description: SSPSpec defines the desired state of SSP properties: - commonInstancetypes: - description: |- - CommonInstancetypes is ignored. - Deprecated: This field is ignored. - properties: - url: - description: |- - URL of a remote Kustomize target from which to generate and deploy resources. - - - The following caveats apply to the provided URL: - - - * Only 'https://' and 'git://' URLs are supported. - - - * The URL must include '?ref=$ref' or '?version=$ref' pinning it to a specific - reference. It is recommended that the reference be a specific commit or tag - to ensure the generated contents does not change over time. As such it is - recommended not to use branches as the ref for the time being. - - - * Only VirtualMachineClusterPreference and VirtualMachineClusterInstancetype - resources generated from the URL are deployed by the operand. - - - See the following Kustomize documentation for more details: - - - remote targets - https://github.com/kubernetes-sigs/kustomize/blob/master/examples/remoteBuild.md - type: string - type: object commonTemplates: description: CommonTemplates is the configuration of the common templates operand properties: dataImportCronTemplates: - description: |- - DataImportCronTemplates defines a list of DataImportCrons managed by the SSP - Operator. This is intended for images used by CommonTemplates. + description: DataImportCronTemplates defines a list of DataImportCrons + managed by the SSP Operator. items: description: |- DataImportCronTemplate defines the template type for DataImportCrons. @@ -4183,35 +3168,6 @@ spec: required: - namespace type: object - featureGates: - description: FeatureGates for SSP - properties: - deployCommonInstancetypes: - description: 'Deprecated: This field is ignored.' - type: boolean - deployTektonTaskResources: - description: 'Deprecated: This field is ignored.' - type: boolean - deployVmConsoleProxy: - description: 'Deprecated: This field is ignored.' - type: boolean - type: object - tektonPipelines: - description: |- - TektonPipelines is the configuration of the tekton-pipelines operand - Deprecated: This field is ignored. - properties: - namespace: - type: string - type: object - tektonTasks: - description: |- - TektonTasks is the configuration of the tekton-tasks operand - Deprecated: This field is ignored. - properties: - namespace: - type: string - type: object templateValidator: description: TemplateValidator is configuration of the template validator operand @@ -5542,7 +4498,7 @@ spec: type: object type: object served: true - storage: true + storage: false subresources: status: {} status: diff --git a/data/olm-catalog/ssp-operator.clusterserviceversion.yaml b/data/olm-catalog/ssp-operator.clusterserviceversion.yaml index ce008a8fc..e2d3bcb8a 100644 --- a/data/olm-catalog/ssp-operator.clusterserviceversion.yaml +++ b/data/olm-catalog/ssp-operator.clusterserviceversion.yaml @@ -23,6 +23,22 @@ metadata: "replicas": 2 } } + }, + { + "apiVersion": "ssp.kubevirt.io/v1beta3", + "kind": "SSP", + "metadata": { + "name": "ssp-sample", + "namespace": "kubevirt" + }, + "spec": { + "commonTemplates": { + "namespace": "kubevirt" + }, + "templateValidator": { + "replicas": 2 + } + } } ] capabilities: Basic Install @@ -39,14 +55,14 @@ spec: enabled: false customresourcedefinitions: owned: - - kind: SSP - name: ssps.ssp.kubevirt.io - version: v1beta1 - description: SSP is the Schema for the ssps API displayName: SSP kind: SSP name: ssps.ssp.kubevirt.io version: v1beta2 + - kind: SSP + name: ssps.ssp.kubevirt.io + version: v1beta3 description: Operator that deploys and controls additional KubeVirt resources displayName: ssp-operator icon: @@ -625,12 +641,11 @@ spec: containerPort: 9443 deploymentName: ssp-operator failurePolicy: Fail - generateName: validation.ssp.kubevirt.io + generateName: validation.v1beta2.ssp.kubevirt.io rules: - apiGroups: - ssp.kubevirt.io apiVersions: - - v1beta1 - v1beta2 operations: - CREATE @@ -641,3 +656,23 @@ spec: targetPort: 9443 type: ValidatingAdmissionWebhook webhookPath: /validate-ssp-kubevirt-io-v1beta2-ssp + - admissionReviewVersions: + - v1 + containerPort: 9443 + deploymentName: ssp-operator + failurePolicy: Fail + generateName: validation.v1beta3.ssp.kubevirt.io + rules: + - apiGroups: + - ssp.kubevirt.io + apiVersions: + - v1beta3 + operations: + - CREATE + - UPDATE + resources: + - ssps + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-ssp-kubevirt-io-v1beta3-ssp diff --git a/docs/configuration.md b/docs/configuration.md index cdd88570f..e48d15184 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,7 +1,7 @@ # Configuration Operator configuration can be modified by editing the SSP Custom Resource (CR). -See [SSP Specification](https://github.com/kubevirt/ssp-operator/blob/main/api/v1beta2/ssp_types.go#L74) +See [SSP Specification](https://github.com/kubevirt/ssp-operator/blob/main/api/v1beta3/ssp_types.go#L51) that defines the desired state of SSP. ## Table Of Contents @@ -10,13 +10,13 @@ that defines the desired state of SSP. - [Annotations](#annotations) - [Common Templates](#common-templates) - [Template Validator](#template-validator) -- [Feature Gates](#feature-gates) +- [VNC Token Generation Service](#vnc-token-generation-service) ## SSP Custom Resource (CR) To activate the operator, create the SSP Custom Resource (CR): -``` -apiVersion: ssp.kubevirt.io/v1beta2 +```yaml +apiVersion: ssp.kubevirt.io/v1beta3 kind: SSP metadata: name: ssp-sample @@ -26,8 +26,6 @@ spec: namespace: kubevirt templateValidator: replicas: 2 - featureGates: - deployVmConsoleProxy: true ``` ## Annotations @@ -36,8 +34,8 @@ spec: This annotation will pause operator reconciliation. -``` -apiVersion: ssp.kubevirt.io/v1beta1 +```yaml +apiVersion: ssp.kubevirt.io/v1beta3 kind: SSP metadata: annotations: @@ -55,8 +53,8 @@ the operator will still cleanup all the dependent resources. A set of common templates to create KubeVirt Virtual Machines (VMs). -``` -apiVersion: ssp.kubevirt.io/v1beta1 +```yaml +apiVersion: ssp.kubevirt.io/v1beta3 kind: SSP metadata: name: ssp-sample @@ -70,8 +68,8 @@ spec: Template Validator is designed to inspect virtual machines (VMs) and detect any violations of the rules defined in VM's annotations. -``` -apiVersion: ssp.kubevirt.io/v1beta1 +```yaml +apiVersion: ssp.kubevirt.io/v1beta3 kind: SSP metadata: name: ssp-sample @@ -81,29 +79,19 @@ spec: replicas: 2 # Customize the number of replicas for the validator deployment ``` -## Feature Gates - -The `featureGates` field is an optional set of optional boolean feature enabler. -The features in the list are experimental features that are not enabled by default. +## VNC Token Generation Service -To enable a feature, add its name to the `featureGates` list and set it to true. -Missing or false feature gates disables the feature. +The [VM Console Proxy](https://github.com/kubevirt/vm-console-proxy) +can be deployed by SSP operator when it is enabled in the `.spec`. +It is a service that exposes an API for generating VNC access tokens for VMs. -### `deployVmConsoleProxy` - -Set the `deployVmConsoleProxy` feature gate to true to allow the operator -to deploy VM console proxy resources. - -Resources will be deployed that provide access to the VNC console of a KubeVirt VM, -enabling users to access VMs without requiring access to the cluster's API. - -``` -apiVersion: ssp.kubevirt.io/v1beta2 +```yaml +apiVersion: ssp.kubevirt.io/v1beta3 kind: SSP metadata: name: ssp-sample namespace: kubevirt spec: - featureGates: - deployVmConsoleProxy: true + tokenGenerationService: + enabled: true ``` diff --git a/hack/csv-generator.go b/hack/csv-generator.go index 7bf549ed4..8166e9223 100644 --- a/hack/csv-generator.go +++ b/hack/csv-generator.go @@ -222,7 +222,9 @@ func replaceVariables(flags generatorFlags, csv *csvv1.ClusterServiceVersion) er } if flags.webhookPort > 0 { - csv.Spec.WebhookDefinitions[0].ContainerPort = flags.webhookPort + for i := range csv.Spec.WebhookDefinitions { + csv.Spec.WebhookDefinitions[i].ContainerPort = flags.webhookPort + } } return nil diff --git a/internal/common/scheme.go b/internal/common/scheme.go index 2472681dc..59f47ed5c 100644 --- a/internal/common/scheme.go +++ b/internal/common/scheme.go @@ -12,8 +12,9 @@ import ( kubevirtv1 "kubevirt.io/api/core/v1" instancetypev1alpha2 "kubevirt.io/api/instancetype/v1alpha2" instancetypev1beta1 "kubevirt.io/api/instancetype/v1beta1" - sspv1beta1 "kubevirt.io/ssp-operator/api/v1beta1" + sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2" + sspv1beta3 "kubevirt.io/ssp-operator/api/v1beta3" ) var ( @@ -25,8 +26,8 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(Scheme)) utilruntime.Must(extv1.AddToScheme(Scheme)) utilruntime.Must(internalmeta.AddToScheme(Scheme)) - utilruntime.Must(sspv1beta1.AddToScheme(Scheme)) utilruntime.Must(sspv1beta2.AddToScheme(Scheme)) + utilruntime.Must(sspv1beta3.AddToScheme(Scheme)) utilruntime.Must(osconfv1.Install(Scheme)) utilruntime.Must(instancetypev1alpha2.AddToScheme(Scheme)) utilruntime.Must(instancetypev1beta1.AddToScheme(Scheme)) diff --git a/tests/tests_suite_test.go b/tests/tests_suite_test.go index 4fa830a38..210838dfb 100644 --- a/tests/tests_suite_test.go +++ b/tests/tests_suite_test.go @@ -41,8 +41,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/client/config" - sspv1beta1 "kubevirt.io/ssp-operator/api/v1beta1" sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2" + sspv1beta3 "kubevirt.io/ssp-operator/api/v1beta3" "kubevirt.io/ssp-operator/internal/common" "kubevirt.io/ssp-operator/tests/env" ) @@ -420,8 +420,8 @@ func expectSuccessOrNotFound(err error) { } func setupApiClient() { - Expect(sspv1beta1.AddToScheme(testScheme)).ToNot(HaveOccurred()) Expect(sspv1beta2.AddToScheme(testScheme)).ToNot(HaveOccurred()) + Expect(sspv1beta3.AddToScheme(testScheme)).ToNot(HaveOccurred()) Expect(promv1.AddToScheme(testScheme)).ToNot(HaveOccurred()) Expect(templatev1.Install(testScheme)).ToNot(HaveOccurred()) Expect(secv1.Install(testScheme)).ToNot(HaveOccurred()) @@ -471,6 +471,13 @@ func getSsp() *sspv1beta2.SSP { return foundSsp } +func getSspV1Beta3() *sspv1beta3.SSP { + key := client.ObjectKey{Name: strategy.GetName(), Namespace: strategy.GetNamespace()} + foundSsp := &sspv1beta3.SSP{} + Expect(apiClient.Get(ctx, key, foundSsp)).ToNot(HaveOccurred()) + return foundSsp +} + func getTemplateValidatorDeployment() *apps.Deployment { key := client.ObjectKey{Name: "virt-template-validator", Namespace: strategy.GetNamespace()} deployment := &apps.Deployment{} diff --git a/tests/webhook_test.go b/tests/webhook_test.go index be7fd7b74..b14cf9533 100644 --- a/tests/webhook_test.go +++ b/tests/webhook_test.go @@ -1,20 +1,20 @@ package tests import ( - "fmt" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - sspv1beta1 "kubevirt.io/ssp-operator/api/v1beta1" + "kubevirt.io/controller-lifecycle-operator-sdk/api" "sigs.k8s.io/controller-runtime/pkg/client" - "kubevirt.io/controller-lifecycle-operator-sdk/api" sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2" + sspv1beta3 "kubevirt.io/ssp-operator/api/v1beta3" ) // Placement API tests variables @@ -70,7 +70,7 @@ var _ = Describe("Validation webhook", func() { }) Context("creation", func() { - It("[test_id:5242] should fail to create a second SSP CR", func() { + It("[test_id:5242] should fail to create a second v1beta2.SSP CR", func() { foundSsp := getSsp() ssp2 := foundSsp.DeepCopy() ssp2.ObjectMeta = v1.ObjectMeta{ @@ -79,11 +79,23 @@ var _ = Describe("Validation webhook", func() { } err := apiClient.Create(ctx, ssp2, client.DryRunAll) - if err == nil { - Fail("Second SSP resource created.") - return + Expect(err).To(MatchError(ContainSubstring( + "creation failed, an SSP CR already exists in namespace %v: %v", + foundSsp.Namespace, + foundSsp.Name, + ))) + }) + + It("[test_id:TODO] should fail to create a second v1beta3.SSP CR", func() { + foundSsp := getSspV1Beta3() + ssp2 := foundSsp.DeepCopy() + ssp2.ObjectMeta = v1.ObjectMeta{ + Name: "test-ssp2", + Namespace: foundSsp.GetNamespace(), } - Expect(err.Error()).To(ContainSubstring(fmt.Sprintf( + + err := apiClient.Create(ctx, ssp2, client.DryRunAll) + Expect(err).To(MatchError(ContainSubstring( "creation failed, an SSP CR already exists in namespace %v: %v", foundSsp.Namespace, foundSsp.Name, @@ -92,13 +104,16 @@ var _ = Describe("Validation webhook", func() { Context("removed existing SSP CR", func() { var ( - newSsp *sspv1beta2.SSP + newSspV1Beta2 *sspv1beta2.SSP + newSspV1Beta3 *sspv1beta3.SSP ) BeforeEach(func() { strategy.SkipSspUpdateTestsIfNeeded() foundSsp := getSsp() + newSspV1Beta3 = getSspV1Beta3() + Expect(apiClient.Delete(ctx, foundSsp)).ToNot(HaveOccurred()) waitForDeletion(client.ObjectKey{Name: foundSsp.GetName(), Namespace: foundSsp.GetNamespace()}, &sspv1beta2.SSP{}) @@ -107,7 +122,7 @@ var _ = Describe("Validation webhook", func() { Namespace: foundSsp.GetNamespace(), } - newSsp = foundSsp + newSspV1Beta2 = foundSsp }) AfterEach(func() { @@ -116,55 +131,61 @@ var _ = Describe("Validation webhook", func() { Context("Placement API validation", func() { It("[test_id:5988]should succeed with valid template-validator placement fields", func() { - newSsp.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{ + newSspV1Beta2.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{ + Placement: &placementAPIValidationValidPlacement, + } + + Expect(apiClient.Create(ctx, newSspV1Beta2, client.DryRunAll)).ToNot(HaveOccurred(), + "failed to create SSP CR with valid template-validator placement fields") + }) + + It("[test_id:TODO] [v1beta3] should succeed with valid template-validator placement fields", func() { + newSspV1Beta3.Spec.TemplateValidator = &sspv1beta3.TemplateValidator{ Placement: &placementAPIValidationValidPlacement, } - Expect(apiClient.Create(ctx, newSsp, client.DryRunAll)).ToNot(HaveOccurred(), + Expect(apiClient.Create(ctx, newSspV1Beta3, client.DryRunAll)).ToNot(HaveOccurred(), "failed to create SSP CR with valid template-validator placement fields") }) It("[test_id:5987]should fail with invalid template-validator placement fields", func() { - newSsp.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{ + newSspV1Beta2.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{ Placement: &placementAPIValidationInvalidPlacement, } - Expect(apiClient.Create(ctx, newSsp, client.DryRunAll)).To(HaveOccurred(), + Expect(apiClient.Create(ctx, newSspV1Beta2, client.DryRunAll)).To(HaveOccurred(), + "created SSP CR with invalid template-validator placement fields") + }) + + It("[test_id:5987] [v1beat3] should fail with invalid template-validator placement fields", func() { + newSspV1Beta3.Spec.TemplateValidator = &sspv1beta3.TemplateValidator{ + Placement: &placementAPIValidationInvalidPlacement, + } + + Expect(apiClient.Create(ctx, newSspV1Beta3, client.DryRunAll)).To(HaveOccurred(), "created SSP CR with invalid template-validator placement fields") }) }) It("[test_id:TODO] should fail when DataImportCronTemplate does not have a name", func() { - newSsp.Spec.CommonTemplates.DataImportCronTemplates = []sspv1beta2.DataImportCronTemplate{{ + newSspV1Beta2.Spec.CommonTemplates.DataImportCronTemplates = []sspv1beta2.DataImportCronTemplate{{ ObjectMeta: metav1.ObjectMeta{Name: ""}, }} - err := apiClient.Create(ctx, newSsp, client.DryRunAll) + err := apiClient.Create(ctx, newSspV1Beta2, client.DryRunAll) Expect(err).To(MatchError(ContainSubstring("missing name in DataImportCronTemplate"))) }) - It("[test_id:TODO] should accept v1beta1 SSP object", func() { - ssp := &sspv1beta1.SSP{ - ObjectMeta: metav1.ObjectMeta{ - Name: newSsp.GetName(), - Namespace: newSsp.GetNamespace(), - }, - Spec: sspv1beta1.SSPSpec{ - CommonTemplates: sspv1beta1.CommonTemplates{ - Namespace: newSsp.Spec.CommonTemplates.Namespace, - }, - }, - } - - Expect(apiClient.Create(ctx, ssp, client.DryRunAll)).To(Succeed()) + It("[test_id:TODO] [v1beta3] should fail when DataImportCronTemplate does not have a name", func() { + newSspV1Beta3.Spec.CommonTemplates.DataImportCronTemplates = []sspv1beta3.DataImportCronTemplate{{ + ObjectMeta: metav1.ObjectMeta{Name: ""}, + }} + err := apiClient.Create(ctx, newSspV1Beta3, client.DryRunAll) + Expect(err).To(MatchError(ContainSubstring("missing name in DataImportCronTemplate"))) }) }) }) Context("update", func() { - var ( - foundSsp *sspv1beta2.SSP - ) - BeforeEach(func() { strategy.SkipSspUpdateTestsIfNeeded() }) @@ -176,7 +197,7 @@ var _ = Describe("Validation webhook", func() { Context("Placement API validation", func() { It("[test_id:5990]should succeed with valid template-validator placement fields", func() { Eventually(func() error { - foundSsp = getSsp() + foundSsp := getSsp() foundSsp.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{ Placement: &placementAPIValidationValidPlacement, } @@ -184,9 +205,19 @@ var _ = Describe("Validation webhook", func() { }, 20*time.Second, time.Second).ShouldNot(HaveOccurred(), "failed to update SSP CR with valid template-validator placement fields") }) + It("[test_id:5990] [v1beta3] should succeed with valid template-validator placement fields", func() { + Eventually(func() error { + foundSsp := getSspV1Beta3() + foundSsp.Spec.TemplateValidator = &sspv1beta3.TemplateValidator{ + Placement: &placementAPIValidationValidPlacement, + } + return apiClient.Update(ctx, foundSsp, client.DryRunAll) + }, 20*time.Second, time.Second).ShouldNot(HaveOccurred(), "failed to update SSP CR with valid template-validator placement fields") + }) + It("[test_id:5989]should fail with invalid template-validator placement fields", func() { Eventually(func() v1.StatusReason { - foundSsp = getSsp() + foundSsp := getSsp() foundSsp.Spec.TemplateValidator = &sspv1beta2.TemplateValidator{ Placement: &placementAPIValidationInvalidPlacement, } @@ -194,16 +225,37 @@ var _ = Describe("Validation webhook", func() { return errors.ReasonForError(err) }, 20*time.Second, time.Second).Should(Equal(metav1.StatusReasonInvalid), "SSP CR updated with invalid template-validator placement fields") }) + + It("[test_id:5989] [v1beta3] should fail with invalid template-validator placement fields", func() { + Eventually(func() v1.StatusReason { + foundSsp := getSspV1Beta3() + foundSsp.Spec.TemplateValidator = &sspv1beta3.TemplateValidator{ + Placement: &placementAPIValidationInvalidPlacement, + } + err := apiClient.Update(ctx, foundSsp, client.DryRunAll) + return errors.ReasonForError(err) + }, 20*time.Second, time.Second).Should(Equal(metav1.StatusReasonInvalid), "SSP CR updated with invalid template-validator placement fields") + }) }) It("[test_id:TODO] should fail when DataImportCronTemplate does not have a name", func() { Eventually(func() error { - foundSsp = getSsp() + foundSsp := getSsp() foundSsp.Spec.CommonTemplates.DataImportCronTemplates = []sspv1beta2.DataImportCronTemplate{{ ObjectMeta: metav1.ObjectMeta{Name: ""}, }} return apiClient.Update(ctx, foundSsp, client.DryRunAll) }, 20*time.Second, time.Second).Should(MatchError(ContainSubstring("missing name in DataImportCronTemplate"))) }) + + It("[test_id:TODO] [v1beta3] should fail when DataImportCronTemplate does not have a name", func() { + Eventually(func() error { + foundSsp := getSspV1Beta3() + foundSsp.Spec.CommonTemplates.DataImportCronTemplates = []sspv1beta3.DataImportCronTemplate{{ + ObjectMeta: metav1.ObjectMeta{Name: ""}, + }} + return apiClient.Update(ctx, foundSsp, client.DryRunAll) + }, 20*time.Second, time.Second).Should(MatchError(ContainSubstring("missing name in DataImportCronTemplate"))) + }) }) }) diff --git a/vendor/kubevirt.io/ssp-operator/api/v1beta1/groupversion_info.go b/vendor/kubevirt.io/ssp-operator/api/v1beta3/groupversion_info.go similarity index 90% rename from vendor/kubevirt.io/ssp-operator/api/v1beta1/groupversion_info.go rename to vendor/kubevirt.io/ssp-operator/api/v1beta3/groupversion_info.go index 6a35a63df..131ba91cd 100644 --- a/vendor/kubevirt.io/ssp-operator/api/v1beta1/groupversion_info.go +++ b/vendor/kubevirt.io/ssp-operator/api/v1beta3/groupversion_info.go @@ -14,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1beta1 contains API Schema definitions for the ssp v1beta1 API group +// Package v1beta3 contains API Schema definitions for the ssp v1beta3 API group // +kubebuilder:object:generate=true // +groupName=ssp.kubevirt.io -package v1beta1 +package v1beta3 import ( "k8s.io/apimachinery/pkg/runtime/schema" @@ -26,7 +26,7 @@ import ( var ( // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "ssp.kubevirt.io", Version: "v1beta1"} + GroupVersion = schema.GroupVersion{Group: "ssp.kubevirt.io", Version: "v1beta3"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/api/v1beta1/ssp_types.go b/vendor/kubevirt.io/ssp-operator/api/v1beta3/ssp_types.go similarity index 60% rename from api/v1beta1/ssp_types.go rename to vendor/kubevirt.io/ssp-operator/api/v1beta3/ssp_types.go index 1f962e17d..c2af6b9bb 100644 --- a/api/v1beta1/ssp_types.go +++ b/vendor/kubevirt.io/ssp-operator/api/v1beta3/ssp_types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1 +package v1beta3 import ( ocpv1 "github.com/openshift/api/config/v1" @@ -43,38 +43,10 @@ type CommonTemplates struct { //+kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ Namespace string `json:"namespace"` - // DataImportCronTemplates defines a list of DataImportCrons managed by the SSP - // Operator. This is intended for images used by CommonTemplates. + // DataImportCronTemplates defines a list of DataImportCrons managed by the SSP Operator. DataImportCronTemplates []DataImportCronTemplate `json:"dataImportCronTemplates,omitempty"` } -type NodeLabeller struct { - // Placement describes the node scheduling configuration - Placement *lifecycleapi.NodePlacement `json:"placement,omitempty"` -} - -type CommonInstancetypes struct { - // URL of a remote Kustomize target from which to generate and deploy resources. - // - // The following caveats apply to the provided URL: - // - // * Only 'https://' and 'git://' URLs are supported. - // - // * The URL must include '?ref=$ref' or '?version=$ref' pinning it to a specific - // reference. It is recommended that the reference be a specific commit or tag - // to ensure the generated contents does not change over time. As such it is - // recommended not to use branches as the ref for the time being. - // - // * Only VirtualMachineClusterPreference and VirtualMachineClusterInstancetype - // resources generated from the URL are deployed by the operand. - // - // See the following Kustomize documentation for more details: - // - // remote targets - // https://github.com/kubernetes-sigs/kustomize/blob/master/examples/remoteBuild.md - URL *string `json:"url,omitempty"` -} - // SSPSpec defines the desired state of SSP type SSPSpec struct { // TemplateValidator is configuration of the template validator operand @@ -83,38 +55,11 @@ type SSPSpec struct { // CommonTemplates is the configuration of the common templates operand CommonTemplates CommonTemplates `json:"commonTemplates"` - // NodeLabeller is configuration of the node-labeller operand - NodeLabeller *NodeLabeller `json:"nodeLabeller,omitempty"` - // TLSSecurityProfile is a configuration for the TLS. TLSSecurityProfile *ocpv1.TLSSecurityProfile `json:"tlsSecurityProfile,omitempty"` - // CommonInstancetypes is the configuration of the common-instancetypes operand - CommonInstancetypes *CommonInstancetypes `json:"commonInstancetypes,omitempty"` - - // TektonPipelines is the configuration of the tekton-pipelines operand - TektonPipelines *TektonPipelines `json:"tektonPipelines,omitempty"` - - // TektonTasks is the configuration of the tekton-tasks operand - TektonTasks *TektonTasks `json:"tektonTasks,omitempty"` - - // FeatureGates is the configuration of the tekton operands - FeatureGates *FeatureGates `json:"featureGates,omitempty"` -} - -// TektonPipelines defines the desired state of pipelines -type TektonPipelines struct { - Namespace string `json:"namespace,omitempty"` -} - -// TektonTasks defines variables for configuration of tasks -type TektonTasks struct { - Namespace string `json:"namespace,omitempty"` -} - -// FeatureGates defines feature gate for tto operator -type FeatureGates struct { - DeployTektonTaskResources bool `json:"deployTektonTaskResources,omitempty"` + // TokenGenerationService configures the service for generating tokens to access VNC for a VM. + TokenGenerationService *TokenGenerationService `json:"tokenGenerationService,omitempty"` } // DataImportCronTemplate defines the template type for DataImportCrons. @@ -133,6 +78,11 @@ func (t *DataImportCronTemplate) AsDataImportCron() cdiv1beta1.DataImportCron { } } +// TokenGenerationService configures the service for generating tokens to access VNC for a VM. +type TokenGenerationService struct { + Enabled bool `json:"enabled,omitempty"` +} + // SSPStatus defines the observed state of SSP type SSPStatus struct { lifecycleapi.Status `json:",inline"` @@ -146,9 +96,9 @@ type SSPStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status -// +kubebuilder:deprecatedversion:warning="ssp.kubevirt.io/v1beta1 ssp is deprecated" -// SSP is the Schema for the ssps API // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" + +// SSP is the Schema for the ssps API type SSP struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/vendor/kubevirt.io/ssp-operator/api/v1beta1/zz_generated.deepcopy.go b/vendor/kubevirt.io/ssp-operator/api/v1beta3/zz_generated.deepcopy.go similarity index 67% rename from vendor/kubevirt.io/ssp-operator/api/v1beta1/zz_generated.deepcopy.go rename to vendor/kubevirt.io/ssp-operator/api/v1beta3/zz_generated.deepcopy.go index 92c3e9d9c..b572d7c1f 100644 --- a/vendor/kubevirt.io/ssp-operator/api/v1beta1/zz_generated.deepcopy.go +++ b/vendor/kubevirt.io/ssp-operator/api/v1beta3/zz_generated.deepcopy.go @@ -18,33 +18,13 @@ limitations under the License. // Code generated by controller-gen. DO NOT EDIT. -package v1beta1 +package v1beta3 import ( "github.com/openshift/api/config/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonInstancetypes) DeepCopyInto(out *CommonInstancetypes) { - *out = *in - if in.URL != nil { - in, out := &in.URL, &out.URL - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonInstancetypes. -func (in *CommonInstancetypes) DeepCopy() *CommonInstancetypes { - if in == nil { - return nil - } - out := new(CommonInstancetypes) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CommonTemplates) DeepCopyInto(out *CommonTemplates) { *out = *in @@ -84,40 +64,6 @@ func (in *DataImportCronTemplate) DeepCopy() *DataImportCronTemplate { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FeatureGates) DeepCopyInto(out *FeatureGates) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureGates. -func (in *FeatureGates) DeepCopy() *FeatureGates { - if in == nil { - return nil - } - out := new(FeatureGates) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NodeLabeller) DeepCopyInto(out *NodeLabeller) { - *out = *in - if in.Placement != nil { - in, out := &in.Placement, &out.Placement - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeLabeller. -func (in *NodeLabeller) DeepCopy() *NodeLabeller { - if in == nil { - return nil - } - out := new(NodeLabeller) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SSP) DeepCopyInto(out *SSP) { *out = *in @@ -186,34 +132,14 @@ func (in *SSPSpec) DeepCopyInto(out *SSPSpec) { (*in).DeepCopyInto(*out) } in.CommonTemplates.DeepCopyInto(&out.CommonTemplates) - if in.NodeLabeller != nil { - in, out := &in.NodeLabeller, &out.NodeLabeller - *out = new(NodeLabeller) - (*in).DeepCopyInto(*out) - } if in.TLSSecurityProfile != nil { in, out := &in.TLSSecurityProfile, &out.TLSSecurityProfile *out = new(v1.TLSSecurityProfile) (*in).DeepCopyInto(*out) } - if in.CommonInstancetypes != nil { - in, out := &in.CommonInstancetypes, &out.CommonInstancetypes - *out = new(CommonInstancetypes) - (*in).DeepCopyInto(*out) - } - if in.TektonPipelines != nil { - in, out := &in.TektonPipelines, &out.TektonPipelines - *out = new(TektonPipelines) - **out = **in - } - if in.TektonTasks != nil { - in, out := &in.TektonTasks, &out.TektonTasks - *out = new(TektonTasks) - **out = **in - } - if in.FeatureGates != nil { - in, out := &in.FeatureGates, &out.FeatureGates - *out = new(FeatureGates) + if in.TokenGenerationService != nil { + in, out := &in.TokenGenerationService, &out.TokenGenerationService + *out = new(TokenGenerationService) **out = **in } } @@ -244,36 +170,6 @@ func (in *SSPStatus) DeepCopy() *SSPStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TektonPipelines) DeepCopyInto(out *TektonPipelines) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TektonPipelines. -func (in *TektonPipelines) DeepCopy() *TektonPipelines { - if in == nil { - return nil - } - out := new(TektonPipelines) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TektonTasks) DeepCopyInto(out *TektonTasks) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TektonTasks. -func (in *TektonTasks) DeepCopy() *TektonTasks { - if in == nil { - return nil - } - out := new(TektonTasks) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TemplateValidator) DeepCopyInto(out *TemplateValidator) { *out = *in @@ -297,3 +193,18 @@ func (in *TemplateValidator) DeepCopy() *TemplateValidator { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TokenGenerationService) DeepCopyInto(out *TokenGenerationService) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TokenGenerationService. +func (in *TokenGenerationService) DeepCopy() *TokenGenerationService { + if in == nil { + return nil + } + out := new(TokenGenerationService) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 82153fbee..5c610f8cf 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -885,8 +885,8 @@ kubevirt.io/qe-tools/pkg/ginkgo-reporters kubevirt.io/qe-tools/pkg/polarion-xml # kubevirt.io/ssp-operator/api v0.0.0 => ./api ## explicit; go 1.22.4 -kubevirt.io/ssp-operator/api/v1beta1 kubevirt.io/ssp-operator/api/v1beta2 +kubevirt.io/ssp-operator/api/v1beta3 # sigs.k8s.io/controller-runtime v0.18.5 ## explicit; go 1.22.0 sigs.k8s.io/controller-runtime diff --git a/webhooks/convert/convert.go b/webhooks/convert/convert.go new file mode 100644 index 000000000..393e7ea77 --- /dev/null +++ b/webhooks/convert/convert.go @@ -0,0 +1,70 @@ +package convert + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2" + sspv1beta3 "kubevirt.io/ssp-operator/api/v1beta3" +) + +func ConvertSSP(src *sspv1beta3.SSP) *sspv1beta2.SSP { + return &sspv1beta2.SSP{ + TypeMeta: metav1.TypeMeta{ + Kind: src.Kind, + APIVersion: sspv1beta2.GroupVersion.String(), + }, + ObjectMeta: src.ObjectMeta, + Spec: sspv1beta2.SSPSpec{ + TemplateValidator: convertTemplateValidator(src.Spec.TemplateValidator), + CommonTemplates: sspv1beta2.CommonTemplates{ + Namespace: src.Spec.CommonTemplates.Namespace, + DataImportCronTemplates: convertDataImportCronTemplates(src.Spec.CommonTemplates.DataImportCronTemplates), + }, + TLSSecurityProfile: src.Spec.TLSSecurityProfile, + TokenGenerationService: convertTokenGenerationService(src.Spec.TokenGenerationService), + }, + Status: sspv1beta2.SSPStatus{ + Status: src.Status.Status, + Paused: src.Status.Paused, + ObservedGeneration: src.Status.ObservedGeneration, + }, + } +} + +func convertTemplateValidator(src *sspv1beta3.TemplateValidator) *sspv1beta2.TemplateValidator { + if src == nil { + return nil + } + + return &sspv1beta2.TemplateValidator{ + Replicas: src.Replicas, + Placement: src.Placement, + } +} + +func convertDataImportCronTemplates(src []sspv1beta3.DataImportCronTemplate) []sspv1beta2.DataImportCronTemplate { + if len(src) == 0 { + return nil + } + + result := make([]sspv1beta2.DataImportCronTemplate, 0, len(src)) + for i := range src { + oldTemplate := &src[i] + result = append(result, sspv1beta2.DataImportCronTemplate{ + ObjectMeta: oldTemplate.ObjectMeta, + Spec: oldTemplate.Spec, + }) + } + + return result +} + +func convertTokenGenerationService(src *sspv1beta3.TokenGenerationService) *sspv1beta2.TokenGenerationService { + if src == nil { + return nil + } + + return &sspv1beta2.TokenGenerationService{ + Enabled: src.Enabled, + } +} diff --git a/webhooks/ssp_webhook.go b/webhooks/ssp_webhook.go index ca8a6975c..46ff71463 100644 --- a/webhooks/ssp_webhook.go +++ b/webhooks/ssp_webhook.go @@ -23,36 +23,43 @@ import ( apps "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" "kubevirt.io/controller-lifecycle-operator-sdk/api" + "kubevirt.io/ssp-operator/webhooks/convert" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - sspv1beta1 "kubevirt.io/ssp-operator/api/v1beta1" sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2" + sspv1beta3 "kubevirt.io/ssp-operator/api/v1beta3" ) +// +kubebuilder:webhook:verbs=create;update,path=/validate-ssp-kubevirt-io-v1beta2-ssp,mutating=false,failurePolicy=fail,groups=ssp.kubevirt.io,resources=ssps,versions=v1beta2,name=validation.v1beta2.ssp.kubevirt.io,admissionReviewVersions=v1,sideEffects=None +// +kubebuilder:webhook:verbs=create;update,path=/validate-ssp-kubevirt-io-v1beta3-ssp,mutating=false,failurePolicy=fail,groups=ssp.kubevirt.io,resources=ssps,versions=v1beta3,name=validation.v1beta3.ssp.kubevirt.io,admissionReviewVersions=v1,sideEffects=None + var ssplog = logf.Log.WithName("ssp-resource") func Setup(mgr ctrl.Manager) error { - // This is a hack. Using Unstructured allows the webhook to correctly decode different versions of objects. - // Controller-runtime currently does not support a single webhook for multiple versions. - - obj := &unstructured.Unstructured{} - obj.SetAPIVersion(sspv1beta2.GroupVersion.String()) - obj.SetKind("SSP") + err := ctrl.NewWebhookManagedBy(mgr). + For(&sspv1beta2.SSP{}). + WithValidator(newSspValidator(mgr.GetClient())). + Complete() + if err != nil { + return fmt.Errorf("failed to create webhook for v1beta2.SSP") + } - return ctrl.NewWebhookManagedBy(mgr). - For(obj). + err = ctrl.NewWebhookManagedBy(mgr). + For(&sspv1beta3.SSP{}). WithValidator(newSspValidator(mgr.GetClient())). Complete() -} + if err != nil { + return fmt.Errorf("failed to create webhook for v1beta3.SSP") + } -// +kubebuilder:webhook:verbs=create;update,path=/validate-ssp-kubevirt-io-v1beta2-ssp,mutating=false,failurePolicy=fail,groups=ssp.kubevirt.io,resources=ssps,versions=v1beta1;v1beta2,name=validation.ssp.kubevirt.io,admissionReviewVersions=v1,sideEffects=None + return nil +} type sspValidator struct { apiClient client.Client @@ -61,7 +68,7 @@ type sspValidator struct { var _ admission.CustomValidator = &sspValidator{} func (s *sspValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - sspObj, err := getSspWithConversion(obj) + sspObj, err := getSsp(obj) if err != nil { return nil, err } @@ -82,7 +89,7 @@ func (s *sspValidator) ValidateCreate(ctx context.Context, obj runtime.Object) ( } func (s *sspValidator) ValidateUpdate(ctx context.Context, _, newObj runtime.Object) (admission.Warnings, error) { - newSsp, err := getSspWithConversion(newObj) + newSsp, err := getSsp(newObj) if err != nil { return nil, err } @@ -166,37 +173,6 @@ func (s *sspValidator) validateOperandPlacement(ctx context.Context, namespace s return s.apiClient.Create(ctx, deployment, &client.CreateOptions{DryRun: []string{metav1.DryRunAll}}) } -func getSspWithConversion(obj runtime.Object) (*sspv1beta2.SSP, error) { - unstructuredSsp, ok := obj.(*unstructured.Unstructured) - if !ok { - return nil, fmt.Errorf("expected unstructured object, got %T", obj) - } - - if unstructuredSsp.GetKind() != "SSP" { - return nil, fmt.Errorf("expected SSP kind, got %s", unstructuredSsp.GetKind()) - } - - switch unstructuredSsp.GetAPIVersion() { - case sspv1beta1.GroupVersion.String(): - // Currently the two versions differ only in one removed field. - // We can decode the v1beta1 object into v1beta2. - // TODO: Use proper conversion logic. - unstructuredSsp.SetAPIVersion(sspv1beta2.GroupVersion.String()) - - case sspv1beta2.GroupVersion.String(): - break - default: - return nil, fmt.Errorf("unexpected group version %s", unstructuredSsp.GetAPIVersion()) - } - - ssp := &sspv1beta2.SSP{} - err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredSsp.UnstructuredContent(), ssp) - if err != nil { - return nil, fmt.Errorf("error converting unstructured object to SSP: %w", err) - } - return ssp, nil -} - // TODO: also validate DataImportCronTemplates in general once CDI exposes its own validation func validateDataImportCronTemplates(ssp *sspv1beta2.SSP) error { for _, cron := range ssp.Spec.CommonTemplates.DataImportCronTemplates { @@ -210,3 +186,14 @@ func validateDataImportCronTemplates(ssp *sspv1beta2.SSP) error { func newSspValidator(clt client.Client) *sspValidator { return &sspValidator{apiClient: clt} } + +func getSsp(obj runtime.Object) (*sspv1beta2.SSP, error) { + switch sspObj := obj.(type) { + case *sspv1beta2.SSP: + return sspObj, nil + case *sspv1beta3.SSP: + return convert.ConvertSSP(sspObj), nil + default: + return nil, fmt.Errorf("unexpected type: %T", obj) + } +} diff --git a/webhooks/ssp_webhook_test.go b/webhooks/ssp_webhook_test.go index 5d40620da..990a6f762 100644 --- a/webhooks/ssp_webhook_test.go +++ b/webhooks/ssp_webhook_test.go @@ -18,222 +18,377 @@ package webhooks import ( "context" - "fmt" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + apps "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" + "kubevirt.io/controller-lifecycle-operator-sdk/api" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/interceptor" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - sspv1beta1 "kubevirt.io/ssp-operator/api/v1beta1" sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2" + sspv1beta3 "kubevirt.io/ssp-operator/api/v1beta3" "kubevirt.io/ssp-operator/internal" + "kubevirt.io/ssp-operator/internal/common" ) var _ = Describe("SSP Validation", func() { + var ( - client client.Client - objects = make([]runtime.Object, 0) + apiClient client.Client + createIntercept func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error validator admission.CustomValidator ctx context.Context ) - JustBeforeEach(func() { - scheme := runtime.NewScheme() - // add our own scheme - Expect(sspv1beta2.SchemeBuilder.AddToScheme(scheme)).To(Succeed()) - Expect(sspv1beta1.SchemeBuilder.AddToScheme(scheme)).To(Succeed()) - // add more schemes - Expect(v1.AddToScheme(scheme)).To(Succeed()) + BeforeEach(func() { + createIntercept = func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + return client.Create(ctx, obj, opts...) + } - client = fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build() + apiClient = fake.NewClientBuilder(). + WithScheme(common.Scheme). + WithInterceptorFuncs(interceptor.Funcs{ + Create: func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + return createIntercept(ctx, client, obj, opts...) + }, + }). + Build() - validator = newSspValidator(client) - ctx = context.Background() + validator = newSspValidator(apiClient) }) Context("creating SSP CR", func() { - const ( - templatesNamespace = "test-templates-ns" - ) - BeforeEach(func() { - objects = append(objects, &v1.Namespace{ + err := apiClient.Create(ctx, &sspv1beta2.SSP{ ObjectMeta: metav1.ObjectMeta{ - Name: templatesNamespace, - ResourceVersion: "1", + Name: "test-ssp", + Namespace: "test-ns", }, + Spec: sspv1beta2.SSPSpec{}, }) + Expect(err).ToNot(HaveOccurred()) + }) + + It("should reject v1beta2.SSP when one is already present", func() { + ssp := &sspv1beta2.SSP{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ssp2", + Namespace: "test-ns2", + }, + Spec: sspv1beta2.SSPSpec{}, + } + + _, err := validator.ValidateCreate(ctx, ssp) + Expect(err).To(MatchError(ContainSubstring("creation failed, an SSP CR already exists in namespace test-ns: test-ssp"))) }) - AfterEach(func() { - objects = make([]runtime.Object, 0) + It("should reject v1beta3.SSP when v1beta2.SSP is already present", func() { + ssp := &sspv1beta3.SSP{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ssp2", + Namespace: "test-ns2", + }, + Spec: sspv1beta3.SSPSpec{}, + } + + _, err := validator.ValidateCreate(ctx, ssp) + Expect(err).To(MatchError(ContainSubstring("creation failed, an SSP CR already exists in namespace test-ns: test-ssp"))) }) + }) + + Context("v1beta2", func() { + Context("DataImportCronTemplates", func() { + var ( + oldSSP *sspv1beta2.SSP + newSSP *sspv1beta2.SSP + ) - Context("when one is already present", func() { BeforeEach(func() { - // add an SSP CR to fake client - objects = append(objects, &sspv1beta2.SSP{ + oldSSP = &sspv1beta2.SSP{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-ssp", - Namespace: "test-ns", - ResourceVersion: "1", + Name: "test-ssp", + Namespace: "test-ns", }, Spec: sspv1beta2.SSPSpec{ CommonTemplates: sspv1beta2.CommonTemplates{ - Namespace: templatesNamespace, + Namespace: "test-templates-ns", + DataImportCronTemplates: []sspv1beta2.DataImportCronTemplate{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: internal.GoldenImagesNamespace, + }, + }, + }, }, }, - }) + } + + newSSP = oldSSP.DeepCopy() }) - It("should be rejected", func() { + It("should validate dataImportCronTemplates on create", func() { + _, err := validator.ValidateCreate(ctx, newSSP) + Expect(err).To(MatchError(ContainSubstring("missing name in DataImportCronTemplate"))) + + newSSP.Spec.CommonTemplates.DataImportCronTemplates[0].Name = "test-name" + + _, err = validator.ValidateCreate(ctx, newSSP) + Expect(err).ToNot(HaveOccurred()) + }) + + It("should validate dataImportCronTemplates on update", func() { + _, err := validator.ValidateUpdate(ctx, oldSSP, newSSP) + Expect(err).To(MatchError(ContainSubstring("missing name in DataImportCronTemplate"))) + + newSSP.Spec.CommonTemplates.DataImportCronTemplates[0].Name = "test-name" + + _, err = validator.ValidateUpdate(ctx, oldSSP, newSSP) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + Context("validate placement", func() { + It("should not call create API, if placement is nil", func() { + createIntercept = func(_ context.Context, _ client.WithWatch, _ client.Object, _ ...client.CreateOption) error { + Fail("Called create API") + return nil + } + ssp := &sspv1beta2.SSP{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-ssp2", - Namespace: "test-ns2", + Name: "test-ssp", + Namespace: "test-ns", }, Spec: sspv1beta2.SSPSpec{ - CommonTemplates: sspv1beta2.CommonTemplates{ - Namespace: templatesNamespace, + TemplateValidator: &sspv1beta2.TemplateValidator{ + Replicas: ptr.To(int32(2)), + Placement: nil, }, }, } - _, err := validator.ValidateCreate(ctx, toUnstructured(ssp)) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("creation failed, an SSP CR already exists in namespace test-ns: test-ssp")) + _, err := validator.ValidateCreate(ctx, ssp) + Expect(err).ToNot(HaveOccurred()) }) - }) - It("should accept old v1beta1 SSP CR", func() { - ssp := &sspv1beta1.SSP{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-ssp", - Namespace: "test-ns", - }, - Spec: sspv1beta1.SSPSpec{ - TemplateValidator: &sspv1beta1.TemplateValidator{ - Replicas: ptr.To[int32](2), + It("should call create API dry run", func() { + placement := &api.NodePlacement{ + NodeSelector: map[string]string{ + "test-label": "test-value", }, - CommonTemplates: sspv1beta1.CommonTemplates{ - Namespace: templatesNamespace, - }, - NodeLabeller: &sspv1beta1.NodeLabeller{}, - CommonInstancetypes: &sspv1beta1.CommonInstancetypes{ - URL: ptr.To("https://foo.com/bar?ref=1234"), - }, - TektonPipelines: &sspv1beta1.TektonPipelines{ - Namespace: "test-pipelines-ns", + Affinity: &v1.Affinity{ + PodAffinity: &v1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "foo": "bar", + }, + }, + }}, + }, }, - TektonTasks: &sspv1beta1.TektonTasks{ - Namespace: "test-tasks-ns", + Tolerations: []v1.Toleration{{ + Key: "key", + Value: "value", + }}, + } + + var createWasCalled bool + createIntercept = func(ctx context.Context, cli client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + deployment, ok := obj.(*apps.Deployment) + if !ok { + Fail("Expected created object to be Deployment.") + } + + createOptions := &client.CreateOptions{} + for _, opt := range opts { + opt.ApplyToCreate(createOptions) + } + + if len(createOptions.DryRun) != 1 || createOptions.DryRun[0] != metav1.DryRunAll { + Fail("Create call should be dry run.") + } + + Expect(deployment.Spec.Template.Spec.NodeSelector).To(Equal(placement.NodeSelector)) + Expect(deployment.Spec.Template.Spec.Affinity).To(Equal(placement.Affinity)) + Expect(deployment.Spec.Template.Spec.Tolerations).To(Equal(placement.Tolerations)) + createWasCalled = true + return nil + } + + ssp := &sspv1beta2.SSP{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ssp", + Namespace: "test-ns", }, - FeatureGates: &sspv1beta1.FeatureGates{ - DeployTektonTaskResources: true, + Spec: sspv1beta2.SSPSpec{ + TemplateValidator: &sspv1beta2.TemplateValidator{ + Replicas: ptr.To(int32(2)), + Placement: placement, + }, }, - }, - } + } - _, err := validator.ValidateCreate(ctx, toUnstructured(ssp)) - Expect(err).ToNot(HaveOccurred()) + _, err := validator.ValidateCreate(ctx, ssp) + Expect(err).ToNot(HaveOccurred()) + + Expect(createWasCalled).To(BeTrue()) + }) }) }) - Context("DataImportCronTemplates", func() { - const ( - templatesNamespace = "test-templates-ns" - ) - - var ( - oldSSP *sspv1beta2.SSP - newSSP *sspv1beta2.SSP - ) - - BeforeEach(func() { - objects = append(objects, &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: templatesNamespace, - ResourceVersion: "1", - }, - }) + Context("v1beta3", func() { + Context("DataImportCronTemplates", func() { + var ( + oldSSP *sspv1beta3.SSP + newSSP *sspv1beta3.SSP + ) - oldSSP = &sspv1beta2.SSP{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-ssp", - Namespace: "test-ns", - }, - Spec: sspv1beta2.SSPSpec{ - CommonTemplates: sspv1beta2.CommonTemplates{ - Namespace: templatesNamespace, - DataImportCronTemplates: []sspv1beta2.DataImportCronTemplate{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: internal.GoldenImagesNamespace, + BeforeEach(func() { + oldSSP = &sspv1beta3.SSP{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ssp", + Namespace: "test-ns", + }, + Spec: sspv1beta3.SSPSpec{ + CommonTemplates: sspv1beta3.CommonTemplates{ + Namespace: "test-templates-ns", + DataImportCronTemplates: []sspv1beta3.DataImportCronTemplate{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: internal.GoldenImagesNamespace, + }, }, }, }, }, - }, - } + } - newSSP = oldSSP.DeepCopy() - }) + newSSP = oldSSP.DeepCopy() + }) - AfterEach(func() { - objects = make([]runtime.Object, 0) - }) + It("should validate dataImportCronTemplates on create", func() { + _, err := validator.ValidateCreate(ctx, newSSP) + Expect(err).To(MatchError(ContainSubstring("missing name in DataImportCronTemplate"))) - It("should validate dataImportCronTemplates on create", func() { - _, err := validator.ValidateCreate(ctx, toUnstructured(newSSP)) - Expect(err).To(HaveOccurred()) + newSSP.Spec.CommonTemplates.DataImportCronTemplates[0].Name = "test-name" - newSSP.Spec.CommonTemplates.DataImportCronTemplates[0].Name = "test-name" + _, err = validator.ValidateCreate(ctx, newSSP) + Expect(err).ToNot(HaveOccurred()) + }) - _, err = validator.ValidateCreate(ctx, toUnstructured(newSSP)) - Expect(err).ToNot(HaveOccurred()) + It("should validate dataImportCronTemplates on update", func() { + _, err := validator.ValidateUpdate(ctx, oldSSP, newSSP) + Expect(err).To(MatchError(ContainSubstring("missing name in DataImportCronTemplate"))) + + newSSP.Spec.CommonTemplates.DataImportCronTemplates[0].Name = "test-name" + + _, err = validator.ValidateUpdate(ctx, oldSSP, newSSP) + Expect(err).ToNot(HaveOccurred()) + }) }) - It("should validate dataImportCronTemplates on update", func() { - _, err := validator.ValidateUpdate(ctx, toUnstructured(oldSSP), toUnstructured(newSSP)) - Expect(err).To(HaveOccurred()) + Context("validate placement", func() { + It("should not call create API, if placement is nil", func() { + createIntercept = func(_ context.Context, _ client.WithWatch, _ client.Object, _ ...client.CreateOption) error { + Fail("Called create API") + return nil + } + + ssp := &sspv1beta3.SSP{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ssp", + Namespace: "test-ns", + }, + Spec: sspv1beta3.SSPSpec{ + TemplateValidator: &sspv1beta3.TemplateValidator{ + Replicas: ptr.To(int32(2)), + Placement: nil, + }, + }, + } + + _, err := validator.ValidateCreate(ctx, ssp) + Expect(err).ToNot(HaveOccurred()) + }) - newSSP.Spec.CommonTemplates.DataImportCronTemplates[0].Name = "test-name" + It("should call create API dry run", func() { + placement := &api.NodePlacement{ + NodeSelector: map[string]string{ + "test-label": "test-value", + }, + Affinity: &v1.Affinity{ + PodAffinity: &v1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "foo": "bar", + }, + }, + }}, + }, + }, + Tolerations: []v1.Toleration{{ + Key: "key", + Value: "value", + }}, + } - _, err = validator.ValidateUpdate(ctx, toUnstructured(oldSSP), toUnstructured(newSSP)) - Expect(err).ToNot(HaveOccurred()) + var createWasCalled bool + createIntercept = func(ctx context.Context, cli client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + deployment, ok := obj.(*apps.Deployment) + if !ok { + Fail("Expected created object to be Deployment.") + } + + createOptions := &client.CreateOptions{} + for _, opt := range opts { + opt.ApplyToCreate(createOptions) + } + + if len(createOptions.DryRun) != 1 || createOptions.DryRun[0] != metav1.DryRunAll { + Fail("Create call should be dry run.") + } + + Expect(deployment.Spec.Template.Spec.NodeSelector).To(Equal(placement.NodeSelector)) + Expect(deployment.Spec.Template.Spec.Affinity).To(Equal(placement.Affinity)) + Expect(deployment.Spec.Template.Spec.Tolerations).To(Equal(placement.Tolerations)) + createWasCalled = true + return nil + } + + ssp := &sspv1beta3.SSP{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ssp", + Namespace: "test-ns", + }, + Spec: sspv1beta3.SSPSpec{ + TemplateValidator: &sspv1beta3.TemplateValidator{ + Replicas: ptr.To(int32(2)), + Placement: placement, + }, + }, + } + + _, err := validator.ValidateCreate(ctx, ssp) + Expect(err).ToNot(HaveOccurred()) + + Expect(createWasCalled).To(BeTrue()) + }) }) }) - }) -func toUnstructured(obj runtime.Object) *unstructured.Unstructured { - switch t := obj.(type) { - case *sspv1beta1.SSP: - t.APIVersion = sspv1beta1.GroupVersion.String() - t.Kind = "SSP" - case *sspv1beta2.SSP: - t.APIVersion = sspv1beta2.GroupVersion.String() - t.Kind = "SSP" - } - - data, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) - if err != nil { - panic(fmt.Sprintf("cannot convert object to unstructured: %s", err)) - } - return &unstructured.Unstructured{Object: data} -} - func TestWebhook(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "API Suite") + RunSpecs(t, "Webhook Suite") }