diff --git a/api/v1alpha1/common_types.go b/api/v1alpha1/common_types.go
index 4afeb5fbb..3f3e018ec 100644
--- a/api/v1alpha1/common_types.go
+++ b/api/v1alpha1/common_types.go
@@ -101,3 +101,27 @@ type KubernetesNameRef string
// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:MaxLength:=64
type KeystoneName string
+
+type ExtraSpec struct {
+ // name is the name of the extraspec
+ // +kubebuilder:validation:MaxLength:=255
+ // +required
+ Name string `json:"name"`
+
+ // value is the value of the extraspec
+ // +kubebuilder:validation:MaxLength:=255
+ // +required
+ Value string `json:"value"`
+}
+
+type ExtraSpecStatus struct {
+ // name is the name of the extraspec
+ // +kubebuilder:validation:MaxLength:=255
+ // +optional
+ Name string `json:"name,omitempty"`
+
+ // value is the value of the extraspec
+ // +kubebuilder:validation:MaxLength:=255
+ // +optional
+ Value string `json:"value,omitempty"`
+}
diff --git a/api/v1alpha1/flavor_types.go b/api/v1alpha1/flavor_types.go
index 4ebbeccf9..648094c5f 100644
--- a/api/v1alpha1/flavor_types.go
+++ b/api/v1alpha1/flavor_types.go
@@ -17,10 +17,10 @@ limitations under the License.
package v1alpha1
// FlavorResourceSpec contains the desired state of a flavor
-// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="FlavorResourceSpec is immutable"
type FlavorResourceSpec struct {
// name will be the name of the created resource. If not specified, the
// name of the ORC object will be used.
+ // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable"
// +optional
Name *OpenStackName `json:"name,omitempty"`
@@ -29,22 +29,26 @@ type FlavorResourceSpec struct {
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=255
// +kubebuilder:validation:Pattern=^[a-zA-Z0-9._-]([a-zA-Z0-9. _-]*[a-zA-Z0-9._-])?$
+ // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="id is immutable"
// +optional
ID string `json:"id,omitempty"` //nolint:kubeapilinter // intentionally allow raw ID
// description contains a free form description of the flavor.
// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:MaxLength:=65535
+ // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="description is immutable"
// +optional
Description *string `json:"description,omitempty"`
// ram is the memory of the flavor, measured in MB.
// +kubebuilder:validation:Minimum=1
+ // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="ram is immutable"
// +required
RAM int32 `json:"ram,omitempty"`
// vcpus is the number of vcpus for the flavor.
// +kubebuilder:validation:Minimum=1
+ // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="vcpus is immutable"
// +required
Vcpus int32 `json:"vcpus,omitempty"`
@@ -57,16 +61,25 @@ type FlavorResourceSpec struct {
// zero root disk via the
// os_compute_api:servers:create:zero_disk_flavor policy rule.
// +kubebuilder:validation:Minimum=0
+ // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="disk is immutable"
// +required
Disk int32 `json:"disk"`
// swap is the size of a dedicated swap disk that will be allocated, in
// MiB. If 0 (the default), no dedicated swap disk will be created.
// +kubebuilder:validation:Minimum=0
+ // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="swap is immutable"
// +optional
Swap int32 `json:"swap,omitempty"`
+ // extraSpecs is a map of key-value pairs that define extra specifications for the flavor.
+ // +kubebuilder:validation:MaxItems:=128
+ // +listType=atomic
+ // +optional
+ ExtraSpecs []ExtraSpec `json:"extraSpecs,omitempty"`
+
// isPublic flags a flavor as being available to all projects or not.
+ // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="isPublic is immutable"
// +optional
IsPublic *bool `json:"isPublic,omitempty"`
@@ -75,6 +88,7 @@ type FlavorResourceSpec struct {
// be used as a scratch space for applications that are aware of its
// limitations. Defaults to 0.
// +kubebuilder:validation:Minimum=0
+ // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="ephemeral is immutable"
// +optional
Ephemeral int32 `json:"ephemeral,omitempty"`
}
@@ -131,6 +145,12 @@ type FlavorResourceStatus struct {
// +optional
Swap *int32 `json:"swap,omitempty"`
+ // extraSpecs is a map of key-value pairs that define extra specifications for the flavor.
+ // +kubebuilder:validation:MaxItems:=128
+ // +listType=atomic
+ // +optional
+ ExtraSpecs []ExtraSpecStatus `json:"extraSpecs"`
+
// isPublic flags a flavor as being available to all projects or not.
// +optional
IsPublic *bool `json:"isPublic,omitempty"`
diff --git a/api/v1alpha1/volumetype_types.go b/api/v1alpha1/volumetype_types.go
index 76bdb210b..c7fc9b35a 100644
--- a/api/v1alpha1/volumetype_types.go
+++ b/api/v1alpha1/volumetype_types.go
@@ -33,7 +33,7 @@ type VolumeTypeResourceSpec struct {
// +kubebuilder:validation:MaxItems:=64
// +listType=atomic
// +optional
- ExtraSpecs []VolumeTypeExtraSpec `json:"extraSpecs,omitempty"`
+ ExtraSpecs []ExtraSpec `json:"extraSpecs,omitempty"`
// isPublic indicates whether the volume type is public.
// +optional
@@ -74,33 +74,9 @@ type VolumeTypeResourceStatus struct {
// +kubebuilder:validation:MaxItems:=64
// +listType=atomic
// +optional
- ExtraSpecs []VolumeTypeExtraSpecStatus `json:"extraSpecs"`
+ ExtraSpecs []ExtraSpecStatus `json:"extraSpecs"`
// isPublic indicates whether the VolumeType is public.
// +optional
IsPublic *bool `json:"isPublic"`
}
-
-type VolumeTypeExtraSpec struct {
- // name is the name of the extraspec
- // +kubebuilder:validation:MaxLength:=255
- // +required
- Name string `json:"name"`
-
- // value is the value of the extraspec
- // +kubebuilder:validation:MaxLength:=255
- // +required
- Value string `json:"value"`
-}
-
-type VolumeTypeExtraSpecStatus struct {
- // name is the name of the extraspec
- // +kubebuilder:validation:MaxLength:=255
- // +optional
- Name string `json:"name,omitempty"`
-
- // value is the value of the extraspec
- // +kubebuilder:validation:MaxLength:=255
- // +optional
- Value string `json:"value,omitempty"`
-}
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index ead3ef708..03c840141 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -1162,6 +1162,36 @@ func (in *ExternalGatewayStatus) DeepCopy() *ExternalGatewayStatus {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ExtraSpec) DeepCopyInto(out *ExtraSpec) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraSpec.
+func (in *ExtraSpec) DeepCopy() *ExtraSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(ExtraSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ExtraSpecStatus) DeepCopyInto(out *ExtraSpecStatus) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraSpecStatus.
+func (in *ExtraSpecStatus) DeepCopy() *ExtraSpecStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(ExtraSpecStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FilterByKeystoneTags) DeepCopyInto(out *FilterByKeystoneTags) {
*out = *in
@@ -1414,6 +1444,11 @@ func (in *FlavorResourceSpec) DeepCopyInto(out *FlavorResourceSpec) {
*out = new(string)
**out = **in
}
+ if in.ExtraSpecs != nil {
+ in, out := &in.ExtraSpecs, &out.ExtraSpecs
+ *out = make([]ExtraSpec, len(*in))
+ copy(*out, *in)
+ }
if in.IsPublic != nil {
in, out := &in.IsPublic, &out.IsPublic
*out = new(bool)
@@ -1454,6 +1489,11 @@ func (in *FlavorResourceStatus) DeepCopyInto(out *FlavorResourceStatus) {
*out = new(int32)
**out = **in
}
+ if in.ExtraSpecs != nil {
+ in, out := &in.ExtraSpecs, &out.ExtraSpecs
+ *out = make([]ExtraSpecStatus, len(*in))
+ copy(*out, *in)
+ }
if in.IsPublic != nil {
in, out := &in.IsPublic, &out.IsPublic
*out = new(bool)
@@ -7077,36 +7117,6 @@ func (in *VolumeType) DeepCopyObject() runtime.Object {
return nil
}
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *VolumeTypeExtraSpec) DeepCopyInto(out *VolumeTypeExtraSpec) {
- *out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeTypeExtraSpec.
-func (in *VolumeTypeExtraSpec) DeepCopy() *VolumeTypeExtraSpec {
- if in == nil {
- return nil
- }
- out := new(VolumeTypeExtraSpec)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *VolumeTypeExtraSpecStatus) DeepCopyInto(out *VolumeTypeExtraSpecStatus) {
- *out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeTypeExtraSpecStatus.
-func (in *VolumeTypeExtraSpecStatus) DeepCopy() *VolumeTypeExtraSpecStatus {
- if in == nil {
- return nil
- }
- out := new(VolumeTypeExtraSpecStatus)
- in.DeepCopyInto(out)
- return out
-}
-
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeTypeFilter) DeepCopyInto(out *VolumeTypeFilter) {
*out = *in
@@ -7209,7 +7219,7 @@ func (in *VolumeTypeResourceSpec) DeepCopyInto(out *VolumeTypeResourceSpec) {
}
if in.ExtraSpecs != nil {
in, out := &in.ExtraSpecs, &out.ExtraSpecs
- *out = make([]VolumeTypeExtraSpec, len(*in))
+ *out = make([]ExtraSpec, len(*in))
copy(*out, *in)
}
if in.IsPublic != nil {
@@ -7234,7 +7244,7 @@ func (in *VolumeTypeResourceStatus) DeepCopyInto(out *VolumeTypeResourceStatus)
*out = *in
if in.ExtraSpecs != nil {
in, out := &in.ExtraSpecs, &out.ExtraSpecs
- *out = make([]VolumeTypeExtraSpecStatus, len(*in))
+ *out = make([]ExtraSpecStatus, len(*in))
copy(*out, *in)
}
if in.IsPublic != nil {
diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go
index 5601a14eb..7ba520cd9 100644
--- a/cmd/models-schema/zz_generated.openapi.go
+++ b/cmd/models-schema/zz_generated.openapi.go
@@ -73,6 +73,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.EndpointStatus": schema_openstack_resource_controller_v2_api_v1alpha1_EndpointStatus(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExternalGateway": schema_openstack_resource_controller_v2_api_v1alpha1_ExternalGateway(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExternalGatewayStatus": schema_openstack_resource_controller_v2_api_v1alpha1_ExternalGatewayStatus(ref),
+ "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpec": schema_openstack_resource_controller_v2_api_v1alpha1_ExtraSpec(ref),
+ "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpecStatus": schema_openstack_resource_controller_v2_api_v1alpha1_ExtraSpecStatus(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.FilterByKeystoneTags": schema_openstack_resource_controller_v2_api_v1alpha1_FilterByKeystoneTags(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.FilterByNeutronTags": schema_openstack_resource_controller_v2_api_v1alpha1_FilterByNeutronTags(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.FilterByServerTags": schema_openstack_resource_controller_v2_api_v1alpha1_FilterByServerTags(ref),
@@ -271,8 +273,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeSpec": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeSpec(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeStatus": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeStatus(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeType": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeType(ref),
- "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeTypeExtraSpec": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeExtraSpec(ref),
- "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeTypeExtraSpecStatus": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeExtraSpecStatus(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeTypeFilter": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeFilter(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeTypeImport": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeImport(ref),
"github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeTypeList": schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeList(ref),
@@ -2369,6 +2369,61 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_ExternalGatewayStatus(
}
}
+func schema_openstack_resource_controller_v2_api_v1alpha1_ExtraSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "name": {
+ SchemaProps: spec.SchemaProps{
+ Description: "name is the name of the extraspec",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "value": {
+ SchemaProps: spec.SchemaProps{
+ Description: "value is the value of the extraspec",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"name", "value"},
+ },
+ },
+ }
+}
+
+func schema_openstack_resource_controller_v2_api_v1alpha1_ExtraSpecStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "name": {
+ SchemaProps: spec.SchemaProps{
+ Description: "name is the name of the extraspec",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "value": {
+ SchemaProps: spec.SchemaProps{
+ Description: "value is the value of the extraspec",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
func schema_openstack_resource_controller_v2_api_v1alpha1_FilterByKeystoneTags(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -2899,6 +2954,25 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_FlavorResourceSpec(ref
Format: "int32",
},
},
+ "extraSpecs": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-type": "atomic",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "extraSpecs is a map of key-value pairs that define extra specifications for the flavor.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpec"),
+ },
+ },
+ },
+ },
+ },
"isPublic": {
SchemaProps: spec.SchemaProps{
Description: "isPublic flags a flavor as being available to all projects or not.",
@@ -2917,6 +2991,8 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_FlavorResourceSpec(ref
Required: []string{"ram", "vcpus", "disk"},
},
},
+ Dependencies: []string{
+ "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpec"},
}
}
@@ -2969,6 +3045,25 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_FlavorResourceStatus(r
Format: "int32",
},
},
+ "extraSpecs": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-type": "atomic",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "extraSpecs is a map of key-value pairs that define extra specifications for the flavor.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpecStatus"),
+ },
+ },
+ },
+ },
+ },
"isPublic": {
SchemaProps: spec.SchemaProps{
Description: "isPublic flags a flavor as being available to all projects or not.",
@@ -2986,6 +3081,8 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_FlavorResourceStatus(r
},
},
},
+ Dependencies: []string{
+ "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpecStatus"},
}
}
@@ -13464,61 +13561,6 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_VolumeType(ref common.
}
}
-func schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeExtraSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "name": {
- SchemaProps: spec.SchemaProps{
- Description: "name is the name of the extraspec",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "value": {
- SchemaProps: spec.SchemaProps{
- Description: "value is the value of the extraspec",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"name", "value"},
- },
- },
- }
-}
-
-func schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeExtraSpecStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "name": {
- SchemaProps: spec.SchemaProps{
- Description: "name is the name of the extraspec",
- Type: []string{"string"},
- Format: "",
- },
- },
- "value": {
- SchemaProps: spec.SchemaProps{
- Description: "value is the value of the extraspec",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- },
- },
- }
-}
-
func schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeFilter(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -13666,7 +13708,7 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeResourceSpec
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
- Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeTypeExtraSpec"),
+ Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpec"),
},
},
},
@@ -13683,7 +13725,7 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeResourceSpec
},
},
Dependencies: []string{
- "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeTypeExtraSpec"},
+ "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpec"},
}
}
@@ -13721,7 +13763,7 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeResourceStat
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
- Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeTypeExtraSpecStatus"),
+ Ref: ref("github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpecStatus"),
},
},
},
@@ -13738,7 +13780,7 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_VolumeTypeResourceStat
},
},
Dependencies: []string{
- "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.VolumeTypeExtraSpecStatus"},
+ "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1.ExtraSpecStatus"},
}
}
diff --git a/config/crd/bases/openstack.k-orc.cloud_flavors.yaml b/config/crd/bases/openstack.k-orc.cloud_flavors.yaml
index d85dd72f1..3223e7208 100644
--- a/config/crd/bases/openstack.k-orc.cloud_flavors.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_flavors.yaml
@@ -167,6 +167,9 @@ spec:
maxLength: 65535
minLength: 1
type: string
+ x-kubernetes-validations:
+ - message: description is immutable
+ rule: self == oldSelf
disk:
description: |-
disk is the size of the root disk that will be created in GiB. If 0
@@ -180,6 +183,9 @@ spec:
format: int32
minimum: 0
type: integer
+ x-kubernetes-validations:
+ - message: disk is immutable
+ rule: self == oldSelf
ephemeral:
description: |-
ephemeral is the size of the ephemeral disk that will be created, in GiB.
@@ -189,6 +195,29 @@ spec:
format: int32
minimum: 0
type: integer
+ x-kubernetes-validations:
+ - message: ephemeral is immutable
+ rule: self == oldSelf
+ extraSpecs:
+ description: extraSpecs is a map of key-value pairs that define
+ extra specifications for the flavor.
+ items:
+ properties:
+ name:
+ description: name is the name of the extraspec
+ maxLength: 255
+ type: string
+ value:
+ description: value is the value of the extraspec
+ maxLength: 255
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ maxItems: 128
+ type: array
+ x-kubernetes-list-type: atomic
id:
description: |-
id will be the id of the created resource. If not specified, a random
@@ -197,10 +226,16 @@ spec:
minLength: 1
pattern: ^[a-zA-Z0-9._-]([a-zA-Z0-9. _-]*[a-zA-Z0-9._-])?$
type: string
+ x-kubernetes-validations:
+ - message: id is immutable
+ rule: self == oldSelf
isPublic:
description: isPublic flags a flavor as being available to all
projects or not.
type: boolean
+ x-kubernetes-validations:
+ - message: isPublic is immutable
+ rule: self == oldSelf
name:
description: |-
name will be the name of the created resource. If not specified, the
@@ -209,11 +244,17 @@ spec:
minLength: 1
pattern: ^[^,]+$
type: string
+ x-kubernetes-validations:
+ - message: name is immutable
+ rule: self == oldSelf
ram:
description: ram is the memory of the flavor, measured in MB.
format: int32
minimum: 1
type: integer
+ x-kubernetes-validations:
+ - message: ram is immutable
+ rule: self == oldSelf
swap:
description: |-
swap is the size of a dedicated swap disk that will be allocated, in
@@ -221,19 +262,22 @@ spec:
format: int32
minimum: 0
type: integer
+ x-kubernetes-validations:
+ - message: swap is immutable
+ rule: self == oldSelf
vcpus:
description: vcpus is the number of vcpus for the flavor.
format: int32
minimum: 1
type: integer
+ x-kubernetes-validations:
+ - message: vcpus is immutable
+ rule: self == oldSelf
required:
- disk
- ram
- vcpus
type: object
- x-kubernetes-validations:
- - message: FlavorResourceSpec is immutable
- rule: self == oldSelf
required:
- cloudCredentialsRef
type: object
@@ -351,6 +395,23 @@ spec:
description: ephemeral is the size of the ephemeral disk, in GiB.
format: int32
type: integer
+ extraSpecs:
+ description: extraSpecs is a map of key-value pairs that define
+ extra specifications for the flavor.
+ items:
+ properties:
+ name:
+ description: name is the name of the extraspec
+ maxLength: 255
+ type: string
+ value:
+ description: value is the value of the extraspec
+ maxLength: 255
+ type: string
+ type: object
+ maxItems: 128
+ type: array
+ x-kubernetes-list-type: atomic
isPublic:
description: isPublic flags a flavor as being available to all
projects or not.
diff --git a/config/samples/openstack_v1alpha1_flavor.yaml b/config/samples/openstack_v1alpha1_flavor.yaml
index 87990fb6c..c46f5bc5e 100644
--- a/config/samples/openstack_v1alpha1_flavor.yaml
+++ b/config/samples/openstack_v1alpha1_flavor.yaml
@@ -15,3 +15,8 @@ spec:
swap: 2
isPublic: false
ephemeral: 1
+ extraSpecs:
+ - name: spec1
+ value: foo
+ - name: spec2
+ value: bar
diff --git a/config/samples/openstack_v1alpha1_volumetype.yaml b/config/samples/openstack_v1alpha1_volumetype.yaml
index 1e8ee928e..77c8d458d 100644
--- a/config/samples/openstack_v1alpha1_volumetype.yaml
+++ b/config/samples/openstack_v1alpha1_volumetype.yaml
@@ -12,5 +12,7 @@ spec:
description: Sample VolumeType
isPublic: false
extraSpecs:
- spec1: "foo"
- spec2: "bar"
+ - name: spec1
+ value: foo
+ - name: spec2
+ value: bar
diff --git a/internal/controllers/flavor/actuator.go b/internal/controllers/flavor/actuator.go
index f80146268..90bceccc4 100644
--- a/internal/controllers/flavor/actuator.go
+++ b/internal/controllers/flavor/actuator.go
@@ -28,6 +28,7 @@ import (
orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1"
generic "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces"
"github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress"
+ "github.com/k-orc/openstack-resource-controller/v2/internal/logging"
osclients "github.com/k-orc/openstack-resource-controller/v2/internal/osclients"
orcerrors "github.com/k-orc/openstack-resource-controller/v2/internal/util/errors"
)
@@ -38,6 +39,7 @@ type (
createResourceActuator = generic.CreateResourceActuator[orcObjectPT, orcObjectT, filterT, osResourceT]
deleteResourceActuator = generic.DeleteResourceActuator[orcObjectPT, orcObjectT, osResourceT]
+ resourceReconciler = generic.ResourceReconciler[orcObjectPT, osResourceT]
helperFactory = generic.ResourceHelperFactory[orcObjectPT, orcObjectT, resourceSpecT, filterT, osResourceT]
)
@@ -45,7 +47,9 @@ type flavorClient interface {
GetFlavor(context.Context, string) (*flavors.Flavor, error)
ListFlavors(context.Context, flavors.ListOptsBuilder) iter.Seq2[*flavors.Flavor, error]
CreateFlavor(context.Context, flavors.CreateOptsBuilder) (*flavors.Flavor, error)
+ CreateFlavorExtraSpecs(context.Context, string, flavors.CreateExtraSpecsOptsBuilder) (map[string]string, error)
DeleteFlavor(context.Context, string) error
+ DeleteFlavorExtraSpec(context.Context, string, string) error
}
type flavorActuator struct {
@@ -164,6 +168,22 @@ func (actuator flavorActuator) CreateResource(ctx context.Context, obj orcObject
return nil, progress.WrapError(err)
}
+ if len(resource.ExtraSpecs) > 0 {
+ extraSpecs := extraSpecsToMap(resource.ExtraSpecs)
+
+ _, err = actuator.osClient.CreateFlavorExtraSpecs(ctx, osResource.ID, flavors.ExtraSpecsOpts(extraSpecs))
+ if err != nil {
+ if !orcerrors.IsRetryable(err) {
+ err = orcerrors.Terminal(
+ orcv1alpha1.ConditionReasonInvalidConfiguration,
+ "invalid configuration creating resource extra specs: "+err.Error(),
+ err,
+ )
+ }
+ return nil, progress.WrapError(err)
+ }
+ }
+
return osResource, nil
}
@@ -171,6 +191,108 @@ func (actuator flavorActuator) DeleteResource(ctx context.Context, _ orcObjectPT
return progress.WrapError(actuator.osClient.DeleteFlavor(ctx, flavor.ID))
}
+func (actuator flavorActuator) updateResource(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus {
+ log := ctrl.LoggerFrom(ctx)
+ resource := obj.Spec.Resource
+ if resource == nil {
+ // Should have been caught by API validation
+ return progress.WrapError(
+ orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Update requested, but spec.resource is not set"))
+ }
+
+ desiredExtraSpecs := extraSpecsToMap(resource.ExtraSpecs)
+ currentExtraSpecs := osResource.ExtraSpecs
+
+ updates := extraSpecUpdates(desiredExtraSpecs, currentExtraSpecs)
+ deletes := extraSpecDeletes(desiredExtraSpecs, currentExtraSpecs)
+
+ if len(updates) == 0 && len(deletes) == 0 {
+ log.V(logging.Debug).Info("No changes")
+ return nil
+ }
+
+ if len(updates) > 0 {
+ _, err := actuator.osClient.CreateFlavorExtraSpecs(
+ ctx,
+ osResource.ID,
+ flavors.ExtraSpecsOpts(updates),
+ )
+ if err != nil {
+ if !orcerrors.IsRetryable(err) {
+ err = orcerrors.Terminal(
+ orcv1alpha1.ConditionReasonInvalidConfiguration,
+ "invalid configuration updating resource extra specs: "+err.Error(),
+ err,
+ )
+ }
+ return progress.WrapError(err)
+ }
+ }
+
+ for _, d := range deletes {
+ if err := actuator.osClient.DeleteFlavorExtraSpec(
+ ctx,
+ osResource.ID,
+ d,
+ ); err != nil {
+ if orcerrors.IsNotFound(err) {
+ continue
+ }
+ if !orcerrors.IsRetryable(err) {
+ err = orcerrors.Terminal(
+ orcv1alpha1.ConditionReasonInvalidConfiguration,
+ "invalid configuration deleting resource extra spec: "+err.Error(),
+ err,
+ )
+ }
+ return progress.WrapError(err)
+ }
+ }
+
+ return progress.NeedsRefresh()
+}
+
+func extraSpecsToMap(extraSpecs []orcv1alpha1.ExtraSpec) map[string]string {
+ specs := make(map[string]string)
+
+ for _, spec := range extraSpecs {
+ specs[spec.Name] = spec.Value
+ }
+
+ return specs
+}
+
+func extraSpecUpdates(desired, current map[string]string) map[string]string {
+ updates := make(map[string]string)
+
+ for k, v := range desired {
+ cur, exists := current[k]
+ if !exists || cur != v {
+ updates[k] = v
+ }
+ }
+
+ return updates
+}
+
+func extraSpecDeletes(desired, current map[string]string) []string {
+ var deletes []string
+
+ for k := range current {
+ if _, found := desired[k]; !found {
+ deletes = append(deletes, k)
+ }
+ }
+
+ return deletes
+}
+
+func (actuator flavorActuator) GetResourceReconcilers(ctx context.Context, orcObject orcObjectPT, osResource *osResourceT, controller generic.ResourceController) ([]resourceReconciler, progress.ReconcileStatus) {
+ return []resourceReconciler{
+ actuator.updateResource,
+ }, nil
+}
+
type flavorHelperFactory struct{}
var _ helperFactory = flavorHelperFactory{}
diff --git a/internal/controllers/flavor/actuator_test.go b/internal/controllers/flavor/actuator_test.go
index 40be7dd4e..85275363e 100644
--- a/internal/controllers/flavor/actuator_test.go
+++ b/internal/controllers/flavor/actuator_test.go
@@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"iter"
+ reflect "reflect"
+ "sort"
"testing"
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors"
@@ -44,10 +46,18 @@ func (l mockFlavorClient) CreateFlavor(_ context.Context, _ flavors.CreateOptsBu
return nil, errNotImplemented
}
+func (l mockFlavorClient) CreateFlavorExtraSpecs(_ context.Context, _ string, _ flavors.CreateExtraSpecsOptsBuilder) (map[string]string, error) {
+ return nil, errNotImplemented
+}
+
func (l mockFlavorClient) DeleteFlavor(_ context.Context, _ string) error {
return errNotImplemented
}
+func (l mockFlavorClient) DeleteFlavorExtraSpec(_ context.Context, _, _ string) error {
+ return errNotImplemented
+}
+
type flavorResult struct {
flavor *flavors.Flavor
err error
@@ -377,3 +387,86 @@ func TestGetFlavorBySpec(t *testing.T) {
})
}
}
+
+func TestExtraSpecUpdates(t *testing.T) {
+ tests := []struct {
+ name string
+ desired map[string]string
+ current map[string]string
+ expected map[string]string
+ }{
+ {
+ name: "No changes",
+ desired: map[string]string{"a": "1"},
+ current: map[string]string{"a": "1"},
+ expected: map[string]string{},
+ },
+ {
+ name: "Create new key",
+ desired: map[string]string{"a": "1"},
+ current: map[string]string{},
+ expected: map[string]string{"a": "1"},
+ },
+ {
+ name: "Update value",
+ desired: map[string]string{"a": "2"},
+ current: map[string]string{"a": "1"},
+ expected: map[string]string{"a": "2"},
+ },
+ {
+ name: "Multiple keys mixed",
+ desired: map[string]string{"a": "2", "b": "1"},
+ current: map[string]string{"a": "1", "c": "9"},
+ expected: map[string]string{"a": "2", "b": "1"},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := extraSpecUpdates(tt.desired, tt.current)
+
+ if !reflect.DeepEqual(got, tt.expected) {
+ t.Errorf("extraSpecUpdates() = %#v, want %#v", got, tt.expected)
+ }
+ })
+ }
+}
+
+func TestExtraSpecDeletes(t *testing.T) {
+ tests := []struct {
+ name string
+ desired map[string]string
+ current map[string]string
+ expected []string
+ }{
+ {
+ name: "No deletes",
+ desired: map[string]string{"a": "1"},
+ current: map[string]string{"a": "1"},
+ expected: nil,
+ },
+ {
+ name: "Delete missing key",
+ desired: map[string]string{},
+ current: map[string]string{"a": "1"},
+ expected: []string{"a"},
+ },
+ {
+ name: "Partial delete",
+ desired: map[string]string{"a": "1"},
+ current: map[string]string{"a": "1", "b": "2"},
+ expected: []string{"b"},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := extraSpecDeletes(tt.desired, tt.current)
+
+ sort.Strings(got)
+ sort.Strings(tt.expected)
+
+ if !reflect.DeepEqual(got, tt.expected) {
+ t.Errorf("extraSpecDeletes() = %#v, want %#v", got, tt.expected)
+ }
+ })
+ }
+}
diff --git a/internal/controllers/flavor/status.go b/internal/controllers/flavor/status.go
index 697df64ef..02cc0040e 100644
--- a/internal/controllers/flavor/status.go
+++ b/internal/controllers/flavor/status.go
@@ -65,6 +65,11 @@ func (flavorStatusWriter) ApplyResourceStatus(_ logr.Logger, osResource *flavors
if osResource.Ephemeral > 0 {
resourceStatus.WithEphemeral(int32(osResource.Ephemeral))
}
+ for k, v := range osResource.ExtraSpecs {
+ resourceStatus.WithExtraSpecs(orcapplyconfigv1alpha1.ExtraSpecStatus().
+ WithName(k).
+ WithValue(v))
+ }
if osResource.Description != "" {
resourceStatus.WithDescription(osResource.Description)
}
diff --git a/internal/controllers/flavor/tests/flavor-create-full/00-assert.yaml b/internal/controllers/flavor/tests/flavor-create-full/00-assert.yaml
index dd94b805e..94a024ae5 100644
--- a/internal/controllers/flavor/tests/flavor-create-full/00-assert.yaml
+++ b/internal/controllers/flavor/tests/flavor-create-full/00-assert.yaml
@@ -13,4 +13,7 @@ status:
disk: 20
swap: 2
isPublic: false
- ephemeral: 1
\ No newline at end of file
+ ephemeral: 1
+ extraSpecs:
+ - name: spec
+ value: specValue
diff --git a/internal/controllers/flavor/tests/flavor-create-full/00-create-resource.yaml b/internal/controllers/flavor/tests/flavor-create-full/00-create-resource.yaml
index 470180d72..de6789f29 100644
--- a/internal/controllers/flavor/tests/flavor-create-full/00-create-resource.yaml
+++ b/internal/controllers/flavor/tests/flavor-create-full/00-create-resource.yaml
@@ -18,3 +18,6 @@ spec:
isPublic: false
ephemeral: 1
id: testId-123
+ extraSpecs:
+ - name: spec
+ value: specValue
diff --git a/internal/controllers/flavor/tests/flavor-update/00-assert.yaml b/internal/controllers/flavor/tests/flavor-update/00-assert.yaml
new file mode 100644
index 000000000..ffe085e81
--- /dev/null
+++ b/internal/controllers/flavor/tests/flavor-update/00-assert.yaml
@@ -0,0 +1,29 @@
+---
+apiVersion: kuttl.dev/v1beta1
+kind: TestAssert
+resourceRefs:
+ - apiVersion: openstack.k-orc.cloud/v1alpha1
+ kind: Flavor
+ name: flavor-update
+ ref: flavor
+assertAll:
+ - celExpr: "!has(flavor.status.resource.extraSpecs)"
+---
+apiVersion: openstack.k-orc.cloud/v1alpha1
+kind: Flavor
+metadata:
+ name: flavor-update
+status:
+ resource:
+ disk: 0
+ isPublic: true
+ name: flavor-update
+ ram: 1
+ vcpus: 2
+ conditions:
+ - type: Available
+ status: "True"
+ reason: Success
+ - type: Progressing
+ status: "False"
+ reason: Success
diff --git a/internal/controllers/flavor/tests/flavor-update/00-minimal-resource.yaml b/internal/controllers/flavor/tests/flavor-update/00-minimal-resource.yaml
new file mode 100644
index 000000000..827048769
--- /dev/null
+++ b/internal/controllers/flavor/tests/flavor-update/00-minimal-resource.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: openstack.k-orc.cloud/v1alpha1
+kind: Flavor
+metadata:
+ name: flavor-update
+spec:
+ cloudCredentialsRef:
+ cloudName: openstack-admin
+ secretName: openstack-clouds
+ managementPolicy: managed
+ resource:
+ ram: 1
+ vcpus: 2
+ disk: 0
diff --git a/internal/controllers/flavor/tests/flavor-update/00-prerequisites.yaml b/internal/controllers/flavor/tests/flavor-update/00-prerequisites.yaml
new file mode 100644
index 000000000..f0fb63e85
--- /dev/null
+++ b/internal/controllers/flavor/tests/flavor-update/00-prerequisites.yaml
@@ -0,0 +1,5 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+commands:
+ - command: kubectl create secret generic openstack-clouds --from-file=clouds.yaml=${E2E_KUTTL_OSCLOUDS} ${E2E_KUTTL_CACERT_OPT}
+ namespaced: true
diff --git a/internal/controllers/flavor/tests/flavor-update/01-assert.yaml b/internal/controllers/flavor/tests/flavor-update/01-assert.yaml
new file mode 100644
index 000000000..b98226b93
--- /dev/null
+++ b/internal/controllers/flavor/tests/flavor-update/01-assert.yaml
@@ -0,0 +1,22 @@
+---
+apiVersion: openstack.k-orc.cloud/v1alpha1
+kind: Flavor
+metadata:
+ name: flavor-update
+status:
+ resource:
+ disk: 0
+ extraSpecs:
+ - name: spec
+ value: specValue
+ isPublic: true
+ name: flavor-update
+ ram: 1
+ vcpus: 2
+ conditions:
+ - type: Available
+ status: "True"
+ reason: Success
+ - type: Progressing
+ status: "False"
+ reason: Success
diff --git a/internal/controllers/flavor/tests/flavor-update/01-updated-resource.yaml b/internal/controllers/flavor/tests/flavor-update/01-updated-resource.yaml
new file mode 100644
index 000000000..9276df64b
--- /dev/null
+++ b/internal/controllers/flavor/tests/flavor-update/01-updated-resource.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: openstack.k-orc.cloud/v1alpha1
+kind: Flavor
+metadata:
+ name: flavor-update
+spec:
+ cloudCredentialsRef:
+ cloudName: openstack-admin
+ secretName: openstack-clouds
+ managementPolicy: managed
+ resource:
+ extraSpecs:
+ - name: spec
+ value: specValue
diff --git a/internal/controllers/flavor/tests/flavor-update/02-assert.yaml b/internal/controllers/flavor/tests/flavor-update/02-assert.yaml
new file mode 100644
index 000000000..ffe085e81
--- /dev/null
+++ b/internal/controllers/flavor/tests/flavor-update/02-assert.yaml
@@ -0,0 +1,29 @@
+---
+apiVersion: kuttl.dev/v1beta1
+kind: TestAssert
+resourceRefs:
+ - apiVersion: openstack.k-orc.cloud/v1alpha1
+ kind: Flavor
+ name: flavor-update
+ ref: flavor
+assertAll:
+ - celExpr: "!has(flavor.status.resource.extraSpecs)"
+---
+apiVersion: openstack.k-orc.cloud/v1alpha1
+kind: Flavor
+metadata:
+ name: flavor-update
+status:
+ resource:
+ disk: 0
+ isPublic: true
+ name: flavor-update
+ ram: 1
+ vcpus: 2
+ conditions:
+ - type: Available
+ status: "True"
+ reason: Success
+ - type: Progressing
+ status: "False"
+ reason: Success
diff --git a/internal/controllers/flavor/tests/flavor-update/02-reverted-resource.yaml b/internal/controllers/flavor/tests/flavor-update/02-reverted-resource.yaml
new file mode 100644
index 000000000..2c6c253ff
--- /dev/null
+++ b/internal/controllers/flavor/tests/flavor-update/02-reverted-resource.yaml
@@ -0,0 +1,7 @@
+# NOTE: kuttl only does patch updates, which means we can't delete a field.
+# We have to use a kubectl apply command instead.
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+commands:
+ - command: kubectl replace -f 00-minimal-resource.yaml
+ namespaced: true
diff --git a/internal/controllers/flavor/tests/flavor-update/README.md b/internal/controllers/flavor/tests/flavor-update/README.md
new file mode 100644
index 000000000..93708de17
--- /dev/null
+++ b/internal/controllers/flavor/tests/flavor-update/README.md
@@ -0,0 +1,17 @@
+# Update Flavor
+
+## Step 00
+
+Create a Flavor using only mandatory fields.
+
+## Step 01
+
+Update all mutable fields.
+
+## Step 02
+
+Revert the resource to its original value and verify the resulting object is similar to when if was first created.
+
+## Reference
+
+https://k-orc.cloud/development/writing-tests/#update
diff --git a/internal/controllers/volumetype/status.go b/internal/controllers/volumetype/status.go
index ba361bcf4..e3638407f 100644
--- a/internal/controllers/volumetype/status.go
+++ b/internal/controllers/volumetype/status.go
@@ -54,7 +54,7 @@ func (volumetypeStatusWriter) ApplyResourceStatus(log logr.Logger, osResource *o
WithIsPublic(osResource.IsPublic)
for k, v := range osResource.ExtraSpecs {
- resourceStatus.WithExtraSpecs(orcapplyconfigv1alpha1.VolumeTypeExtraSpecStatus().
+ resourceStatus.WithExtraSpecs(orcapplyconfigv1alpha1.ExtraSpecStatus().
WithName(k).
WithValue(v))
}
diff --git a/internal/osclients/compute.go b/internal/osclients/compute.go
index 43f4396d5..59bec1621 100644
--- a/internal/osclients/compute.go
+++ b/internal/osclients/compute.go
@@ -49,8 +49,10 @@ const NovaMinimumMicroversion = "2.71"
type ComputeClient interface {
CreateFlavor(ctx context.Context, opts flavors.CreateOptsBuilder) (*flavors.Flavor, error)
+ CreateFlavorExtraSpecs(ctx context.Context, id string, opts flavors.CreateExtraSpecsOptsBuilder) (map[string]string, error)
GetFlavor(ctx context.Context, id string) (*flavors.Flavor, error)
DeleteFlavor(ctx context.Context, id string) error
+ DeleteFlavorExtraSpec(ctx context.Context, id, key string) error
ListFlavors(ctx context.Context, listOpts flavors.ListOptsBuilder) iter.Seq2[*flavors.Flavor, error]
CreateServer(ctx context.Context, createOpts servers.CreateOptsBuilder, schedulerHints servers.SchedulerHintOptsBuilder) (*servers.Server, error)
@@ -107,10 +109,18 @@ func (c computeClient) CreateFlavor(ctx context.Context, opts flavors.CreateOpts
return flavors.Create(ctx, c.client, opts).Extract()
}
+func (c computeClient) CreateFlavorExtraSpecs(ctx context.Context, id string, opts flavors.CreateExtraSpecsOptsBuilder) (map[string]string, error) {
+ return flavors.CreateExtraSpecs(ctx, c.client, id, opts).Extract()
+}
+
func (c computeClient) DeleteFlavor(ctx context.Context, id string) error {
return flavors.Delete(ctx, c.client, id).ExtractErr()
}
+func (c computeClient) DeleteFlavorExtraSpec(ctx context.Context, id, key string) error {
+ return flavors.DeleteExtraSpec(ctx, c.client, id, key).ExtractErr()
+}
+
func (c computeClient) ListFlavors(ctx context.Context, opts flavors.ListOptsBuilder) iter.Seq2[*flavors.Flavor, error] {
pager := flavors.ListDetail(c.client, opts)
return func(yield func(*flavors.Flavor, error) bool) {
@@ -201,12 +211,18 @@ func NewComputeErrorClient(e error) ComputeClient {
func (e computeErrorClient) CreateFlavor(ctx context.Context, opts flavors.CreateOptsBuilder) (*flavors.Flavor, error) {
return nil, e.error
}
+func (e computeErrorClient) CreateFlavorExtraSpecs(ctx context.Context, id string, opts flavors.CreateExtraSpecsOptsBuilder) (map[string]string, error) {
+ return nil, e.error
+}
func (e computeErrorClient) GetFlavor(ctx context.Context, id string) (*flavors.Flavor, error) {
return nil, e.error
}
func (e computeErrorClient) DeleteFlavor(ctx context.Context, id string) error {
return e.error
}
+func (e computeErrorClient) DeleteFlavorExtraSpec(ctx context.Context, id, key string) error {
+ return e.error
+}
func (e computeErrorClient) ListFlavors(_ context.Context, _ flavors.ListOptsBuilder) iter.Seq2[*flavors.Flavor, error] {
return func(yield func(*flavors.Flavor, error) bool) {
yield(nil, e.error)
diff --git a/internal/osclients/mock/compute.go b/internal/osclients/mock/compute.go
index 2fdea0de4..1d73fbf72 100644
--- a/internal/osclients/mock/compute.go
+++ b/internal/osclients/mock/compute.go
@@ -92,6 +92,21 @@ func (mr *MockComputeClientMockRecorder) CreateFlavor(ctx, opts any) *gomock.Cal
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFlavor", reflect.TypeOf((*MockComputeClient)(nil).CreateFlavor), ctx, opts)
}
+// CreateFlavorExtraSpecs mocks base method.
+func (m *MockComputeClient) CreateFlavorExtraSpecs(ctx context.Context, id string, opts flavors.CreateExtraSpecsOptsBuilder) (map[string]string, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "CreateFlavorExtraSpecs", ctx, id, opts)
+ ret0, _ := ret[0].(map[string]string)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// CreateFlavorExtraSpecs indicates an expected call of CreateFlavorExtraSpecs.
+func (mr *MockComputeClientMockRecorder) CreateFlavorExtraSpecs(ctx, id, opts any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFlavorExtraSpecs", reflect.TypeOf((*MockComputeClient)(nil).CreateFlavorExtraSpecs), ctx, id, opts)
+}
+
// CreateServer mocks base method.
func (m *MockComputeClient) CreateServer(ctx context.Context, createOpts servers.CreateOptsBuilder, schedulerHints servers.SchedulerHintOptsBuilder) (*servers.Server, error) {
m.ctrl.T.Helper()
@@ -165,6 +180,20 @@ func (mr *MockComputeClientMockRecorder) DeleteFlavor(ctx, id any) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFlavor", reflect.TypeOf((*MockComputeClient)(nil).DeleteFlavor), ctx, id)
}
+// DeleteFlavorExtraSpec mocks base method.
+func (m *MockComputeClient) DeleteFlavorExtraSpec(ctx context.Context, id, key string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeleteFlavorExtraSpec", ctx, id, key)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// DeleteFlavorExtraSpec indicates an expected call of DeleteFlavorExtraSpec.
+func (mr *MockComputeClientMockRecorder) DeleteFlavorExtraSpec(ctx, id, key any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFlavorExtraSpec", reflect.TypeOf((*MockComputeClient)(nil).DeleteFlavorExtraSpec), ctx, id, key)
+}
+
// DeleteServer mocks base method.
func (m *MockComputeClient) DeleteServer(ctx context.Context, serverID string) error {
m.ctrl.T.Helper()
diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/volumetypeextraspec.go b/pkg/clients/applyconfiguration/api/v1alpha1/extraspec.go
similarity index 67%
rename from pkg/clients/applyconfiguration/api/v1alpha1/volumetypeextraspec.go
rename to pkg/clients/applyconfiguration/api/v1alpha1/extraspec.go
index bebc95ca9..9fb1c40b1 100644
--- a/pkg/clients/applyconfiguration/api/v1alpha1/volumetypeextraspec.go
+++ b/pkg/clients/applyconfiguration/api/v1alpha1/extraspec.go
@@ -18,23 +18,23 @@ limitations under the License.
package v1alpha1
-// VolumeTypeExtraSpecApplyConfiguration represents a declarative configuration of the VolumeTypeExtraSpec type for use
+// ExtraSpecApplyConfiguration represents a declarative configuration of the ExtraSpec type for use
// with apply.
-type VolumeTypeExtraSpecApplyConfiguration struct {
+type ExtraSpecApplyConfiguration struct {
Name *string `json:"name,omitempty"`
Value *string `json:"value,omitempty"`
}
-// VolumeTypeExtraSpecApplyConfiguration constructs a declarative configuration of the VolumeTypeExtraSpec type for use with
+// ExtraSpecApplyConfiguration constructs a declarative configuration of the ExtraSpec type for use with
// apply.
-func VolumeTypeExtraSpec() *VolumeTypeExtraSpecApplyConfiguration {
- return &VolumeTypeExtraSpecApplyConfiguration{}
+func ExtraSpec() *ExtraSpecApplyConfiguration {
+ return &ExtraSpecApplyConfiguration{}
}
// 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 *VolumeTypeExtraSpecApplyConfiguration) WithName(value string) *VolumeTypeExtraSpecApplyConfiguration {
+func (b *ExtraSpecApplyConfiguration) WithName(value string) *ExtraSpecApplyConfiguration {
b.Name = &value
return b
}
@@ -42,7 +42,7 @@ func (b *VolumeTypeExtraSpecApplyConfiguration) WithName(value string) *VolumeTy
// 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 *VolumeTypeExtraSpecApplyConfiguration) WithValue(value string) *VolumeTypeExtraSpecApplyConfiguration {
+func (b *ExtraSpecApplyConfiguration) WithValue(value string) *ExtraSpecApplyConfiguration {
b.Value = &value
return b
}
diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/volumetypeextraspecstatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/extraspecstatus.go
similarity index 65%
rename from pkg/clients/applyconfiguration/api/v1alpha1/volumetypeextraspecstatus.go
rename to pkg/clients/applyconfiguration/api/v1alpha1/extraspecstatus.go
index 17928fc31..562220a7c 100644
--- a/pkg/clients/applyconfiguration/api/v1alpha1/volumetypeextraspecstatus.go
+++ b/pkg/clients/applyconfiguration/api/v1alpha1/extraspecstatus.go
@@ -18,23 +18,23 @@ limitations under the License.
package v1alpha1
-// VolumeTypeExtraSpecStatusApplyConfiguration represents a declarative configuration of the VolumeTypeExtraSpecStatus type for use
+// ExtraSpecStatusApplyConfiguration represents a declarative configuration of the ExtraSpecStatus type for use
// with apply.
-type VolumeTypeExtraSpecStatusApplyConfiguration struct {
+type ExtraSpecStatusApplyConfiguration struct {
Name *string `json:"name,omitempty"`
Value *string `json:"value,omitempty"`
}
-// VolumeTypeExtraSpecStatusApplyConfiguration constructs a declarative configuration of the VolumeTypeExtraSpecStatus type for use with
+// ExtraSpecStatusApplyConfiguration constructs a declarative configuration of the ExtraSpecStatus type for use with
// apply.
-func VolumeTypeExtraSpecStatus() *VolumeTypeExtraSpecStatusApplyConfiguration {
- return &VolumeTypeExtraSpecStatusApplyConfiguration{}
+func ExtraSpecStatus() *ExtraSpecStatusApplyConfiguration {
+ return &ExtraSpecStatusApplyConfiguration{}
}
// 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 *VolumeTypeExtraSpecStatusApplyConfiguration) WithName(value string) *VolumeTypeExtraSpecStatusApplyConfiguration {
+func (b *ExtraSpecStatusApplyConfiguration) WithName(value string) *ExtraSpecStatusApplyConfiguration {
b.Name = &value
return b
}
@@ -42,7 +42,7 @@ func (b *VolumeTypeExtraSpecStatusApplyConfiguration) WithName(value string) *Vo
// 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 *VolumeTypeExtraSpecStatusApplyConfiguration) WithValue(value string) *VolumeTypeExtraSpecStatusApplyConfiguration {
+func (b *ExtraSpecStatusApplyConfiguration) WithValue(value string) *ExtraSpecStatusApplyConfiguration {
b.Value = &value
return b
}
diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/flavorresourcespec.go b/pkg/clients/applyconfiguration/api/v1alpha1/flavorresourcespec.go
index 4fc4161e0..6565a9411 100644
--- a/pkg/clients/applyconfiguration/api/v1alpha1/flavorresourcespec.go
+++ b/pkg/clients/applyconfiguration/api/v1alpha1/flavorresourcespec.go
@@ -25,15 +25,16 @@ import (
// FlavorResourceSpecApplyConfiguration represents a declarative configuration of the FlavorResourceSpec type for use
// with apply.
type FlavorResourceSpecApplyConfiguration struct {
- Name *apiv1alpha1.OpenStackName `json:"name,omitempty"`
- ID *string `json:"id,omitempty"`
- Description *string `json:"description,omitempty"`
- RAM *int32 `json:"ram,omitempty"`
- Vcpus *int32 `json:"vcpus,omitempty"`
- Disk *int32 `json:"disk,omitempty"`
- Swap *int32 `json:"swap,omitempty"`
- IsPublic *bool `json:"isPublic,omitempty"`
- Ephemeral *int32 `json:"ephemeral,omitempty"`
+ Name *apiv1alpha1.OpenStackName `json:"name,omitempty"`
+ ID *string `json:"id,omitempty"`
+ Description *string `json:"description,omitempty"`
+ RAM *int32 `json:"ram,omitempty"`
+ Vcpus *int32 `json:"vcpus,omitempty"`
+ Disk *int32 `json:"disk,omitempty"`
+ Swap *int32 `json:"swap,omitempty"`
+ ExtraSpecs []ExtraSpecApplyConfiguration `json:"extraSpecs,omitempty"`
+ IsPublic *bool `json:"isPublic,omitempty"`
+ Ephemeral *int32 `json:"ephemeral,omitempty"`
}
// FlavorResourceSpecApplyConfiguration constructs a declarative configuration of the FlavorResourceSpec type for use with
@@ -98,6 +99,19 @@ func (b *FlavorResourceSpecApplyConfiguration) WithSwap(value int32) *FlavorReso
return b
}
+// WithExtraSpecs adds the given value to the ExtraSpecs 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 ExtraSpecs field.
+func (b *FlavorResourceSpecApplyConfiguration) WithExtraSpecs(values ...*ExtraSpecApplyConfiguration) *FlavorResourceSpecApplyConfiguration {
+ for i := range values {
+ if values[i] == nil {
+ panic("nil value passed to WithExtraSpecs")
+ }
+ b.ExtraSpecs = append(b.ExtraSpecs, *values[i])
+ }
+ return b
+}
+
// WithIsPublic sets the IsPublic 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 IsPublic field is set to the value of the last call.
diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/flavorresourcestatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/flavorresourcestatus.go
index 7b4996ed4..9d2890d4c 100644
--- a/pkg/clients/applyconfiguration/api/v1alpha1/flavorresourcestatus.go
+++ b/pkg/clients/applyconfiguration/api/v1alpha1/flavorresourcestatus.go
@@ -21,14 +21,15 @@ package v1alpha1
// FlavorResourceStatusApplyConfiguration represents a declarative configuration of the FlavorResourceStatus type for use
// with apply.
type FlavorResourceStatusApplyConfiguration struct {
- Name *string `json:"name,omitempty"`
- Description *string `json:"description,omitempty"`
- RAM *int32 `json:"ram,omitempty"`
- Vcpus *int32 `json:"vcpus,omitempty"`
- Disk *int32 `json:"disk,omitempty"`
- Swap *int32 `json:"swap,omitempty"`
- IsPublic *bool `json:"isPublic,omitempty"`
- Ephemeral *int32 `json:"ephemeral,omitempty"`
+ Name *string `json:"name,omitempty"`
+ Description *string `json:"description,omitempty"`
+ RAM *int32 `json:"ram,omitempty"`
+ Vcpus *int32 `json:"vcpus,omitempty"`
+ Disk *int32 `json:"disk,omitempty"`
+ Swap *int32 `json:"swap,omitempty"`
+ ExtraSpecs []ExtraSpecStatusApplyConfiguration `json:"extraSpecs,omitempty"`
+ IsPublic *bool `json:"isPublic,omitempty"`
+ Ephemeral *int32 `json:"ephemeral,omitempty"`
}
// FlavorResourceStatusApplyConfiguration constructs a declarative configuration of the FlavorResourceStatus type for use with
@@ -85,6 +86,19 @@ func (b *FlavorResourceStatusApplyConfiguration) WithSwap(value int32) *FlavorRe
return b
}
+// WithExtraSpecs adds the given value to the ExtraSpecs 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 ExtraSpecs field.
+func (b *FlavorResourceStatusApplyConfiguration) WithExtraSpecs(values ...*ExtraSpecStatusApplyConfiguration) *FlavorResourceStatusApplyConfiguration {
+ for i := range values {
+ if values[i] == nil {
+ panic("nil value passed to WithExtraSpecs")
+ }
+ b.ExtraSpecs = append(b.ExtraSpecs, *values[i])
+ }
+ return b
+}
+
// WithIsPublic sets the IsPublic 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 IsPublic field is set to the value of the last call.
diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/volumetyperesourcespec.go b/pkg/clients/applyconfiguration/api/v1alpha1/volumetyperesourcespec.go
index 88691bffb..f1cf11ca0 100644
--- a/pkg/clients/applyconfiguration/api/v1alpha1/volumetyperesourcespec.go
+++ b/pkg/clients/applyconfiguration/api/v1alpha1/volumetyperesourcespec.go
@@ -25,10 +25,10 @@ import (
// VolumeTypeResourceSpecApplyConfiguration represents a declarative configuration of the VolumeTypeResourceSpec type for use
// with apply.
type VolumeTypeResourceSpecApplyConfiguration struct {
- Name *apiv1alpha1.OpenStackName `json:"name,omitempty"`
- Description *string `json:"description,omitempty"`
- ExtraSpecs []VolumeTypeExtraSpecApplyConfiguration `json:"extraSpecs,omitempty"`
- IsPublic *bool `json:"isPublic,omitempty"`
+ Name *apiv1alpha1.OpenStackName `json:"name,omitempty"`
+ Description *string `json:"description,omitempty"`
+ ExtraSpecs []ExtraSpecApplyConfiguration `json:"extraSpecs,omitempty"`
+ IsPublic *bool `json:"isPublic,omitempty"`
}
// VolumeTypeResourceSpecApplyConfiguration constructs a declarative configuration of the VolumeTypeResourceSpec type for use with
@@ -56,7 +56,7 @@ func (b *VolumeTypeResourceSpecApplyConfiguration) WithDescription(value string)
// WithExtraSpecs adds the given value to the ExtraSpecs 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 ExtraSpecs field.
-func (b *VolumeTypeResourceSpecApplyConfiguration) WithExtraSpecs(values ...*VolumeTypeExtraSpecApplyConfiguration) *VolumeTypeResourceSpecApplyConfiguration {
+func (b *VolumeTypeResourceSpecApplyConfiguration) WithExtraSpecs(values ...*ExtraSpecApplyConfiguration) *VolumeTypeResourceSpecApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithExtraSpecs")
diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/volumetyperesourcestatus.go b/pkg/clients/applyconfiguration/api/v1alpha1/volumetyperesourcestatus.go
index a6e393435..a1dff3c96 100644
--- a/pkg/clients/applyconfiguration/api/v1alpha1/volumetyperesourcestatus.go
+++ b/pkg/clients/applyconfiguration/api/v1alpha1/volumetyperesourcestatus.go
@@ -21,10 +21,10 @@ package v1alpha1
// VolumeTypeResourceStatusApplyConfiguration represents a declarative configuration of the VolumeTypeResourceStatus type for use
// with apply.
type VolumeTypeResourceStatusApplyConfiguration struct {
- Name *string `json:"name,omitempty"`
- Description *string `json:"description,omitempty"`
- ExtraSpecs []VolumeTypeExtraSpecStatusApplyConfiguration `json:"extraSpecs,omitempty"`
- IsPublic *bool `json:"isPublic,omitempty"`
+ Name *string `json:"name,omitempty"`
+ Description *string `json:"description,omitempty"`
+ ExtraSpecs []ExtraSpecStatusApplyConfiguration `json:"extraSpecs,omitempty"`
+ IsPublic *bool `json:"isPublic,omitempty"`
}
// VolumeTypeResourceStatusApplyConfiguration constructs a declarative configuration of the VolumeTypeResourceStatus type for use with
@@ -52,7 +52,7 @@ func (b *VolumeTypeResourceStatusApplyConfiguration) WithDescription(value strin
// WithExtraSpecs adds the given value to the ExtraSpecs 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 ExtraSpecs field.
-func (b *VolumeTypeResourceStatusApplyConfiguration) WithExtraSpecs(values ...*VolumeTypeExtraSpecStatusApplyConfiguration) *VolumeTypeResourceStatusApplyConfiguration {
+func (b *VolumeTypeResourceStatusApplyConfiguration) WithExtraSpecs(values ...*ExtraSpecStatusApplyConfiguration) *VolumeTypeResourceStatusApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithExtraSpecs")
diff --git a/pkg/clients/applyconfiguration/internal/internal.go b/pkg/clients/applyconfiguration/internal/internal.go
index 10461ed9c..f519cb6ee 100644
--- a/pkg/clients/applyconfiguration/internal/internal.go
+++ b/pkg/clients/applyconfiguration/internal/internal.go
@@ -611,6 +611,26 @@ var schemaYAML = typed.YAMLObject(`types:
- name: networkID
type:
scalar: string
+- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ExtraSpec
+ map:
+ fields:
+ - name: name
+ type:
+ scalar: string
+ default: ""
+ - name: value
+ type:
+ scalar: string
+ default: ""
+- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ExtraSpecStatus
+ map:
+ fields:
+ - name: name
+ type:
+ scalar: string
+ - name: value
+ type:
+ scalar: string
- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.FixedIPStatus
map:
fields:
@@ -678,6 +698,12 @@ var schemaYAML = typed.YAMLObject(`types:
- name: ephemeral
type:
scalar: numeric
+ - name: extraSpecs
+ type:
+ list:
+ elementType:
+ namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ExtraSpec
+ elementRelationship: atomic
- name: id
type:
scalar: string
@@ -708,6 +734,12 @@ var schemaYAML = typed.YAMLObject(`types:
- name: ephemeral
type:
scalar: numeric
+ - name: extraSpecs
+ type:
+ list:
+ elementType:
+ namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ExtraSpecStatus
+ elementRelationship: atomic
- name: isPublic
type:
scalar: boolean
@@ -4106,26 +4138,6 @@ var schemaYAML = typed.YAMLObject(`types:
type:
namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.VolumeTypeStatus
default: {}
-- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.VolumeTypeExtraSpec
- map:
- fields:
- - name: name
- type:
- scalar: string
- default: ""
- - name: value
- type:
- scalar: string
- default: ""
-- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.VolumeTypeExtraSpecStatus
- map:
- fields:
- - name: name
- type:
- scalar: string
- - name: value
- type:
- scalar: string
- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.VolumeTypeFilter
map:
fields:
@@ -4157,7 +4169,7 @@ var schemaYAML = typed.YAMLObject(`types:
type:
list:
elementType:
- namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.VolumeTypeExtraSpec
+ namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ExtraSpec
elementRelationship: atomic
- name: isPublic
type:
@@ -4175,7 +4187,7 @@ var schemaYAML = typed.YAMLObject(`types:
type:
list:
elementType:
- namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.VolumeTypeExtraSpecStatus
+ namedType: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.ExtraSpecStatus
elementRelationship: atomic
- name: isPublic
type:
diff --git a/pkg/clients/applyconfiguration/utils.go b/pkg/clients/applyconfiguration/utils.go
index 5d8f68cd9..c12636c64 100644
--- a/pkg/clients/applyconfiguration/utils.go
+++ b/pkg/clients/applyconfiguration/utils.go
@@ -110,6 +110,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &apiv1alpha1.ExternalGatewayApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ExternalGatewayStatus"):
return &apiv1alpha1.ExternalGatewayStatusApplyConfiguration{}
+ case v1alpha1.SchemeGroupVersion.WithKind("ExtraSpec"):
+ return &apiv1alpha1.ExtraSpecApplyConfiguration{}
+ case v1alpha1.SchemeGroupVersion.WithKind("ExtraSpecStatus"):
+ return &apiv1alpha1.ExtraSpecStatusApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("FilterByKeystoneTags"):
return &apiv1alpha1.FilterByKeystoneTagsApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("FilterByNeutronTags"):
@@ -466,10 +470,6 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &apiv1alpha1.VolumeStatusApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("VolumeType"):
return &apiv1alpha1.VolumeTypeApplyConfiguration{}
- case v1alpha1.SchemeGroupVersion.WithKind("VolumeTypeExtraSpec"):
- return &apiv1alpha1.VolumeTypeExtraSpecApplyConfiguration{}
- case v1alpha1.SchemeGroupVersion.WithKind("VolumeTypeExtraSpecStatus"):
- return &apiv1alpha1.VolumeTypeExtraSpecStatusApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("VolumeTypeFilter"):
return &apiv1alpha1.VolumeTypeFilterApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("VolumeTypeImport"):
diff --git a/test/apivalidations/flavor_test.go b/test/apivalidations/flavor_test.go
index 847af3a15..af35a6055 100644
--- a/test/apivalidations/flavor_test.go
+++ b/test/apivalidations/flavor_test.go
@@ -98,13 +98,51 @@ var _ = Describe("ORC Flavor API validations", func() {
},
})
- It("should be immutable", func(ctx context.Context) {
+ It("should be immutable except extraSpecs", func(ctx context.Context) {
flavor := flavorStub(namespace)
+
patch := baseFlavorPatch(flavor)
- patch.Spec.WithResource(applyconfigv1alpha1.FlavorResourceSpec().WithRAM(1).WithVcpus(1).WithDisk(1))
+ patch.Spec.WithResource(applyconfigv1alpha1.FlavorResourceSpec().
+ WithName("base-name").
+ WithID("base-id").
+ WithDescription("base-desc").
+ WithRAM(1).
+ WithVcpus(1).
+ WithDisk(1).
+ WithSwap(1).
+ WithIsPublic(true).
+ WithEphemeral(1).
+ WithExtraSpecs(
+ applyconfigv1alpha1.ExtraSpec().
+ WithName("spec").
+ WithValue("specValue"),
+ ),
+ )
Expect(applyObj(ctx, flavor, patch)).To(Succeed())
- patch.Spec.WithResource(applyconfigv1alpha1.FlavorResourceSpec().WithRAM(2).WithVcpus(1).WithDisk(1))
- Expect(applyObj(ctx, flavor, patch)).To(MatchError(ContainSubstring("FlavorResourceSpec is immutable")))
+
+ patch = baseFlavorPatch(flavor)
+ patch.Spec.WithResource(applyconfigv1alpha1.FlavorResourceSpec().
+ WithName("mutated-name").
+ WithID("mutated-id").
+ WithDescription("mutated-desc").
+ WithRAM(2).
+ WithVcpus(2).
+ WithDisk(2).
+ WithSwap(2).
+ WithIsPublic(false).
+ WithEphemeral(2).
+ WithExtraSpecs(
+ applyconfigv1alpha1.ExtraSpec().
+ WithName("spec2").
+ WithValue("specValue2"),
+ ),
+ )
+ err := applyObj(ctx, flavor, patch)
+ fields := []string{"name", "id", "description", "ram", "vcpus", "disk", "swap", "isPublic", "ephemeral"}
+ for _, field := range fields {
+ Expect(err).To(MatchError(ContainSubstring(field + " is immutable")))
+ }
+ Expect(err.Error()).To(Not(ContainSubstring("extraSpecs is immutable")))
})
It("should reject a flavor without required fields", func(ctx context.Context) {
@@ -164,4 +202,13 @@ var _ = Describe("ORC Flavor API validations", func() {
patch.Spec.WithResource(applyconfigv1alpha1.FlavorResourceSpec().WithID("test.id -123_").WithRAM(1).WithVcpus(1).WithDescription("test").WithDisk(1))
Expect(applyObj(ctx, flavor, patch)).To(Succeed())
})
+
+ It("should permit extraSpecs with required fields", func(ctx context.Context) {
+ flavor := flavorStub(namespace)
+ patch := baseFlavorPatch(flavor)
+ patch.Spec.WithResource(testFlavorResource().
+ WithExtraSpecs(applyconfigv1alpha1.ExtraSpec().
+ WithName("key").WithValue("value")))
+ Expect(applyObj(ctx, flavor, patch)).To(Succeed())
+ })
})
diff --git a/test/apivalidations/volumetype_test.go b/test/apivalidations/volumetype_test.go
index 08338b184..fd70b8087 100644
--- a/test/apivalidations/volumetype_test.go
+++ b/test/apivalidations/volumetype_test.go
@@ -99,7 +99,7 @@ var _ = Describe("ORC VolumeType API validations", func() {
volumeType := volumeTypeStub(namespace)
patch := baseVolumeTypePatch(volumeType)
patch.Spec.WithResource(applyconfigv1alpha1.VolumeTypeResourceSpec().
- WithExtraSpecs(applyconfigv1alpha1.VolumeTypeExtraSpec().
+ WithExtraSpecs(applyconfigv1alpha1.ExtraSpec().
WithName("key").WithValue("value")))
Expect(applyObj(ctx, volumeType, patch)).To(Succeed())
})
diff --git a/website/docs/crd-reference.md b/website/docs/crd-reference.md
index de769a2d0..a9324ef09 100644
--- a/website/docs/crd-reference.md
+++ b/website/docs/crd-reference.md
@@ -866,6 +866,42 @@ _Appears in:_
| `networkID` _string_ | networkID is the ID of the network the gateway is on. | | MaxLength: 1024
Optional: \{\}
|
+#### ExtraSpec
+
+
+
+
+
+
+
+_Appears in:_
+- [FlavorResourceSpec](#flavorresourcespec)
+- [VolumeTypeResourceSpec](#volumetyperesourcespec)
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `name` _string_ | name is the name of the extraspec | | MaxLength: 255
Required: \{\}
|
+| `value` _string_ | value is the value of the extraspec | | MaxLength: 255
Required: \{\}
|
+
+
+#### ExtraSpecStatus
+
+
+
+
+
+
+
+_Appears in:_
+- [FlavorResourceStatus](#flavorresourcestatus)
+- [VolumeTypeResourceStatus](#volumetyperesourcestatus)
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `name` _string_ | name is the name of the extraspec | | MaxLength: 255
Optional: \{\}
|
+| `value` _string_ | value is the value of the extraspec | | MaxLength: 255
Optional: \{\}
|
+
+
#### FilterByKeystoneTags
@@ -1025,6 +1061,7 @@ _Appears in:_
| `vcpus` _integer_ | vcpus is the number of vcpus for the flavor. | | Minimum: 1
Required: \{\}
|
| `disk` _integer_ | disk is the size of the root disk that will be created in GiB. If 0
the root disk will be set to exactly the size of the image used to
deploy the instance. However, in this case the scheduler cannot
select the compute host based on the virtual image size. Therefore,
0 should only be used for volume booted instances or for testing
purposes. Volume-backed instances can be enforced for flavors with
zero root disk via the
os_compute_api:servers:create:zero_disk_flavor policy rule. | | Minimum: 0
Required: \{\}
|
| `swap` _integer_ | swap is the size of a dedicated swap disk that will be allocated, in
MiB. If 0 (the default), no dedicated swap disk will be created. | | Minimum: 0
Optional: \{\}
|
+| `extraSpecs` _[ExtraSpec](#extraspec) array_ | extraSpecs is a map of key-value pairs that define extra specifications for the flavor. | | MaxItems: 128
Optional: \{\}
|
| `isPublic` _boolean_ | isPublic flags a flavor as being available to all projects or not. | | Optional: \{\}
|
| `ephemeral` _integer_ | ephemeral is the size of the ephemeral disk that will be created, in GiB.
Ephemeral disks may be written over on server state changes. So should only
be used as a scratch space for applications that are aware of its
limitations. Defaults to 0. | | Minimum: 0
Optional: \{\}
|
@@ -1048,6 +1085,7 @@ _Appears in:_
| `vcpus` _integer_ | vcpus is the number of vcpus for the flavor. | | Optional: \{\}
|
| `disk` _integer_ | disk is the size of the root disk that will be created in GiB. | | Optional: \{\}
|
| `swap` _integer_ | swap is the size of a dedicated swap disk that will be allocated, in
MiB. | | Optional: \{\}
|
+| `extraSpecs` _[ExtraSpecStatus](#extraspecstatus) array_ | extraSpecs is a map of key-value pairs that define extra specifications for the flavor. | | MaxItems: 128
Optional: \{\}
|
| `isPublic` _boolean_ | isPublic flags a flavor as being available to all projects or not. | | Optional: \{\}
|
| `ephemeral` _integer_ | ephemeral is the size of the ephemeral disk, in GiB. | | Optional: \{\}
|
@@ -5176,40 +5214,6 @@ VolumeType is the Schema for an ORC resource.
| `status` _[VolumeTypeStatus](#volumetypestatus)_ | status defines the observed state of the resource. | | Optional: \{\}
|
-#### VolumeTypeExtraSpec
-
-
-
-
-
-
-
-_Appears in:_
-- [VolumeTypeResourceSpec](#volumetyperesourcespec)
-
-| Field | Description | Default | Validation |
-| --- | --- | --- | --- |
-| `name` _string_ | name is the name of the extraspec | | MaxLength: 255
Required: \{\}
|
-| `value` _string_ | value is the value of the extraspec | | MaxLength: 255
Required: \{\}
|
-
-
-#### VolumeTypeExtraSpecStatus
-
-
-
-
-
-
-
-_Appears in:_
-- [VolumeTypeResourceStatus](#volumetyperesourcestatus)
-
-| Field | Description | Default | Validation |
-| --- | --- | --- | --- |
-| `name` _string_ | name is the name of the extraspec | | MaxLength: 255
Optional: \{\}
|
-| `value` _string_ | value is the value of the extraspec | | MaxLength: 255
Optional: \{\}
|
-
-
#### VolumeTypeFilter
@@ -5264,7 +5268,7 @@ _Appears in:_
| --- | --- | --- | --- |
| `name` _[OpenStackName](#openstackname)_ | name will be the name of the created resource. If not specified, the
name of the ORC object will be used. | | MaxLength: 255
MinLength: 1
Pattern: `^[^,]+$`
Optional: \{\}
|
| `description` _string_ | description is a human-readable description for the resource. | | MaxLength: 255
MinLength: 1
Optional: \{\}
|
-| `extraSpecs` _[VolumeTypeExtraSpec](#volumetypeextraspec) array_ | extraSpecs is a map of key-value pairs that define extra specifications for the volume type. | | MaxItems: 64
Optional: \{\}
|
+| `extraSpecs` _[ExtraSpec](#extraspec) array_ | extraSpecs is a map of key-value pairs that define extra specifications for the volume type. | | MaxItems: 64
Optional: \{\}
|
| `isPublic` _boolean_ | isPublic indicates whether the volume type is public. | | Optional: \{\}
|
@@ -5283,7 +5287,7 @@ _Appears in:_
| --- | --- | --- | --- |
| `name` _string_ | name is a Human-readable name for the resource. Might not be unique. | | MaxLength: 1024
Optional: \{\}
|
| `description` _string_ | description is a human-readable description for the resource. | | MaxLength: 1024
Optional: \{\}
|
-| `extraSpecs` _[VolumeTypeExtraSpecStatus](#volumetypeextraspecstatus) array_ | extraSpecs is a map of key-value pairs that define extra specifications for the volume type. | | MaxItems: 64
Optional: \{\}
|
+| `extraSpecs` _[ExtraSpecStatus](#extraspecstatus) array_ | extraSpecs is a map of key-value pairs that define extra specifications for the volume type. | | MaxItems: 64
Optional: \{\}
|
| `isPublic` _boolean_ | isPublic indicates whether the VolumeType is public. | | Optional: \{\}
|