Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(vm): add reusable mode to use previously created virtual machines for e2e tests #641

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading