From bb6cf4d3b6cc182d8c3d0e4b5718a4591c1e66fc Mon Sep 17 00:00:00 2001 From: okozachenko1203 Date: Tue, 20 Aug 2024 12:21:23 +1000 Subject: [PATCH] feat: support server group and scheduler hint additional properties manual conversion for schedulerhintproperties use named object instead of map update manifests fix lint error remove object from SchedulerHintAdditionalValue struct add tests and validations fix cel validation add more unit tests add additional annotations update generated manifests fix one apivalidation testcase --- api/v1alpha1/openstackserver_types.go | 8 + api/v1alpha1/zz_generated.deepcopy.go | 7 + api/v1alpha6/openstackmachine_conversion.go | 1 + api/v1alpha6/zz_generated.conversion.go | 1 + api/v1alpha7/openstackmachine_conversion.go | 1 + api/v1alpha7/zz_generated.conversion.go | 1 + api/v1beta1/openstackmachine_types.go | 66 ++++++ api/v1beta1/zz_generated.deepcopy.go | 53 +++++ ...re.cluster.x-k8s.io_openstackclusters.yaml | 71 +++++++ ...er.x-k8s.io_openstackclustertemplates.yaml | 71 +++++++ ...re.cluster.x-k8s.io_openstackmachines.yaml | 71 +++++++ ...er.x-k8s.io_openstackmachinetemplates.yaml | 71 +++++++ ...ure.cluster.x-k8s.io_openstackservers.yaml | 71 +++++++ controllers/openstackmachine_controller.go | 18 +- controllers/openstackserver_controller.go | 23 ++- docs/book/src/api/v1alpha1/api.md | 32 +++ docs/book/src/api/v1beta1/api.md | 189 ++++++++++++++++++ hack/codegen/openapi/zz_generated.openapi.go | 124 +++++++++++- pkg/cloud/services/compute/instance.go | 16 +- pkg/cloud/services/compute/instance_test.go | 84 ++++++++ pkg/cloud/services/compute/instance_types.go | 27 +-- .../api/v1alpha1/openstackserverspec.go | 46 +++-- .../api/v1beta1/openstackmachinespec.go | 44 ++-- .../schedulerhintadditionalproperty.go | 48 +++++ .../v1beta1/schedulerhintadditionalvalue.go | 70 +++++++ .../applyconfiguration/internal/internal.go | 43 ++++ pkg/generated/applyconfiguration/utils.go | 4 + .../apivalidations/openstackmachine_test.go | 152 ++++++++++++++ 28 files changed, 1347 insertions(+), 66 deletions(-) create mode 100644 pkg/generated/applyconfiguration/api/v1beta1/schedulerhintadditionalproperty.go create mode 100644 pkg/generated/applyconfiguration/api/v1beta1/schedulerhintadditionalvalue.go diff --git a/api/v1alpha1/openstackserver_types.go b/api/v1alpha1/openstackserver_types.go index c82e27b0b8..d14c942c5f 100644 --- a/api/v1alpha1/openstackserver_types.go +++ b/api/v1alpha1/openstackserver_types.go @@ -104,6 +104,14 @@ type OpenStackServerSpec struct { // be injected into the server instance. // +optional UserDataRef *corev1.LocalObjectReference `json:"userDataRef,omitempty"` + + // SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints + // to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, + // such as specifying certain host aggregates or availability zones. + // +optional + // +listType=map + // +listMapKey=name + SchedulerHintAdditionalProperties []infrav1.SchedulerHintAdditionalProperty `json:"schedulerHintAdditionalProperties,omitempty"` } // OpenStackServerStatus defines the observed state of OpenStackServer. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 55a7485a71..e5814f92e8 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -289,6 +289,13 @@ func (in *OpenStackServerSpec) DeepCopyInto(out *OpenStackServerSpec) { *out = new(v1.LocalObjectReference) **out = **in } + if in.SchedulerHintAdditionalProperties != nil { + in, out := &in.SchedulerHintAdditionalProperties, &out.SchedulerHintAdditionalProperties + *out = make([]v1beta1.SchedulerHintAdditionalProperty, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackServerSpec. diff --git a/api/v1alpha6/openstackmachine_conversion.go b/api/v1alpha6/openstackmachine_conversion.go index 47a757bd7d..2729e9775a 100644 --- a/api/v1alpha6/openstackmachine_conversion.go +++ b/api/v1alpha6/openstackmachine_conversion.go @@ -190,6 +190,7 @@ func restorev1beta1MachineSpec(previous *infrav1.OpenStackMachineSpec, dst *infr dst.ServerGroup = previous.ServerGroup dst.Image = previous.Image dst.FloatingIPPoolRef = previous.FloatingIPPoolRef + dst.SchedulerHintAdditionalProperties = previous.SchedulerHintAdditionalProperties if len(dst.SecurityGroups) == len(previous.SecurityGroups) { for i := range dst.SecurityGroups { diff --git a/api/v1alpha6/zz_generated.conversion.go b/api/v1alpha6/zz_generated.conversion.go index 291fc701f6..798382eb72 100644 --- a/api/v1alpha6/zz_generated.conversion.go +++ b/api/v1alpha6/zz_generated.conversion.go @@ -1267,6 +1267,7 @@ func autoConvert_v1beta1_OpenStackMachineSpec_To_v1alpha6_OpenStackMachineSpec(i out.IdentityRef = nil } // WARNING: in.FloatingIPPoolRef requires manual conversion: does not exist in peer-type + // WARNING: in.SchedulerHintAdditionalProperties requires manual conversion: does not exist in peer-type return nil } diff --git a/api/v1alpha7/openstackmachine_conversion.go b/api/v1alpha7/openstackmachine_conversion.go index 5183b3fae2..db8337dc25 100644 --- a/api/v1alpha7/openstackmachine_conversion.go +++ b/api/v1alpha7/openstackmachine_conversion.go @@ -191,6 +191,7 @@ func restorev1beta1MachineSpec(previous *infrav1.OpenStackMachineSpec, dst *infr } } dst.FloatingIPPoolRef = previous.FloatingIPPoolRef + dst.SchedulerHintAdditionalProperties = previous.SchedulerHintAdditionalProperties if dst.RootVolume != nil && previous.RootVolume != nil { restorev1beta1BlockDeviceVolume( diff --git a/api/v1alpha7/zz_generated.conversion.go b/api/v1alpha7/zz_generated.conversion.go index 74c7234b7b..ad8f82500b 100644 --- a/api/v1alpha7/zz_generated.conversion.go +++ b/api/v1alpha7/zz_generated.conversion.go @@ -1520,6 +1520,7 @@ func autoConvert_v1beta1_OpenStackMachineSpec_To_v1alpha7_OpenStackMachineSpec(i out.IdentityRef = nil } // WARNING: in.FloatingIPPoolRef requires manual conversion: does not exist in peer-type + // WARNING: in.SchedulerHintAdditionalProperties requires manual conversion: does not exist in peer-type return nil } diff --git a/api/v1beta1/openstackmachine_types.go b/api/v1beta1/openstackmachine_types.go index 07bf7bc02f..7021baaa6d 100644 --- a/api/v1beta1/openstackmachine_types.go +++ b/api/v1beta1/openstackmachine_types.go @@ -33,6 +33,64 @@ const ( IPClaimMachineFinalizer = "openstackmachine.infrastructure.cluster.x-k8s.io/ip-claim" ) +// SchedulerHintValueType is the type that represents allowed values for the Type field. +// +kubebuilder:validation:Enum=Bool;String;Number +type SchedulerHintValueType string + +// Constants representing the allowed types for SchedulerHintAdditionalValue. +const ( + SchedulerHintTypeBool SchedulerHintValueType = "Bool" + SchedulerHintTypeString SchedulerHintValueType = "String" + SchedulerHintTypeNumber SchedulerHintValueType = "Number" +) + +// SchedulerHintAdditionalValue represents the value of a scheduler hint property. +// The value can be of various types: Bool, String, or Number. +// The Type field indicates the type of the value being used. +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Bool' ? has(self.bool) : !has(self.bool)",message="bool is required when type is Bool, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Number' ? has(self.number) : !has(self.number)",message="number is required when type is Number, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'String' ? has(self.string) : !has(self.string)",message="string is required when type is String, and forbidden otherwise" +// +union. +type SchedulerHintAdditionalValue struct { + // Type represents the type of the value. + // Valid values are Bool, String, and Number. + // +kubebuilder:validation:Required + // +unionDiscriminator + Type SchedulerHintValueType `json:"type"` + + // Bool is the boolean value of the scheduler hint, used when Type is "Bool". + // This field is required if type is 'Bool', and must not be set otherwise. + // +unionMember,optional + Bool *bool `json:"bool,omitempty"` + + // Number is the integer value of the scheduler hint, used when Type is "Number". + // This field is required if type is 'Number', and must not be set otherwise. + // +unionMember,optional + Number *int `json:"number,omitempty"` + + // String is the string value of the scheduler hint, used when Type is "String". + // This field is required if type is 'String', and must not be set otherwise. + // +unionMember,optional + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:MaxLength:=255 + String *string `json:"string,omitempty"` +} + +// SchedulerHintAdditionalProperty represents a single additional property for a scheduler hint. +// It includes a Name to identify the property and a Value that can be of various types. +type SchedulerHintAdditionalProperty struct { + // Name is the name of the scheduler hint property. + // It is a unique identifier for the property. + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:Required + Name string `json:"name"` + + // Value is the value of the scheduler hint property, which can be of various types + // (e.g., bool, string, int). The type is indicated by the Value.Type field. + // +kubebuilder:validation:Required + Value SchedulerHintAdditionalValue `json:"value"` +} + // OpenStackMachineSpec defines the desired state of OpenStackMachine. type OpenStackMachineSpec struct { // ProviderID is the unique identifier as specified by the cloud provider. @@ -98,6 +156,14 @@ type OpenStackMachineSpec struct { // will be assigned to the OpenStackMachine. // +optional FloatingIPPoolRef *corev1.TypedLocalObjectReference `json:"floatingIPPoolRef,omitempty"` + + // SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints + // to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, + // such as specifying certain host aggregates or availability zones. + // +optional + // +listType=map + // +listMapKey=name + SchedulerHintAdditionalProperties []SchedulerHintAdditionalProperty `json:"schedulerHintAdditionalProperties,omitempty"` } type ServerMetadata struct { diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 9ffcd7785a..2aafb48ca2 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -1029,6 +1029,13 @@ func (in *OpenStackMachineSpec) DeepCopyInto(out *OpenStackMachineSpec) { *out = new(v1.TypedLocalObjectReference) (*in).DeepCopyInto(*out) } + if in.SchedulerHintAdditionalProperties != nil { + in, out := &in.SchedulerHintAdditionalProperties, &out.SchedulerHintAdditionalProperties + *out = make([]SchedulerHintAdditionalProperty, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackMachineSpec. @@ -1502,6 +1509,52 @@ func (in *RouterParam) DeepCopy() *RouterParam { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SchedulerHintAdditionalProperty) DeepCopyInto(out *SchedulerHintAdditionalProperty) { + *out = *in + in.Value.DeepCopyInto(&out.Value) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SchedulerHintAdditionalProperty. +func (in *SchedulerHintAdditionalProperty) DeepCopy() *SchedulerHintAdditionalProperty { + if in == nil { + return nil + } + out := new(SchedulerHintAdditionalProperty) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SchedulerHintAdditionalValue) DeepCopyInto(out *SchedulerHintAdditionalValue) { + *out = *in + if in.Bool != nil { + in, out := &in.Bool, &out.Bool + *out = new(bool) + **out = **in + } + if in.Number != nil { + in, out := &in.Number, &out.Number + *out = new(int) + **out = **in + } + if in.String != nil { + in, out := &in.String, &out.String + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SchedulerHintAdditionalValue. +func (in *SchedulerHintAdditionalValue) DeepCopy() *SchedulerHintAdditionalValue { + if in == nil { + return nil + } + out := new(SchedulerHintAdditionalValue) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecurityGroupFilter) DeepCopyInto(out *SecurityGroupFilter) { *out = *in diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml index 960e81e1e1..afa770cb25 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -3992,6 +3992,77 @@ spec: required: - sizeGiB type: object + schedulerHintAdditionalProperties: + description: |- + SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints + to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, + such as specifying certain host aggregates or availability zones. + items: + description: |- + SchedulerHintAdditionalProperty represents a single additional property for a scheduler hint. + It includes a Name to identify the property and a Value that can be of various types. + properties: + name: + description: |- + Name is the name of the scheduler hint property. + It is a unique identifier for the property. + minLength: 1 + type: string + value: + description: |- + Value is the value of the scheduler hint property, which can be of various types + (e.g., bool, string, int). The type is indicated by the Value.Type field. + properties: + bool: + description: |- + Bool is the boolean value of the scheduler hint, used when Type is "Bool". + This field is required if type is 'Bool', and must not be set otherwise. + type: boolean + number: + description: |- + Number is the integer value of the scheduler hint, used when Type is "Number". + This field is required if type is 'Number', and must not be set otherwise. + type: integer + string: + description: |- + String is the string value of the scheduler hint, used when Type is "String". + This field is required if type is 'String', and must not be set otherwise. + maxLength: 255 + minLength: 1 + type: string + type: + description: |- + Type represents the type of the value. + Valid values are Bool, String, and Number. + enum: + - Bool + - String + - Number + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: bool is required when type is Bool, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''Bool'' ? has(self.bool) + : !has(self.bool)' + - message: number is required when type is Number, and + forbidden otherwise + rule: 'has(self.type) && self.type == ''Number'' ? + has(self.number) : !has(self.number)' + - message: string is required when type is String, and + forbidden otherwise + rule: 'has(self.type) && self.type == ''String'' ? + has(self.string) : !has(self.string)' + required: + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map securityGroups: description: The names of the security groups to assign to the instance diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml index f09af98d18..d701c897a1 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml @@ -2486,6 +2486,77 @@ spec: required: - sizeGiB type: object + schedulerHintAdditionalProperties: + description: |- + SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints + to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, + such as specifying certain host aggregates or availability zones. + items: + description: |- + SchedulerHintAdditionalProperty represents a single additional property for a scheduler hint. + It includes a Name to identify the property and a Value that can be of various types. + properties: + name: + description: |- + Name is the name of the scheduler hint property. + It is a unique identifier for the property. + minLength: 1 + type: string + value: + description: |- + Value is the value of the scheduler hint property, which can be of various types + (e.g., bool, string, int). The type is indicated by the Value.Type field. + properties: + bool: + description: |- + Bool is the boolean value of the scheduler hint, used when Type is "Bool". + This field is required if type is 'Bool', and must not be set otherwise. + type: boolean + number: + description: |- + Number is the integer value of the scheduler hint, used when Type is "Number". + This field is required if type is 'Number', and must not be set otherwise. + type: integer + string: + description: |- + String is the string value of the scheduler hint, used when Type is "String". + This field is required if type is 'String', and must not be set otherwise. + maxLength: 255 + minLength: 1 + type: string + type: + description: |- + Type represents the type of the value. + Valid values are Bool, String, and Number. + enum: + - Bool + - String + - Number + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: bool is required when type is Bool, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Bool'' + ? has(self.bool) : !has(self.bool)' + - message: number is required when type is Number, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''Number'' + ? has(self.number) : !has(self.number)' + - message: string is required when type is String, + and forbidden otherwise + rule: 'has(self.type) && self.type == ''String'' + ? has(self.string) : !has(self.string)' + required: + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map securityGroups: description: The names of the security groups to assign to the instance diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml index 64cec5465a..731a8f8cc1 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml @@ -1810,6 +1810,77 @@ spec: required: - sizeGiB type: object + schedulerHintAdditionalProperties: + description: |- + SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints + to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, + such as specifying certain host aggregates or availability zones. + items: + description: |- + SchedulerHintAdditionalProperty represents a single additional property for a scheduler hint. + It includes a Name to identify the property and a Value that can be of various types. + properties: + name: + description: |- + Name is the name of the scheduler hint property. + It is a unique identifier for the property. + minLength: 1 + type: string + value: + description: |- + Value is the value of the scheduler hint property, which can be of various types + (e.g., bool, string, int). The type is indicated by the Value.Type field. + properties: + bool: + description: |- + Bool is the boolean value of the scheduler hint, used when Type is "Bool". + This field is required if type is 'Bool', and must not be set otherwise. + type: boolean + number: + description: |- + Number is the integer value of the scheduler hint, used when Type is "Number". + This field is required if type is 'Number', and must not be set otherwise. + type: integer + string: + description: |- + String is the string value of the scheduler hint, used when Type is "String". + This field is required if type is 'String', and must not be set otherwise. + maxLength: 255 + minLength: 1 + type: string + type: + description: |- + Type represents the type of the value. + Valid values are Bool, String, and Number. + enum: + - Bool + - String + - Number + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: bool is required when type is Bool, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''Bool'' ? has(self.bool) + : !has(self.bool)' + - message: number is required when type is Number, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''Number'' ? has(self.number) + : !has(self.number)' + - message: string is required when type is String, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''String'' ? has(self.string) + : !has(self.string)' + required: + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map securityGroups: description: The names of the security groups to assign to the instance items: diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml index 7b7225df11..4fa0814b2a 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml @@ -1594,6 +1594,77 @@ spec: required: - sizeGiB type: object + schedulerHintAdditionalProperties: + description: |- + SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints + to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, + such as specifying certain host aggregates or availability zones. + items: + description: |- + SchedulerHintAdditionalProperty represents a single additional property for a scheduler hint. + It includes a Name to identify the property and a Value that can be of various types. + properties: + name: + description: |- + Name is the name of the scheduler hint property. + It is a unique identifier for the property. + minLength: 1 + type: string + value: + description: |- + Value is the value of the scheduler hint property, which can be of various types + (e.g., bool, string, int). The type is indicated by the Value.Type field. + properties: + bool: + description: |- + Bool is the boolean value of the scheduler hint, used when Type is "Bool". + This field is required if type is 'Bool', and must not be set otherwise. + type: boolean + number: + description: |- + Number is the integer value of the scheduler hint, used when Type is "Number". + This field is required if type is 'Number', and must not be set otherwise. + type: integer + string: + description: |- + String is the string value of the scheduler hint, used when Type is "String". + This field is required if type is 'String', and must not be set otherwise. + maxLength: 255 + minLength: 1 + type: string + type: + description: |- + Type represents the type of the value. + Valid values are Bool, String, and Number. + enum: + - Bool + - String + - Number + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: bool is required when type is Bool, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''Bool'' ? has(self.bool) + : !has(self.bool)' + - message: number is required when type is Number, and + forbidden otherwise + rule: 'has(self.type) && self.type == ''Number'' ? + has(self.number) : !has(self.number)' + - message: string is required when type is String, and + forbidden otherwise + rule: 'has(self.type) && self.type == ''String'' ? + has(self.string) : !has(self.string)' + required: + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map securityGroups: description: The names of the security groups to assign to the instance diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackservers.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackservers.yaml index 8084127b0d..6edbca3dfd 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackservers.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackservers.yaml @@ -691,6 +691,77 @@ spec: required: - sizeGiB type: object + schedulerHintAdditionalProperties: + description: |- + SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints + to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, + such as specifying certain host aggregates or availability zones. + items: + description: |- + SchedulerHintAdditionalProperty represents a single additional property for a scheduler hint. + It includes a Name to identify the property and a Value that can be of various types. + properties: + name: + description: |- + Name is the name of the scheduler hint property. + It is a unique identifier for the property. + minLength: 1 + type: string + value: + description: |- + Value is the value of the scheduler hint property, which can be of various types + (e.g., bool, string, int). The type is indicated by the Value.Type field. + properties: + bool: + description: |- + Bool is the boolean value of the scheduler hint, used when Type is "Bool". + This field is required if type is 'Bool', and must not be set otherwise. + type: boolean + number: + description: |- + Number is the integer value of the scheduler hint, used when Type is "Number". + This field is required if type is 'Number', and must not be set otherwise. + type: integer + string: + description: |- + String is the string value of the scheduler hint, used when Type is "String". + This field is required if type is 'String', and must not be set otherwise. + maxLength: 255 + minLength: 1 + type: string + type: + description: |- + Type represents the type of the value. + Valid values are Bool, String, and Number. + enum: + - Bool + - String + - Number + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: bool is required when type is Bool, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''Bool'' ? has(self.bool) + : !has(self.bool)' + - message: number is required when type is Number, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''Number'' ? has(self.number) + : !has(self.number)' + - message: string is required when type is String, and forbidden + otherwise + rule: 'has(self.type) && self.type == ''String'' ? has(self.string) + : !has(self.string)' + required: + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map securityGroups: description: SecurityGroups is a list of security groups names to assign to the instance. diff --git a/controllers/openstackmachine_controller.go b/controllers/openstackmachine_controller.go index 5499e57257..ac667389a7 100644 --- a/controllers/openstackmachine_controller.go +++ b/controllers/openstackmachine_controller.go @@ -472,14 +472,16 @@ func (r *OpenStackMachineReconciler) getMachineServer(ctx context.Context, openS // It returns the OpenStackServerSpec object and an error if there is any. func openStackMachineSpecToOpenStackServerSpec(openStackMachineSpec *infrav1.OpenStackMachineSpec, identityRef infrav1.OpenStackIdentityReference, tags []string, failureDomain string, userDataRef *corev1.LocalObjectReference, defaultSecGroup *string, defaultNetworkID string) *infrav1alpha1.OpenStackServerSpec { openStackServerSpec := &infrav1alpha1.OpenStackServerSpec{ - AdditionalBlockDevices: openStackMachineSpec.AdditionalBlockDevices, - ConfigDrive: openStackMachineSpec.ConfigDrive, - Flavor: openStackMachineSpec.Flavor, - IdentityRef: identityRef, - Image: openStackMachineSpec.Image, - RootVolume: openStackMachineSpec.RootVolume, - ServerMetadata: openStackMachineSpec.ServerMetadata, - SSHKeyName: openStackMachineSpec.SSHKeyName, + AdditionalBlockDevices: openStackMachineSpec.AdditionalBlockDevices, + ConfigDrive: openStackMachineSpec.ConfigDrive, + Flavor: openStackMachineSpec.Flavor, + IdentityRef: identityRef, + Image: openStackMachineSpec.Image, + RootVolume: openStackMachineSpec.RootVolume, + ServerMetadata: openStackMachineSpec.ServerMetadata, + SSHKeyName: openStackMachineSpec.SSHKeyName, + ServerGroup: openStackMachineSpec.ServerGroup, + SchedulerHintAdditionalProperties: openStackMachineSpec.SchedulerHintAdditionalProperties, } if len(tags) > 0 { diff --git a/controllers/openstackserver_controller.go b/controllers/openstackserver_controller.go index a4fa497dd0..01df3614fa 100644 --- a/controllers/openstackserver_controller.go +++ b/controllers/openstackserver_controller.go @@ -532,17 +532,18 @@ func (r *OpenStackServerReconciler) serverToInstanceSpec(ctx context.Context, op } instanceSpec := &compute.InstanceSpec{ - AdditionalBlockDevices: openStackServer.Spec.AdditionalBlockDevices, - ConfigDrive: openStackServer.Spec.ConfigDrive != nil && *openStackServer.Spec.ConfigDrive, - Flavor: openStackServer.Spec.Flavor, - ImageID: resolved.ImageID, - Metadata: serverMetadata, - Name: openStackServer.Name, - RootVolume: openStackServer.Spec.RootVolume, - SSHKeyName: openStackServer.Spec.SSHKeyName, - ServerGroupID: resolved.ServerGroupID, - Tags: openStackServer.Spec.Tags, - Trunk: openStackServer.Spec.Trunk != nil && *openStackServer.Spec.Trunk, + AdditionalBlockDevices: openStackServer.Spec.AdditionalBlockDevices, + ConfigDrive: openStackServer.Spec.ConfigDrive != nil && *openStackServer.Spec.ConfigDrive, + Flavor: openStackServer.Spec.Flavor, + ImageID: resolved.ImageID, + Metadata: serverMetadata, + Name: openStackServer.Name, + RootVolume: openStackServer.Spec.RootVolume, + SSHKeyName: openStackServer.Spec.SSHKeyName, + ServerGroupID: resolved.ServerGroupID, + Tags: openStackServer.Spec.Tags, + Trunk: openStackServer.Spec.Trunk != nil && *openStackServer.Spec.Trunk, + SchedulerAdditionalProperties: openStackServer.Spec.SchedulerHintAdditionalProperties, } if openStackServer.Spec.UserDataRef != nil { diff --git a/docs/book/src/api/v1alpha1/api.md b/docs/book/src/api/v1alpha1/api.md index 3e7e1e0047..0279ad17c7 100644 --- a/docs/book/src/api/v1alpha1/api.md +++ b/docs/book/src/api/v1alpha1/api.md @@ -267,6 +267,22 @@ Kubernetes core/v1.LocalObjectReference be injected into the server instance.

+ + +schedulerHintAdditionalProperties
+ + +[]sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalProperty + + + + +(Optional) +

SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints +to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, +such as specifying certain host aggregates or availability zones.

+ + @@ -790,6 +806,22 @@ Kubernetes core/v1.LocalObjectReference be injected into the server instance.

+ + +schedulerHintAdditionalProperties
+ + +[]sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalProperty + + + + +(Optional) +

SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints +to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, +such as specifying certain host aggregates or availability zones.

+ +

OpenStackServerStatus diff --git a/docs/book/src/api/v1beta1/api.md b/docs/book/src/api/v1beta1/api.md index 210d357dcc..22647fb162 100644 --- a/docs/book/src/api/v1beta1/api.md +++ b/docs/book/src/api/v1beta1/api.md @@ -744,6 +744,22 @@ to an IPAddressClaim. Once the IPAddressClaim is fulfilled, the FloatingIP will be assigned to the OpenStackMachine.

+ + +schedulerHintAdditionalProperties
+ + +[]SchedulerHintAdditionalProperty + + + + +(Optional) +

SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints +to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, +such as specifying certain host aggregates or availability zones.

+ + @@ -3387,6 +3403,22 @@ to an IPAddressClaim. Once the IPAddressClaim is fulfilled, the FloatingIP will be assigned to the OpenStackMachine.

+ + +schedulerHintAdditionalProperties
+ + +[]SchedulerHintAdditionalProperty + + + + +(Optional) +

SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints +to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, +such as specifying certain host aggregates or availability zones.

+ +

OpenStackMachineStatus @@ -3760,6 +3792,22 @@ to an IPAddressClaim. Once the IPAddressClaim is fulfilled, the FloatingIP will be assigned to the OpenStackMachine.

+ + +schedulerHintAdditionalProperties
+ + +[]SchedulerHintAdditionalProperty + + + + +(Optional) +

SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints +to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, +such as specifying certain host aggregates or availability zones.

+ + @@ -4571,6 +4619,147 @@ RouterFilter +

SchedulerHintAdditionalProperty +

+

+(Appears on: +OpenStackMachineSpec) +

+

+

SchedulerHintAdditionalProperty represents a single additional property for a scheduler hint. +It includes a Name to identify the property and a Value that can be of various types.

+

+ + + + + + + + + + + + + + + + + +
FieldDescription
+name
+ +string + +
+

Name is the name of the scheduler hint property. +It is a unique identifier for the property.

+
+value
+ + +SchedulerHintAdditionalValue + + +
+

Value is the value of the scheduler hint property, which can be of various types +(e.g., bool, string, int). The type is indicated by the Value.Type field.

+
+

SchedulerHintAdditionalValue +

+

+(Appears on: +SchedulerHintAdditionalProperty) +

+

+

SchedulerHintAdditionalValue represents the value of a scheduler hint property. +The value can be of various types: Bool, String, or Number. +The Type field indicates the type of the value being used.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+type
+ + +SchedulerHintValueType + + +
+

Type represents the type of the value. +Valid values are Bool, String, and Number.

+
+bool
+ +bool + +
+

Bool is the boolean value of the scheduler hint, used when Type is “Bool”. +This field is required if type is ‘Bool’, and must not be set otherwise.

+
+number
+ +int + +
+

Number is the integer value of the scheduler hint, used when Type is “Number”. +This field is required if type is ‘Number’, and must not be set otherwise.

+
+string
+ +string + +
+

String is the string value of the scheduler hint, used when Type is “String”. +This field is required if type is ‘String’, and must not be set otherwise.

+
+

SchedulerHintValueType +(string alias)

+

+(Appears on: +SchedulerHintAdditionalValue) +

+

+

SchedulerHintValueType is the type that represents allowed values for the Type field.

+

+ + + + + + + + + + + + + + +
ValueDescription

"Bool"

"Number"

"String"

SecurityGroupFilter

diff --git a/hack/codegen/openapi/zz_generated.openapi.go b/hack/codegen/openapi/zz_generated.openapi.go index 35b305b44a..22e4d15847 100644 --- a/hack/codegen/openapi/zz_generated.openapi.go +++ b/hack/codegen/openapi/zz_generated.openapi.go @@ -444,6 +444,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.Router": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_Router(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.RouterFilter": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_RouterFilter(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.RouterParam": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_RouterParam(ref), + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalProperty": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SchedulerHintAdditionalProperty(ref), + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalValue": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SchedulerHintAdditionalValue(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupFilter": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SecurityGroupFilter(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SecurityGroupParam(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupRuleSpec": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SecurityGroupRuleSpec(ref), @@ -16711,12 +16713,34 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1alpha1_OpenStackServe Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), }, }, + "schedulerHintAdditionalProperties": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, such as specifying certain host aggregates or availability zones.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalProperty"), + }, + }, + }, + }, + }, }, Required: []string{"flavor", "identityRef", "image", "ports", "sshKeyName"}, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.TypedLocalObjectReference", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AdditionalBlockDevice", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ImageParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.OpenStackIdentityReference", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.PortOpts", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.RootVolume", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerMetadata"}, + "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.TypedLocalObjectReference", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AdditionalBlockDevice", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ImageParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.OpenStackIdentityReference", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.PortOpts", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.RootVolume", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalProperty", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerMetadata"}, } } @@ -23444,12 +23468,34 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_OpenStackMachin Ref: ref("k8s.io/api/core/v1.TypedLocalObjectReference"), }, }, + "schedulerHintAdditionalProperties": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "SchedulerHintAdditionalProperties are arbitrary key/value pairs that provide additional hints to the OpenStack scheduler. These hints can influence how instances are placed on the infrastructure, such as specifying certain host aggregates or availability zones.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalProperty"), + }, + }, + }, + }, + }, }, Required: []string{"flavor", "image"}, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.TypedLocalObjectReference", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AdditionalBlockDevice", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ImageParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.OpenStackIdentityReference", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.PortOpts", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.RootVolume", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerMetadata"}, + "k8s.io/api/core/v1.TypedLocalObjectReference", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AdditionalBlockDevice", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ImageParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.OpenStackIdentityReference", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.PortOpts", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.RootVolume", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalProperty", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerMetadata"}, } } @@ -24492,6 +24538,80 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_RouterParam(ref } } +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SchedulerHintAdditionalProperty(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SchedulerHintAdditionalProperty represents a single additional property for a scheduler hint. It includes a Name to identify the property and a Value that can be of various types.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the scheduler hint property. It is a unique identifier for the property.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the value of the scheduler hint property, which can be of various types (e.g., bool, string, int). The type is indicated by the Value.Type field.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalValue"), + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SchedulerHintAdditionalValue"}, + } +} + +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SchedulerHintAdditionalValue(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SchedulerHintAdditionalValue represents the value of a scheduler hint property. The value can be of various types: Bool, String, or Number. The Type field indicates the type of the value being used.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type represents the type of the value. Valid values are Bool, String, and Number.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "bool": { + SchemaProps: spec.SchemaProps{ + Description: "Bool is the boolean value of the scheduler hint, used when Type is \"Bool\". This field is required if type is 'Bool', and must not be set otherwise.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "number": { + SchemaProps: spec.SchemaProps{ + Description: "Number is the integer value of the scheduler hint, used when Type is \"Number\". This field is required if type is 'Number', and must not be set otherwise.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "string": { + SchemaProps: spec.SchemaProps{ + Description: "String is the string value of the scheduler hint, used when Type is \"String\". This field is required if type is 'String', and must not be set otherwise.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type"}, + }, + }, + } +} + func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SecurityGroupFilter(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/cloud/services/compute/instance.go b/pkg/cloud/services/compute/instance.go index 90284ba926..a47348b0b3 100644 --- a/pkg/cloud/services/compute/instance.go +++ b/pkg/cloud/services/compute/instance.go @@ -107,13 +107,27 @@ func (s *Service) createInstanceImpl(eventObject runtime.Object, instanceSpec *I BlockDevice: blockDevices, } + schedulerAdditionalProperties := make(map[string]interface{}) + + for _, prop := range instanceSpec.SchedulerAdditionalProperties { + switch prop.Value.Type { + case infrav1.SchedulerHintTypeBool: + schedulerAdditionalProperties[prop.Name] = *prop.Value.Bool + case infrav1.SchedulerHintTypeString: + schedulerAdditionalProperties[prop.Name] = *prop.Value.String + case infrav1.SchedulerHintTypeNumber: + schedulerAdditionalProperties[prop.Name] = *prop.Value.Number + } + } + server, err := s.getComputeClient().CreateServer( keypairs.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, KeyName: instanceSpec.SSHKeyName, }, servers.SchedulerHintOpts{ - Group: instanceSpec.ServerGroupID, + Group: instanceSpec.ServerGroupID, + AdditionalProperties: schedulerAdditionalProperties, }, ) if err != nil { diff --git a/pkg/cloud/services/compute/instance_test.go b/pkg/cloud/services/compute/instance_test.go index 53eeb9bae0..88c88a4a48 100644 --- a/pkg/cloud/services/compute/instance_test.go +++ b/pkg/cloud/services/compute/instance_test.go @@ -913,6 +913,90 @@ func TestService_ReconcileInstance(t *testing.T) { }, wantErr: true, }, + { + name: "With custom scheduler hint bool", + getInstanceSpec: func() *InstanceSpec { + s := getDefaultInstanceSpec() + s.SchedulerAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "custom_hint", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeBool, + Bool: ptr.To(true), + }, + }, + } + return s + }, + expect: func(g Gomega, r *recorders) { + expectDefaultFlavor(r.compute) + createOpts := getDefaultServerCreateOpts() + schedulerHintOpts := servers.SchedulerHintOpts{ + Group: serverGroupUUID, + AdditionalProperties: map[string]any{ + "custom_hint": true, + }, + } + expectCreateServer(g, r.compute, withSSHKey(createOpts), schedulerHintOpts, false) + }, + wantErr: false, + }, + { + name: "With custom scheduler hint number", + getInstanceSpec: func() *InstanceSpec { + s := getDefaultInstanceSpec() + s.SchedulerAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "custom_hint", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeNumber, + Number: ptr.To(1), + }, + }, + } + return s + }, + expect: func(g Gomega, r *recorders) { + expectDefaultFlavor(r.compute) + createOpts := getDefaultServerCreateOpts() + schedulerHintOpts := servers.SchedulerHintOpts{ + Group: serverGroupUUID, + AdditionalProperties: map[string]any{ + "custom_hint": 1, + }, + } + expectCreateServer(g, r.compute, withSSHKey(createOpts), schedulerHintOpts, false) + }, + wantErr: false, + }, + { + name: "With custom scheduler hint string", + getInstanceSpec: func() *InstanceSpec { + s := getDefaultInstanceSpec() + s.SchedulerAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "custom_hint", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeString, + String: ptr.To("custom hint"), + }, + }, + } + return s + }, + expect: func(g Gomega, r *recorders) { + expectDefaultFlavor(r.compute) + createOpts := getDefaultServerCreateOpts() + schedulerHintOpts := servers.SchedulerHintOpts{ + Group: serverGroupUUID, + AdditionalProperties: map[string]any{ + "custom_hint": "custom hint", + }, + } + expectCreateServer(g, r.compute, withSSHKey(createOpts), schedulerHintOpts, false) + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/cloud/services/compute/instance_types.go b/pkg/cloud/services/compute/instance_types.go index ecf927921e..0322926ed8 100644 --- a/pkg/cloud/services/compute/instance_types.go +++ b/pkg/cloud/services/compute/instance_types.go @@ -30,19 +30,20 @@ import ( // InstanceSpec defines the fields which can be set on a new OpenStack instance. type InstanceSpec struct { - Name string - ImageID string - Flavor string - SSHKeyName string - UserData string - Metadata map[string]string - ConfigDrive bool - FailureDomain string - RootVolume *infrav1.RootVolume - AdditionalBlockDevices []infrav1.AdditionalBlockDevice - ServerGroupID string - Trunk bool - Tags []string + Name string + ImageID string + Flavor string + SSHKeyName string + UserData string + Metadata map[string]string + ConfigDrive bool + FailureDomain string + RootVolume *infrav1.RootVolume + AdditionalBlockDevices []infrav1.AdditionalBlockDevice + ServerGroupID string + Trunk bool + Tags []string + SchedulerAdditionalProperties []infrav1.SchedulerHintAdditionalProperty } // InstanceIdentifier describes an instance which has not necessarily been fetched. diff --git a/pkg/generated/applyconfiguration/api/v1alpha1/openstackserverspec.go b/pkg/generated/applyconfiguration/api/v1alpha1/openstackserverspec.go index d07a3bbdbc..1067d7729b 100644 --- a/pkg/generated/applyconfiguration/api/v1alpha1/openstackserverspec.go +++ b/pkg/generated/applyconfiguration/api/v1alpha1/openstackserverspec.go @@ -26,22 +26,23 @@ import ( // OpenStackServerSpecApplyConfiguration represents an declarative configuration of the OpenStackServerSpec type for use // with apply. type OpenStackServerSpecApplyConfiguration struct { - AdditionalBlockDevices []v1beta1.AdditionalBlockDeviceApplyConfiguration `json:"additionalBlockDevices,omitempty"` - AvailabilityZone *string `json:"availabilityZone,omitempty"` - ConfigDrive *bool `json:"configDrive,omitempty"` - Flavor *string `json:"flavor,omitempty"` - FloatingIPPoolRef *v1.TypedLocalObjectReference `json:"floatingIPPoolRef,omitempty"` - IdentityRef *v1beta1.OpenStackIdentityReferenceApplyConfiguration `json:"identityRef,omitempty"` - Image *v1beta1.ImageParamApplyConfiguration `json:"image,omitempty"` - Ports []v1beta1.PortOptsApplyConfiguration `json:"ports,omitempty"` - RootVolume *v1beta1.RootVolumeApplyConfiguration `json:"rootVolume,omitempty"` - SSHKeyName *string `json:"sshKeyName,omitempty"` - SecurityGroups []v1beta1.SecurityGroupParamApplyConfiguration `json:"securityGroups,omitempty"` - ServerGroup *v1beta1.ServerGroupParamApplyConfiguration `json:"serverGroup,omitempty"` - ServerMetadata []v1beta1.ServerMetadataApplyConfiguration `json:"serverMetadata,omitempty"` - Tags []string `json:"tags,omitempty"` - Trunk *bool `json:"trunk,omitempty"` - UserDataRef *v1.LocalObjectReference `json:"userDataRef,omitempty"` + AdditionalBlockDevices []v1beta1.AdditionalBlockDeviceApplyConfiguration `json:"additionalBlockDevices,omitempty"` + AvailabilityZone *string `json:"availabilityZone,omitempty"` + ConfigDrive *bool `json:"configDrive,omitempty"` + Flavor *string `json:"flavor,omitempty"` + FloatingIPPoolRef *v1.TypedLocalObjectReference `json:"floatingIPPoolRef,omitempty"` + IdentityRef *v1beta1.OpenStackIdentityReferenceApplyConfiguration `json:"identityRef,omitempty"` + Image *v1beta1.ImageParamApplyConfiguration `json:"image,omitempty"` + Ports []v1beta1.PortOptsApplyConfiguration `json:"ports,omitempty"` + RootVolume *v1beta1.RootVolumeApplyConfiguration `json:"rootVolume,omitempty"` + SSHKeyName *string `json:"sshKeyName,omitempty"` + SecurityGroups []v1beta1.SecurityGroupParamApplyConfiguration `json:"securityGroups,omitempty"` + ServerGroup *v1beta1.ServerGroupParamApplyConfiguration `json:"serverGroup,omitempty"` + ServerMetadata []v1beta1.ServerMetadataApplyConfiguration `json:"serverMetadata,omitempty"` + Tags []string `json:"tags,omitempty"` + Trunk *bool `json:"trunk,omitempty"` + UserDataRef *v1.LocalObjectReference `json:"userDataRef,omitempty"` + SchedulerHintAdditionalProperties []v1beta1.SchedulerHintAdditionalPropertyApplyConfiguration `json:"schedulerHintAdditionalProperties,omitempty"` } // OpenStackServerSpecApplyConfiguration constructs an declarative configuration of the OpenStackServerSpec type for use with @@ -199,3 +200,16 @@ func (b *OpenStackServerSpecApplyConfiguration) WithUserDataRef(value v1.LocalOb b.UserDataRef = &value return b } + +// WithSchedulerHintAdditionalProperties adds the given value to the SchedulerHintAdditionalProperties field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SchedulerHintAdditionalProperties field. +func (b *OpenStackServerSpecApplyConfiguration) WithSchedulerHintAdditionalProperties(values ...*v1beta1.SchedulerHintAdditionalPropertyApplyConfiguration) *OpenStackServerSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSchedulerHintAdditionalProperties") + } + b.SchedulerHintAdditionalProperties = append(b.SchedulerHintAdditionalProperties, *values[i]) + } + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinespec.go b/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinespec.go index 6a80ea4093..b495fdf89e 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinespec.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinespec.go @@ -25,21 +25,22 @@ import ( // OpenStackMachineSpecApplyConfiguration represents an declarative configuration of the OpenStackMachineSpec type for use // with apply. type OpenStackMachineSpecApplyConfiguration struct { - ProviderID *string `json:"providerID,omitempty"` - Flavor *string `json:"flavor,omitempty"` - Image *ImageParamApplyConfiguration `json:"image,omitempty"` - SSHKeyName *string `json:"sshKeyName,omitempty"` - Ports []PortOptsApplyConfiguration `json:"ports,omitempty"` - SecurityGroups []SecurityGroupParamApplyConfiguration `json:"securityGroups,omitempty"` - Trunk *bool `json:"trunk,omitempty"` - Tags []string `json:"tags,omitempty"` - ServerMetadata []ServerMetadataApplyConfiguration `json:"serverMetadata,omitempty"` - ConfigDrive *bool `json:"configDrive,omitempty"` - RootVolume *RootVolumeApplyConfiguration `json:"rootVolume,omitempty"` - AdditionalBlockDevices []AdditionalBlockDeviceApplyConfiguration `json:"additionalBlockDevices,omitempty"` - ServerGroup *ServerGroupParamApplyConfiguration `json:"serverGroup,omitempty"` - IdentityRef *OpenStackIdentityReferenceApplyConfiguration `json:"identityRef,omitempty"` - FloatingIPPoolRef *v1.TypedLocalObjectReference `json:"floatingIPPoolRef,omitempty"` + ProviderID *string `json:"providerID,omitempty"` + Flavor *string `json:"flavor,omitempty"` + Image *ImageParamApplyConfiguration `json:"image,omitempty"` + SSHKeyName *string `json:"sshKeyName,omitempty"` + Ports []PortOptsApplyConfiguration `json:"ports,omitempty"` + SecurityGroups []SecurityGroupParamApplyConfiguration `json:"securityGroups,omitempty"` + Trunk *bool `json:"trunk,omitempty"` + Tags []string `json:"tags,omitempty"` + ServerMetadata []ServerMetadataApplyConfiguration `json:"serverMetadata,omitempty"` + ConfigDrive *bool `json:"configDrive,omitempty"` + RootVolume *RootVolumeApplyConfiguration `json:"rootVolume,omitempty"` + AdditionalBlockDevices []AdditionalBlockDeviceApplyConfiguration `json:"additionalBlockDevices,omitempty"` + ServerGroup *ServerGroupParamApplyConfiguration `json:"serverGroup,omitempty"` + IdentityRef *OpenStackIdentityReferenceApplyConfiguration `json:"identityRef,omitempty"` + FloatingIPPoolRef *v1.TypedLocalObjectReference `json:"floatingIPPoolRef,omitempty"` + SchedulerHintAdditionalProperties []SchedulerHintAdditionalPropertyApplyConfiguration `json:"schedulerHintAdditionalProperties,omitempty"` } // OpenStackMachineSpecApplyConfiguration constructs an declarative configuration of the OpenStackMachineSpec type for use with @@ -189,3 +190,16 @@ func (b *OpenStackMachineSpecApplyConfiguration) WithFloatingIPPoolRef(value v1. b.FloatingIPPoolRef = &value return b } + +// WithSchedulerHintAdditionalProperties adds the given value to the SchedulerHintAdditionalProperties field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SchedulerHintAdditionalProperties field. +func (b *OpenStackMachineSpecApplyConfiguration) WithSchedulerHintAdditionalProperties(values ...*SchedulerHintAdditionalPropertyApplyConfiguration) *OpenStackMachineSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSchedulerHintAdditionalProperties") + } + b.SchedulerHintAdditionalProperties = append(b.SchedulerHintAdditionalProperties, *values[i]) + } + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/schedulerhintadditionalproperty.go b/pkg/generated/applyconfiguration/api/v1beta1/schedulerhintadditionalproperty.go new file mode 100644 index 0000000000..56b5173a51 --- /dev/null +++ b/pkg/generated/applyconfiguration/api/v1beta1/schedulerhintadditionalproperty.go @@ -0,0 +1,48 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1beta1 + +// SchedulerHintAdditionalPropertyApplyConfiguration represents an declarative configuration of the SchedulerHintAdditionalProperty type for use +// with apply. +type SchedulerHintAdditionalPropertyApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Value *SchedulerHintAdditionalValueApplyConfiguration `json:"value,omitempty"` +} + +// SchedulerHintAdditionalPropertyApplyConfiguration constructs an declarative configuration of the SchedulerHintAdditionalProperty type for use with +// apply. +func SchedulerHintAdditionalProperty() *SchedulerHintAdditionalPropertyApplyConfiguration { + return &SchedulerHintAdditionalPropertyApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *SchedulerHintAdditionalPropertyApplyConfiguration) WithName(value string) *SchedulerHintAdditionalPropertyApplyConfiguration { + b.Name = &value + return b +} + +// WithValue sets the Value field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Value field is set to the value of the last call. +func (b *SchedulerHintAdditionalPropertyApplyConfiguration) WithValue(value *SchedulerHintAdditionalValueApplyConfiguration) *SchedulerHintAdditionalPropertyApplyConfiguration { + b.Value = value + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/schedulerhintadditionalvalue.go b/pkg/generated/applyconfiguration/api/v1beta1/schedulerhintadditionalvalue.go new file mode 100644 index 0000000000..abde2eca0c --- /dev/null +++ b/pkg/generated/applyconfiguration/api/v1beta1/schedulerhintadditionalvalue.go @@ -0,0 +1,70 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1beta1 + +import ( + v1beta1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" +) + +// SchedulerHintAdditionalValueApplyConfiguration represents an declarative configuration of the SchedulerHintAdditionalValue type for use +// with apply. +type SchedulerHintAdditionalValueApplyConfiguration struct { + Type *v1beta1.SchedulerHintValueType `json:"type,omitempty"` + Bool *bool `json:"bool,omitempty"` + Number *int `json:"number,omitempty"` + String *string `json:"string,omitempty"` +} + +// SchedulerHintAdditionalValueApplyConfiguration constructs an declarative configuration of the SchedulerHintAdditionalValue type for use with +// apply. +func SchedulerHintAdditionalValue() *SchedulerHintAdditionalValueApplyConfiguration { + return &SchedulerHintAdditionalValueApplyConfiguration{} +} + +// WithType sets the Type field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Type field is set to the value of the last call. +func (b *SchedulerHintAdditionalValueApplyConfiguration) WithType(value v1beta1.SchedulerHintValueType) *SchedulerHintAdditionalValueApplyConfiguration { + b.Type = &value + return b +} + +// WithBool sets the Bool field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Bool field is set to the value of the last call. +func (b *SchedulerHintAdditionalValueApplyConfiguration) WithBool(value bool) *SchedulerHintAdditionalValueApplyConfiguration { + b.Bool = &value + return b +} + +// WithNumber sets the Number field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Number field is set to the value of the last call. +func (b *SchedulerHintAdditionalValueApplyConfiguration) WithNumber(value int) *SchedulerHintAdditionalValueApplyConfiguration { + b.Number = &value + return b +} + +// WithString sets the String field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the String field is set to the value of the last call. +func (b *SchedulerHintAdditionalValueApplyConfiguration) WithString(value string) *SchedulerHintAdditionalValueApplyConfiguration { + b.String = &value + return b +} diff --git a/pkg/generated/applyconfiguration/internal/internal.go b/pkg/generated/applyconfiguration/internal/internal.go index 2350bc7f1a..f9fd93ba0d 100644 --- a/pkg/generated/applyconfiguration/internal/internal.go +++ b/pkg/generated/applyconfiguration/internal/internal.go @@ -262,6 +262,14 @@ var schemaYAML = typed.YAMLObject(`types: - name: rootVolume type: namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.RootVolume + - name: schedulerHintAdditionalProperties + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SchedulerHintAdditionalProperty + elementRelationship: associative + keys: + - name - name: securityGroups type: list: @@ -2710,6 +2718,14 @@ var schemaYAML = typed.YAMLObject(`types: - name: rootVolume type: namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.RootVolume + - name: schedulerHintAdditionalProperties + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SchedulerHintAdditionalProperty + elementRelationship: associative + keys: + - name - name: securityGroups type: list: @@ -3065,6 +3081,33 @@ var schemaYAML = typed.YAMLObject(`types: - name: id type: scalar: string +- name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SchedulerHintAdditionalProperty + map: + fields: + - name: name + type: + scalar: string + default: "" + - name: value + type: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SchedulerHintAdditionalValue + default: {} +- name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SchedulerHintAdditionalValue + map: + fields: + - name: bool + type: + scalar: boolean + - name: number + type: + scalar: numeric + - name: string + type: + scalar: string + - name: type + type: + scalar: string + default: "" - name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SecurityGroupFilter map: fields: diff --git a/pkg/generated/applyconfiguration/utils.go b/pkg/generated/applyconfiguration/utils.go index 4fa234c0fc..ab07b20a40 100644 --- a/pkg/generated/applyconfiguration/utils.go +++ b/pkg/generated/applyconfiguration/utils.go @@ -283,6 +283,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1beta1.RouterFilterApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("RouterParam"): return &apiv1beta1.RouterParamApplyConfiguration{} + case v1beta1.SchemeGroupVersion.WithKind("SchedulerHintAdditionalProperty"): + return &apiv1beta1.SchedulerHintAdditionalPropertyApplyConfiguration{} + case v1beta1.SchemeGroupVersion.WithKind("SchedulerHintAdditionalValue"): + return &apiv1beta1.SchedulerHintAdditionalValueApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("SecurityGroupFilter"): return &apiv1beta1.SecurityGroupFilterApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("SecurityGroupParam"): diff --git a/test/e2e/suites/apivalidations/openstackmachine_test.go b/test/e2e/suites/apivalidations/openstackmachine_test.go index 4faad1d789..83eaf23d48 100644 --- a/test/e2e/suites/apivalidations/openstackmachine_test.go +++ b/test/e2e/suites/apivalidations/openstackmachine_test.go @@ -354,6 +354,158 @@ var _ = Describe("OpenStackMachine API validations", func() { }) }) + Context("schedulerHints", func() { + It("should allow empty schedulerHints", func() { + machine := defaultMachine() + machine.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{} + Expect(k8sClient.Create(ctx, machine)).To(Succeed(), "Creating a machine with an empty SchedulerHintAdditionalProperties should succeed.") + }) + + It("should not allow item with empty name", func() { + machine := defaultMachine() + machine.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeBool, + Bool: ptr.To(false), + }, + }, + } + Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "Creating a machine with SchedulerHintAdditionalProperties including an item with empty name should fail.") + }) + + It("should not allow item with empty value", func() { + machine := defaultMachine() + machine.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + }, + } + Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "Creating a machine with SchedulerHintAdditionalProperties including an item with empty value should fail.") + }) + + It("should allow correct SchedulerHintAdditionalProperties", func() { + machineB := defaultMachine() + machineB.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeBool, + Bool: ptr.To(true), + }, + }, + } + By("Creating SchedulerHint with bool type") + Expect(k8sClient.Create(ctx, machineB)).To(Succeed(), "Creating a machine with bool type scheduler hint property should succeed.") + machineN := defaultMachine() + machineN.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeNumber, + Number: ptr.To(1), + }, + }, + } + By("Creating SchedulerHint with number type") + Expect(k8sClient.Create(ctx, machineN)).To(Succeed(), "Creating a machine with number type scheduler hint property should succeed.") + machineS := defaultMachine() + machineS.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeString, + String: ptr.To("test-hint"), + }, + }, + } + By("Creating SchedulerHint with string type") + Expect(k8sClient.Create(ctx, machineS)).To(Succeed(), "Creating a machine with string type scheduler hint property should succeed.") + }) + + It("should not allow incorrect SchedulerHintAdditionalProperties with bool type", func() { + machineBN := defaultMachine() + machineBN.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeBool, + Number: ptr.To(1), + }, + }, + } + By("Creating SchedulerHint with bool type and number value") + Expect(k8sClient.Create(ctx, machineBN)).NotTo(Succeed(), "Creating a machine with bool type but number value scheduler hint property should fail.") + machineBS := defaultMachine() + machineBS.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeBool, + String: ptr.To("test-hint"), + }, + }, + } + By("Creating SchedulerHint with bool type and string value") + Expect(k8sClient.Create(ctx, machineBS)).NotTo(Succeed(), "Creating a machine with bool type but string value scheduler hint property should fail.") + }) + + It("should not allow incorrect SchedulerHintAdditionalProperties with number type", func() { + machineNB := defaultMachine() + machineNB.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeNumber, + Bool: ptr.To(true), + }, + }, + } + By("Creating SchedulerHint with number type and bool value") + Expect(k8sClient.Create(ctx, machineNB)).NotTo(Succeed(), "Creating a machine with number type but bool value scheduler hint property should fail.") + machineNS := defaultMachine() + machineNS.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeNumber, + String: ptr.To("test-hint"), + }, + }, + } + By("Creating SchedulerHint with number type and string value") + Expect(k8sClient.Create(ctx, machineNS)).NotTo(Succeed(), "Creating a machine with number type but string value scheduler hint property should fail.") + }) + + It("should not allow incorrect SchedulerHintAdditionalProperties with string type", func() { + machineSB := defaultMachine() + machineSB.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeString, + Bool: ptr.To(true), + }, + }, + } + By("Creating SchedulerHint with string type and bool value") + Expect(k8sClient.Create(ctx, machineSB)).NotTo(Succeed(), "Creating a machine with string type but bool value scheduler hint property should fail.") + machineSN := defaultMachine() + machineSN.Spec.SchedulerHintAdditionalProperties = []infrav1.SchedulerHintAdditionalProperty{ + { + Name: "test-hints", + Value: infrav1.SchedulerHintAdditionalValue{ + Type: infrav1.SchedulerHintTypeString, + Number: ptr.To(1), + }, + }, + } + By("Creating SchedulerHint with string type and number value") + Expect(k8sClient.Create(ctx, machineSN)).NotTo(Succeed(), "Creating a machine with string type but number value scheduler hint property should fail.") + }) + }) + Context("v1alpha7", func() { It("should downgrade cleanly from infrav1", func() { infrav1Machine := &infrav1.OpenStackMachine{}