diff --git a/pkg/cloudprovider/provider/kubevirt/provider.go b/pkg/cloudprovider/provider/kubevirt/provider.go index fa385cf17..b57b833a6 100644 --- a/pkg/cloudprovider/provider/kubevirt/provider.go +++ b/pkg/cloudprovider/provider/kubevirt/provider.go @@ -76,6 +76,10 @@ const ( registrySource imageSource = "registry" // pvcSource defines the pvc source type for VM Disk Image. pvcSource imageSource = "pvc" + // topologyRegionKey and topologyZoneKey on PVC is a topology-aware volume provisioners will automatically set + // node affinity constraints on a PersistentVolume. + topologyRegionKey = "topology.kubernetes.io/region" + topologyZoneKey = "topology.kubernetes.io/zone" ) type provider struct { @@ -106,6 +110,8 @@ type Config struct { SecondaryDisks []SecondaryDisks NodeAffinityPreset NodeAffinityPreset TopologySpreadConstraints []corev1.TopologySpreadConstraint + Region string + Zone string } // StorageTarget represents targeted storage definition that will be used to provision VirtualMachine volumes. Currently, @@ -331,6 +337,11 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *p return nil, nil, fmt.Errorf(`failed to parse "topologySpreadConstraints" field: %w`, err) } + if rawConfig.VirtualMachine.Location != nil { + config.Zone = rawConfig.VirtualMachine.Location.Zone + config.Region = rawConfig.VirtualMachine.Location.Region + } + return &config, pconfig, nil } @@ -649,6 +660,14 @@ func (p *provider) newVirtualMachine(_ context.Context, c *Config, pc *providerc labels["cluster.x-k8s.io/cluster-name"] = c.ClusterName labels["cluster.x-k8s.io/role"] = "worker" + if c.Region != "" { + labels[topologyRegionKey] = c.Region + } + + if c.Zone != "" { + labels[topologyZoneKey] = c.Zone + } + var ( dataVolumeName = machine.Name annotations = map[string]string{} diff --git a/pkg/cloudprovider/provider/kubevirt/provider_test.go b/pkg/cloudprovider/provider/kubevirt/provider_test.go index a1ef8545a..d4ab2bc58 100644 --- a/pkg/cloudprovider/provider/kubevirt/provider_test.go +++ b/pkg/cloudprovider/provider/kubevirt/provider_test.go @@ -21,6 +21,7 @@ import ( "context" "embed" "html/template" + "k8c.io/machine-controller/pkg/cloudprovider/provider/kubevirt/types" "path" "reflect" "testing" @@ -68,6 +69,7 @@ type kubevirtProviderSpecConf struct { OsImageSource imageSource OsImageSourceURL string PullMethod cdiv1beta1.RegistryPullMethod + Location *types.Location } func (k kubevirtProviderSpecConf) rawProviderSpec(t *testing.T) []byte { @@ -101,6 +103,12 @@ func (k kubevirtProviderSpecConf) rawProviderSpec(t *testing.T) []byte { }, {{- end }} "virtualMachine": { + {{- if .Location }} + "location": { + "zone": "{{ .Location.Zone }}", + "region": "{{ .Location.Region }}" + }, + {{- end }} {{- if .Instancetype }} "instancetype": { "name": "{{ .Instancetype.Name }}", @@ -202,6 +210,15 @@ func TestNewVirtualMachine(t *testing.T) { }, }, }, + { + name: "location-zone-and-region-aware", + specConf: kubevirtProviderSpecConf{ + Location: &types.Location{ + Region: "europe-central", + Zone: "hh", + }, + }, + }, { name: "topologyspreadconstraints", specConf: kubevirtProviderSpecConf{TopologySpreadConstraint: true}, diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/location-zone-and-region-aware.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/location-zone-and-region-aware.yaml new file mode 100644 index 000000000..66c747353 --- /dev/null +++ b/pkg/cloudprovider/provider/kubevirt/testdata/location-zone-and-region-aware.yaml @@ -0,0 +1,83 @@ +apiVersion: kubevirt.io/v1 +kind: VirtualMachine +metadata: + annotations: + labels: + cluster.x-k8s.io/cluster-name: cluster-name + cluster.x-k8s.io/role: worker + kubevirt.io/vm: location-zone-and-region-aware + topology.kubernetes.io/region: europe-central + topology.kubernetes.io/zone: hh + md: md-name + name: location-zone-and-region-aware + namespace: test-namespace +spec: + dataVolumeTemplates: + - metadata: + name: location-zone-and-region-aware + spec: + pvc: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: longhorn + source: + http: + url: http://x.y.z.t/ubuntu.img + runStrategy: Once + template: + metadata: + creationTimestamp: null + annotations: + "ovn.kubernetes.io/allow_live_migration": "true" + "kubevirt.io/allow-pod-bridge-network-live-migration": "true" + labels: + cluster.x-k8s.io/cluster-name: cluster-name + cluster.x-k8s.io/role: worker + kubevirt.io/vm: location-zone-and-region-aware + md: md-name + topology.kubernetes.io/region: europe-central + topology.kubernetes.io/zone: hh + spec: + affinity: {} + domain: + devices: + disks: + - disk: + bus: virtio + name: datavolumedisk + - disk: + bus: virtio + name: cloudinitdisk + interfaces: + - name: default + bridge: {} + resources: + limits: + cpu: "2" + memory: 2Gi + requests: + cpu: "2" + memory: 2Gi + networks: + - name: default + pod: {} + terminationGracePeriodSeconds: 30 + topologyspreadconstraints: + - maxskew: 1 + topologykey: kubernetes.io/hostname + whenunsatisfiable: ScheduleAnyway + labelselector: + matchlabels: + md: md-name + volumes: + - dataVolume: + name: location-zone-and-region-aware + name: datavolumedisk + - cloudInitNoCloud: + secretRef: + name: udsn + name: cloudinitdisk + evictionStrategy: External diff --git a/pkg/cloudprovider/provider/kubevirt/types/types.go b/pkg/cloudprovider/provider/kubevirt/types/types.go index 52a2c2e63..e5505e714 100644 --- a/pkg/cloudprovider/provider/kubevirt/types/types.go +++ b/pkg/cloudprovider/provider/kubevirt/types/types.go @@ -57,6 +57,7 @@ type VirtualMachine struct { Template Template `json:"template,omitempty"` DNSPolicy providerconfigtypes.ConfigVarString `json:"dnsPolicy,omitempty"` DNSConfig *corev1.PodDNSConfig `json:"dnsConfig,omitempty"` + Location *Location `json:"location,omitempty"` } // Flavor. @@ -125,6 +126,12 @@ type TopologySpreadConstraint struct { WhenUnsatisfiable providerconfigtypes.ConfigVarString `json:"whenUnsatisfiable,omitempty"` } +// Location describes the region and zone where the machines are created at and where the deployed resources will reside. +type Location struct { + Region string `json:"region,omitempty"` + Zone string `json:"zone,omitempty"` +} + func GetConfig(pconfig providerconfigtypes.Config) (*RawConfig, error) { rawConfig := &RawConfig{}