Skip to content

Commit

Permalink
test(vm): add reusable mode to use previously created virtual machine…
Browse files Browse the repository at this point in the history
…s for e2e tests

Signed-off-by: Isteb4k <[email protected]>
  • Loading branch information
Isteb4k committed Jan 16, 2025
1 parent b89aca4 commit 761d876
Show file tree
Hide file tree
Showing 15 changed files with 283 additions and 38 deletions.
17 changes: 17 additions & 0 deletions tests/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ For example, run only "Complex text" without cleanup on failure:
FOCUS="Complex test" STOP_ON_FAILURE=yes task run
```

### Reusable mode option

The environment variable REUSABLE used to retain all resources created during e2e test after its completion (no cleanup).
When a test starts, it will reuse existing virtual machines created earlier, if they exist.
If no virtual machines were found, they will be created.

For example, run test in reusable mode:
```bash
REUSABLE=yes task run
```

! Only the following e2e tests are supported in REUSABLE mode. All other tests will be skipped.
- "Virtual machine configuration"
- "Virtual machine migration"
- "VM connectivity"
- "Complex test"

## Run tests in CI
```bash
task run:ci
Expand Down
7 changes: 7 additions & 0 deletions tests/e2e/affinity_toleration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,18 @@ import (
v1 "k8s.io/api/core/v1"

virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2"
"github.com/deckhouse/virtualization/tests/e2e/config"
"github.com/deckhouse/virtualization/tests/e2e/ginkgoutil"
kc "github.com/deckhouse/virtualization/tests/e2e/kubectl"
)

