Repository: kubermatic/machine-controller
Pull request #1871 (2024-10-21 13:53:18)

replace packethost/packngo with equinix SDK
**What this PR does / why we need it**:
This replaces one Go package with another.

**Which issue(s) this PR fixes**:
part of https://github.com/kubermatic/kubermatic/issues/13631

**What type of PR is this?**
/kind feature

**Does this PR introduce a user-facing change? Then add your Release Note here**:
```release-note
Use official Equinix SDK instead of deprecated packethost/packngo.
```

**Documentation**:
```documentation
None
```


kubermatic-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please ask for approval from [xrstf](https://github.com/xrstf). For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubermatic%2Fmachine-controller).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubermatic/machine-controller/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["xrstf"]} -->
kubermatic-bot: @xrstf: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-e2e-equinix-metal | dd60f9b8a3df749a982b89976d5257db79171bbe | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1871/pull-machine-controller-e2e-equinix-metal/1848361978876137472) | false | `/test pull-machine-controller-e2e-equinix-metal`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1871)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
xrstf: /test pull-machine-controller-e2e-equinix-metal

Patch:
diff --git a/go.mod b/go.mod
index 0385bc16..fe644147 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,7 @@ require (
 	github.com/aws/smithy-go v1.20.4
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
 	github.com/digitalocean/godo v1.124.0
+	github.com/equinix/equinix-sdk-go v0.46.0
 	github.com/go-logr/logr v1.4.2
 	github.com/go-logr/zapr v1.3.0
 	github.com/go-test/deep v1.1.0
@@ -30,7 +31,6 @@ require (
 	github.com/hetznercloud/hcloud-go/v2 v2.13.1
 	github.com/linode/linodego v1.40.0
 	github.com/nutanix-cloud-native/prism-go-client v0.5.1
-	github.com/packethost/packngo v0.31.0
 	github.com/patrickmn/go-cache v2.1.0+incompatible
 	github.com/pborman/uuid v1.2.1
 	github.com/pkg/errors v0.9.1
diff --git a/go.sum b/go.sum
index 5f7cef61..ee7d9e6c 100644
--- a/go.sum
+++ b/go.sum
@@ -130,6 +130,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/equinix/equinix-sdk-go v0.46.0 h1:ldQo4GtXNr+0XsThQJf/pUdx5wcLFe9QpLFtAwonqH8=
+github.com/equinix/equinix-sdk-go v0.46.0/go.mod h1:hEb3XLaedz7xhl/dpPIS6eOIiXNPeqNiVoyDrT6paIg=
 github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
 github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
@@ -238,7 +240,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -349,7 +350,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@@ -380,8 +380,6 @@ github.com/openshift/custom-resource-status v1.1.2 h1:C3DL44LEbvlbItfd8mT5jWrqPf
 github.com/openshift/custom-resource-status v1.1.2/go.mod h1:DB/Mf2oTeiAmVVX1gN+NEqweonAPY0TKUwADizj8+ZA=
 github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
 github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
-github.com/packethost/packngo v0.31.0 h1:LLH90ardhULWbagBIc3I3nl2uU75io0a7AwY6hyi0S4=
-github.com/packethost/packngo v0.31.0/go.mod h1:Io6VJqzkiqmIEQbpOjeIw9v8q9PfcTEq8TEY/tMQsfw=
 github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
@@ -484,7 +482,6 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
diff --git a/pkg/cloudprovider/provider/equinixmetal/provider.go b/pkg/cloudprovider/provider/equinixmetal/provider.go
index 5e4946c3..d7a27137 100644
--- a/pkg/cloudprovider/provider/equinixmetal/provider.go
+++ b/pkg/cloudprovider/provider/equinixmetal/provider.go
@@ -21,10 +21,11 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"reflect"
+	"net/http"
+	"slices"
 	"strings"
 
-	"github.com/packethost/packngo"
+	"github.com/equinix/equinix-sdk-go/services/metalv1"
 	"go.uber.org/zap"
 
 	"k8c.io/machine-controller/pkg/apis/cluster/common"
@@ -36,9 +37,10 @@ import (
 	"k8c.io/machine-controller/pkg/providerconfig"
 	providerconfigtypes "k8c.io/machine-controller/pkg/providerconfig/types"
 
-	v1 "k8s.io/api/core/v1"
+	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/sets"
 )
 
 const (
@@ -148,7 +150,7 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *e
 	return &c, rawConfig, pconfig, err
 }
 
-func (p *provider) getMetalDevice(machine *clusterv1alpha1.Machine) (*packngo.Device, *packngo.Client, error) {
+func (p *provider) getMetalDevice(ctx context.Context, machine *clusterv1alpha1.Machine) (*metalv1.Device, *metalv1.APIClient, error) {
 	c, _, _, err := p.getConfig(machine.Spec.ProviderSpec)
 	if err != nil {
 		return nil, nil, cloudprovidererrors.TerminalError{
@@ -158,14 +160,14 @@ func (p *provider) getMetalDevice(machine *clusterv1alpha1.Machine) (*packngo.De
 	}
 
 	client := getClient(c.Token)
-	device, err := getDeviceByTag(client, c.ProjectID, generateTag(string(machine.UID)))
+	device, err := getDeviceByTag(ctx, client, c.ProjectID, generateTag(string(machine.UID)))
 	if err != nil {
 		return nil, nil, err
 	}
 	return device, client, nil
 }
 
-func (p *provider) Validate(_ context.Context, _ *zap.SugaredLogger, spec clusterv1alpha1.MachineSpec) error {
+func (p *provider) Validate(ctx context.Context, _ *zap.SugaredLogger, spec clusterv1alpha1.MachineSpec) error {
 	c, _, pc, err := p.getConfig(spec.ProviderSpec)
 	if err != nil {
 		return fmt.Errorf("failed to parse config: %w", err)
@@ -194,49 +196,65 @@ func (p *provider) Validate(_ context.Context, _ *zap.SugaredLogger, spec cluste
 
 	if c.Facilities != nil && (len(c.Facilities) > 0 || c.Facilities[0] != "") {
 		// get all valid facilities
-		facilities, _, err := client.Facilities.List(nil)
+		request := client.FacilitiesApi.FindFacilitiesByProject(ctx, c.ProjectID)
+		facilities, resp, err := client.FacilitiesApi.FindFacilitiesByProjectExecute(request)
 		if err != nil {
 			return fmt.Errorf("failed to list facilities: %w", err)
 		}
+		resp.Body.Close()
+
+		expectedFacilities := sets.New(c.Facilities...)
+		availableFacilities := sets.New[string]()
+		for _, facility := range facilities.Facilities {
+			availableFacilities.Insert(*facility.Code)
+		}
+
 		// ensure our requested facilities are in those facilities
-		if missingFacilities := itemsNotInList(facilityProp(facilities, "Code"), c.Facilities); len(missingFacilities) > 0 {
-			return fmt.Errorf("unknown facilities: %s", strings.Join(missingFacilities, ","))
+		if diff := expectedFacilities.Difference(availableFacilities); diff.Len() > 0 {
+			return fmt.Errorf("unknown facilities: %v", sets.List(diff))
 		}
 	}
 
 	if c.Metro != "" {
-		metros, _, err := client.Metros.List(nil)
+		request := client.MetrosApi.FindMetros(ctx)
+		metros, resp, err := client.MetrosApi.FindMetrosExecute(request)
 		if err != nil {
 			return fmt.Errorf("failed to list metros: %w", err)
 		}
+		resp.Body.Close()
 
-		var metroExists bool
-		for _, metro := range metros {
-			if strings.EqualFold(metro.Code, c.Metro) {
-				metroExists = true
-			}
-		}
+		metroExists := slices.ContainsFunc(metros.Metros, func(m metalv1.Metro) bool {
+			return strings.EqualFold(*m.Code, c.Metro)
+		})
 
 		if !metroExists {
-			return fmt.Errorf("unknown metro: %s", c.Metro)
+			return fmt.Errorf("unknown metro %q", c.Metro)
 		}
 	}
 
 	// get all valid plans a.k.a. instance types
-	plans, _, err := client.Plans.List(nil)
+	request := client.PlansApi.FindPlansByProject(ctx, c.ProjectID)
+	plans, resp, err := client.PlansApi.FindPlansByProjectExecute(request)
 	if err != nil {
 		return fmt.Errorf("failed to list instance types / plans: %w", err)
 	}
+	resp.Body.Close()
+
 	// ensure our requested plan is in those plans
-	validPlanNames := planProp(plans, "Name")
-	if missingPlans := itemsNotInList(validPlanNames, []string{c.InstanceType}); len(missingPlans) > 0 {
-		return fmt.Errorf("unknown instance type / plan: %s, acceptable plans: %s", strings.Join(missingPlans, ","), strings.Join(validPlanNames, ","))
+	expectedPlans := sets.New(c.InstanceType)
+	availablePlans := sets.New[string]()
+	for _, plan := range plans.Plans {
+		availablePlans.Insert(*plan.Name)
+	}
+
+	if diff := expectedPlans.Difference(availablePlans); diff.Len() > 0 {
+		return fmt.Errorf("unknown instance type / plan: %s, acceptable plans: %v", c.InstanceType, sets.List(availablePlans))
 	}
 
 	return nil
 }
 
-func (p *provider) Create(_ context.Context, _ *zap.SugaredLogger, machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.ProviderData, userdata string) (instance.Instance, error) {
+func (p *provider) Create(ctx context.Context, _ *zap.SugaredLogger, machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.ProviderData, userdata string) (instance.Instance, error) {
 	c, _, pc, err := p.getConfig(machine.Spec.ProviderSpec)
 	if err != nil {
 		return nil, cloudprovidererrors.TerminalError{
@@ -246,6 +264,7 @@ func (p *provider) Create(_ context.Context, _ *zap.SugaredLogger, machine *clus
 	}
 
 	client := getClient(c.Token)
+	request := client.DevicesApi.CreateDevice(ctx, c.ProjectID)
 
 	imageName, err := getNameForOS(pc.OperatingSystem)
 	if err != nil {
@@ -255,24 +274,43 @@ func (p *provider) Create(_ context.Context, _ *zap.SugaredLogger, machine *clus
 		}
 	}
 
-	serverCreateOpts := &packngo.DeviceCreateRequest{
-		Hostname:     machine.Spec.Name,
-		UserData:     userdata,
-		ProjectID:    c.ProjectID,
-		Facility:     c.Facilities,
-		Metro:        c.Metro,
-		BillingCycle: c.BillingCycle,
-		Plan:         c.InstanceType,
-		OS:           imageName,
-		Tags: []string{
-			generateTag(string(machine.UID)),
-		},
-	}
+	billingCycle := metalv1.DeviceCreateInputBillingCycle(c.BillingCycle)
 
-	device, res, err := client.Devices.Create(serverCreateOpts)
+	if c.Metro != "" {
+		request = request.CreateDeviceRequest(metalv1.CreateDeviceRequest{
+			DeviceCreateInMetroInput: &metalv1.DeviceCreateInMetroInput{
+				Hostname:        &machine.Spec.Name,
+				Userdata:        &userdata,
+				Metro:           c.Metro,
+				BillingCycle:    &billingCycle,
+				Plan:            c.InstanceType,
+				OperatingSystem: imageName,
+				Tags: []string{
+					generateTag(string(machine.UID)),
+				},
+			},
+		})
+	} else {
+		request = request.CreateDeviceRequest(metalv1.CreateDeviceRequest{
+			DeviceCreateInFacilityInput: &metalv1.DeviceCreateInFacilityInput{
+				Hostname:        &machine.Spec.Name,
+				Userdata:        &userdata,
+				Facility:        c.Facilities,
+				BillingCycle:    &billingCycle,
+				Plan:            c.InstanceType,
+				OperatingSystem: imageName,
+				Tags: []string{
+					generateTag(string(machine.UID)),
+				},
+			},
+		})
+	}
+
+	device, resp, err := client.DevicesApi.CreateDeviceExecute(request)
 	if err != nil {
-		return nil, metalErrorToTerminalError(err, res, "failed to create server")
+		return nil, metalErrorToTerminalError(err, resp, "failed to create server")
 	}
+	resp.Body.Close()
 
 	return &metalDevice{device: device}, nil
 }
@@ -295,10 +333,13 @@ func (p *provider) Cleanup(ctx context.Context, log *zap.SugaredLogger, machine
 	}
 
 	client := getClient(c.Token)
-	res, err := client.Devices.Delete(instance.(*metalDevice).device.ID, false)
+	request := client.DevicesApi.DeleteDevice(ctx, *instance.(*metalDevice).device.Id)
+
+	resp, err := client.DevicesApi.DeleteDeviceExecute(request)
 	if err != nil {
-		return false, metalErrorToTerminalError(err, res, "failed to delete the server")
+		return false, metalErrorToTerminalError(err, resp, "failed to delete the server")
 	}
+	resp.Body.Close()
 
 	return false, nil
 }
@@ -316,8 +357,8 @@ func (p *provider) AddDefaults(_ *zap.SugaredLogger, spec clusterv1alpha1.Machin
 	return spec, nil
 }
 
-func (p *provider) Get(_ context.Context, _ *zap.SugaredLogger, machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.ProviderData) (instance.Instance, error) {
-	device, _, err := p.getMetalDevice(machine)
+func (p *provider) Get(ctx context.Context, _ *zap.SugaredLogger, machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.ProviderData) (instance.Instance, error) {
+	device, _, err := p.getMetalDevice(ctx, machine)
 	if err != nil {
 		return nil, err
 	}
@@ -328,8 +369,8 @@ func (p *provider) Get(_ context.Context, _ *zap.SugaredLogger, machine *cluster
 	return nil, cloudprovidererrors.ErrInstanceNotFound
 }
 
-func (p *provider) MigrateUID(_ context.Context, log *zap.SugaredLogger, machine *clusterv1alpha1.Machine, newID types.UID) error {
-	device, client, err := p.getMetalDevice(machine)
+func (p *provider) MigrateUID(ctx context.Context, log *zap.SugaredLogger, machine *clusterv1alpha1.Machine, newID types.UID) error {
+	device, client, err := p.getMetalDevice(ctx, machine)
 	if err != nil {
 		return err
 	}
@@ -351,13 +392,19 @@ func (p *provider) MigrateUID(_ context.Context, log *zap.SugaredLogger, machine
 	tags = append(tags, generateTag(string(newID)))
 
 	log.Info("Setting UID label for machine")
-	dur := &packngo.DeviceUpdateRequest{
-		Tags: &tags,
-	}
-	_, response, err := client.Devices.Update(device.ID, dur)
+
+	dur := client.DevicesApi.
+		UpdateDevice(ctx, *device.Id).
+		DeviceUpdateInput(metalv1.DeviceUpdateInput{
+			Tags: tags,
+		})
+
+	_, response, err := client.DevicesApi.UpdateDeviceExecute(dur)
 	if err != nil {
 		return metalErrorToTerminalError(err, response, "failed to update UID label")
 	}
+	response.Body.Close()
+
 	log.Info("Successfully set UID label for machine")
 
 	return nil
@@ -380,43 +427,48 @@ func (p *provider) SetMetricsForMachines(_ clusterv1alpha1.MachineList) error {
 }
 
 type metalDevice struct {
-	device *packngo.Device
+	device *metalv1.Device
 }
 
 func (s *metalDevice) Name() string {
-	return s.device.Hostname
+	return *s.device.Hostname
 }
 
 func (s *metalDevice) ID() string {
-	return s.device.ID
+	return *s.device.Id
 }
 
 func (s *metalDevice) ProviderID() string {
-	if s.device == nil || s.device.ID == "" {
+	if s.device == nil || *s.device.Id == "" {
 		return ""
 	}
-	return "equinixmetal://" + s.device.ID
+	return "equinixmetal://" + *s.device.Id
 }
 
-func (s *metalDevice) Addresses() map[string]v1.NodeAddressType {
-	// returns addresses in CIDR format
-	addresses := map[string]v1.NodeAddressType{}
-	for _, ip := range s.device.Network {
-		if ip.Public {
-			addresses[ip.Address] = v1.NodeExternalIP
-			continue
+// Addresses returns addresses in CIDR format.
+func (s *metalDevice) Addresses() map[string]corev1.NodeAddressType {
+	addresses := map[string]corev1.NodeAddressType{}
+	for _, ip := range s.device.IpAddresses {
+		kind := corev1.NodeInternalIP
+		if *ip.Public {
+			kind = corev1.NodeExternalIP
 		}
-		addresses[ip.Address] = v1.NodeInternalIP
+
+		addresses[*ip.Address] = kind
 	}
 
 	return addresses
 }
 
 func (s *metalDevice) Status() instance.Status {
-	switch s.device.State {
-	case "provisioning":
+	if s.device.State == nil {
+		return instance.StatusUnknown
+	}
+
+	switch *s.device.State {
+	case metalv1.DEVICESTATE_PROVISIONING:
 		return instance.StatusCreating
-	case "active":
+	case metalv1.DEVICESTATE_ACTIVE:
 		return instance.StatusRunning
 	default:
 		return instance.StatusUnknown
@@ -448,17 +500,23 @@ func setProviderSpec(rawConfig equinixmetaltypes.RawConfig, s clusterv1alpha1.Pr
 	return &runtime.RawExtension{Raw: rawPconfig}, nil
 }
 
-func getDeviceByTag(client *packngo.Client, projectID, tag string) (*packngo.Device, error) {
-	devices, response, err := client.Devices.List(projectID, nil)
+func getDeviceByTag(ctx context.Context, client *metalv1.APIClient, projectID, tag string) (*metalv1.Device, error) {
+	request := client.DevicesApi.
+		FindProjectDevices(ctx, projectID).
+		Tag(tag)
+
+	devices, response, err := client.DevicesApi.FindProjectDevicesExecute(request)
 	if err != nil {
 		return nil, metalErrorToTerminalError(err, response, "failed to list devices")
 	}
+	response.Body.Close()
 
-	for _, device := range devices {
-		if itemInList(device.Tags, tag) {
+	for _, device := range devices.Devices {
+		if slices.Contains(device.Tags, tag) {
 			return &device, nil
 		}
 	}
+
 	return nil, nil
 }
 
@@ -477,10 +535,12 @@ func getNameForOS(os providerconfigtypes.OperatingSystem) (string, error) {
 	return "", providerconfigtypes.ErrOSNotSupported
 }
 
-func getClient(apiKey string) *packngo.Client {
-	client := packngo.NewClientWithAuth("kubermatic", apiKey, nil)
-	client.UserAgent = fmt.Sprintf("kubermatic/machine-controller %s", client.UserAgent)
-	return client
+func getClient(apiKey string) *metalv1.APIClient {
+	configuration := metalv1.NewConfiguration()
+	configuration.UserAgent = fmt.Sprintf("kubermatic/machine-controller %s", configuration.UserAgent)
+	configuration.AddDefaultHeader("X-Auth-Token", apiKey)
+
+	return metalv1.NewAPIClient(configuration)
 }
 
 func generateTag(ID string) string {
@@ -499,13 +559,13 @@ func getTagUID(tag string) (string, error) {
 // can be qualified as a "terminal" error, for more info see v1alpha1.MachineStatus
 //
 // if the given error doesn't qualify the error passed as an argument will be returned.
-func metalErrorToTerminalError(err error, response *packngo.Response, msg string) error {
+func metalErrorToTerminalError(err error, response *http.Response, msg string) error {
 	prepareAndReturnError := func() error {
-		return fmt.Errorf("%s, due to %w", msg, err)
+		return fmt.Errorf("%s: %w", msg, err)
 	}
 
 	if err != nil {
-		if response != nil && response.Response != nil && response.Response.StatusCode == 403 {
+		if response != nil && response.StatusCode == http.StatusForbidden {
 			// authorization primitives come from MachineSpec
 			// thus we are setting InvalidConfigurationMachineError
 			return cloudprovidererrors.TerminalError{
@@ -519,44 +579,3 @@ func metalErrorToTerminalError(err error, response *packngo.Response, msg string
 
 	return err
 }
-
-func itemInList(list []string, item string) bool {
-	for _, elm := range list {
-		if elm == item {
-			return true
-		}
-	}
-	return false
-}
-
-func itemsNotInList(list, items []string) []string {
-	listMap := make(map[string]bool)
-	missing := make([]string, 0)
-	for _, item := range list {
-		listMap[item] = true
-	}
-	for _, item := range items {
-		if _, ok := listMap[item]; !ok {
-			missing = append(missing, item)
-		}
-	}
-	return missing
-}
-
-func facilityProp(vs []packngo.Facility, field string) []string {
-	vsm := make([]string, len(vs))
-	for i, v := range vs {
-		val := reflect.ValueOf(v)
-		vsm[i] = val.FieldByName(field).String()
-	}
-	return vsm
-}
-
-func planProp(vs []packngo.Plan, field string) []string {
-	vsm := make([]string, len(vs))
-	for i, v := range vs {
-		val := reflect.ValueOf(v)
-		vsm[i] = val.FieldByName(field).String()
-	}
-	return vsm
-}
diff --git a/pkg/cloudprovider/provider/kubevirt/provider.go b/pkg/cloudprovider/provider/kubevirt/provider.go
index c08eb189..723cbef5 100644
--- a/pkg/cloudprovider/provider/kubevirt/provider.go
+++ b/pkg/cloudprovider/provider/kubevirt/provider.go
@@ -124,6 +124,7 @@ type StorageTarget string
 
 const (
 	Storage StorageTarget = "storage"
+	PVC     StorageTarget = "pvc"
 )
 
 type AffinityType string
@@ -308,30 +309,14 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *p
 	if rawConfig.VirtualMachine.DNSConfig != nil {
 		config.DNSConfig = rawConfig.VirtualMachine.DNSConfig
 	}
-	config.SecondaryDisks = make([]SecondaryDisks, 0, len(rawConfig.VirtualMachine.Template.SecondaryDisks))
-	for i, sd := range rawConfig.VirtualMachine.Template.SecondaryDisks {
-		sdSizeString, err := p.configVarResolver.GetConfigVarStringValue(sd.Size)
-		if err != nil {
-			return nil, nil, fmt.Errorf(`failed to parse "secondaryDisks.size" field: %w`, err)
-		}
-		pvc, err := resource.ParseQuantity(sdSizeString)
-		if err != nil {
-			return nil, nil, fmt.Errorf(`failed to parse value of "secondaryDisks.size" field: %w`, err)
-		}
-
-		scString, err := p.configVarResolver.GetConfigVarStringValue(sd.StorageClassName)
-		if err != nil {
-			return nil, nil, fmt.Errorf(`failed to parse value of "secondaryDisks.storageClass" field: %w`, err)
-		}
-		config.SecondaryDisks = append(config.SecondaryDisks, SecondaryDisks{
-			Name:              fmt.Sprintf("secondarydisk%d", i),
-			Size:              pvc,
-			StorageClassName:  scString,
-			StorageAccessType: p.getStorageAccessType(sd.StorageAccessType),
-		})
+	infraClient, err := client.New(config.RestConfig, client.Options{})
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to get kubevirt client: %w", err)
+	}
+	config.StorageAccessType, config.SecondaryDisks, err = p.configureStorage(infraClient, rawConfig.VirtualMachine.Template)
+	if err != nil {
+		return nil, nil, fmt.Errorf(`failed to configure storage: %w`, err)
 	}
-	config.StorageAccessType = p.getStorageAccessType(rawConfig.VirtualMachine.Template.PrimaryDisk.StorageAccessType)
-
 	config.NodeAffinityPreset, err = p.parseNodeAffinityPreset(rawConfig.Affinity.NodeAffinityPreset)
 	if err != nil {
 		return nil, nil, fmt.Errorf(`failed to parse "nodeAffinityPreset" field: %w`, err)
@@ -356,12 +341,29 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *p
 	return &config, pconfig, nil
 }
 
-func (p *provider) getStorageAccessType(accessType providerconfigtypes.ConfigVarString) corev1.PersistentVolumeAccessMode {
+func (p *provider) getStorageAccessType(ctx context.Context, accessType providerconfigtypes.ConfigVarString,
+	infraClient client.Client, storageClassName string) (corev1.PersistentVolumeAccessMode, error) {
 	at, _ := p.configVarResolver.GetConfigVarStringValue(accessType)
 	if at == "" {
-		return corev1.ReadWriteOnce
+		sp := &cdiv1beta1.StorageProfile{}
+		if err := infraClient.Get(ctx, types.NamespacedName{Name: storageClassName}, sp); err != nil {
+			return "", fmt.Errorf(`failed to get cdi storageprofile: %w`, err)
+		}
+
+		// choose RWO as a default access mode and if RWX is supported then choose it instead.
+		accessMode := corev1.ReadWriteOnce
+		for _, claimProperty := range sp.Status.ClaimPropertySets {
+			for _, am := range claimProperty.AccessModes {
+				if am == corev1.ReadWriteMany {
+					accessMode = corev1.ReadWriteMany
+				}
+			}
+		}
+
+		return accessMode, nil
 	}
-	return corev1.PersistentVolumeAccessMode(at)
+
+	return corev1.PersistentVolumeAccessMode(at), nil
 }
 
 func (p *provider) parseNodeAffinityPreset(nodeAffinityPreset kubevirttypes.NodeAffinityPreset) (NodeAffinityPreset, error) {
@@ -712,7 +714,13 @@ func (p *provider) newVirtualMachine(c *Config, pc *providerconfigtypes.Config,
 	}
 
 	defaultBridgeNetwork := defaultBridgeNetwork()
-	runStrategyOnce := kubevirtv1.RunStrategyOnce
+	runStrategy := kubevirtv1.RunStrategyOnce
+	// currently we only support KubeOvn as a ProviderNetwork and KubeOvn has the ability to pin the IP of the VM(static ip)
+	// even if the VMi was stopped or deleted thus we can have the VM always running and in the events of VM restarts the
+	// ip address of the VMI will not change.
+	if c.SubnetName != "" {
+		runStrategy = kubevirtv1.RunStrategyAlways
+	}
 
 	virtualMachine := &kubevirtv1.VirtualMachine{
 		ObjectMeta: metav1.ObjectMeta{
@@ -721,7 +729,7 @@ func (p *provider) newVirtualMachine(c *Config, pc *providerconfigtypes.Config,
 			Labels:    labels,
 		},
 		Spec: kubevirtv1.VirtualMachineSpec{
-			RunStrategy:  &runStrategyOnce,
+			RunStrategy:  &runStrategy,
 			Instancetype: c.Instancetype,
 			Preference:   c.Preference,
 			Template: &kubevirtv1.VirtualMachineInstanceTemplateSpec{
@@ -885,8 +893,8 @@ func getDataVolumeTemplates(config *Config, dataVolumeName string, annotations m
 	}
 
 	switch config.StorageTarget {
-	case Storage:
-		dataVolumeTemplates[0].Spec.Storage = &cdiv1beta1.StorageSpec{
+	case PVC:
+		dataVolumeTemplates[0].Spec.PVC = &corev1.PersistentVolumeClaimSpec{
 			StorageClassName: ptr.To(config.StorageClassName),
 			AccessModes: []corev1.PersistentVolumeAccessMode{
 				config.StorageAccessType,
@@ -896,7 +904,7 @@ func getDataVolumeTemplates(config *Config, dataVolumeName string, annotations m
 			},
 		}
 	default:
-		dataVolumeTemplates[0].Spec.PVC = &corev1.PersistentVolumeClaimSpec{
+		dataVolumeTemplates[0].Spec.Storage = &cdiv1beta1.StorageSpec{
 			StorageClassName: ptr.To(config.StorageClassName),
 			AccessModes: []corev1.PersistentVolumeAccessMode{
 				config.StorageAccessType,
@@ -1064,3 +1072,44 @@ func setOVNAnnotations(c *Config, annotations map[string]string) error {
 
 	return nil
 }
+
+func (p *provider) configureStorage(infraClient client.Client, template kubevirttypes.Template) (corev1.PersistentVolumeAccessMode, []SecondaryDisks, error) {
+	secondaryDisks := make([]SecondaryDisks, 0, len(template.SecondaryDisks))
+	for i, sd := range template.SecondaryDisks {
+		sdSizeString, err := p.configVarResolver.GetConfigVarStringValue(sd.Size)
+		if err != nil {
+			return "", nil, fmt.Errorf(`failed to parse "secondaryDisks.size" field: %w`, err)
+		}
+		pvc, err := resource.ParseQuantity(sdSizeString)
+		if err != nil {
+			return "", nil, fmt.Errorf(`failed to parse value of "secondaryDisks.size" field: %w`, err)
+		}
+
+		scString, err := p.configVarResolver.GetConfigVarStringValue(sd.StorageClassName)
+		if err != nil {
+			return "", nil, fmt.Errorf(`failed to parse value of "secondaryDisks.storageClass" field: %w`, err)
+		}
+		storageAccessMode, err := p.getStorageAccessType(context.TODO(), sd.StorageAccessType, infraClient, scString)
+		if err != nil {
+			return "", nil, fmt.Errorf(`failed to get value of storageAccessMode: %w`, err)
+		}
+		secondaryDisks = append(secondaryDisks, SecondaryDisks{
+			Name:              fmt.Sprintf("secondarydisk%d", i),
+			Size:              pvc,
+			StorageClassName:  scString,
+			StorageAccessType: storageAccessMode,
+		})
+	}
+	scString, err := p.configVarResolver.GetConfigVarStringValue(template.PrimaryDisk.StorageClassName)
+	if err != nil {
+		return "", nil, fmt.Errorf(`failed to parse value of "primaryDisk.storageClass" field: %w`, err)
+	}
+
+	primaryDisk, err := p.getStorageAccessType(context.TODO(), template.PrimaryDisk.StorageAccessType,
+		infraClient, scString)
+	if err != nil {
+		return "", nil, fmt.Errorf(`failed to get value of primaryDiskstorageAccessType: %w`, err)
+	}
+
+	return primaryDisk, secondaryDisks, nil
+}


Test patch:
diff --git a/pkg/cloudprovider/provider/kubevirt/provider_test.go b/pkg/cloudprovider/provider/kubevirt/provider_test.go
index 991d5f13..c5841214 100644
--- a/pkg/cloudprovider/provider/kubevirt/provider_test.go
+++ b/pkg/cloudprovider/provider/kubevirt/provider_test.go
@@ -132,11 +132,14 @@ func (k kubevirtProviderSpecConf) rawProviderSpec(t *testing.T) []byte {
 				{{- if .SecondaryDisks }}
 				"secondaryDisks": [{
 					"size": "20Gi",
+                    "storageAccessType": "ReadWriteMany",
 					"storageClassName": "longhorn2"},{
 					"size": "30Gi",
+                    "storageAccessType": "ReadWriteMany",
 					"storageClassName": "longhorn3"}],
 				{{- end }}
 				"primaryDisk": {
+                    "storageAccessType": "ReadWriteMany",
 					{{- if .StorageTarget }}
 					"storageTarget": "{{ .StorageTarget }}",
 					{{- end }}
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/affinity-no-values.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/affinity-no-values.yaml
index 48dbb7d0..a8382d8e 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/affinity-no-values.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/affinity-no-values.yaml
@@ -15,9 +15,9 @@ spec:
         creationTimestamp: null
         name: affinity-no-values
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/affinity.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/affinity.yaml
index 2460731d..7c0a3e9d 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/affinity.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/affinity.yaml
@@ -15,9 +15,9 @@ spec:
         creationTimestamp: null
         name: affinity
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/custom-local-disk.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/custom-local-disk.yaml
index 32d50db2..b83d4b01 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/custom-local-disk.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/custom-local-disk.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: custom-local-disk
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/http-image-source.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/http-image-source.yaml
index 289549ed..f36d5392 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/http-image-source.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/http-image-source.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: http-image-source
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/instancetype-preference-custom.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/instancetype-preference-custom.yaml
index 6df4ec46..9154f71c 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/instancetype-preference-custom.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/instancetype-preference-custom.yaml
@@ -15,9 +15,9 @@ spec:
         creationTimestamp: null
         name: instancetype-preference-custom
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/instancetype-preference-standard.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/instancetype-preference-standard.yaml
index 12f591b5..c890481c 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/instancetype-preference-standard.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/instancetype-preference-standard.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: instancetype-preference-standard
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/kubeovn-provider-network.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/kubeovn-provider-network.yaml
index 9f0f8948..aa8dc5b6 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/kubeovn-provider-network.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/kubeovn-provider-network.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: kubeovn-provider-network
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
@@ -24,7 +24,7 @@ spec:
         source:
           http:
             url: http://x.y.z.t/ubuntu.img
-  runStrategy: Once
+  runStrategy: Always
   template:
     metadata:
       creationTimestamp: null
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/nominal-case.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/nominal-case.yaml
index 81651774..58f1ae77 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/nominal-case.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/nominal-case.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: nominal-case
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/pvc-image-source.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/pvc-image-source.yaml
index 07159c1a..61884708 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/pvc-image-source.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/pvc-image-source.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: pvc-image-source
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/registry-image-source-pod.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/registry-image-source-pod.yaml
index 5e197b77..69aaaac7 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/registry-image-source-pod.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/registry-image-source-pod.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: registry-image-source-pod
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/registry-image-source.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/registry-image-source.yaml
index 7ea3bdfd..f4608a12 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/registry-image-source.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/registry-image-source.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: registry-image-source
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/secondary-disks.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/secondary-disks.yaml
index 2976b746..8abda0e5 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/secondary-disks.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/secondary-disks.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: secondary-disks
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
@@ -29,7 +29,7 @@ spec:
       spec:
         pvc:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 20Gi
@@ -42,7 +42,7 @@ spec:
       spec:
         pvc:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 30Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/topologyspreadconstraints.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/topologyspreadconstraints.yaml
index 71828981..14109166 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/topologyspreadconstraints.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/topologyspreadconstraints.yaml
@@ -14,9 +14,9 @@ spec:
     - metadata:
         name: topologyspreadconstraints
       spec:
-        pvc:
+        storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi
diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/use-storage-as-storage-target.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/use-storage-as-storage-target.yaml
index 813faf8f..b441d553 100644
--- a/pkg/cloudprovider/provider/kubevirt/testdata/use-storage-as-storage-target.yaml
+++ b/pkg/cloudprovider/provider/kubevirt/testdata/use-storage-as-storage-target.yaml
@@ -20,7 +20,7 @@ spec:
             url: "http://x.y.z.t/ubuntu.img"
         storage:
           accessModes:
-            - ReadWriteOnce
+            - ReadWriteMany
           resources:
             requests:
               storage: 10Gi