From aa4730970037ce293a0df845381d18d172cdc6d3 Mon Sep 17 00:00:00 2001 From: "David L. Smith-Uchida" Date: Tue, 24 Nov 2020 11:12:52 -0800 Subject: [PATCH] Add an E2E test framework to test Velero across cloud platforms (#3060) * Basic end-to-end tests, generate data/backup/remove/restore/verify Uses distributed data generator Signed-off-by: Dave Smith-Uchida * Moved backup/restore into velero_utils, started using a name for the restore Signed-off-by: Dave Smith-Uchida * remove checked in binary and update test/e2e Makefile Signed-off-by: Ashish Amarnath * Ran make update Signed-off-by: Dave Smith-Uchida * Save Signed-off-by: Ashish Amarnath * Ran make update Signed-off-by: Dave Smith-Uchida * Basic end-to-end test, generate data/backup/remove/restore/verify Uses distributed data generator Signed-off-by: Dave Smith-Uchida * Changed tests/e2e Makefile to just use go get to install ginkgo in the GOPATH/bin Updated to ginkgo 1.14.2 Put cobra back to v0.0.7 Signed-off-by: Dave Smith-Uchida * Added CLOUD_PLATFORM env variable to Makefile, updated README, removed ginkgo from .gitignore Signed-off-by: Dave Smith-Uchida * choose velero CLI binary based on local env Signed-off-by: Ashish Amarnath Co-authored-by: Ashish Amarnath --- Makefile | 4 + changelogs/unreleased/3060-dsu-igeek | 2 + go.mod | 5 +- go.sum | 7 +- test/e2e/Makefile | 61 +++++++++++ test/e2e/README.md | 46 +++++++++ test/e2e/aws_test.go | 24 +++++ test/e2e/azure_test.go | 24 +++++ test/e2e/backup_test.go | 83 +++++++++++++++ test/e2e/common.go | 27 +++++ test/e2e/e2e_suite_test.go | 26 +++++ test/e2e/kibishii_utils.go | 145 +++++++++++++++++++++++++++ test/e2e/kind_test.go | 24 +++++ test/e2e/velero_utils.go | 114 +++++++++++++++++++++ 14 files changed, 587 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/3060-dsu-igeek create mode 100644 test/e2e/Makefile create mode 100644 test/e2e/README.md create mode 100644 test/e2e/aws_test.go create mode 100644 test/e2e/azure_test.go create mode 100644 test/e2e/backup_test.go create mode 100644 test/e2e/common.go create mode 100644 test/e2e/e2e_suite_test.go create mode 100644 test/e2e/kibishii_utils.go create mode 100644 test/e2e/kind_test.go create mode 100644 test/e2e/velero_utils.go diff --git a/Makefile b/Makefile index ea06e19db..fdfddb556 100644 --- a/Makefile +++ b/Makefile @@ -331,3 +331,7 @@ serve-docs: build-image-hugo # Please read the documentation in the script for instructions on how to use it. gen-docs: @hack/release-tools/gen-docs.sh + +.PHONY: test-e2e +test-e2e: + $(MAKE) -C test/e2e run diff --git a/changelogs/unreleased/3060-dsu-igeek b/changelogs/unreleased/3060-dsu-igeek new file mode 100644 index 000000000..b5891231f --- /dev/null +++ b/changelogs/unreleased/3060-dsu-igeek @@ -0,0 +1,2 @@ +Basic end-to-end tests, generate data/backup/remove/restore/verify +Uses distributed data generator diff --git a/go.mod b/go.mod index 587b3ba0c..25d35f098 100644 --- a/go.mod +++ b/go.mod @@ -15,12 +15,13 @@ require ( github.com/gobwas/glob v0.2.3 github.com/gofrs/uuid v3.2.0+incompatible github.com/golang/protobuf v1.4.2 + github.com/google/uuid v1.1.1 github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26 github.com/joho/godotenv v1.3.0 github.com/kubernetes-csi/external-snapshotter/v2 v2.2.0-rc1 - github.com/onsi/ginkgo v1.13.0 - github.com/onsi/gomega v1.10.1 + github.com/onsi/ginkgo v1.14.2 + github.com/onsi/gomega v1.10.2 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.5.1 github.com/robfig/cron v1.1.0 diff --git a/go.sum b/go.sum index c9015e89d..6e788f1fd 100644 --- a/go.sum +++ b/go.sum @@ -512,8 +512,8 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= +github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -522,6 +522,8 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -579,7 +581,6 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= diff --git a/test/e2e/Makefile b/test/e2e/Makefile new file mode 100644 index 000000000..5c6a00b9e --- /dev/null +++ b/test/e2e/Makefile @@ -0,0 +1,61 @@ +# Copyright 2020 The Kubernetes Authors. +# +# 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. + +# If you update this file, please follow: +# https://suva.sh/posts/well-documented-makefiles/ + +# Use GOPROXY environment variable if set + +.DEFAULT_GOAL:=help + +ARCH ?= $(shell go env GOOS)-$(shell go env GOARCH) +platform_temp = $(subst -, ,$(ARCH)) +GOOS = $(word 1, $(platform_temp)) +GOARCH = $(word 2, $(platform_temp)) + +GOPROXY := $(shell go env GOPROXY) +ifeq ($(GOPROXY),) +GOPROXY := https://proxy.golang.org +endif +export GOPROXY + +REPO_ROOT := $(shell git rev-parse --show-toplevel) + +help: ## Display this help + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-25s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +## -------------------------------------- +## Binaries +## -------------------------------------- + +TOOLS_DIR := $(REPO_ROOT)/hack/tools +BIN_DIR := bin +TOOLS_BIN_DIR := $(TOOLS_DIR)/$(BIN_DIR) +GINKGO := $(GOPATH)/bin/ginkgo +KUSTOMIZE := $(TOOLS_BIN_DIR)/kustomize +OUTPUT_DIR := _output/$(GOOS)/$(GOARCH)/bin +GINKGO_FOCUS ?= +CLOUD_PLATFORM ?= kind + +.PHONY:ginkgo +ginkgo: # Make sure ginkgo is in $GOPATH/bin + go get github.com/onsi/ginkgo/ginkgo + +.PHONY: run +run: ginkgo + $(GINKGO) -v -focus="$(GINKGO_FOCUS)" . -- -velerocli=../../_output/bin/$(GOOS)/$(GOARCH)/velero -cloudplatform=$(CLOUD_PLATFORM) + +build: ginkgo + mkdir -p $(OUTPUT_DIR) + $(GINKGO) build . diff --git a/test/e2e/README.md b/test/e2e/README.md new file mode 100644 index 000000000..b703d507a --- /dev/null +++ b/test/e2e/README.md @@ -0,0 +1,46 @@ +# End-to-end tests + +Document for running Velero end-to-end test suite. + +## Command line flags for E2E tests + +Command line flags can be set after +``` +velerocli - the velero CLI to use +kibishiins - the namespace to install kibishii in +cloudplatform - the cloud platform the tests will be run against (aws, vsphere, azure) +``` + +## Running tests locally + +1. From Velero repository root + + ``` + make test-e2e + ``` + +1. From `test/e2e/` directory + + ``` + make run + ``` + +## Running tests based on cloud platforms + +1. Running Velero E2E tests on KinD + + ``` + CLOUD_PLATFORM=kind make test-e2e + ``` + +1. Running Velero E2E tests on AWS + + ``` + CLOUD_PLATFORM=aws make test-e2e + ``` + +1. Running Velero E2E tests on Azure + + ``` + CLOUD_PLATFORM=azure make test-e2e + ``` diff --git a/test/e2e/aws_test.go b/test/e2e/aws_test.go new file mode 100644 index 000000000..4f59861c3 --- /dev/null +++ b/test/e2e/aws_test.go @@ -0,0 +1,24 @@ +package e2e + +import ( + "context" + "flag" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Testing Velero on an aws cluster", func() { + BeforeEach(func() { + flag.Parse() + ctx := context.TODO() + err := EnsureClusterExists(ctx) + Expect(err).NotTo(HaveOccurred()) + }) + Describe("", func() { + Context("Dummy test", func() { + It("is a dummy test", func() { + }) + }) + }) +}) diff --git a/test/e2e/azure_test.go b/test/e2e/azure_test.go new file mode 100644 index 000000000..575f58658 --- /dev/null +++ b/test/e2e/azure_test.go @@ -0,0 +1,24 @@ +package e2e + +import ( + "context" + "flag" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Testing Velero on an azure cluster", func() { + BeforeEach(func() { + flag.Parse() + ctx := context.TODO() + err := EnsureClusterExists(ctx) + Expect(err).NotTo(HaveOccurred()) + }) + Describe("Dummy test", func() { + Context("Dummy test", func() { + It("is a dummy test", func() { + }) + }) + }) +}) diff --git a/test/e2e/backup_test.go b/test/e2e/backup_test.go new file mode 100644 index 000000000..c1461e1c6 --- /dev/null +++ b/test/e2e/backup_test.go @@ -0,0 +1,83 @@ +package e2e + +import ( + "flag" + "time" + + "github.com/google/uuid" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "golang.org/x/net/context" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +var ( + backupName string + restoreName string +) + +var _ = Describe("Backup Restore test using Kibishii to generate/verify data", func() { + + BeforeEach(func() { + flag.Parse() + }) + Describe("backing up and restoring namespace with data", func() { + Context("when the backup is successful", func() { + It("generates data, backups up the namespace, deletes the namespace, restores the namespace and verifies data", func() { + backupUUID, err := uuid.NewRandom() + Expect(err).NotTo(HaveOccurred()) + backupName = "backup-" + backupUUID.String() + restoreName = "restore-" + backupUUID.String() + println("backupName = " + backupName) + println("creating namespace " + kibishiNamespace) + timeoutCTX, _ := context.WithTimeout(context.Background(), time.Minute) + err = CreateNamespace(timeoutCTX, kibishiNamespace) + Expect(err).NotTo(HaveOccurred()) + + println("installing kibishii in namespace " + kibishiNamespace) + timeoutCTX, _ = context.WithTimeout(context.Background(), 30*time.Minute) + err = InstallKibishii(timeoutCTX, kibishiNamespace, cloudPlatform) + Expect(err).NotTo(HaveOccurred()) + + println("running kibishii generate") + timeoutCTX, _ = context.WithTimeout(context.Background(), time.Minute*60) + + err = GenerateData(timeoutCTX, kibishiNamespace, 2, 10, 10, 1024, 1024, 0, 2) + Expect(err).NotTo(HaveOccurred()) + + println("executing backup") + timeoutCTX, _ = context.WithTimeout(context.Background(), time.Minute*30) + + err = BackupNamespace(timeoutCTX, veleroCLI, backupName, kibishiNamespace) + Expect(err).NotTo(HaveOccurred()) + timeoutCTX, _ = context.WithTimeout(context.Background(), time.Minute) + err = CheckBackupPhase(timeoutCTX, veleroCLI, backupName, velerov1.BackupPhaseCompleted) + + Expect(err).NotTo(HaveOccurred()) + + println("removing namespace " + kibishiNamespace) + timeoutCTX, _ = context.WithTimeout(context.Background(), time.Minute) + err = RemoveNamespace(timeoutCTX, kibishiNamespace) + Expect(err).NotTo(HaveOccurred()) + + println("restoring namespace") + timeoutCTX, _ = context.WithTimeout(context.Background(), time.Minute*30) + err = RestoreNamespace(timeoutCTX, veleroCLI, restoreName, backupName) + Expect(err).NotTo(HaveOccurred()) + println("Checking that namespace is present") + // TODO - check that namespace exists + println("running kibishii verify") + timeoutCTX, _ = context.WithTimeout(context.Background(), time.Minute*60) + + err = VerifyData(timeoutCTX, kibishiNamespace, 2, 10, 10, 1024, 1024, 0, 2) + Expect(err).NotTo(HaveOccurred()) + + println("removing namespace " + kibishiNamespace) + timeoutCTX, _ = context.WithTimeout(context.Background(), time.Minute) + err = RemoveNamespace(timeoutCTX, kibishiNamespace) + Expect(err).NotTo(HaveOccurred()) + }) + }) + }) +}) diff --git a/test/e2e/common.go b/test/e2e/common.go new file mode 100644 index 000000000..99b1d8481 --- /dev/null +++ b/test/e2e/common.go @@ -0,0 +1,27 @@ +package e2e + +import ( + "os/exec" + + "golang.org/x/net/context" +) + +func EnsureClusterExists(ctx context.Context) error { + return exec.CommandContext(ctx, "kubectl", "cluster-info").Run() +} + +func CreateNamespace(ctx context.Context, namespace string) error { + // TODO - should we talk directly to the API server? + err := exec.CommandContext(ctx, "kubectl", "create", "namespace", namespace).Run() + return err +} + +func RemoveNamespace(ctx context.Context, namespace string) error { + // TODO - should we talk directly to the API server? + err := exec.CommandContext(ctx, "kubectl", "delete", "namespace", namespace).Run() + return err +} + +func NamespaceExists(ctx context.Context, namespace string) (bool, error) { + return false, nil +} diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go new file mode 100644 index 000000000..8d78df5d6 --- /dev/null +++ b/test/e2e/e2e_suite_test.go @@ -0,0 +1,26 @@ +package e2e + +import ( + "flag" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var ( + veleroCLI string + kibishiNamespace string + cloudPlatform string // aws, vsphere, azure +) + +func init() { + flag.StringVar(&veleroCLI, "velerocli", "velero", "path to the velero application to use") + flag.StringVar(&kibishiNamespace, "kibishiins", "kibishii", "namespace to use for Kibishii distributed data generator") + flag.StringVar(&cloudPlatform, "cloudplatform", "aws", "cloud platform we are deploying on (aws, vsphere, azure)") +} + +func TestE2e(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "E2e Suite") +} diff --git a/test/e2e/kibishii_utils.go b/test/e2e/kibishii_utils.go new file mode 100644 index 000000000..d54c3cc7e --- /dev/null +++ b/test/e2e/kibishii_utils.go @@ -0,0 +1,145 @@ +package e2e + +import ( + "bufio" + "fmt" + "io" + "os" + "os/exec" + "strconv" + "strings" + + "github.com/pkg/errors" + "golang.org/x/net/context" +) + +func InstallKibishii(ctx context.Context, namespace string, cloudPlatform string) error { + // We use kustomize to generate YAML for Kibishii from the checked-in yaml directories + kibishiiInstallCmd := exec.CommandContext(ctx, "kubectl", "apply", "-n", namespace, "-k", + "github.com/vmware-tanzu-labs/distributed-data-generator/kubernetes/yaml/"+cloudPlatform) + stdoutPipe, err := kibishiiInstallCmd.StdoutPipe() + if err != nil { + return err + } + err = kibishiiInstallCmd.Start() + if err != nil { + return err + } + + defer stdoutPipe.Close() + // copy the data written to the PipeReader via the cmd to stdout + _, err = io.Copy(os.Stdout, stdoutPipe) + + if err != nil { + return err + } + err = kibishiiInstallCmd.Wait() + if err != nil { + return err + } + + kibishiiSetWaitCmd := exec.CommandContext(ctx, "kubectl", "rollout", "status", "statefulset.apps/kibishii-deployment", + "-n", namespace, "-w", "--timeout=30m") + err = kibishiiSetWaitCmd.Run() + + if err != nil { + return err + } + + jumpPadWaitCmd := exec.CommandContext(ctx, "kubectl", "wait", "--for=condition=ready", "-n", namespace, "pod/jump-pad") + err = jumpPadWaitCmd.Run() + + return err +} + +func GenerateData(ctx context.Context, namespace string, levels int, filesPerLevel int, dirsPerLevel int, fileSize int, + blockSize int, passNum int, expectedNodes int) error { + kibishiiGenerateCmd := exec.CommandContext(ctx, "kubectl", "exec", "-n", namespace, "jump-pad", "--", + "/usr/local/bin/generate.sh", strconv.Itoa(levels), strconv.Itoa(filesPerLevel), strconv.Itoa(dirsPerLevel), strconv.Itoa(fileSize), + strconv.Itoa(blockSize), strconv.Itoa(passNum), strconv.Itoa(expectedNodes)) + fmt.Printf("kibishiiGenerateCmd cmd =%v\n", kibishiiGenerateCmd) + stdoutPipe, err := kibishiiGenerateCmd.StdoutPipe() + if err != nil { + return err + } + err = kibishiiGenerateCmd.Start() + if err != nil { + return err + } + defer stdoutPipe.Close() + + stdoutReader := bufio.NewReader(stdoutPipe) + var readErr error + for true { + buf, isPrefix, err := stdoutReader.ReadLine() + if err != nil { + readErr = err + break + } else { + if isPrefix { + readErr = errors.New("line returned exceeded max length") + break + } + line := strings.TrimSpace(string(buf)) + if line == "success" { + break + } + if line == "failed" { + readErr = errors.New("generate failed") + break + } + fmt.Println(line) + } + } + err = kibishiiGenerateCmd.Wait() + if readErr != nil { + err = readErr // Squash the Wait err, the read error is probably more interesting + } + return err +} + +func VerifyData(ctx context.Context, namespace string, levels int, filesPerLevel int, dirsPerLevel int, fileSize int, + blockSize int, passNum int, expectedNodes int) error { + kibishiiVerifyCmd := exec.CommandContext(ctx, "kubectl", "exec", "-n", namespace, "jump-pad", "--", + "/usr/local/bin/verify.sh", strconv.Itoa(levels), strconv.Itoa(filesPerLevel), strconv.Itoa(dirsPerLevel), strconv.Itoa(fileSize), + strconv.Itoa(blockSize), strconv.Itoa(passNum), strconv.Itoa(expectedNodes)) + fmt.Printf("kibishiiVerifyCmd cmd =%v\n", kibishiiVerifyCmd) + stdoutPipe, err := kibishiiVerifyCmd.StdoutPipe() + if err != nil { + return err + } + err = kibishiiVerifyCmd.Start() + if err != nil { + return err + } + defer stdoutPipe.Close() + + stdoutReader := bufio.NewReader(stdoutPipe) + var readErr error + for true { + buf, isPrefix, err := stdoutReader.ReadLine() + if err != nil { + readErr = err + break + } else { + if isPrefix { + readErr = errors.New("line returned exceeded max length") + break + } + line := strings.TrimSpace(string(buf)) + if line == "success" { + break + } + if line == "failed" { + readErr = errors.New("generate failed") + break + } + fmt.Println(line) + } + } + err = kibishiiVerifyCmd.Wait() + if readErr != nil { + err = readErr // Squash the Wait err, the read error is probably more interesting + } + return err +} diff --git a/test/e2e/kind_test.go b/test/e2e/kind_test.go new file mode 100644 index 000000000..9a68e3f23 --- /dev/null +++ b/test/e2e/kind_test.go @@ -0,0 +1,24 @@ +package e2e + +import ( + "context" + "flag" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Testing Velero on a kind cluster", func() { + BeforeEach(func() { + flag.Parse() + ctx := context.TODO() + err := EnsureClusterExists(ctx) + Expect(err).NotTo(HaveOccurred()) + }) + Describe("Dummy test", func() { + Context("Dummy test", func() { + It("is a dummy test", func() { + }) + }) + }) +}) diff --git a/test/e2e/velero_utils.go b/test/e2e/velero_utils.go new file mode 100644 index 000000000..9050cab6d --- /dev/null +++ b/test/e2e/velero_utils.go @@ -0,0 +1,114 @@ +package e2e + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os/exec" + + "github.com/pkg/errors" + + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +func CheckBackupPhase(ctx context.Context, veleroCLI string, backupName string, expectedPhase v1.BackupPhase) error { + checkCMD := exec.CommandContext(ctx, veleroCLI, "backup", "get", "-o", "json", backupName) + fmt.Printf("get backup cmd =%v\n", checkCMD) + stdoutPipe, err := checkCMD.StdoutPipe() + if err != nil { + return err + } + + jsonBuf := make([]byte, 16*1024) // If the YAML is bigger than 16K, there's probably something bad happening + + err = checkCMD.Start() + if err != nil { + return err + } + + bytesRead, err := io.ReadFull(stdoutPipe, jsonBuf) + + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + if bytesRead == len(jsonBuf) { + return errors.New("yaml returned bigger than max allowed") + } + + jsonBuf = jsonBuf[0:bytesRead] + err = checkCMD.Wait() + if err != nil { + return err + } + backup := v1.Backup{} + err = json.Unmarshal(jsonBuf, &backup) + if err != nil { + return err + } + if backup.Status.Phase != expectedPhase { + return errors.Errorf("Unexpected backup phase got %s, expecting %s", backup.Status.Phase, expectedPhase) + } + return nil +} + +func CheckRestorePhase(ctx context.Context, veleroCLI string, restoreName string, expectedPhase v1.RestorePhase) error { + checkCMD := exec.CommandContext(ctx, veleroCLI, "restore", "get", "-o", "json", restoreName) + fmt.Printf("get restore cmd =%v\n", checkCMD) + stdoutPipe, err := checkCMD.StdoutPipe() + if err != nil { + return err + } + + jsonBuf := make([]byte, 16*1024) // If the YAML is bigger than 16K, there's probably something bad happening + + err = checkCMD.Start() + if err != nil { + return err + } + + bytesRead, err := io.ReadFull(stdoutPipe, jsonBuf) + + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + if bytesRead == len(jsonBuf) { + return errors.New("yaml returned bigger than max allowed") + } + + jsonBuf = jsonBuf[0:bytesRead] + err = checkCMD.Wait() + if err != nil { + return err + } + restore := v1.Restore{} + err = json.Unmarshal(jsonBuf, &restore) + if err != nil { + return err + } + if restore.Status.Phase != expectedPhase { + return errors.Errorf("Unexpected restore phase got %s, expecting %s", restore.Status.Phase, expectedPhase) + } + return nil +} + +func BackupNamespace(ctx context.Context, veleroCLI string, backupName string, namespace string) error { + backupCmd := exec.CommandContext(ctx, veleroCLI, "create", "backup", backupName, "--include-namespaces", namespace, + "--default-volumes-to-restic", "--wait") + fmt.Printf("backup cmd =%v\n", backupCmd) + err := backupCmd.Run() + if err != nil { + return err + } + return CheckBackupPhase(ctx, veleroCLI, backupName, v1.BackupPhaseCompleted) +} + +func RestoreNamespace(ctx context.Context, veleroCLI string, restoreName string, backupName string) error { + restoreCmd := exec.CommandContext(ctx, veleroCLI, "create", "restore", restoreName, "--from-backup", backupName, "--wait") + fmt.Printf("restore cmd =%v\n", restoreCmd) + err := restoreCmd.Run() + if err != nil { + return err + } + return CheckRestorePhase(ctx, veleroCLI, restoreName, v1.RestorePhaseCompleted) +}