var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2ETestDecorators(), func() {
BeforeEach(func() {
if config.IsReusable() {
Skip("Test not available in REUSABLE mode: not supported yet.")
}
})

var (
testCaseLabel = map[string]string{"testcase": "affinity-toleration"}
vmA = map[string]string{"vm": "vm-a"}
Expand Down
25 changes: 22 additions & 3 deletions tests/e2e/complex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
. "github.com/onsi/gomega"

virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2"
"github.com/deckhouse/virtualization/tests/e2e/config"
"github.com/deckhouse/virtualization/tests/e2e/ginkgoutil"
kc "github.com/deckhouse/virtualization/tests/e2e/kubectl"
)
Expand Down Expand Up @@ -67,6 +68,19 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() {

Context("When virtualization resources are applied", func() {
It("result should be succeeded", func() {
if config.IsReusable() {
res := kubectl.List(kc.ResourceVM, kc.GetOptions{
Labels: testCaseLabel,
Namespace: conf.Namespace,
Output: "jsonpath='{.items[*].metadata.name}'",
})
Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr())

if res.StdOut() != "" {
return
}
}

res := kubectl.Apply(kc.ApplyOptions{
Filename: []string{conf.TestData.ComplexTest},
FilenameOption: kc.Kustomize,
Expand Down Expand Up @@ -231,15 +245,20 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() {

Context("When test is completed", func() {
It("deletes test case resources", func() {
DeleteTestCaseResources(ResourcesToDelete{
KustomizationDir: conf.TestData.ComplexTest,
resourcesToDelete := ResourcesToDelete{
AdditionalResources: []AdditionalResource{
{
kc.ResourceKubevirtVMIM,
testCaseLabel,
},
},
})
}

if !config.IsReusable() {
resourcesToDelete.KustomizationDir = conf.TestData.ComplexTest
}

DeleteTestCaseResources(resourcesToDelete)
})
})
})
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ import (

var (
conf *Config
err error
git gt.Git
kubectl kc.Kubectl
)

func init() {
var err error
if conf, err = GetConfig(); err != nil {
log.Fatal(err)
}
Expand Down
48 changes: 48 additions & 0 deletions tests/e2e/config/reusable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
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"
"os"
)

// ReusableEnv defines an environment variable used to retain all resources created during e2e test after its completion (no cleanup).
// When a test starts, it will reuse existing virtual machines created earlier, if they exist.
// If no virtual machines were found, they will be created.
// Only the following e2e tests are supported in REUSABLE mode. All other tests will be skipped.
// - "Virtual machine configuration"
// - "Virtual machine migration"
// - "VM connectivity"
// - "Complex test"
const ReusableEnv = "REUSABLE"

const reusableValue = "yes"

func CheckReusableOption() error {
env := os.Getenv(ReusableEnv)
switch env {
case reusableValue, "":
return nil
default:
return fmt.Errorf("invalid value for the REUSABLE env: %q", env)
}
}

func IsReusable() bool {
return os.Getenv(ReusableEnv) == reusableValue
}
7 changes: 7 additions & 0 deletions tests/e2e/sizing_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
. "github.com/onsi/gomega"

virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2"
"github.com/deckhouse/virtualization/tests/e2e/config"
"github.com/deckhouse/virtualization/tests/e2e/ginkgoutil"
. "github.com/deckhouse/virtualization/tests/e2e/helper"
kc "github.com/deckhouse/virtualization/tests/e2e/kubectl"
Expand Down Expand Up @@ -67,6 +68,12 @@ func CompareVirtualMachineClassReadyStatus(vmName, expectedStatus string) {
}

var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() {
BeforeEach(func() {
if config.IsReusable() {
Skip("Test not available in REUSABLE mode: not supported yet.")
}
})

var (
vmNotValidSizingPolicyChanging string
vmNotValidSizingPolicyCreating string
Expand Down
32 changes: 25 additions & 7 deletions tests/e2e/tests_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package e2e
import (
"fmt"
"log"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -64,7 +65,11 @@ var (
)

func init() {
var err error
err := config.CheckReusableOption()
if err != nil {
log.Println("To run tests in REUSABLE mode, set REUSABLE=yes. If you don't intend to use this mode, leave the variable unset.")
log.Fatal(err)
}
if conf, err = config.GetConfig(); err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -108,23 +113,36 @@ func init() {
log.Fatal(err)
}
}
err = Cleanup()
if err != nil {
log.Fatal(err)

if !config.IsReusable() {
err = Cleanup()
if err != nil {
log.Fatal(err)
}
} else {
log.Printf("Run test in REUSABLE mode\n")
}

res := kubectl.CreateResource(kc.ResourceNamespace, conf.Namespace, kc.CreateOptions{})
if !res.WasSuccess() {
log.Fatalf("err: %v\n%s", res.Error(), res.StdErr())
if strings.Contains(res.StdErr(), "AlreadyExists") {
log.Printf("Namespace %q already exists: it will be reused", conf.Namespace)
} else {
log.Fatalf("err: %v\n%s", res.Error(), res.StdErr())
}
}
}

func TestTests(t *testing.T) {
RegisterFailHandler(Fail)
fmt.Fprintf(GinkgoWriter, "Starting test suite\n")
RunSpecs(t, "Tests")
if !(ginkgoutil.FailureBehaviourEnvSwitcher{}).IsStopOnFailure() {
Cleanup()

if (ginkgoutil.FailureBehaviourEnvSwitcher{}).IsStopOnFailure() || config.IsReusable() {
return
}

Cleanup()
}

func Cleanup() error {
Expand Down
9 changes: 5 additions & 4 deletions tests/e2e/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,12 +454,13 @@ type ResourcesToDelete struct {
// This function checks that all resources in test case can be deleted correctly.
func DeleteTestCaseResources(resources ResourcesToDelete) {
By("Response on deletion request should be successful")
errMessage := "cannot delete test case resources"
kustimizationFile := fmt.Sprintf("%s/%s", resources.KustomizationDir, "kustomization.yaml")
err := kustomize.ExcludeResource(kustimizationFile, "ns.yaml")
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("%s\nkustomizationDir: %s\nstderr: %s", errMessage, resources.KustomizationDir, err))
const errMessage = "cannot delete test case resources"

if resources.KustomizationDir != "" {
kustimizationFile := fmt.Sprintf("%s/%s", resources.KustomizationDir, "kustomization.yaml")
err := kustomize.ExcludeResource(kustimizationFile, "ns.yaml")
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("%s\nkustomizationDir: %s\nstderr: %s", errMessage, resources.KustomizationDir, err))

res := kubectl.Delete(kc.DeleteOptions{
Filename: []string{resources.KustomizationDir},
FilenameOption: kc.Kustomize,
Expand Down
10 changes: 9 additions & 1 deletion tests/e2e/vd_snapshots_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ import (
"sync"
"time"

sdsrepvolv1 "github.com/deckhouse/sds-replicated-volume/api/v1alpha1"
snapshotvolv1 "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
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/tests/e2e/config"
"github.com/deckhouse/virtualization/tests/e2e/ginkgoutil"
. "github.com/deckhouse/virtualization/tests/e2e/helper"
kc "github.com/deckhouse/virtualization/tests/e2e/kubectl"
Expand Down Expand Up @@ -226,6 +228,12 @@ func CheckFilesystemReadyStatus(vmName string, status v1.ConditionStatus) (strin
}

var _ = Describe("Virtual disk snapshots", ginkgoutil.CommonE2ETestDecorators(), func() {
BeforeEach(func() {
if config.IsReusable() {
Skip("Test not available in REUSABLE mode: not supported yet.")
}
})

var (
immediateStorageClassName string // require for unattached virtual disk snapshots
defaultVolumeSnapshotClassName string
Expand Down
Loading

0 comments on commit 761d876

Please sign in to comment.