diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index c9935da..e934c0c 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -60,3 +60,23 @@ jobs: - name: Zfs e2e test run: | nix-shell shell.nix --run "./scripts/e2e-test.sh --testplan zfs" + hostpath: + needs: ['lint'] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: DeterminateSystems/nix-installer-action@v11 + with: + kvm: true + - uses: DeterminateSystems/magic-nix-cache-action@v6 + - name: Pre-populate nix-shell + run: | + export NIX_PATH=nixpkgs=$(jq '.nixpkgs.url' nix/sources.json -r) + echo "NIX_PATH=$NIX_PATH" >> $GITHUB_ENV + nix-shell shell.nix --run "echo" + - name: BootStrap k8s cluster + run: | + nix-shell shell.nix --run "./scripts/k8s/deployer.sh start --workers 1" + - name: Hostpath e2e test + run: | + nix-shell shell.nix --run "./scripts/e2e-test.sh --testplan hostpath" diff --git a/common/hostpath/util.go b/common/hostpath/util.go index 3a04341..d43aee5 100644 --- a/common/hostpath/util.go +++ b/common/hostpath/util.go @@ -1,6 +1,7 @@ package hostpath import ( + "encoding/json" "fmt" "github.com/openebs/openebs-e2e/common/e2e_agent" @@ -75,3 +76,27 @@ func (hostPathConfig *HostPathDeviceNodeConfig) ConfigureHostPathDevices() error } return nil } + +func GetHostPathAnnotationConfig() (map[string]string, error) { + type hostpathConfig struct { + Name string `json:"name"` + Value string `json:"value"` + } + config := []hostpathConfig{ + {Name: "StorageType", Value: "hostpath"}, + {Name: "BasePath", Value: "/var/local-hostpath"}, + } + // Marshal the configuration to a JSON string + configBytes, err := json.Marshal(config) + if err != nil { + logf.Log.Info("Error marshalling configuration:", "Error", err) + return nil, err + + } + configString := string(configBytes) + + return map[string]string{ + "openebs.io/cas-type": "local", + "cas.openebs.io/config": configString, + }, err +} diff --git a/common/k8stest/util_busybox.go b/common/k8stest/util_busybox.go new file mode 100644 index 0000000..eb0507f --- /dev/null +++ b/common/k8stest/util_busybox.go @@ -0,0 +1,83 @@ +package k8stest + +import ( + "fmt" + + "github.com/openebs/openebs-e2e/common" + coreV1 "k8s.io/api/core/v1" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +var defBusyboxTimeoutSecs = 120 // in seconds + +func DeployBusyBoxPod(podName, pvcName string, volType common.VolumeType) error { + args := []string{"sleep", "10000000"} + podContainer := coreV1.Container{ + Name: podName, + Image: "busybox", + ImagePullPolicy: coreV1.PullAlways, + Args: args, + } + + volume := coreV1.Volume{ + Name: "ms-volume", + VolumeSource: coreV1.VolumeSource{ + PersistentVolumeClaim: &coreV1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvcName, + }, + }, + } + + podObj, err := NewPodBuilder(podName). + WithName(podName). + WithNamespace(common.NSDefault). + WithRestartPolicy(coreV1.RestartPolicyNever). + WithContainer(podContainer). + WithVolume(volume). + WithVolumeDeviceOrMount(volType).Build() + + if err != nil { + return fmt.Errorf("failed to generate busybox pod definition, error: %v", err) + } + if podObj == nil { + return fmt.Errorf("busybox pod definition is nil") + } + + _, err = CreatePod(podObj, common.NSDefault) + if err != nil { + return fmt.Errorf("failed to create busybox pod, error: %v", err) + } + + isPodRunning := WaitPodRunning(podName, common.NSDefault, defBusyboxTimeoutSecs) + if !isPodRunning { + return fmt.Errorf("failed to create busybox pod, error: %v", err) + } + logf.Log.Info(fmt.Sprintf("%s pod is running.", podName)) + return nil +} + +func CleanUpBusyboxResources(pods []string, pvcName string) error { + for _, pod := range pods { + err := DeletePod(pod, common.NSDefault) + if err != nil { + return fmt.Errorf("failed to delete pod %s err %v", pod, err) + } + + } + if pvcName != "" { + pvc, err := GetPVC(pvcName, common.NSDefault) + if err != nil { + return fmt.Errorf("failed to get pvc %s err %v", pvcName, err) + } + err = RemovePVC(pvcName, *pvc.Spec.StorageClassName, common.NSDefault, true) + if err != nil { + return fmt.Errorf("failed to delete pvc %s err %v", pvcName, err) + } + err = RmStorageClass(*pvc.Spec.StorageClassName) + if err != nil { + return fmt.Errorf("failed to delete sc from pvc %s err %v", pvcName, err) + } + } + + return nil +} diff --git a/scripts/exec-tests.sh b/scripts/exec-tests.sh index 20d94ef..3a2159e 100755 --- a/scripts/exec-tests.sh +++ b/scripts/exec-tests.sh @@ -374,6 +374,9 @@ for testname in $tests; do elif [ -d "$TESTDIR/zfs/$testname" ]; then testrootdir=$TESTDIR fqtestname="zfs/$testname" + elif [ -d "$TESTDIR/hostpath/$testname" ]; then + testrootdir=$TESTDIR + fqtestname="hostpath/$testname" elif [ -d "$TESTDIR/$testname" ]; then testrootdir=$TESTDIR fqtestname="$testname" diff --git a/src/tests/hostpath/hostpath_ci_smoke_test/hostpath_ci_smoke_test.go b/src/tests/hostpath/hostpath_ci_smoke_test/hostpath_ci_smoke_test.go new file mode 100644 index 0000000..9584045 --- /dev/null +++ b/src/tests/hostpath/hostpath_ci_smoke_test/hostpath_ci_smoke_test.go @@ -0,0 +1,61 @@ +package hostpath_ci_smoke_test + +import ( + "testing" + + "github.com/openebs/openebs-e2e/common" + "github.com/openebs/openebs-e2e/common/e2e_ginkgo" + "github.com/openebs/openebs-e2e/src/tests/hostpath/hostpath_volume_provisioning" + + "github.com/openebs/openebs-e2e/common/k8stest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var podName, pvcName string +var err error + +func TestHostpathCiSmokeTest(t *testing.T) { + // Initialise test and set class and file names for reports + e2e_ginkgo.InitTesting(t, "hostpath_ci_smoke_test", "hostpath_ci_smoke_test") +} + +var _ = Describe("hostpath_ci_smoke_test", func() { + + BeforeEach(func() { + // Check ready to run + err := e2e_ginkgo.BeforeEachK8sCheck() + Expect(err).ToNot(HaveOccurred()) + }) + + AfterEach(func() { + // cleanup k8s resources if exist + logf.Log.Info("cleanup k8s resources") + err := k8stest.CleanUpBusyboxResources([]string{podName}, pvcName) + Expect(err).ToNot(HaveOccurred(), "failed delete busybox resource") + + // Check resource leakage. + after_err := e2e_ginkgo.AfterEachK8sCheck() + Expect(after_err).ToNot(HaveOccurred()) + + }) + + It("hostpath ext4: should verify a volume can be created, used and deleted", func() { + podName, pvcName, err = hostpath_volume_provisioning.VolumeProvisioningTest("ext4", common.Ext4FsType) + Expect(err).ToNot(HaveOccurred()) + }) + +}) + +var _ = BeforeSuite(func() { + err = e2e_ginkgo.SetupTestEnv() + Expect(err).ToNot(HaveOccurred(), "failed to setup test environment in BeforeSuite : SetupTestEnv %v", err) + +}) + +var _ = AfterSuite(func() { + err = k8stest.TeardownTestEnv() + Expect(err).ToNot(HaveOccurred(), "failed to tear down test environment in AfterSuite : TeardownTestEnv %v", err) +}) diff --git a/src/tests/hostpath/hostpath_volume_provisioning/hostpath_volume_provisioning_test.go b/src/tests/hostpath/hostpath_volume_provisioning/hostpath_volume_provisioning_test.go new file mode 100644 index 0000000..9a3f812 --- /dev/null +++ b/src/tests/hostpath/hostpath_volume_provisioning/hostpath_volume_provisioning_test.go @@ -0,0 +1,69 @@ +package hostpath_volume_provisioning + +import ( + "testing" + + "github.com/openebs/openebs-e2e/common" + "github.com/openebs/openebs-e2e/common/e2e_ginkgo" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/openebs/openebs-e2e/common/k8stest" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var podName, pvcName string +var err error + +func TestHostpathVolumeProvisioningTest(t *testing.T) { + // Initialise test and set class and file names for reports + e2e_ginkgo.InitTesting(t, "hostpath_volume_provisioning", "hostpath_volume_provisioning") +} + +var _ = Describe("hostpath_volume_provisioning", func() { + + BeforeEach(func() { + // Check ready to run + err := e2e_ginkgo.BeforeEachK8sCheck() + Expect(err).ToNot(HaveOccurred()) + }) + + AfterEach(func() { + + // cleanup k8s resources + logf.Log.Info("cleanup k8s resources ") + err = k8stest.CleanUpBusyboxResources([]string{podName}, pvcName) + Expect(err).ToNot(HaveOccurred(), "failed to k8s resource") + + // Check resource leakage. + err := e2e_ginkgo.AfterEachK8sCheck() + Expect(err).ToNot(HaveOccurred()) + + }) + + It("hostpath ext4: should verify a volume can be created, used and deleted", func() { + podName, pvcName, err = VolumeProvisioningTest("ext4", common.Ext4FsType) + Expect(err).ToNot(HaveOccurred()) + }) + It("hostpath xfs: should verify a volume can be created, used and deleted", func() { + podName, pvcName, err = VolumeProvisioningTest("xfs", common.XfsFsType) + Expect(err).ToNot(HaveOccurred()) + }) + It("hostpath btrfs: should verify a volume can be created, used and deleted", func() { + podName, pvcName, err = VolumeProvisioningTest("btrfs", common.BtrfsFsType) + Expect(err).ToNot(HaveOccurred()) + }) + +}) + +var _ = BeforeSuite(func() { + err = e2e_ginkgo.SetupTestEnv() + Expect(err).ToNot(HaveOccurred(), "failed to setup test environment in BeforeSuite : SetupTestEnv %v", err) + +}) + +var _ = AfterSuite(func() { + err = k8stest.TeardownTestEnv() + Expect(err).ToNot(HaveOccurred(), "failed to tear down test environment in AfterSuite : TeardownTestEnv %v", err) +}) diff --git a/src/tests/hostpath/hostpath_volume_provisioning/util.go b/src/tests/hostpath/hostpath_volume_provisioning/util.go new file mode 100644 index 0000000..c606b79 --- /dev/null +++ b/src/tests/hostpath/hostpath_volume_provisioning/util.go @@ -0,0 +1,69 @@ +package hostpath_volume_provisioning + +import ( + "fmt" + + "github.com/openebs/openebs-e2e/common" + "github.com/openebs/openebs-e2e/common/k8stest" + + "github.com/openebs/openebs-e2e/common/hostpath" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +func VolumeProvisioningTest(decor string, + fstype common.FileSystemType, +) (string, string, error) { + + // FIXME: here we are using k8stest.FioApplication to use its functionality + // and not deploying FIO, instead busybox application will be deployed. + app := k8stest.FioApplication{ + Decor: decor, + VolSizeMb: 1024, + OpenEbsEngine: common.Hostpath, + VolType: common.VolFileSystem, + FsType: fstype, + VolWaitForFirstConsumer: true, + } + + hostpathConfig, err := hostpath.GetHostPathAnnotationConfig() + if err != nil { + return "", "", err + } + + // setup sc parameters + app.HostPath = k8stest.HostPathOptions{ + Annotations: hostpathConfig, + } + + // Create volume + logf.Log.Info("create volume") + err = app.CreateVolume() + if err != nil { + return "", "", err + } + + // Deploy BusyBox pod and create file with MD5 checksum + podName := "busybox" + err = k8stest.DeployBusyBoxPod(podName, app.GetPvcName(), app.VolType) + if err != nil { + return "", app.GetPvcName(), err + } + + filePath := "/volume/testfile.txt" + fileContent := "This is some test data." + md5FilePath1 := "/volume/md5sum1.txt" + combinedCmd1 := fmt.Sprintf( + "echo '%s' > %s && md5sum %s > %s", + fileContent, + filePath, + filePath, + md5FilePath1, + ) + + out, _, err := k8stest.ExecuteCommandInPod(common.NSDefault, podName, combinedCmd1) + if err != nil { + logf.Log.Info("Failed to execute command", "Error", err, "Output", out) + return podName, app.GetPvcName(), err + } + return podName, app.GetPvcName(), nil +} diff --git a/src/tests/lvm/lvm_shared_mount/lvm_shared_mount_test.go b/src/tests/lvm/lvm_shared_mount/lvm_shared_mount_test.go index 6443cb7..1f34ac0 100644 --- a/src/tests/lvm/lvm_shared_mount/lvm_shared_mount_test.go +++ b/src/tests/lvm/lvm_shared_mount/lvm_shared_mount_test.go @@ -3,7 +3,6 @@ package lvm_shared_mount_volume import ( "fmt" "testing" - "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -55,7 +54,8 @@ func fsVolumeSharedMountTest(decor string, engine common.OpenEbsEngine, fstype c // Deploy first BusyBox pod and create file with MD5 checksum podName1 := "busybox" - deployBusyBoxPod(podName1, busyboxapp.GetPvcName(), busyboxapp.VolType) + err = k8stest.DeployBusyBoxPod(podName1, busyboxapp.GetPvcName(), busyboxapp.VolType) + Expect(err).To(BeNil(), "failed to deploy busybox") podNames = append(podNames, podName1) filePath := "/volume/testfile.txt" @@ -75,7 +75,8 @@ func fsVolumeSharedMountTest(decor string, engine common.OpenEbsEngine, fstype c // Deploy second BusyBox pod to verify data podName2 := "busybox-second" - deployBusyBoxPod(podName2, busyboxapp.GetPvcName(), busyboxapp.VolType) + err = k8stest.DeployBusyBoxPod(podName2, busyboxapp.GetPvcName(), busyboxapp.VolType) + Expect(err).To(BeNil(), "failed to deploy busybox") podNames = append(podNames, podName2) combinedCmd2 := fmt.Sprintf( @@ -106,68 +107,6 @@ func fsVolumeSharedMountTest(decor string, engine common.OpenEbsEngine, fstype c } } -func deployBusyBoxPod(podName, pvcName string, volType common.VolumeType) *coreV1.Pod { - args := []string{"sleep", "10000000"} - podContainer := coreV1.Container{ - Name: podName, - Image: "busybox", - ImagePullPolicy: coreV1.PullAlways, - Args: args, - } - - volume := coreV1.Volume{ - Name: "ms-volume", - VolumeSource: coreV1.VolumeSource{ - PersistentVolumeClaim: &coreV1.PersistentVolumeClaimVolumeSource{ - ClaimName: pvcName, - }, - }, - } - - podObj, err := k8stest.NewPodBuilder(podName). - WithName(podName). - WithNamespace(common.NSDefault). - WithRestartPolicy(coreV1.RestartPolicyNever). - WithContainer(podContainer). - WithVolume(volume). - WithVolumeDeviceOrMount(volType).Build() - Expect(err).ToNot(HaveOccurred(), "Generating pod definition, err: %v", err) - Expect(podObj).ToNot(BeNil(), "failed to generate pod definition") - - _, err = k8stest.CreatePod(podObj, common.NSDefault) - Expect(err).ToNot(HaveOccurred(), "Creating pod, err: %v", err) - - Eventually(func() bool { - return k8stest.IsPodRunning(podName, common.NSDefault) - }, k8stest.DefTimeoutSecs, "2s").Should(Equal(true)) - - logf.Log.Info(fmt.Sprintf("%s pod is running.", podName)) - return podObj -} - -func cleanUpResources(pods []string, pvcName string) { - for _, pod := range pods { - err := k8stest.DeletePod(pod, common.NSDefault) - Expect(err).ToNot(HaveOccurred(), "failed to delete pod %s err %v", pod, err) - - // check if pod is deleted successfully - Eventually(func() bool { - return k8stest.IsPodRunning(pod, common.NSDefault) - }, - k8stest.DefTimeoutSecs, - "5s", - ).Should(Equal(false), "busybox pod deletion failed") - } - - err := k8stest.DeletePVC(pvcName, common.NSDefault) - Expect(err).ToNot(HaveOccurred(), "failed to delete pvc %s err %v", pvcName, err) - - err = k8stest.RmStorageClass(pvcName) - Expect(err).ToNot(HaveOccurred(), "Deleting storage class %s", pvcName) - - time.Sleep(10 * time.Second) -} - func TestLvmVolumeResizeTest(t *testing.T) { e2e_ginkgo.InitTesting(t, "lvm_shared_mount_volume", "lvm_shared_mount_volume") } @@ -180,10 +119,11 @@ var _ = Describe("lvm_shared_mount_volume", func() { AfterEach(func() { // Clean up after each test - cleanUpResources(podNames, busyboxapp.GetPvcName()) + err := k8stest.CleanUpBusyboxResources(podNames, busyboxapp.GetPvcName()) + Expect(err).ToNot(HaveOccurred()) podNames = nil // Reset the pod list for the next test - err := e2e_ginkgo.AfterEachK8sCheck() + err = e2e_ginkgo.AfterEachK8sCheck() Expect(err).ToNot(HaveOccurred()) }) diff --git a/testplans/selfci.yaml b/testplans/selfci.yaml index 53da0fd..1de277a 100644 --- a/testplans/selfci.yaml +++ b/testplans/selfci.yaml @@ -7,3 +7,4 @@ meta: testsuites: - zfs_ci_smoke_test - lvm_ci_smoke_test + - hostpath_ci_smoke_test