diff --git a/pkg/common/utils/utils.go b/pkg/common/utils/utils.go index 5ca8055fd0..ed68fd75d6 100644 --- a/pkg/common/utils/utils.go +++ b/pkg/common/utils/utils.go @@ -33,16 +33,174 @@ import ( cnsvolume "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/cns-lib/volume" cnsvsphere "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/cns-lib/vsphere" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger" + "sigs.k8s.io/vsphere-csi-driver/v3/pkg/kubernetes" ) const ( // DefaultQuerySnapshotLimit constant is already present in pkg/csi/service/common/constants.go // However, using that constant creates an import cycle. // TODO: Refactor to move all the constants into a top level directory. - DefaultQuerySnapshotLimit = int64(128) + DefaultQuerySnapshotLimit = int64(128) + vmOperatorApiVersionPrefix = "vmoperator.vmware.com" + virtualMachineCRDName = "virtualmachines.vmoperator.vmware.com" ) +var getLatestCRDVersion = kubernetes.GetLatestCRDVersion + +// ListVirtualMachines lists all the virtual machines +// converted to the latest API version(v1alpha4). +// Since, VM Operator converts all the older API versions to the latest version, +// this function determines the latest API version of the VirtualMachine CRD and lists the resources. +func ListVirtualMachines(ctx context.Context, clt client.Client, + namespace string) (*vmoperatorv1alpha4.VirtualMachineList, error) { + log := logger.GetLogger(ctx) + + version, err := getLatestCRDVersion(ctx, virtualMachineCRDName) + if err != nil { + log.Errorf("failed to get latest CRD version for %s: %s", virtualMachineCRDName, err) + return nil, err + } + + vmList := &vmoperatorv1alpha4.VirtualMachineList{} + log.Info("Attempting to list virtual machines with the latest API version ", version) + switch version { + case "v1alpha1": + vmAlpha1List := &vmoperatorv1alpha1.VirtualMachineList{} + err := clt.List(ctx, vmAlpha1List, client.InNamespace(namespace)) + if err != nil { + log.Error("failed listing virtual machines for v1alpha1: ", err) + return nil, err + } + + err = vmoperatorv1alpha1.Convert_v1alpha1_VirtualMachineList_To_v1alpha4_VirtualMachineList( + vmAlpha1List, vmList, nil) + if err != nil { + log.Fatal("Error converting v1alpha1 virtual machines to v1alpha4: ", err) + return nil, err + } + case "v1alpha2": + vmAlpha2List := &vmoperatorv1alpha2.VirtualMachineList{} + err := clt.List(ctx, vmAlpha2List, client.InNamespace(namespace)) + if err != nil { + log.Error("failed listing virtual machines for v1alpha2: ", err) + return nil, err + } + + err = vmoperatorv1alpha2.Convert_v1alpha2_VirtualMachineList_To_v1alpha4_VirtualMachineList( + vmAlpha2List, vmList, nil) + if err != nil { + log.Fatal("Error converting v1alpha2 virtual machines to v1alpha4: ", err) + return nil, err + } + case "v1alpha3": + vmAlpha3List := &vmoperatorv1alpha3.VirtualMachineList{} + err := clt.List(ctx, vmAlpha3List, client.InNamespace(namespace)) + if err != nil { + log.Error("failed listing virtual machines for v1alpha3: ", err) + return nil, err + } + + err = vmoperatorv1alpha3.Convert_v1alpha3_VirtualMachineList_To_v1alpha4_VirtualMachineList( + vmAlpha3List, vmList, nil) + if err != nil { + log.Error("Error converting v1alpha3 virtual machines to v1alpha4: ", err) + return nil, err + } + case "v1alpha4": + vmAlpha4List := &vmoperatorv1alpha4.VirtualMachineList{} + err := clt.List(context.Background(), vmAlpha4List, client.InNamespace(namespace)) + if err != nil { + log.Error("failed listing virtual machines for v1alpha4: ", err) + return nil, err + } + + // No conversion needed for v1alpha4 as it is the latest version. + vmList.Items = append(vmList.Items, vmAlpha4List.Items...) + default: + // XXX: This should ideally never happen. + return nil, logger.LogNewErrorCodef(log, codes.Internal, + "Unsupported version: %s. Something is fishy...", version) + } + + log.Infof("Successfully listed %d virtual machines in namespace %s", + len(vmList.Items), namespace) + return vmList, nil +} + +// GetVirtualMachine fetches the virtual machine with the specified key +// converted to the latest API version(v1alpha4). +// Additionally, it returns the API version of the virtual machine CRD. +func GetVirtualMachine(ctx context.Context, vmOperatorClient client.Client, + vmKey types.NamespacedName) (*vmoperatorv1alpha4.VirtualMachine, string, error) { + log := logger.GetLogger(ctx) + + version, err := getLatestCRDVersion(ctx, virtualMachineCRDName) + if err != nil { + log.Errorf("failed to get latest CRD version for %s: %s", virtualMachineCRDName, err) + return nil, "", err + } + + log.Infof("finding virtual machine with key: %s, version: %s", vmKey.String(), version) + vm := &vmoperatorv1alpha4.VirtualMachine{} + apiVersion := vmOperatorApiVersionPrefix + "/" + version + switch version { + case "v1alpha1": + vmV1alpha1 := &vmoperatorv1alpha1.VirtualMachine{} + err = vmOperatorClient.Get(ctx, vmKey, vmV1alpha1) + if err != nil { + log.Errorf("failed to get virtual machine with key %s in v1alpha1: %s", vmKey.String(), err) + return nil, "", err + } + + err = vmoperatorv1alpha1.Convert_v1alpha1_VirtualMachine_To_v1alpha4_VirtualMachine( + vmV1alpha1, vm, nil) + if err != nil { + log.Error("Error converting v1alpha1 virtual machine to v1alpha4:", err) + return nil, "", err + } + case "v1alpha2": + vmV1alpha2 := &vmoperatorv1alpha2.VirtualMachine{} + err = vmOperatorClient.Get(ctx, vmKey, vmV1alpha2) + if err != nil { + log.Errorf("failed to get virtual machine with key %s in v1alpha2: %s", vmKey.String(), err) + return nil, "", err + } + + err = vmoperatorv1alpha2.Convert_v1alpha2_VirtualMachine_To_v1alpha4_VirtualMachine( + vmV1alpha2, vm, nil) + if err != nil { + log.Error("Error converting v1alpha2 virtual machine to v1alpha4:", err) + return nil, "", err + } + case "v1alpha3": + vmV1alpha3 := &vmoperatorv1alpha3.VirtualMachine{} + err = vmOperatorClient.Get(ctx, vmKey, vmV1alpha3) + if err != nil { + log.Errorf("failed to get virtual machine with key %s in v1alpha3: %s", vmKey.String(), err) + return nil, "", err + } + + err = vmoperatorv1alpha3.Convert_v1alpha3_VirtualMachine_To_v1alpha4_VirtualMachine( + vmV1alpha3, vm, nil) + if err != nil { + log.Error("Error converting v1alpha3 virtual machine to v1alpha4:", err) + return nil, "", err + } + case "v1alpha4": + err = vmOperatorClient.Get(ctx, vmKey, vm) + if err != nil { + log.Errorf("failed to get virtual machine with key %s in v1alpha4: %s", vmKey.String(), err) + return nil, "", err + } + default: + return nil, "", logger.LogNewErrorCodef(log, codes.Internal, + "Unsupported version: %s. Something is fishy...", version) + } + + return vm, apiVersion, nil +} + // QueryVolumeUtil helps to invoke query volume API based on the feature // state set for using query async volume. If useQueryVolumeAsync is set to // true, the function invokes CNS QueryVolumeAsync, otherwise it invokes @@ -274,161 +432,9 @@ func QueryAllVolumesForCluster(ctx context.Context, m cnsvolume.Manager, cluster return queryAllResult, nil } -func GetVirtualMachineAllApiVersions(ctx context.Context, vmKey types.NamespacedName, - vmOperatorClient client.Client) (*vmoperatorv1alpha4.VirtualMachine, string, error) { - log := logger.GetLogger(ctx) - apiVersion := vmOperatorApiVersionPrefix + "/v1alpha4" - vmV1alpha1 := &vmoperatorv1alpha1.VirtualMachine{} - vmV1alpha2 := &vmoperatorv1alpha2.VirtualMachine{} - vmV1alpha3 := &vmoperatorv1alpha3.VirtualMachine{} - vmV1alpha4 := &vmoperatorv1alpha4.VirtualMachine{} - var err error - log.Infof("get machine with vm-operator api version v1alpha4 name: %s, namespace: %s", - vmKey.Name, vmKey.Namespace) - err = vmOperatorClient.Get(ctx, vmKey, vmV1alpha4) - if err != nil && isKindNotFound(err.Error()) { - log.Warnf("failed to get VirtualMachines. %s", err.Error()) - err = vmOperatorClient.Get(ctx, vmKey, vmV1alpha3) - if err != nil && isKindNotFound(err.Error()) { - log.Warnf("failed to get VirtualMachines. %s", err.Error()) - err = vmOperatorClient.Get(ctx, vmKey, vmV1alpha2) - if err != nil && isKindNotFound(err.Error()) { - log.Warnf("failed to get VirtualMachines. %s", err.Error()) - err = vmOperatorClient.Get(ctx, vmKey, vmV1alpha1) - if err != nil && isKindNotFound(err.Error()) { - log.Warnf("failed to get VirtualMachines. %s", err.Error()) - } else if err == nil { - log.Debugf("GetVirtualMachineAllApiVersions: converting v1alpha1 VirtualMachine "+ - "to v1alpha4 VirtualMachine, name %s", vmV1alpha1.Name) - apiVersion = vmOperatorApiVersionPrefix + "/v1alpha1" - err = vmoperatorv1alpha1.Convert_v1alpha1_VirtualMachine_To_v1alpha4_VirtualMachine( - vmV1alpha1, vmV1alpha4, nil) - if err != nil { - return nil, apiVersion, err - } - } - } else if err == nil { - log.Debugf("GetVirtualMachineAllApiVersions: converting v1alpha2 VirtualMachine "+ - "to v1alpha4 VirtualMachine, name %s", vmV1alpha2.Name) - apiVersion = vmOperatorApiVersionPrefix + "/v1alpha2" - err = vmoperatorv1alpha2.Convert_v1alpha2_VirtualMachine_To_v1alpha4_VirtualMachine( - vmV1alpha2, vmV1alpha4, nil) - if err != nil { - return nil, apiVersion, err - } - } - } else if err == nil { - log.Debugf("GetVirtualMachineAllApiVersions: converting v1alpha3 VirtualMachine "+ - "to v1alpha4 VirtualMachine, name %s", vmV1alpha3.Name) - apiVersion = vmOperatorApiVersionPrefix + "/v1alpha3" - err = vmoperatorv1alpha3.Convert_v1alpha3_VirtualMachine_To_v1alpha4_VirtualMachine( - vmV1alpha3, vmV1alpha4, nil) - if err != nil { - return nil, apiVersion, err - } - } - } - if err != nil { - log.Errorf("GetVirtualMachineAllApiVersions: failed to get VirtualMachine "+ - "with name %s and namespace %s, error %v", vmKey.Name, vmKey.Namespace, err) - return nil, apiVersion, err - } - log.Infof("successfully fetched the virtual machines with name %s and namespace %s", - vmKey.Name, vmKey.Namespace) - return vmV1alpha4, apiVersion, nil -} func isKindNotFound(errMsg string) bool { return strings.Contains(errMsg, "no matches for kind") || strings.Contains(errMsg, "no kind is registered") } -func GetVirtualMachineListAllApiVersions(ctx context.Context, namespace string, - vmOperatorClient client.Client) (*vmoperatorv1alpha4.VirtualMachineList, error) { - log := logger.GetLogger(ctx) - vmListV1alpha1 := &vmoperatorv1alpha1.VirtualMachineList{} - vmListV1alpha2 := &vmoperatorv1alpha2.VirtualMachineList{} - vmListV1alpha3 := &vmoperatorv1alpha3.VirtualMachineList{} - vmListV1alpha4 := &vmoperatorv1alpha4.VirtualMachineList{} - var err error - if namespace != "" { - // get list of virtualmachine for specific namespace - log.Infof("list virtualmachines for namespace %s", namespace) - err = vmOperatorClient.List(ctx, vmListV1alpha4, client.InNamespace(namespace)) - if err != nil && isKindNotFound(err.Error()) { - err = vmOperatorClient.List(ctx, vmListV1alpha3, client.InNamespace(namespace)) - if err != nil && isKindNotFound(err.Error()) { - err := vmOperatorClient.List(ctx, vmListV1alpha2, client.InNamespace(namespace)) - if err != nil && isKindNotFound(err.Error()) { - err := vmOperatorClient.List(ctx, vmListV1alpha1, client.InNamespace(namespace)) - if err != nil { - return nil, err - } else { - log.Info("converting v1alpha1 VirtualMachineList to v1alpha4 VirtualMachineList") - err = vmoperatorv1alpha1.Convert_v1alpha1_VirtualMachineList_To_v1alpha4_VirtualMachineList( - vmListV1alpha1, vmListV1alpha4, nil) - if err != nil { - return nil, err - } - } - } else if err == nil { - log.Info("converting v1alpha2 VirtualMachineList to v1alpha4 VirtualMachineList") - err = vmoperatorv1alpha2.Convert_v1alpha2_VirtualMachineList_To_v1alpha4_VirtualMachineList( - vmListV1alpha2, vmListV1alpha4, nil) - if err != nil { - return nil, err - } - } - } else if err == nil { - log.Info("converting v1alpha3 VirtualMachineList to v1alpha4 VirtualMachineList") - err = vmoperatorv1alpha3.Convert_v1alpha3_VirtualMachineList_To_v1alpha4_VirtualMachineList( - vmListV1alpha3, vmListV1alpha4, nil) - if err != nil { - return nil, err - } - } - } - } else { - // get list of virtualmachine without providing namespace (all) - log.Info("list all virtualmachines") - err = vmOperatorClient.List(ctx, vmListV1alpha4) - if err != nil && isKindNotFound(err.Error()) { - err = vmOperatorClient.List(ctx, vmListV1alpha3) - if err != nil && isKindNotFound(err.Error()) { - err := vmOperatorClient.List(ctx, vmListV1alpha2) - if err != nil && isKindNotFound(err.Error()) { - err := vmOperatorClient.List(ctx, vmListV1alpha1) - if err != nil { - return nil, err - } else { - log.Info("converting v1alpha1 VirtualMachineList to v1alpha4 VirtualMachineList") - err = vmoperatorv1alpha1.Convert_v1alpha1_VirtualMachineList_To_v1alpha4_VirtualMachineList( - vmListV1alpha1, vmListV1alpha4, nil) - if err != nil { - return nil, err - } - } - } else if err == nil { - log.Info("converting v1alpha2 VirtualMachineList to v1alpha4 VirtualMachineList") - err = vmoperatorv1alpha2.Convert_v1alpha2_VirtualMachineList_To_v1alpha4_VirtualMachineList( - vmListV1alpha2, vmListV1alpha4, nil) - if err != nil { - return nil, err - } - } - } else if err == nil { - log.Info("converting v1alpha3 VirtualMachineList to v1alpha4 VirtualMachineList") - err = vmoperatorv1alpha3.Convert_v1alpha3_VirtualMachineList_To_v1alpha4_VirtualMachineList( - vmListV1alpha3, vmListV1alpha4, nil) - if err != nil { - return nil, err - } - } - } - } - if err != nil { - return nil, err - } - log.Infof("successfully fetched the virtual machines for namespace %s", namespace) - return vmListV1alpha4, nil -} func PatchVirtualMachine(ctx context.Context, vmOperatorClient client.Client, vmV1alpha4, old_vmV1alpha4 *vmoperatorv1alpha4.VirtualMachine) error { diff --git a/pkg/common/utils/utils_test.go b/pkg/common/utils/utils_test.go index 74e3f45861..16584845c4 100644 --- a/pkg/common/utils/utils_test.go +++ b/pkg/common/utils/utils_test.go @@ -10,14 +10,26 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + vmoperatorv1alpha1 "github.com/vmware-tanzu/vm-operator/api/v1alpha1" + vmoperatorv1alpha2 "github.com/vmware-tanzu/vm-operator/api/v1alpha2" + vmoperatorv1alpha3 "github.com/vmware-tanzu/vm-operator/api/v1alpha3" + vmoperatorv1alpha4 "github.com/vmware-tanzu/vm-operator/api/v1alpha4" cnssim "github.com/vmware/govmomi/cns/simulator" "github.com/vmware/govmomi/cns/types" "github.com/vmware/govmomi/simulator" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + k8stypes "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" - + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/interceptor" cnsvolumes "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/cns-lib/volume" cnsvsphere "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/cns-lib/vsphere" cnsconfig "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/config" + "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger" ) const ( @@ -174,3 +186,778 @@ func TestQuerySnapshotsUtil(t *testing.T) { t.Log(entry) } } + +func TestListVirtualMachines(t *testing.T) { + getLatestCRDVersionOriginal := getLatestCRDVersion + defer func() { + getLatestCRDVersion = getLatestCRDVersionOriginal + }() + + t.Run("WhenLatestCRDVersionIsNotAvailable", func(tt *testing.T) { + // Setup + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "", fmt.Errorf("CRD version not available") + } + + // Execute + _, err := ListVirtualMachines(context.Background(), fake.NewFakeClient(), "") + + // Assert + assert.NotNil(tt, err) + }) + + t.Run("WhenLatestCRDVersionIsV1Alpha1", func(tt *testing.T) { + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "v1alpha1", nil + } + tt.Run("WhenListFails", func(ttt *testing.T) { + // Setup + clientBuilder := fake.NewClientBuilder() + clientBuilder.WithInterceptorFuncs(interceptor.Funcs{List: func(ctx context.Context, client client.WithWatch, list client.ObjectList, opts ...client.ListOption) error { + return fmt.Errorf("failing list for testing purposes") + }}) + + // Execute + _, err := ListVirtualMachines(context.Background(), clientBuilder.Build(), "") + + // Assert + assert.NotNil(ttt, err) + }) + tt.Run("WhenListSucceeds", func(ttt *testing.T) { + // Setup + namespace := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace", + }, + Spec: v1.NamespaceSpec{ + Finalizers: []v1.FinalizerName{ + v1.FinalizerKubernetes, + }, + }, + Status: v1.NamespaceStatus{ + Phase: v1.NamespaceActive, + }, + } + vm1 := &vmoperatorv1alpha1.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + vm2 := &vmoperatorv1alpha1.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm2", + Namespace: namespace.Name, + }, + } + clientBuilder := fake.NewClientBuilder() + scheme := runtime.NewScheme() + clientBuilder = registerSchemes(context.Background(), clientBuilder, scheme, runtime.SchemeBuilder{ + v1.AddToScheme, + vmoperatorv1alpha1.AddToScheme, + }) + clientBuilder.WithRuntimeObjects(namespace, vm1, vm2) + v1Alpha4VM1 := vmoperatorv1alpha4.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + v1Alpha4VM2 := vmoperatorv1alpha4.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vm2", + Namespace: namespace.Name, + }, + } + exp := vmoperatorv1alpha4.VirtualMachineList{ + TypeMeta: metav1.TypeMeta{}, + ListMeta: metav1.ListMeta{}, + Items: []vmoperatorv1alpha4.VirtualMachine{ + v1Alpha4VM1, + v1Alpha4VM2, + }, + } + + // Execute + actual, err := ListVirtualMachines(context.Background(), clientBuilder.Build(), namespace.Name) + + // Assert + assert.Nil(tt, err) + assert.NotNil(tt, actual) + assert.True(tt, compareVirtualMachineLists(exp, *actual)) + }) + }) + + t.Run("WhenLatestCRDVersionIsV1Alpha2", func(tt *testing.T) { + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "v1alpha2", nil + } + tt.Run("WhenListFails", func(ttt *testing.T) { + // Setup + clientBuilder := fake.NewClientBuilder() + clientBuilder.WithInterceptorFuncs(interceptor.Funcs{List: func(ctx context.Context, client client.WithWatch, list client.ObjectList, opts ...client.ListOption) error { + return fmt.Errorf("failing list for testing purposes") + }}) + + // Execute + _, err := ListVirtualMachines(context.Background(), clientBuilder.Build(), "") + + // Assert + assert.NotNil(ttt, err) + }) + tt.Run("WhenListSucceeds", func(ttt *testing.T) { + // Setup + namespace := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace", + }, + Spec: v1.NamespaceSpec{ + Finalizers: []v1.FinalizerName{ + v1.FinalizerKubernetes, + }, + }, + Status: v1.NamespaceStatus{ + Phase: v1.NamespaceActive, + }, + } + vm1 := &vmoperatorv1alpha2.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + vm2 := &vmoperatorv1alpha2.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm2", + Namespace: namespace.Name, + }, + } + clientBuilder := fake.NewClientBuilder() + scheme := runtime.NewScheme() + clientBuilder = registerSchemes(context.Background(), clientBuilder, scheme, runtime.SchemeBuilder{ + v1.AddToScheme, + vmoperatorv1alpha2.AddToScheme, + }) + clientBuilder.WithRuntimeObjects(namespace, vm1, vm2) + v1Alpha4VM1 := vmoperatorv1alpha4.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + v1Alpha4VM2 := vmoperatorv1alpha4.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vm2", + Namespace: namespace.Name, + }, + } + exp := vmoperatorv1alpha4.VirtualMachineList{ + TypeMeta: metav1.TypeMeta{}, + ListMeta: metav1.ListMeta{}, + Items: []vmoperatorv1alpha4.VirtualMachine{ + v1Alpha4VM1, + v1Alpha4VM2, + }, + } + + // Execute + actual, err := ListVirtualMachines(context.Background(), clientBuilder.Build(), namespace.Name) + + // Assert + assert.Nil(tt, err) + assert.NotNil(tt, actual) + assert.True(tt, compareVirtualMachineLists(exp, *actual)) + }) + }) + + t.Run("WhenLatestCRDVersionIsV1Alpha3", func(tt *testing.T) { + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "v1alpha3", nil + } + tt.Run("WhenListFails", func(ttt *testing.T) { + // Setup + clientBuilder := fake.NewClientBuilder() + clientBuilder.WithInterceptorFuncs(interceptor.Funcs{List: func(ctx context.Context, client client.WithWatch, list client.ObjectList, opts ...client.ListOption) error { + return fmt.Errorf("failing list for testing purposes") + }}) + + // Execute + _, err := ListVirtualMachines(context.Background(), clientBuilder.Build(), "") + + // Assert + assert.NotNil(ttt, err) + }) + tt.Run("WhenListSucceeds", func(ttt *testing.T) { + // Setup + namespace := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace", + }, + Spec: v1.NamespaceSpec{ + Finalizers: []v1.FinalizerName{ + v1.FinalizerKubernetes, + }, + }, + Status: v1.NamespaceStatus{ + Phase: v1.NamespaceActive, + }, + } + vm1 := &vmoperatorv1alpha3.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha3", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + vm2 := &vmoperatorv1alpha3.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha3", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm2", + Namespace: namespace.Name, + }, + } + clientBuilder := fake.NewClientBuilder() + scheme := runtime.NewScheme() + clientBuilder = registerSchemes(context.Background(), clientBuilder, scheme, runtime.SchemeBuilder{ + v1.AddToScheme, + vmoperatorv1alpha3.AddToScheme, + }) + clientBuilder.WithRuntimeObjects(namespace, vm1, vm2) + v1Alpha4VM1 := vmoperatorv1alpha4.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + v1Alpha4VM2 := vmoperatorv1alpha4.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vm2", + Namespace: namespace.Name, + }, + } + exp := vmoperatorv1alpha4.VirtualMachineList{ + TypeMeta: metav1.TypeMeta{}, + ListMeta: metav1.ListMeta{}, + Items: []vmoperatorv1alpha4.VirtualMachine{ + v1Alpha4VM1, + v1Alpha4VM2, + }, + } + + // Execute + actual, err := ListVirtualMachines(context.Background(), clientBuilder.Build(), namespace.Name) + + // Assert + assert.Nil(tt, err) + assert.NotNil(tt, actual) + assert.True(tt, compareVirtualMachineLists(exp, *actual)) + }) + }) + + t.Run("WhenLatestCRDVersionIsV1Alpha4", func(tt *testing.T) { + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "v1alpha4", nil + } + tt.Run("WhenListFails", func(ttt *testing.T) { + // Setup + clientBuilder := fake.NewClientBuilder() + clientBuilder.WithInterceptorFuncs(interceptor.Funcs{List: func(ctx context.Context, client client.WithWatch, list client.ObjectList, opts ...client.ListOption) error { + return fmt.Errorf("failing list for testing purposes") + }}) + + // Execute + _, err := ListVirtualMachines(context.Background(), clientBuilder.Build(), "") + + // Assert + assert.NotNil(ttt, err) + }) + tt.Run("WhenListSucceeds", func(ttt *testing.T) { + // Setup + namespace := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace", + }, + Spec: v1.NamespaceSpec{ + Finalizers: []v1.FinalizerName{ + v1.FinalizerKubernetes, + }, + }, + Status: v1.NamespaceStatus{ + Phase: v1.NamespaceActive, + }, + } + vm1 := &vmoperatorv1alpha4.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha4", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + vm2 := &vmoperatorv1alpha4.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha4", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm2", + Namespace: namespace.Name, + }, + } + clientBuilder := fake.NewClientBuilder() + scheme := runtime.NewScheme() + clientBuilder = registerSchemes(context.Background(), clientBuilder, scheme, runtime.SchemeBuilder{ + v1.AddToScheme, + vmoperatorv1alpha4.AddToScheme, + }) + clientBuilder.WithRuntimeObjects(namespace, vm1, vm2) + exp := vmoperatorv1alpha4.VirtualMachineList{ + TypeMeta: metav1.TypeMeta{}, + ListMeta: metav1.ListMeta{}, + Items: []vmoperatorv1alpha4.VirtualMachine{ + *vm1, + *vm2, + }, + } + + // Execute + actual, err := ListVirtualMachines(context.Background(), clientBuilder.Build(), namespace.Name) + + // Assert + assert.Nil(tt, err) + assert.NotNil(tt, actual) + assert.True(tt, compareVirtualMachineLists(exp, *actual)) + }) + }) + + t.Run("WhenLatestCRDVersionIsInvalid", func(tt *testing.T) { + // Setup + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "invalid", nil + } + + // Execute + _, err := ListVirtualMachines(context.Background(), fake.NewFakeClient(), "") + + // Assert + assert.NotNil(tt, err) + assert.Contains(tt, err.Error(), "Unsupported version") + }) +} + +func TestGetVirtualMachine(t *testing.T) { + getLatestCRDVersionOriginal := getLatestCRDVersion + defer func() { + getLatestCRDVersion = getLatestCRDVersionOriginal + }() + + t.Run("WhenRetrievingCRDVersionFails", func(tt *testing.T) { + // Setup + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "", fmt.Errorf("CRD version not available") + } + + // Execute + _, _, err := GetVirtualMachine(context.Background(), fake.NewFakeClient(), k8stypes.NamespacedName{}) + + // Assert + assert.NotNil(tt, err) + }) + + t.Run("WhenLatestCRDVersionIsV1Alpha1", func(tt *testing.T) { + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "v1alpha1", nil + } + + tt.Run("WhenGetFails", func(t *testing.T) { + // Setup + clientBuilder := fake.NewClientBuilder() + clientBuilder.WithInterceptorFuncs(interceptor.Funcs{ + Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + return fmt.Errorf("failing get for testing purposes") + }, + }) + + // Execute + _, _, err := GetVirtualMachine(context.Background(), clientBuilder.Build(), k8stypes.NamespacedName{}) + + // Assert + assert.NotNil(t, err) + }) + + tt.Run("WhenGetSucceeds", func(t *testing.T) { + // Setup + namespace := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace", + }, + Spec: v1.NamespaceSpec{ + Finalizers: []v1.FinalizerName{ + v1.FinalizerKubernetes, + }, + }, + Status: v1.NamespaceStatus{ + Phase: v1.NamespaceActive, + }, + } + vm := &vmoperatorv1alpha1.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + clientBuilder := fake.NewClientBuilder() + scheme := runtime.NewScheme() + clientBuilder = registerSchemes(context.Background(), clientBuilder, scheme, runtime.SchemeBuilder{ + v1.AddToScheme, + vmoperatorv1alpha1.AddToScheme, + }) + clientBuilder.WithRuntimeObjects(namespace, vm) + exp, expVersion := &vmoperatorv1alpha4.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: vm.Kind, + APIVersion: vm.APIVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: vm.Name, + Namespace: vm.Namespace, + }, + }, vmOperatorApiVersionPrefix+"/v1alpha1" + + // Execute + actual, version, err := GetVirtualMachine(context.Background(), clientBuilder.Build(), k8stypes.NamespacedName{ + Name: exp.Name, + Namespace: exp.Namespace, + }) + + // Assert + assert.Nil(t, err) + assert.True(t, compareVirtualMachines(*exp, *actual)) + assert.Equal(t, expVersion, version) + }) + }) + + t.Run("WhenLatestCRDVersionIsV1Alpha2", func(tt *testing.T) { + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "v1alpha2", nil + } + + tt.Run("WhenGetFails", func(ttt *testing.T) { + // Setup + clientBuilder := fake.NewClientBuilder() + clientBuilder.WithInterceptorFuncs(interceptor.Funcs{ + Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + return fmt.Errorf("failing get for testing purposes") + }, + }) + + // Execute + _, _, err := GetVirtualMachine(context.Background(), clientBuilder.Build(), k8stypes.NamespacedName{}) + + // Assert + assert.NotNil(ttt, err) + }) + + tt.Run("WhenGetSucceeds", func(t *testing.T) { + // Setup + namespace := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace", + }, + Spec: v1.NamespaceSpec{ + Finalizers: []v1.FinalizerName{ + v1.FinalizerKubernetes, + }, + }, + Status: v1.NamespaceStatus{ + Phase: v1.NamespaceActive, + }, + } + vm := &vmoperatorv1alpha2.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + clientBuilder := fake.NewClientBuilder() + scheme := runtime.NewScheme() + clientBuilder = registerSchemes(context.Background(), clientBuilder, scheme, runtime.SchemeBuilder{ + v1.AddToScheme, + vmoperatorv1alpha2.AddToScheme, + }) + clientBuilder.WithRuntimeObjects(namespace, vm) + exp, expVersion := &vmoperatorv1alpha4.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: vm.Kind, + APIVersion: vm.APIVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: vm.Name, + Namespace: vm.Namespace, + }, + }, vmOperatorApiVersionPrefix+"/v1alpha2" + + // Execute + actual, version, err := GetVirtualMachine(context.Background(), clientBuilder.Build(), k8stypes.NamespacedName{ + Name: exp.Name, + Namespace: exp.Namespace, + }) + + // Assert + assert.Nil(t, err) + assert.True(t, compareVirtualMachines(*exp, *actual)) + assert.Equal(t, expVersion, version) + }) + }) + + t.Run("WhenLatestCRDVersionIsV1Alpha3", func(tt *testing.T) { + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "v1alpha3", nil + } + + tt.Run("WhenGetFails", func(ttt *testing.T) { + // Setup + clientBuilder := fake.NewClientBuilder() + clientBuilder.WithInterceptorFuncs(interceptor.Funcs{ + Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + return fmt.Errorf("failing get for testing purposes") + }, + }) + + // Execute + _, _, err := GetVirtualMachine(context.Background(), clientBuilder.Build(), k8stypes.NamespacedName{}) + + // Assert + assert.NotNil(ttt, err) + }) + + tt.Run("WhenGetSucceeds", func(t *testing.T) { + // Setup + namespace := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace", + }, + Spec: v1.NamespaceSpec{ + Finalizers: []v1.FinalizerName{ + v1.FinalizerKubernetes, + }, + }, + Status: v1.NamespaceStatus{ + Phase: v1.NamespaceActive, + }, + } + vm := &vmoperatorv1alpha3.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha3", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + clientBuilder := fake.NewClientBuilder() + scheme := runtime.NewScheme() + clientBuilder = registerSchemes(context.Background(), clientBuilder, scheme, runtime.SchemeBuilder{ + v1.AddToScheme, + vmoperatorv1alpha3.AddToScheme, + }) + clientBuilder.WithRuntimeObjects(namespace, vm) + exp, expVersion := &vmoperatorv1alpha4.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: vm.Kind, + APIVersion: vm.APIVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: vm.Name, + Namespace: vm.Namespace, + }, + }, vmOperatorApiVersionPrefix+"/v1alpha3" + + // Execute + actual, version, err := GetVirtualMachine(context.Background(), clientBuilder.Build(), k8stypes.NamespacedName{ + Name: exp.Name, + Namespace: exp.Namespace, + }) + + // Assert + assert.Nil(t, err) + assert.True(t, compareVirtualMachines(*exp, *actual)) + assert.Equal(t, expVersion, version) + }) + }) + + t.Run("WhenLatestCRDVersionIsV1Alpha4", func(tt *testing.T) { + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "v1alpha4", nil + } + tt.Run("WhenGetFails", func(ttt *testing.T) { + // Setup + clientBuilder := fake.NewClientBuilder() + clientBuilder.WithInterceptorFuncs(interceptor.Funcs{ + Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + return fmt.Errorf("failing get for testing purposes") + }, + }) + + // Execute + _, _, err := GetVirtualMachine(context.Background(), clientBuilder.Build(), k8stypes.NamespacedName{}) + + // Assert + assert.NotNil(ttt, err) + }) + tt.Run("WhenGetSucceeds", func(t *testing.T) { + // Setup + namespace := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "namespace", + }, + Spec: v1.NamespaceSpec{ + Finalizers: []v1.FinalizerName{ + v1.FinalizerKubernetes, + }, + }, + Status: v1.NamespaceStatus{ + Phase: v1.NamespaceActive, + }, + } + vm := &vmoperatorv1alpha4.VirtualMachine{ + TypeMeta: metav1.TypeMeta{ + Kind: "VirtualMachine", + APIVersion: "vmoperator.vmware.com/v1alpha4", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "vm1", + Namespace: namespace.Name, + }, + } + clientBuilder := fake.NewClientBuilder() + scheme := runtime.NewScheme() + clientBuilder = registerSchemes(context.Background(), clientBuilder, scheme, runtime.SchemeBuilder{ + v1.AddToScheme, + vmoperatorv1alpha4.AddToScheme, + }) + clientBuilder.WithRuntimeObjects(namespace, vm) + exp := vm + + // Execute + actual, _, err := GetVirtualMachine(context.Background(), clientBuilder.Build(), k8stypes.NamespacedName{ + Name: exp.Name, + Namespace: exp.Namespace, + }) + + // Assert + assert.Nil(t, err) + assert.True(t, compareVirtualMachines(*exp, *actual), "Expected and actual VirtualMachine objects do not match") + }) + }) + + t.Run("WhenLatestCRDVersionIsInvalid", func(tt *testing.T) { + // Setup + getLatestCRDVersion = func(ctx context.Context, crdName string) (string, error) { + return "invalid", nil + } + + // Execute + _, _, err := GetVirtualMachine(context.Background(), fake.NewFakeClient(), k8stypes.NamespacedName{}) + + // Assert + assert.NotNil(tt, err) + assert.Contains(tt, err.Error(), "Unsupported version") + }) +} + +func registerSchemes(ctx context.Context, clientBuilder *fake.ClientBuilder, scheme *runtime.Scheme, + schemeBuilder runtime.SchemeBuilder) *fake.ClientBuilder { + l := logger.GetLogger(ctx) + if err := schemeBuilder.AddToScheme(scheme); err != nil { + l.Fatalf("Failed to add scheme: %v", err) + } + + clientBuilder.WithScheme(scheme) + return clientBuilder +} + +func compareVirtualMachineLists(exp, actual vmoperatorv1alpha4.VirtualMachineList) bool { + // since the list output may not be in the same order, we will compare the items + // using brute force. + if len(exp.Items) != len(actual.Items) { + return false + } + + for _, expItem := range exp.Items { + found := false + for _, actualItem := range actual.Items { + if expItem.Name == actualItem.Name && expItem.Namespace == actualItem.Namespace { + found = true + break + } + } + if !found { + return false + } + } + + return true +} + +func compareVirtualMachines(exp, actual vmoperatorv1alpha4.VirtualMachine) bool { + return exp.Name == actual.Name && + exp.Namespace == actual.Namespace +} diff --git a/pkg/csi/service/wcp/controller_helper.go b/pkg/csi/service/wcp/controller_helper.go index 236d228951..3d81f40769 100644 --- a/pkg/csi/service/wcp/controller_helper.go +++ b/pkg/csi/service/wcp/controller_helper.go @@ -167,7 +167,7 @@ func validateWCPControllerExpandVolumeRequest(ctx context.Context, req *csi.Cont return logger.LogNewErrorCodef(log, codes.Internal, "failed to get client for group %s with error: %+v", vmoperatorv1alpha4.GroupName, err) } - vmList, err := utils.GetVirtualMachineListAllApiVersions(ctx, "", vmOperatorClient) + vmList, err := utils.ListVirtualMachines(ctx, vmOperatorClient, "") if err != nil { return logger.LogNewErrorCodef(log, codes.Internal, "failed to list virtualmachines with error: %+v", err) diff --git a/pkg/csi/service/wcpguest/controller.go b/pkg/csi/service/wcpguest/controller.go index 476bb112ff..41acaa7772 100644 --- a/pkg/csi/service/wcpguest/controller.go +++ b/pkg/csi/service/wcpguest/controller.go @@ -22,6 +22,7 @@ import ( "math" "net/http" "path/filepath" + "slices" "strconv" "strings" "time" @@ -52,8 +53,6 @@ import ( "k8s.io/client-go/tools/cache" "sigs.k8s.io/controller-runtime/pkg/client" - "slices" - cnsoperatorv1alpha1 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator" cnsfileaccessconfigv1alpha1 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator/cnsfileaccessconfig/v1alpha1" commonconfig "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/config" @@ -689,8 +688,7 @@ func controllerPublishForBlockVolume(ctx context.Context, req *csi.ControllerPub timeoutSeconds := int64(getAttacherTimeoutInMin(ctx) * 60) timeout := time.Now().Add(time.Duration(timeoutSeconds) * time.Second) for { - virtualMachine, _, err = utils.GetVirtualMachineAllApiVersions( - ctx, vmKey, c.vmOperatorClient) + virtualMachine, _, err = utils.GetVirtualMachine(ctx, c.vmOperatorClient, vmKey) if err != nil { msg := fmt.Sprintf("failed to get VirtualMachines for the node: %q. Error: %+v", req.NodeId, err) log.Error(msg) @@ -1069,8 +1067,7 @@ func controllerUnpublishForBlockVolume(ctx context.Context, req *csi.ControllerU timeoutSeconds := int64(getAttacherTimeoutInMin(ctx) * 60) timeout := time.Now().Add(time.Duration(timeoutSeconds) * time.Second) for { - virtualMachine, _, err = utils.GetVirtualMachineAllApiVersions( - ctx, vmKey, c.vmOperatorClient) + virtualMachine, _, err = utils.GetVirtualMachine(ctx, c.vmOperatorClient, vmKey) if err != nil { if errors.IsNotFound(err) { log.Infof("VirtualMachine %s/%s not found. Assuming volume %s was detached.", @@ -1338,7 +1335,7 @@ func (c *controller) ControllerExpandVolume(ctx context.Context, req *csi.Contro volSizeBytes := int64(req.GetCapacityRange().GetRequiredBytes()) if !commonco.ContainerOrchestratorUtility.IsFSSEnabled(ctx, common.OnlineVolumeExtend) { - vmList, err := utils.GetVirtualMachineListAllApiVersions(ctx, c.supervisorNamespace, c.vmOperatorClient) + vmList, err := utils.ListVirtualMachines(ctx, c.vmOperatorClient, c.supervisorNamespace) if err != nil { msg := fmt.Sprintf("failed to list virtualmachines with error: %+v", err) log.Error(msg) diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go index 3936375db6..c95ba23b36 100644 --- a/pkg/kubernetes/kubernetes.go +++ b/pkg/kubernetes/kubernetes.go @@ -20,6 +20,7 @@ import ( "context" "embed" "flag" + "fmt" "net" "os" "strconv" @@ -75,6 +76,7 @@ func GetKubeConfig(ctx context.Context) (*restclient.Config, error) { log := logger.GetLogger(ctx) var config *restclient.Config var err error + // TODO-perf: can this be cached? kubecfgPath := getKubeConfigPath(ctx) if kubecfgPath != "" { log.Debugf("k8s client using kubeconfig from %s", kubecfgPath) @@ -643,3 +645,35 @@ func getCRDFromManifest(ctx context.Context, embedFS embed.FS, fileName string) } return &crd, nil } + +// GetLatestCRDVersion retrieves the latest version of a Custom Resource Definition (CRD) by its name. +func GetLatestCRDVersion(ctx context.Context, crdName string) (string, error) { + log := logger.GetLogger(ctx) + config, err := GetKubeConfig(ctx) + if err != nil { + log.Errorf("Failed to get KubeConfig. err: %s", err) + return "", err + } + + c, err := apiextensionsclientset.NewForConfig(config) + if err != nil { + log.Errorf("Failed to create API extensions client. err: %s", err) + return "", err + } + + crd, err := c.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, crdName, metav1.GetOptions{}) + if err != nil { + log.Errorf("Failed to get CRD %s. Error: %s", crdName, err) + return "", err + } + + for _, version := range crd.Spec.Versions { + if version.Storage { + return version.Name, nil + } + } + + err = fmt.Errorf("no storage version found for CRD %s", crdName) + log.Error(err) + return "", err +} diff --git a/pkg/syncer/cnsoperator/controller/cnsfileaccessconfig/util.go b/pkg/syncer/cnsoperator/controller/cnsfileaccessconfig/util.go index ad10a4bb29..2f65b45925 100644 --- a/pkg/syncer/cnsoperator/controller/cnsfileaccessconfig/util.go +++ b/pkg/syncer/cnsoperator/controller/cnsfileaccessconfig/util.go @@ -42,8 +42,7 @@ func getVirtualMachine(ctx context.Context, vmOperatorClient client.Client, Namespace: namespace, Name: vmName, } - virtualMachine, apiVersion, err := utils.GetVirtualMachineAllApiVersions(ctx, - vmKey, vmOperatorClient) + virtualMachine, apiVersion, err := utils.GetVirtualMachine(ctx, vmOperatorClient, vmKey) if err != nil { msg := fmt.Sprintf("Failed to get virtualmachine instance for the VM with name: %q. Error: %+v", vmName, err) log.Error(msg) diff --git a/pkg/syncer/cnsoperator/controller/cnsnodevmattachment/cnsnodevmattachment_controller.go b/pkg/syncer/cnsoperator/controller/cnsnodevmattachment/cnsnodevmattachment_controller.go index 952239aed1..685f153eba 100644 --- a/pkg/syncer/cnsoperator/controller/cnsnodevmattachment/cnsnodevmattachment_controller.go +++ b/pkg/syncer/cnsoperator/controller/cnsnodevmattachment/cnsnodevmattachment_controller.go @@ -792,7 +792,7 @@ func updateSVPVC(ctx context.Context, client client.Client, func isVmCrPresent(ctx context.Context, vmOperatorClient client.Client, vmuuid string, namespace string) (*vmoperatorv1alpha4.VirtualMachine, error) { log := logger.GetLogger(ctx) - vmList, err := utils.GetVirtualMachineListAllApiVersions(ctx, namespace, vmOperatorClient) + vmList, err := utils.ListVirtualMachines(ctx, vmOperatorClient, namespace) if err != nil { msg := fmt.Sprintf("failed to list virtualmachines with error: %+v", err) log.Error(msg) diff --git a/pkg/syncer/cnsoperator/controller/cnsunregistervolume/cnsunregistervolume_controller.go b/pkg/syncer/cnsoperator/controller/cnsunregistervolume/cnsunregistervolume_controller.go index a55ccfd8eb..3744d32c91 100644 --- a/pkg/syncer/cnsoperator/controller/cnsunregistervolume/cnsunregistervolume_controller.go +++ b/pkg/syncer/cnsoperator/controller/cnsunregistervolume/cnsunregistervolume_controller.go @@ -379,7 +379,7 @@ func validateVolumeNotInUse(ctx context.Context, volumeID string, pvcName string return err } - vmList, err := utils.GetVirtualMachineListAllApiVersions(ctx, pvcNamespace, vmOperatorClient) + vmList, err := utils.ListVirtualMachines(ctx, vmOperatorClient, pvcNamespace) if err != nil { msg := fmt.Sprintf("failed to list virtualmachines with error: %+v", err) log.Error(msg) diff --git a/pkg/syncer/cnsoperator/controller/csinodetopology/csinodetopology_controller.go b/pkg/syncer/cnsoperator/controller/csinodetopology/csinodetopology_controller.go index 7788bb3171..d1c858be31 100644 --- a/pkg/syncer/cnsoperator/controller/csinodetopology/csinodetopology_controller.go +++ b/pkg/syncer/cnsoperator/controller/csinodetopology/csinodetopology_controller.go @@ -428,8 +428,7 @@ func getNodeTopologyInfoForGuest(ctx context.Context, instance *csinodetopologyv Name: instance.Name, // use the nodeName as the VM key } log.Info("fetching virtual machines with all versions") - virtualMachine, _, err := utils.GetVirtualMachineAllApiVersions( - ctx, vmKey, vmOperatorClient) + virtualMachine, _, err := utils.GetVirtualMachine(ctx, vmOperatorClient, vmKey) if err != nil { return nil, logger.LogNewErrorf(log, "failed to get VirtualMachines for the node: %q. Error: %+v", instance.Name, err) diff --git a/pkg/syncer/cnsoperator/util/util.go b/pkg/syncer/cnsoperator/util/util.go index e81beef438..a2f96fb007 100644 --- a/pkg/syncer/cnsoperator/util/util.go +++ b/pkg/syncer/cnsoperator/util/util.go @@ -100,8 +100,7 @@ func GetTKGVMIP(ctx context.Context, vmOperatorClient client.Client, dc dynamic. Namespace: vmNamespace, Name: vmName, } - virtualMachineInstance, _, err := utils.GetVirtualMachineAllApiVersions(ctx, - vmKey, vmOperatorClient) + virtualMachineInstance, _, err := utils.GetVirtualMachine(ctx, vmOperatorClient, vmKey) if err != nil { log.Errorf("failed to get virtualmachine %s/%s with error: %v", vmNamespace, vmName, err) return "", err