diff --git a/tests/e2e/README.md b/tests/e2e/README.md index 4cb82cbbc6..8cbf840179 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -34,21 +34,23 @@ You can check the permissions for the corresponding service account using `kubec kubectl auth can-i --as=virt-e2e-sa ... ``` -### Default StorageClass +### Default Storage Class -Default storage class should be set in the cluster. Annotate a StorageClass with -storageclass.kubernetes.io/is-default-class to mark it as the default: +Default storage class should be set in the cluster. You can set a default storage class in the `global` module config: ```bash +$ kubectl get moduleconfigs.deckhouse.io global --output yaml | yq .spec +``` +```yaml +settings: + defaultClusterStorageClass: linstor-thin-r1 +``` -$ kubectl annotate storageclass linstor-thin-r1 storageclass.kubernetes.io/is-default-class=true +### Immediate Storage Class +Some test cases depend on an immediate storage class. You can skip the immediate storage class check if a test case does not require it. -$ kubectl get storageclass linstor-thin-r1 -o yaml | less -... -metadata: - annotations: - storageclass.kubernetes.io/is-default-class: "true" -... +```bash +FOCUS="VirtualMachineVersions" SKIP_IMMEDIATE_SC_CHECK="yes" task e2e:run ``` ### E2E configuration diff --git a/tests/e2e/config/config.go b/tests/e2e/config/config.go index 1baa84adc1..e25795203e 100644 --- a/tests/e2e/config/config.go +++ b/tests/e2e/config/config.go @@ -159,7 +159,8 @@ type TestData struct { } type StorageClass struct { - VolumeBindingMode storagev1.VolumeBindingMode + DefaultStorageClass *storagev1.StorageClass + ImmediateStorageClass *storagev1.StorageClass } type ClusterTransport struct { diff --git a/tests/e2e/config/storageclass.go b/tests/e2e/config/storageclass.go new file mode 100644 index 0000000000..903f995ba7 --- /dev/null +++ b/tests/e2e/config/storageclass.go @@ -0,0 +1,42 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "fmt" + "log" + "os" +) + +// An immediate storage class is required for test cases where a `VirtualDisk` might not be attached to a `VirtualMachine` but should be in the Ready phase to operate with it. +// If a test case does not require the immediate storage class, this check can be skipped. +const SkipImmediateStorageClassCheckEnv = "SKIP_IMMEDIATE_SC_CHECK" + +func CheckStorageClassOption() error { + env := os.Getenv(SkipImmediateStorageClassCheckEnv) + switch env { + case "yes", "": + return nil + default: + log.Printf("To skip the immediate storage class check, set %s=yes. If you don't intend to skip this check, leave the variable unset.\n", SkipImmediateStorageClassCheckEnv) + return fmt.Errorf("invalid value for the %s env: %q", SkipImmediateStorageClassCheckEnv, env) + } +} + +func SkipImmediateStorageClassCheck() bool { + return os.Getenv(SkipImmediateStorageClassCheckEnv) == "yes" +} diff --git a/tests/e2e/go.mod b/tests/e2e/go.mod index c4a775a1a7..2a08973f6d 100644 --- a/tests/e2e/go.mod +++ b/tests/e2e/go.mod @@ -5,7 +5,6 @@ go 1.23.0 toolchain go1.23.7 require ( - github.com/deckhouse/sds-replicated-volume/api v0.0.0-20241109122839-a1ae840eb5db github.com/deckhouse/virtualization/api v0.0.0-20240923080356-bb5809dba578 github.com/kubernetes-csi/external-snapshotter/client/v8 v8.0.0 github.com/onsi/ginkgo/v2 v2.21.0 diff --git a/tests/e2e/go.sum b/tests/e2e/go.sum index 63dffb493b..e74041966d 100644 --- a/tests/e2e/go.sum +++ b/tests/e2e/go.sum @@ -21,8 +21,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckhouse/sds-replicated-volume/api v0.0.0-20241109122839-a1ae840eb5db h1:ArXEN2TeIX+cX4DUGUtHpRhkJpSzzk80PNqY4PGxNi0= -github.com/deckhouse/sds-replicated-volume/api v0.0.0-20241109122839-a1ae840eb5db/go.mod h1:6yz0RtbkLVJtK2DeuvgfaqBZRl5V5ax1WsfPF5pbnvo= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= diff --git a/tests/e2e/images_creation_test.go b/tests/e2e/images_creation_test.go index e029808a04..65fe69f9e4 100644 --- a/tests/e2e/images_creation_test.go +++ b/tests/e2e/images_creation_test.go @@ -21,9 +21,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - storagev1 "k8s.io/api/storage/v1" - sdsrepvolv1 "github.com/deckhouse/sds-replicated-volume/api/v1alpha1" virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/tests/e2e/config" "github.com/deckhouse/virtualization/tests/e2e/ginkgoutil" @@ -31,16 +29,43 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -var _ = Describe("Virtual images creation", ginkgoutil.CommonE2ETestDecorators(), func() { +var _ = Describe("VirtualImagesCreation", ginkgoutil.CommonE2ETestDecorators(), func() { var ( - immediateStorageClassName string // require for unattached virtual disk snapshots - testCaseLabel = map[string]string{"testcase": "images-creation"} + testCaseLabel = map[string]string{"testcase": "images-creation"} ) - BeforeEach(func() { + BeforeAll(func() { if config.IsReusable() { Skip("Test not available in REUSABLE mode: not supported yet.") } + + kustomization := fmt.Sprintf("%s/%s", conf.TestData.ImagesCreation, "kustomization.yaml") + ns, err := kustomize.GetNamespace(kustomization) + Expect(err).NotTo(HaveOccurred(), "%w", err) + conf.SetNamespace(ns) + + Expect(conf.StorageClass.ImmediateStorageClass).NotTo(BeNil(), "immediate storage class cannot be nil; please set up the immediate storage class in the cluster") + + defaultVolumeSnapshotClassName, err := GetVolumeSnapshotClassName(conf.StorageClass.DefaultStorageClass) + Expect(err).NotTo(HaveOccurred(), "cannot define default `VolumeSnapshotClass`\nstderr: %s", err) + + virtualDisk := virtv2.VirtualDisk{} + vdFilePath := fmt.Sprintf("%s/vd/vd-alpine-http.yaml", conf.TestData.ImagesCreation) + err = UnmarshalResource(vdFilePath, &virtualDisk) + Expect(err).NotTo(HaveOccurred(), "cannot get object from file: %s\nstderr: %s", vdFilePath, err) + + virtualDisk.Spec.PersistentVolumeClaim.StorageClass = &conf.StorageClass.ImmediateStorageClass.Name + err = WriteYamlObject(vdFilePath, &virtualDisk) + Expect(err).NotTo(HaveOccurred(), "cannot update virtual disk with custom storage class: %s\nstderr: %s", vdFilePath, err) + + virtualDiskSnapshot := virtv2.VirtualDiskSnapshot{} + vdSnapshotFilePath := fmt.Sprintf("%s/vdsnapshot/vdsnapshot.yaml", conf.TestData.ImagesCreation) + err = UnmarshalResource(vdSnapshotFilePath, &virtualDiskSnapshot) + Expect(err).NotTo(HaveOccurred(), "cannot get object from file: %s\nstderr: %s", vdSnapshotFilePath, err) + + virtualDiskSnapshot.Spec.VolumeSnapshotClassName = defaultVolumeSnapshotClassName + err = WriteYamlObject(vdSnapshotFilePath, &virtualDiskSnapshot) + Expect(err).NotTo(HaveOccurred(), "cannot update virtual disk with custom storage class: %s\nstderr: %s", vdSnapshotFilePath, err) }) AfterEach(func() { @@ -49,56 +74,6 @@ var _ = Describe("Virtual images creation", ginkgoutil.CommonE2ETestDecorators() } }) - Context("Preparing the environment", func() { - It("sets the namespace", func() { - kustomization := fmt.Sprintf("%s/%s", conf.TestData.ImagesCreation, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) - Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) - }) - - It("prepares `Immediate` storage class and virtual disk that use it", func() { - sc, err := GetDefaultStorageClass() - Expect(err).NotTo(HaveOccurred(), "cannot get default storage class\nstderr: %s", err) - - defaultVolumeSnapshotClassName, err := GetVolumeSnapshotClassName(sc) - Expect(err).NotTo(HaveOccurred(), "cannot define default `VolumeSnapshotClass`\nstderr: %s", err) - - if sc.Provisioner == LinstorProviderName { - storagePoolName := sc.Parameters["replicated.csi.storage.deckhouse.io/storagePool"] - storagePoolObj := sdsrepvolv1.ReplicatedStoragePool{} - err := GetObject(kc.ResourceReplicatedStoragePool, storagePoolName, &storagePoolObj, kc.GetOptions{}) - Expect(err).NotTo(HaveOccurred(), "cannot get `storagePoolObj`: %s\nstderr: %s", storagePoolName, err) - Expect(storagePoolObj.Spec.Type).To(Equal(LVMThinName), "type of replicated storage pool should be `LVMThin`") - } - - if *sc.VolumeBindingMode != storagev1.VolumeBindingImmediate { - immediateStorageClassName, err = CreateImmediateStorageClass(sc.Provisioner, testCaseLabel) - Expect(err).NotTo(HaveOccurred(), "%s", err) - - virtualDisk := virtv2.VirtualDisk{} - vdFilePath := fmt.Sprintf("%s/vd/vd-alpine-http.yaml", conf.TestData.ImagesCreation) - err = UnmarshalResource(vdFilePath, &virtualDisk) - Expect(err).NotTo(HaveOccurred(), "cannot get object from file: %s\nstderr: %s", vdFilePath, err) - - virtualDisk.Spec.PersistentVolumeClaim.StorageClass = &immediateStorageClassName - err = WriteYamlObject(vdFilePath, &virtualDisk) - Expect(err).NotTo(HaveOccurred(), "cannot update virtual disk with custom storage class: %s\nstderr: %s", vdFilePath, err) - } else { - immediateStorageClassName = sc.Name - } - - virtualDiskSnapshot := virtv2.VirtualDiskSnapshot{} - vdSnapshotFilePath := fmt.Sprintf("%s/vdsnapshot/vdsnapshot.yaml", conf.TestData.ImagesCreation) - err = UnmarshalResource(vdSnapshotFilePath, &virtualDiskSnapshot) - Expect(err).NotTo(HaveOccurred(), "cannot get object from file: %s\nstderr: %s", vdSnapshotFilePath, err) - - virtualDiskSnapshot.Spec.VolumeSnapshotClassName = defaultVolumeSnapshotClassName - err = WriteYamlObject(vdSnapshotFilePath, &virtualDiskSnapshot) - Expect(err).NotTo(HaveOccurred(), "cannot update virtual disk with custom storage class: %s\nstderr: %s", vdSnapshotFilePath, err) - }) - }) - Context("When resources are applied", func() { It("result should be succeeded", func() { res := kubectl.Apply(kc.ApplyOptions{ @@ -153,16 +128,6 @@ var _ = Describe("Virtual images creation", ginkgoutil.CommonE2ETestDecorators() It("deletes test case resources", func() { DeleteTestCaseResources(ResourcesToDelete{ KustomizationDir: conf.TestData.ImagesCreation, - AdditionalResources: []AdditionalResource{ - { - Resource: kc.ResourceReplicatedStorageClass, - Labels: testCaseLabel, - }, - { - Resource: kc.ResourceStorageClass, - Labels: testCaseLabel, - }, - }, }) }) }) diff --git a/tests/e2e/kubectl/resource.go b/tests/e2e/kubectl/resource.go index c3affea253..0bd28eca9a 100644 --- a/tests/e2e/kubectl/resource.go +++ b/tests/e2e/kubectl/resource.go @@ -17,27 +17,25 @@ limitations under the License. package kubectl const ( - ResourceNode Resource = "node" - ResourceNamespace Resource = "namespace" - ResourcePod Resource = "pod" - ResourceService Resource = "service" - ResourceStorageClass Resource = "storageclasses.storage.k8s.io" - ResourceVolumeSnapshotClass Resource = "volumesnapshotclasses.snapshot.storage.k8s.io" - ResourceProject Resource = "projects.deckhouse.io" - ResourceReplicatedStorageClass Resource = "replicatedstorageclasses.storage.deckhouse.io" - ResourceReplicatedStoragePool Resource = "replicatedstoragepools.storage.deckhouse.io" - ResourceKubevirtVM Resource = "internalvirtualizationvirtualmachines.internal.virtualization.deckhouse.io" - ResourceKubevirtVMI Resource = "internalvirtualizationvirtualmachineinstances.internal.virtualization.deckhouse.io" - ResourceKubevirtVMIM Resource = "internalvirtualizationvirtualmachineinstancemigrations.internal.virtualization.deckhouse.io" - ResourceModuleConfig Resource = "moduleconfigs.deckhouse.io" - ResourceVD Resource = "virtualdisks.virtualization.deckhouse.io" - ResourceVDSnapshot Resource = "virtualdisksnapshots.virtualization.deckhouse.io" - ResourceVM Resource = "virtualmachine.virtualization.deckhouse.io" - ResourceVMClass Resource = "virtualmachineclasses.virtualization.deckhouse.io" - ResourceVMIP Resource = "virtualmachineipaddresses.virtualization.deckhouse.io" - ResourceVMIPLease Resource = "virtualmachineipaddressleases.virtualization.deckhouse.io" - ResourceCVI Resource = "clustervirtualimages.virtualization.deckhouse.io" - ResourceVI Resource = "virtualimages.virtualization.deckhouse.io" - ResourceVMBDA Resource = "virtualmachineblockdeviceattachments.virtualization.deckhouse.io" - ResourceVMOP Resource = "virtualmachineoperations.virtualization.deckhouse.io" + ResourceNode Resource = "node" + ResourceNamespace Resource = "namespace" + ResourcePod Resource = "pod" + ResourceService Resource = "service" + ResourceStorageClass Resource = "storageclasses.storage.k8s.io" + ResourceVolumeSnapshotClass Resource = "volumesnapshotclasses.snapshot.storage.k8s.io" + ResourceProject Resource = "projects.deckhouse.io" + ResourceKubevirtVM Resource = "internalvirtualizationvirtualmachines.internal.virtualization.deckhouse.io" + ResourceKubevirtVMI Resource = "internalvirtualizationvirtualmachineinstances.internal.virtualization.deckhouse.io" + ResourceKubevirtVMIM Resource = "internalvirtualizationvirtualmachineinstancemigrations.internal.virtualization.deckhouse.io" + ResourceModuleConfig Resource = "moduleconfigs.deckhouse.io" + ResourceVD Resource = "virtualdisks.virtualization.deckhouse.io" + ResourceVDSnapshot Resource = "virtualdisksnapshots.virtualization.deckhouse.io" + ResourceVM Resource = "virtualmachine.virtualization.deckhouse.io" + ResourceVMClass Resource = "virtualmachineclasses.virtualization.deckhouse.io" + ResourceVMIP Resource = "virtualmachineipaddresses.virtualization.deckhouse.io" + ResourceVMIPLease Resource = "virtualmachineipaddressleases.virtualization.deckhouse.io" + ResourceCVI Resource = "clustervirtualimages.virtualization.deckhouse.io" + ResourceVI Resource = "virtualimages.virtualization.deckhouse.io" + ResourceVMBDA Resource = "virtualmachineblockdeviceattachments.virtualization.deckhouse.io" + ResourceVMOP Resource = "virtualmachineoperations.virtualization.deckhouse.io" ) diff --git a/tests/e2e/tests_suite_test.go b/tests/e2e/tests_suite_test.go index 11ce30caae..6c91431e00 100644 --- a/tests/e2e/tests_suite_test.go +++ b/tests/e2e/tests_suite_test.go @@ -27,7 +27,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" - storagev1 "k8s.io/api/storage/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" apiruntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -77,7 +76,6 @@ var ( d8Virtualization d8.D8Virtualization git gt.Git namePrefix string - defaultStorageClass *storagev1.StorageClass phaseByVolumeBindingMode string logStreamByV12nControllerPod = make(map[string]*el.LogStream, 0) ) @@ -87,6 +85,10 @@ func init() { if err != nil { log.Fatal(err) } + err = config.CheckStorageClassOption() + if err != nil { + log.Fatal(err) + } err = config.CheckWithPostCleanUpOption() if err != nil { log.Fatal(err) @@ -128,16 +130,20 @@ func init() { if git, err = gt.NewGit(); err != nil { log.Fatal(err) } - if defaultStorageClass, err = GetDefaultStorageClass(); err != nil { + if conf.StorageClass.DefaultStorageClass, err = GetDefaultStorageClass(); err != nil { log.Fatal(err) } + if !config.SkipImmediateStorageClassCheck() { + if conf.StorageClass.ImmediateStorageClass, err = GetImmediateStorageClass(conf.StorageClass.DefaultStorageClass.Provisioner); err != nil { + log.Fatal(err) + } + } if namePrefix, err = config.GetNamePrefix(); err != nil { log.Fatal(err) } ChmodFile(conf.TestData.Sshkey, 0o600) conf.Namespace = fmt.Sprintf("%s-%s", namePrefix, conf.Namespace) - conf.StorageClass.VolumeBindingMode = *defaultStorageClass.VolumeBindingMode - phaseByVolumeBindingMode = GetPhaseByVolumeBindingMode(conf) + phaseByVolumeBindingMode = GetPhaseByVolumeBindingMode(conf.StorageClass.DefaultStorageClass) // TODO: get kustomization files from testdata directory when all tests will be refactored var kustomizationFiles []string v := reflect.ValueOf(conf.TestData) diff --git a/tests/e2e/util_test.go b/tests/e2e/util_test.go index 9c5542605b..983da91043 100644 --- a/tests/e2e/util_test.go +++ b/tests/e2e/util_test.go @@ -44,7 +44,7 @@ import ( virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" - . "github.com/deckhouse/virtualization/tests/e2e/config" + "github.com/deckhouse/virtualization/tests/e2e/config" "github.com/deckhouse/virtualization/tests/e2e/executor" "github.com/deckhouse/virtualization/tests/e2e/helper" kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" @@ -383,6 +383,25 @@ func GetDefaultStorageClass() (*storagev1.StorageClass, error) { return defaultClasses[0], nil } +func GetImmediateStorageClass(provisioner string) (*storagev1.StorageClass, error) { + scl := &storagev1.StorageClassList{} + err := GetObjects(kc.ResourceStorageClass, scl, kc.GetOptions{}) + if err != nil { + return nil, err + } + + for _, sc := range scl.Items { + if sc.Provisioner == provisioner && *sc.VolumeBindingMode == storagev1.VolumeBindingImmediate { + return &sc, nil + } + } + + return nil, fmt.Errorf("immediate storage class does not found; please set up immediate storage class with the %q provisioner; to skip the immediate storage class check, set %s=yes", + provisioner, + config.SkipImmediateStorageClassCheckEnv, + ) +} + func toIPNet(prefix netip.Prefix) *net.IPNet { return &net.IPNet{ IP: prefix.Masked().Addr().AsSlice(), @@ -491,14 +510,14 @@ func GetCondition(conditionType string, obj client.Object) (metav1.Condition, er return metav1.Condition{}, fmt.Errorf("condition %s not found", conditionType) } -func GetPhaseByVolumeBindingMode(c *Config) string { - switch c.StorageClass.VolumeBindingMode { - case "Immediate": - return PhaseReady - case "WaitForFirstConsumer": - return PhaseWaitForFirstConsumer +func GetPhaseByVolumeBindingMode(sc *storagev1.StorageClass) string { + switch *sc.VolumeBindingMode { + case storagev1.VolumeBindingImmediate: + return string(virtv2.DiskReady) + case storagev1.VolumeBindingWaitForFirstConsumer: + return string(virtv2.DiskWaitForFirstConsumer) default: - return PhaseReady + return string(virtv2.DiskReady) } } diff --git a/tests/e2e/vd_snapshots_test.go b/tests/e2e/vd_snapshots_test.go index 39f4a5ff6d..2c884ac3fc 100644 --- a/tests/e2e/vd_snapshots_test.go +++ b/tests/e2e/vd_snapshots_test.go @@ -29,8 +29,6 @@ import ( storagev1 "k8s.io/api/storage/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - sdsrepvolv1 "github.com/deckhouse/sds-replicated-volume/api/v1alpha1" - virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" "github.com/deckhouse/virtualization/tests/e2e/config" @@ -40,10 +38,6 @@ import ( ) const ( - ReplicatedStorageClassKind = "ReplicatedStorageClass" - LinstorProviderName = "replicated.csi.storage.deckhouse.io" - LVMThinName = "LVMThin" - CephProviderName = "rbd.csi.ceph.com" filesystemReadyTimeout = 60 * time.Second filesystemReadyPollingInterval = 5 * time.Second frozenReasonPollingInterval = 1 * time.Second @@ -85,111 +79,6 @@ func CreateVirtualDiskSnapshot(vdName, snapshotName, volumeSnapshotClassName str return nil } -func CreateImmediateStorageClass(provisioner string, labels map[string]string) (string, error) { - GinkgoHelper() - filePath := fmt.Sprintf("%s/immediate-storage-class.yaml", conf.TestData.VdSnapshots) - switch provisioner { - case LinstorProviderName: - replicatedStorageClassName := fmt.Sprintf("%s-linstor-immediate", namePrefix) - replicatedStoragePoolName, err := GetLVMThinReplicatedStoragePool() - if err != nil { - return "", err - } - err = createLinstorImmediateStorageClass(filePath, replicatedStorageClassName, replicatedStoragePoolName, labels) - if err != nil { - return "", err - } - return replicatedStorageClassName, nil - case CephProviderName: - storageClassName := fmt.Sprintf("%s-ceph-immediate", namePrefix) - err := createCephImmediateStorageClass(filePath, storageClassName, labels) - if err != nil { - return "", err - } - return storageClassName, nil - default: - return "", errors.New("cannot create storage class with `Immediate` volume binding mode") - } -} - -// Get first replicated storage pool with type `LVMThin` and phase `Completed` -func GetLVMThinReplicatedStoragePool() (string, error) { - GinkgoHelper() - rspObjects := sdsrepvolv1.ReplicatedStoragePoolList{} - err := GetObjects(kc.ResourceReplicatedStoragePool, &rspObjects, kc.GetOptions{}) - if err != nil { - return "", err - } - for _, rsp := range rspObjects.Items { - if rsp.Spec.Type == LVMThinName && rsp.Status.Phase == PhaseCompleted { - return rsp.Name, nil - } - } - return "", fmt.Errorf("cannot get completed replicated storage pool with type `LVMThin`") -} - -func createLinstorImmediateStorageClass(filePath, storageClassName, replicatedStoragePoolName string, labels map[string]string) error { - GinkgoHelper() - replicatedStorageClass := sdsrepvolv1.ReplicatedStorageClass{ - TypeMeta: v1.TypeMeta{ - APIVersion: sdsrepvolv1.SchemeGroupVersion.String(), - Kind: ReplicatedStorageClassKind, - }, - ObjectMeta: v1.ObjectMeta{ - Name: storageClassName, - Labels: labels, - }, - Spec: sdsrepvolv1.ReplicatedStorageClassSpec{ - ReclaimPolicy: "Delete", - Replication: "None", - StoragePool: replicatedStoragePoolName, - Topology: "Ignored", - VolumeAccess: "Any", - }, - } - - err := WriteYamlObject(filePath, &replicatedStorageClass) - if err != nil { - return nil - } - - res := kubectl.Apply(kc.ApplyOptions{ - Filename: []string{filePath}, - FilenameOption: kc.Filename, - }) - if res.Error() != nil { - return fmt.Errorf("cmd: %s\nstderr: %s", res.GetCmd(), res.StdErr()) - } - - return nil -} - -func createCephImmediateStorageClass(filePath, storageClassName string, labels map[string]string) error { - GinkgoHelper() - sc, err := GetDefaultStorageClass() - if err != nil { - return err - } - sc.ObjectMeta.Name = storageClassName - sc.ObjectMeta.Labels = labels - *sc.VolumeBindingMode = storagev1.VolumeBindingImmediate - - err = WriteYamlObject(filePath, sc) - if err != nil { - return err - } - - res := kubectl.Apply(kc.ApplyOptions{ - Filename: []string{filePath}, - FilenameOption: kc.Filename, - }) - if res.Error() != nil { - return fmt.Errorf("cmd: %s\nstderr: %s", res.GetCmd(), res.StdErr()) - } - - return nil -} - func GetVolumeSnapshotClassName(storageClass *storagev1.StorageClass) (string, error) { vscObjects := snapshotvolv1.VolumeSnapshotClassList{} err := GetObjects(kc.ResourceVolumeSnapshotClass, &vscObjects, kc.GetOptions{}) @@ -222,15 +111,8 @@ func CheckFileSystemFrozen(vmName string) (bool, error) { return false, nil } -var _ = Describe("Virtual disk snapshots", ginkgoutil.CommonE2ETestDecorators(), func() { - BeforeEach(func() { - if config.IsReusable() { - Skip("Test not available in REUSABLE mode: not supported yet.") - } - }) - +var _ = Describe("VirtualDiskSnapshots", ginkgoutil.CommonE2ETestDecorators(), func() { var ( - immediateStorageClassName string // require for unattached virtual disk snapshots defaultVolumeSnapshotClassName string testCaseLabel = map[string]string{"testcase": "vd-snapshots"} attachedVirtualDiskLabel = map[string]string{"attachedVirtualDisk": ""} @@ -238,49 +120,26 @@ var _ = Describe("Virtual disk snapshots", ginkgoutil.CommonE2ETestDecorators(), vmAutomaticWithHotplug = map[string]string{"vm": "automatic-with-hotplug"} ) - AfterEach(func() { - if CurrentSpecReport().Failed() { - SaveTestResources(testCaseLabel, CurrentSpecReport().LeafNodeText) + BeforeAll(func() { + if config.IsReusable() { + Skip("Test not available in REUSABLE mode: not supported yet.") } - }) - - Context("Preparing the environment", func() { - It("sets the namespace", func() { - kustomization := fmt.Sprintf("%s/%s", conf.TestData.VdSnapshots, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) - Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) - }) - It("prepares `Immediate` storage class and virtual disk that use it", func() { - sc, err := GetDefaultStorageClass() - Expect(err).NotTo(HaveOccurred(), "cannot get default storage class\nstderr: %s", err) - defaultVolumeSnapshotClassName, err = GetVolumeSnapshotClassName(sc) - Expect(err).NotTo(HaveOccurred(), "cannot define default `VolumeSnapshotClass`\nstderr: %s", err) - if sc.Provisioner == LinstorProviderName { - storagePoolName := sc.Parameters["replicated.csi.storage.deckhouse.io/storagePool"] - storagePoolObj := sdsrepvolv1.ReplicatedStoragePool{} - err := GetObject(kc.ResourceReplicatedStoragePool, storagePoolName, &storagePoolObj, kc.GetOptions{}) - Expect(err).NotTo(HaveOccurred(), "cannot get `storagePoolObj`: %s\nstderr: %s", storagePoolName, err) - Expect(storagePoolObj.Spec.Type).To(Equal(LVMThinName), "type of replicated storage pool should be `LVMThin`") - } + kustomization := fmt.Sprintf("%s/%s", conf.TestData.VdSnapshots, "kustomization.yaml") + ns, err := kustomize.GetNamespace(kustomization) + Expect(err).NotTo(HaveOccurred(), "%w", err) + conf.SetNamespace(ns) - if *sc.VolumeBindingMode != storagev1.VolumeBindingImmediate { - immediateStorageClassName, err = CreateImmediateStorageClass(sc.Provisioner, testCaseLabel) - Expect(err).NotTo(HaveOccurred(), "%s", err) + Expect(conf.StorageClass.ImmediateStorageClass).NotTo(BeNil(), "immediate storage class cannot be nil; please set up the immediate storage class in the cluster") - virtualDiskWithoutConsumer := virtv2.VirtualDisk{} - vdWithoutConsumerFilePath := fmt.Sprintf("%s/vd/vd-alpine-http.yaml", conf.TestData.VdSnapshots) - err = UnmarshalResource(vdWithoutConsumerFilePath, &virtualDiskWithoutConsumer) - Expect(err).NotTo(HaveOccurred(), "cannot get object from file: %s\nstderr: %s", vdWithoutConsumerFilePath, err) + defaultVolumeSnapshotClassName, err = GetVolumeSnapshotClassName(conf.StorageClass.DefaultStorageClass) + Expect(err).NotTo(HaveOccurred(), "cannot define default `VolumeSnapshotClass`\nstderr: %s", err) + }) - virtualDiskWithoutConsumer.Spec.PersistentVolumeClaim.StorageClass = &immediateStorageClassName - err = WriteYamlObject(vdWithoutConsumerFilePath, &virtualDiskWithoutConsumer) - Expect(err).NotTo(HaveOccurred(), "cannot update virtual disk with custom storage class: %s\nstderr: %s", vdWithoutConsumerFilePath, err) - } else { - immediateStorageClassName = sc.Name - } - }) + AfterEach(func() { + if CurrentSpecReport().Failed() { + SaveTestResources(testCaseLabel, CurrentSpecReport().LeafNodeText) + } }) Context("When virtualization resources are applied:", func() { @@ -348,11 +207,7 @@ var _ = Describe("Virtual disk snapshots", ginkgoutil.CommonE2ETestDecorators(), vds := strings.Split(res.StdOut(), " ") - sc := storagev1.StorageClass{} - err := GetObject(kc.ResourceStorageClass, immediateStorageClassName, &sc, kc.GetOptions{}) - Expect(err).NotTo(HaveOccurred(), "cannot get storage class: %s\nstderr: %s", immediateStorageClassName, err) - - volumeSnapshotClassName, getErr := GetVolumeSnapshotClassName(&sc) + volumeSnapshotClassName, getErr := GetVolumeSnapshotClassName(conf.StorageClass.ImmediateStorageClass) Expect(getErr).NotTo(HaveOccurred(), "%s", getErr) for _, vdName := range vds { @@ -552,14 +407,6 @@ var _ = Describe("Virtual disk snapshots", ginkgoutil.CommonE2ETestDecorators(), Resource: kc.ResourceVDSnapshot, Labels: attachedVirtualDiskLabel, }, - { - Resource: kc.ResourceReplicatedStorageClass, - Labels: testCaseLabel, - }, - { - Resource: kc.ResourceStorageClass, - Labels: testCaseLabel, - }, }, }) })