Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions data/data/install.openshift.io_installconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -629,8 +629,16 @@ spec:
gibibytes (GiB). Required
type: integer
type:
description: Type defines the type of the volume. Required
description: 'Type defines the type of the volume. Deprecated:
Use Types instead.'
type: string
types:
description: Types is the list of the volume types of
the root volumes. This is mutually exclusive with
Type.
items:
type: string
type: array
zones:
description: Zones is the list of availability zones
where the root volumes should be deployed. If no zones
Expand All @@ -641,7 +649,7 @@ spec:
type: array
required:
- size
- type
- types
type: object
serverGroupPolicy:
description: ServerGroupPolicy will be used to create the
Expand Down Expand Up @@ -1381,8 +1389,15 @@ spec:
(GiB). Required
type: integer
type:
description: Type defines the type of the volume. Required
description: 'Type defines the type of the volume. Deprecated:
Use Types instead.'
type: string
types:
description: Types is the list of the volume types of
the root volumes. This is mutually exclusive with Type.
items:
type: string
type: array
zones:
description: Zones is the list of availability zones where
the root volumes should be deployed. If no zones are
Expand All @@ -1393,7 +1408,7 @@ spec:
type: array
required:
- size
- type
- types
type: object
serverGroupPolicy:
description: ServerGroupPolicy will be used to create the
Expand Down Expand Up @@ -3152,8 +3167,15 @@ spec:
(GiB). Required
type: integer
type:
description: Type defines the type of the volume. Required
description: 'Type defines the type of the volume. Deprecated:
Use Types instead.'
type: string
types:
description: Types is the list of the volume types of
the root volumes. This is mutually exclusive with Type.
items:
type: string
type: array
zones:
description: Zones is the list of availability zones where
the root volumes should be deployed. If no zones are
Expand All @@ -3164,7 +3186,7 @@ spec:
type: array
required:
- size
- type
- types
type: object
serverGroupPolicy:
description: ServerGroupPolicy will be used to create the
Expand Down
2 changes: 1 addition & 1 deletion data/data/openstack/bootstrap/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ resource "openstack_blockstorage_volume_v3" "bootstrap_volume" {
description = local.description

size = var.openstack_master_root_volume_size
volume_type = var.openstack_master_root_volume_type
volume_type = var.openstack_master_root_volume_types[0]
image_id = data.openstack_images_image_v2.base_image.id

availability_zone = var.openstack_master_root_volume_availability_zones[0]
Expand Down
2 changes: 1 addition & 1 deletion data/data/openstack/masters/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ resource "openstack_blockstorage_volume_v3" "master_volume" {
count = var.openstack_master_root_volume_size == null ? 0 : var.master_count

size = var.openstack_master_root_volume_size
volume_type = var.openstack_master_root_volume_type
volume_type = var.openstack_master_root_volume_types[count.index]
image_id = data.openstack_images_image_v2.base_image.id

availability_zone = var.openstack_master_root_volume_availability_zones[count.index]
Expand Down
12 changes: 6 additions & 6 deletions data/data/openstack/variables-openstack.tf
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
variable "openstack_master_root_volume_type" {
type = string
default = null
description = "The type of volume for the root block device of master nodes."
}

variable "openstack_master_root_volume_size" {
type = number
default = null
Expand Down Expand Up @@ -389,6 +383,12 @@ variable "openstack_master_root_volume_availability_zones" {
description = "List of availability Zones to Schedule the masters root volumes on."
}

variable "openstack_master_root_volume_types" {
type = list(string)
default = [""]
description = "List of volume types used by the masters root volumes."
}

variable "openstack_worker_server_group_names" {
type = set(string)
description = "Names of the server groups for the worker nodes."
Expand Down
85 changes: 85 additions & 0 deletions docs/user/openstack/control-plane-machine-set.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,88 @@ spec:
When reconciling the Machines, cluster-control-plane-machine-set-operator will match their spec against the template, after substituting `availabilityZone` and `rootVolume.availabilityZone` for each of them.

The three Control plane Machines will all be provisioned on a different availability zone and have their `rootVolume` provisioned on a different availability zone.

---

## Example 5: three Compute availability zones, three Storage types

The storage types apply to the root volume. The `providerSpec` must contain a `rootVolume` property.

```yaml
apiVersion: machine.openshift.io/v1
kind: ControlPlaneMachineSet
metadata:
creationTimestamp: null
labels:
machine.openshift.io/cluster-api-cluster: ocp1-2g2xs
name: cluster
namespace: openshift-machine-api
spec:
replicas: 3
selector:
matchLabels:
machine.openshift.io/cluster-api-cluster: ocp1-2g2xs
machine.openshift.io/cluster-api-machine-role: master
machine.openshift.io/cluster-api-machine-type: master
state: Active
strategy: {}
template:
machineType: machines_v1beta1_machine_openshift_io
machines_v1beta1_machine_openshift_io:
failureDomains:
openstack:
- availabilityZone: nova-one
rootVolume:
volumeType: fastpool-1
- availabilityZone: nova-two
rootVolume:
volumeType: fastpool-2
- availabilityZone: nova-three
rootVolume:
volumeType: fastpool-3
platform: OpenStack
metadata:
labels:
machine.openshift.io/cluster-api-cluster: ocp1-2g2xs
machine.openshift.io/cluster-api-machine-role: master
machine.openshift.io/cluster-api-machine-type: master
spec:
lifecycleHooks: {}
metadata: {}
providerSpec:
value: # <-- The OpenStack providerSpec
apiVersion: machine.openshift.io/v1alpha1
cloudName: openstack
cloudsSecret:
name: openstack-cloud-credentials
namespace: openshift-machine-api
flavor: m1.xlarge
image: ocp1-2g2xs-rhcos
kind: OpenstackProviderSpec
metadata:
creationTimestamp: null
networks:
- filter: {}
subnets:
- filter:
name: ocp1-2g2xs-nodes
tags: openshiftClusterID=ocp1-2g2xs
rootVolume:
diskSize: 30
securityGroups:
- filter: {}
name: ocp1-2g2xs-master
serverGroupName: ocp1-2g2xs-master
serverMetadata:
Name: ocp1-2g2xs-master
openshiftClusterID: ocp1-2g2xs
tags:
- openshiftClusterID=ocp1-2g2xs
trunk: true
userDataSecret:
name: master-user-data
```

When reconciling the Machines, cluster-control-plane-machine-set-operator will match their spec against the template, after substituting `availabilityZone` and `rootVolume.volumeType` for each of them.

The three Control plane Machines will all be provisioned on a different availability zone and have their `rootVolume` provisioned with a different volume type.
6 changes: 4 additions & 2 deletions docs/user/openstack/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ Beyond the [platform-agnostic `install-config.yaml` properties](../customization
* `type` (optional string): The OpenStack flavor name for machines in the pool.
* `rootVolume` (optional object): Defines the root volume for instances in the machine pool. The instances use ephemeral disks if not set.
* `size` (required integer): Size of the root volume in GB. Must be set to at least 25.
* `type` (required string): The volume pool to create the volume from.
* `type` (optional string): The volume pool to create the volume from. It was replaced by `types`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have been

Suggested change
* `type` (optional string): The volume pool to create the volume from. It was replaced by `types`.
* `type` (deprecated string): The volume pool to create the volume from. It was replaced by `types`.

* `types` (required list of strings): The volume pool to create the volume from. If compute `zones` are defined with more than one type, the number of zones must match the number of types.
* `zones` (optional list of strings): The names of the availability zones you want to install your root volumes on. If unset, the installer will use your default volume zone.
If compute `zones` contains at least one value, `rootVolume.zones` must also contain at least one value.
Indeed, when a machine is created with a compute availability zone and a storage root volume with no specified rootVolume.availabilityZone, [CAPO](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/9d183bd479fe9aed4f6e7ac3d5eee46681c518e7/pkg/cloud/services/compute/instance.go#L439-L442) will use the compute AZ for the volume AZ.
Expand Down Expand Up @@ -106,7 +107,8 @@ compute:
type: ml.large
rootVolume:
size: 30
type: performance
types:
- performance
replicas: 3
metadata:
name: test-cluster
Expand Down
15 changes: 8 additions & 7 deletions pkg/asset/installconfig/openstack/validation/machinepool.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func ValidateMachinePool(p *openstack.MachinePool, ci *CloudInfo, controlPlane b
var checkStorageFlavor bool
// Validate Root Volumes
if p.RootVolume != nil {
allErrs = append(allErrs, validateVolumeTypes(p.RootVolume.Type, ci.VolumeTypes, fldPath.Child("rootVolume").Child("type"))...)
allErrs = append(allErrs, validateVolumeTypes(p.RootVolume.Types, ci.VolumeTypes, fldPath.Child("rootVolume").Child("types"))...)
if p.RootVolume.Size < minimumStorage {
allErrs = append(allErrs, field.Invalid(fldPath.Child("rootVolume").Child("size"), p.RootVolume.Size, fmt.Sprintf("Volume size must be greater than %d GB to use root volumes, had %d GB", minimumStorage, p.RootVolume.Size)))
} else if p.RootVolume.Size < recommendedStorage {
Expand Down Expand Up @@ -89,15 +89,16 @@ func validateZones(input []string, available []string, fldPath *field.Path) fiel
return allErrs
}

func validateVolumeTypes(input string, available []string, fldPath *field.Path) field.ErrorList {
func validateVolumeTypes(input []string, available []string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if input == "" {
allErrs = append(allErrs, field.Invalid(fldPath, input, "Volume type must be specified to use root volumes"))
if len(input) == 0 {
return allErrs
}
volumeTypes := sets.NewString(available...)
if !volumeTypes.Has(input) {
allErrs = append(allErrs, field.Invalid(fldPath, input, "Volume Type either does not exist in this cloud, or is not available"))
volumeTypes := sets.New[string](available...)
for _, volumeType := range input {
if !volumeTypes.Has(volumeType) {
allErrs = append(allErrs, field.Invalid(fldPath, volumeType, "Volume type either does not exist in this cloud, or is not available"))
}
}

return allErrs
Expand Down
46 changes: 30 additions & 16 deletions pkg/asset/installconfig/openstack/validation/machinepool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ const (

baremetalFlavor = "baremetal-flavor"

volumeType = "performance"
invalidType = "invalid-type"
volumeSmallSize = 10
volumeMediumSize = 40
volumeLargeSize = 100
)

var volumeTypes = []string{"performance", "standard"}
var invalidVolumeTypes = []string{"performance", "invalid-type"}
var volumeType = volumeTypes[0]

func validMachinePool() *openstack.MachinePool {
return &openstack.MachinePool{
FlavorName: validCtrlPlaneFlavor,
Expand All @@ -46,8 +49,8 @@ func invalidMachinePoolSmallVolume() *openstack.MachinePool {
FlavorName: validCtrlPlaneFlavor,
Zones: []string{""},
RootVolume: &openstack.RootVolume{
Type: volumeType,
Size: volumeSmallSize,
Types: volumeTypes,
Zones: []string{""},
},
}
Expand All @@ -58,8 +61,8 @@ func warningMachinePoolMediumVolume() *openstack.MachinePool {
FlavorName: validCtrlPlaneFlavor,
Zones: []string{""},
RootVolume: &openstack.RootVolume{
Type: volumeType,
Size: volumeMediumSize,
Types: volumeTypes,
Zones: []string{""},
},
}
Expand All @@ -70,8 +73,8 @@ func validMachinePoolLargeVolume() *openstack.MachinePool {
FlavorName: validCtrlPlaneFlavor,
Zones: []string{""},
RootVolume: &openstack.RootVolume{
Type: volumeType,
Size: volumeLargeSize,
Types: volumeTypes,
Zones: []string{validZone},
},
}
Expand Down Expand Up @@ -144,9 +147,7 @@ func validMpoolCloudInfo() *CloudInfo {
VolumeZones: []string{
validZone,
},
VolumeTypes: []string{
volumeType,
},
VolumeTypes: volumeTypes,
}
}

Expand Down Expand Up @@ -370,28 +371,41 @@ func TestOpenStackMachinepoolValidation(t *testing.T) {
expectedErrMsg: `compute\[0\].platform.openstack.rootVolume.zones: Invalid value: \[\]string{"AZ1", "AZ2"}: there must be either just one volume availability zone common to all nodes or the number of compute and volume availability zones must be equal`,
},
{
name: "empty volume type",
controlPlane: false,
name: "invalid volume types",
controlPlane: true,
mpool: func() *openstack.MachinePool {
mp := validMachinePoolLargeVolume()
mp.RootVolume.Type = ""
mp.RootVolume.Types = invalidVolumeTypes
return mp
}(),
cloudInfo: validMpoolCloudInfo(),
expectedError: true,
expectedErrMsg: `compute\[0\].platform.openstack.rootVolume.type: Invalid value: \"\": Volume type must be specified to use root volumes`,
expectedErrMsg: "controlPlane.platform.openstack.rootVolume.types: Invalid value: \"invalid-type\": Volume type either does not exist in this cloud, or is not available",
},
{
name: "invalid volume type",
controlPlane: false,
name: "valid volume type",
controlPlane: true,
mpool: func() *openstack.MachinePool {
mp := validMachinePoolLargeVolume()
mp.RootVolume.Type = invalidType
mp.RootVolume.DeprecatedType = volumeType
mp.RootVolume.Types = []string{}
return mp
}(),
cloudInfo: validMpoolCloudInfo(),
expectedError: true,
expectedErrMsg: `compute\[0\].platform.openstack.rootVolume.type: Invalid value: \"invalid-type\": Volume Type either does not exist in this cloud, or is not available`,
expectedError: false,
expectedErrMsg: "",
},
{
name: "valid volume types",
controlPlane: true,
mpool: func() *openstack.MachinePool {
mp := validMachinePoolLargeVolume()
mp.RootVolume.Types = volumeTypes
return mp
}(),
cloudInfo: validMpoolCloudInfo(),
expectedError: false,
expectedErrMsg: "",
},
}

Expand Down
Loading