diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index cde20c746..33be3d473 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -28,19 +28,27 @@ jobs: runs-on: pdx01-arc-runners if: ${{ github.event.workflow_run.conclusion == 'success' }} && ${{ github.event.workflow_run.event == 'push' }} steps: - - uses: actions/checkout@v4 - name: Checkout code + - name: Checkout code + uses: actions/checkout@v4 + - name: Install Go uses: actions/setup-go@v5 with: go-version: 'stable' check-latest: true + - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y make + run: | + sudo apt-get update + sudo apt-get install -y make + make ginkgo + - name: Run e2e-aws tests - run: make -f tests/Makefile e2e-aws + run: ./hack/e2e_tests.sh aws + - name: Run e2e-vsphere tests - run: make -f tests/Makefile e2e-vsphere + run: ./hack/e2e_tests.sh vsphere + - name: Archive test logs if: ${{ failure() }} uses: actions/upload-artifact@v4 diff --git a/Makefile b/Makefile index 70f4d65d0..845b33ef0 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ GO_SRC := $(shell find . -type f -name '*.go' -not -path "./vendor/*") BINARY_NAME ?= holodeck VERSION := 0.0.1 +GINKGO_VERSION ?= $(shell $(GO_CMD) list -m -f '{{.Version}}' github.com/onsi/ginkgo/v2) IMAGE_REGISTRY ?= ghcr.io/arangogutierrez IMAGE_TAG_NAME ?= $(VERSION) @@ -67,3 +68,8 @@ controller-gen: ## Download controller-gen locally if necessary. .PHONY: manifests manifests: controller-gen $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +GINKGO = $(PROJECT_DIR)/bin/ginkgo +.PHONY: ginkgo +ginkgo: ## Download ginkgo locally if necessary. + @GOBIN=$(PROJECT_DIR)/bin GO111MODULE=on $(GO_CMD) install github.com/onsi/ginkgo/v2/ginkgo@$(GINKGO_VERSION) diff --git a/hack/e2e_tests.sh b/hack/e2e_tests.sh new file mode 100755 index 000000000..8d830ba6e --- /dev/null +++ b/hack/e2e_tests.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +# Copyright 2022 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. + +set -o errexit +set -o nounset +set -o pipefail + +SOURCE_DIR="$(cd "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" +ROOT_DIR="$SOURCE_DIR/.." + +GINKGO="$ROOT_DIR"/bin/ginkgo +GINKGO_ARGS=${GINKGO_ARGS:-} + +CI=${CI:-"true"} + +LOG_ARTIFACT_DIR=${LOG_ARTIFACT_DIR:-${ROOT_DIR}/e2e_logs} +ENV_FILE=${ENV_FILE:-} +GINKGO_FOCUS=${GINKGO_FOCUS:-} + +if [ "$1" == "aws" ]; then + ENV_FILE=${ROOT_DIR}/tests/test_aws.yml + GINKGO_FOCUS=${GINKGO_FOCUS:-"AWS"} +elif [ "$1" == "vsphere" ]; then + ENV_FILE=${ROOT_DIR}/tests/test_vsphere.yml + GINKGO_FOCUS=${GINKGO_FOCUS:-"VSPHERE"} +fi + +# Set all ENV variables for e2e tests +export LOG_ARTIFACT_DIR ENV_FILE GINKGO_FOCUS CI + +# shellcheck disable=SC2086 +$GINKGO $GINKGO_ARGS -v --focus $GINKGO_FOCUS ./tests/... diff --git a/pkg/provider/aws/create.go b/pkg/provider/aws/create.go index ccbd7fdaf..184225a69 100644 --- a/pkg/provider/aws/create.go +++ b/pkg/provider/aws/create.go @@ -30,7 +30,6 @@ import ( // VPC, Subnet, Internet Gateway, Route Table, Security Group func (p *Provider) Create() error { cache := new(AWS) - defer p.dumpCache(cache) p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Creating AWS resources") @@ -38,26 +37,31 @@ func (p *Provider) Create() error { p.updateDegradedCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Error creating VPC") return fmt.Errorf("error creating VPC: %v", err) } + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "VPC created") if err := p.createSubnet(cache); err != nil { p.updateDegradedCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Error creating subnet") return fmt.Errorf("error creating subnet: %v", err) } + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Subnet created") if err := p.createInternetGateway(cache); err != nil { p.updateDegradedCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Error creating Internet Gateway") return fmt.Errorf("error creating Internet Gateway: %v", err) } + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Internet Gateway created") if err := p.createRouteTable(cache); err != nil { p.updateDegradedCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Error creating route table") return fmt.Errorf("error creating route table: %v", err) } + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Route Table created") if err := p.createSecurityGroup(cache); err != nil { p.updateDegradedCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Error creating security group") return fmt.Errorf("error creating security group: %v", err) } + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Security Group created") if err := p.createEC2Instance(cache); err != nil { p.updateDegradedCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Error creating EC2 instance") diff --git a/pkg/provider/aws/delete.go b/pkg/provider/aws/delete.go index 5be54ab7f..7492d1a2c 100644 --- a/pkg/provider/aws/delete.go +++ b/pkg/provider/aws/delete.go @@ -18,6 +18,7 @@ package aws import ( "context" + "errors" "fmt" "time" @@ -30,7 +31,6 @@ func (p *Provider) Delete() error { if err != nil { return fmt.Errorf("error retrieving cache: %v", err) } - p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Destroying", "Destroying AWS resources") if err := p.delete(cache); err != nil { return fmt.Errorf("error destroying AWS resources: %v", err) @@ -41,99 +41,159 @@ func (p *Provider) Delete() error { func (p *Provider) delete(cache *AWS) error { var err error + // Delete the EC2 instance + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Destroying", "Deleting EC2 instance") if cache.Instanceid == "" { p.log.Warning("No instance found to delete") } else { - terminateInstancesInput := &ec2.TerminateInstancesInput{ - InstanceIds: []string{cache.Instanceid}, - } - _, err := p.ec2.TerminateInstances(context.Background(), terminateInstancesInput) - if err != nil { - return fmt.Errorf("error deleting instance: %v", err) + // call deleteEC2 3 times to ensure the instance is deleted or until it returns nil + for i := 0; i < 3; i++ { + err = p.deleteEC2(cache) + if err == nil { + break + } + + if i == 2 { + p.updateDegradedCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Destroying", "Error deleting EC2 instance") + return fmt.Errorf("error deleting EC2 instance: %v", err) + } } + } - p.log.Wg.Add(1) - go p.log.Loading("Waiting for instance %s to be terminated", cache.Instanceid) - - waiterOptions := []func(*ec2.InstanceTerminatedWaiterOptions){ - func(o *ec2.InstanceTerminatedWaiterOptions) { - o.MaxDelay = 10 * time.Minute - o.MinDelay = 5 * time.Second - }, - } - wait := ec2.NewInstanceTerminatedWaiter(p.ec2, waiterOptions...) - if err := wait.Wait(context.Background(), &ec2.DescribeInstancesInput{ - InstanceIds: []string{cache.Instanceid}, - }, 10*time.Minute, waiterOptions...); err != nil { - p.fail() - return fmt.Errorf("error waiting for instance to be terminated: %v", err) + // Delete the VPC + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Destroying", "Deleting VPC resources") + for i := 0; i < 3; i++ { + err = p.deleteVPC(cache) + if err == nil { + break } - // Delete the security group - deleteSecurityGroup := &ec2.DeleteSecurityGroupInput{ - GroupId: &cache.SecurityGroupid, - } - _, err = p.ec2.DeleteSecurityGroup(context.Background(), deleteSecurityGroup) - if err != nil { - p.fail() - return fmt.Errorf("error deleting security group: %v", err) + if i == 2 { + p.updateDegradedCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Destroying", "Error deleting VPC resources") + return fmt.Errorf("error deleting VPC resources: %v", err) } + } + + return nil +} - p.done() +func (p *Provider) deleteEC2(cache *AWS) error { + terminateInstancesInput := &ec2.TerminateInstancesInput{ + InstanceIds: []string{cache.Instanceid}, + } + _, err := p.ec2.TerminateInstances(context.Background(), terminateInstancesInput) + if err != nil { + return fmt.Errorf("error deleting instance: %v", err) } p.log.Wg.Add(1) - go p.log.Loading("Deleting VPC resources") - // Delete the subnet - deleteSubnet := &ec2.DeleteSubnetInput{ - SubnetId: &cache.Subnetid, + go p.log.Loading("Waiting for instance %s to be terminated", cache.Instanceid) + + waiterOptions := []func(*ec2.InstanceTerminatedWaiterOptions){ + func(o *ec2.InstanceTerminatedWaiterOptions) { + o.MaxDelay = 10 * time.Minute + o.MinDelay = 5 * time.Second + }, } - _, err = p.ec2.DeleteSubnet(context.Background(), deleteSubnet) - if err != nil { + wait := ec2.NewInstanceTerminatedWaiter(p.ec2, waiterOptions...) + if err := wait.Wait(context.Background(), &ec2.DescribeInstancesInput{ + InstanceIds: []string{cache.Instanceid}, + }, 10*time.Minute, waiterOptions...); err != nil { p.fail() - return fmt.Errorf("error deleting subnet: %v", err) + return fmt.Errorf("error waiting for instance to be terminated: %v", err) } - // Delete the route tables - deleteRouteTable := &ec2.DeleteRouteTableInput{ - RouteTableId: &cache.RouteTable, + // Delete the security group + deleteSecurityGroup := &ec2.DeleteSecurityGroupInput{ + GroupId: &cache.SecurityGroupid, } - _, err = p.ec2.DeleteRouteTable(context.Background(), deleteRouteTable) + _, err = p.ec2.DeleteSecurityGroup(context.Background(), deleteSecurityGroup) if err != nil { p.fail() - return fmt.Errorf("error deleting route table: %v", err) + return fmt.Errorf("error deleting security group: %v", err) } - // Detach the Internet Gateway - detachInternetGateway := &ec2.DetachInternetGatewayInput{ - InternetGatewayId: &cache.InternetGwid, - VpcId: &cache.Vpcid, + p.done() + return nil +} + +func (p *Provider) deleteVPC(cache *AWS) error { + var err error + + // Delete the VPC + p.log.Wg.Add(1) + go p.log.Loading("Deleting VPC resources") + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Destroying", "Deleting VPC resources") + // Delete the subnet + if cache.Subnetid == "" { + p.log.Warning("No subnet found to delete") + } else { + deleteSubnet := &ec2.DeleteSubnetInput{ + SubnetId: &cache.Subnetid, + } + _, err = p.ec2.DeleteSubnet(context.Background(), deleteSubnet) + if err != nil { + err = errors.Join(err, fmt.Errorf("error deleting subnet: %v", err)) + } } - _, err = p.ec2.DetachInternetGateway(context.Background(), detachInternetGateway) - if err != nil { - p.fail() - return fmt.Errorf("error detaching Internet Gateway: %v", err) + + // Delete the route tables + if cache.RouteTable == "" { + p.log.Warning("No route table found to delete") + } else { + deleteRouteTable := &ec2.DeleteRouteTableInput{ + RouteTableId: &cache.RouteTable, + } + _, err = p.ec2.DeleteRouteTable(context.Background(), deleteRouteTable) + if err != nil { + err = errors.Join(err, fmt.Errorf("error deleting route table: %v", err)) + } } - // Delete the Internet Gateway - deleteInternetGatewayInput := &ec2.DeleteInternetGatewayInput{ - InternetGatewayId: &cache.InternetGwid, + // Detach the Internet Gateway + if cache.InternetGwid == "" { + p.log.Warning("No Internet Gateway found to delete") + } else { + detachInternetGateway := &ec2.DetachInternetGatewayInput{ + InternetGatewayId: &cache.InternetGwid, + VpcId: &cache.Vpcid, + } + _, err = p.ec2.DetachInternetGateway(context.Background(), detachInternetGateway) + if err != nil { + err = errors.Join(err, fmt.Errorf("error detaching Internet Gateway: %v", err)) + } } - _, err = p.ec2.DeleteInternetGateway(context.Background(), deleteInternetGatewayInput) - if err != nil { - p.fail() - return fmt.Errorf("error deleting Internet Gateway: %v", err) + + // Delete the Internet Gateway + if cache.InternetGwid == "" { + p.log.Warning("No Internet Gateway found to delete") + } else { + deleteInternetGatewayInput := &ec2.DeleteInternetGatewayInput{ + InternetGatewayId: &cache.InternetGwid, + } + _, err = p.ec2.DeleteInternetGateway(context.Background(), deleteInternetGatewayInput) + if err != nil { + err = errors.Join(err, fmt.Errorf("error deleting Internet Gateway: %v", err)) + } } // Delete the VPC - dVpc := &ec2.DeleteVpcInput{ - VpcId: &cache.Vpcid, + if cache.Vpcid == "" { + p.log.Warning("No VPC found to delete") + } else { + dVpc := &ec2.DeleteVpcInput{ + VpcId: &cache.Vpcid, + } + _, err = p.ec2.DeleteVpc(context.Background(), dVpc) + if err != nil { + err = errors.Join(err, fmt.Errorf("error deleting VPC: %v", err)) + } } - _, err = p.ec2.DeleteVpc(context.Background(), dVpc) + if err != nil { p.fail() - return fmt.Errorf("error deleting VPC: %v", err) + return err } p.done() diff --git a/pkg/provider/aws/status.go b/pkg/provider/aws/status.go index 0769b6ec6..b2b1be8f0 100644 --- a/pkg/provider/aws/status.go +++ b/pkg/provider/aws/status.go @@ -43,6 +43,10 @@ func (p *Provider) Status() (string, error) { return "", err } + if len(env.Status.Conditions) == 0 { + return "", nil + } + return env.Status.Conditions[0].Type, nil } diff --git a/pkg/provider/vsphere/create.go b/pkg/provider/vsphere/create.go index eef29bf4b..1ed65474c 100644 --- a/pkg/provider/vsphere/create.go +++ b/pkg/provider/vsphere/create.go @@ -29,7 +29,6 @@ import ( // Create creates a VM on VSphere func (p *Provider) Create() error { cache := new(Vsphere) - defer p.dumpCache(cache) // Create context with timeout ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) @@ -54,6 +53,7 @@ func (p *Provider) Create() error { } finder.SetDatacenter(dc) cache.Datacenter = dc.Name() + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Datacenter Found") // Find the cluster datastore, err := finder.Datastore(ctx, p.Environment.Spec.VsphereVirtualMachine.DataStore) @@ -62,6 +62,7 @@ func (p *Provider) Create() error { return fmt.Errorf("error finding datastore: %v", err) } cache.DataStore = datastore.Name() + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Datastore Found") // Find the network _, err = finder.Network(ctx, p.Environment.Spec.VsphereVirtualMachine.Network) @@ -70,6 +71,7 @@ func (p *Provider) Create() error { return fmt.Errorf("error finding network: %v", err) } cache.Network = p.Environment.Spec.VsphereVirtualMachine.Network + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Network Found") // Find the template template, err := finder.VirtualMachine(ctx, p.Environment.Spec.VsphereVirtualMachine.TemplateImage) @@ -78,6 +80,7 @@ func (p *Provider) Create() error { return fmt.Errorf("error finding template: %v", err) } cache.TemplateImage = p.Environment.Spec.VsphereVirtualMachine.TemplateImage + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Template Found") // Find the folder var folder *object.Folder @@ -95,6 +98,7 @@ func (p *Provider) Create() error { } } cache.VMFolder = folder.Name() + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Folder Found") // Find the resource pool var pool *object.ResourcePool @@ -113,6 +117,7 @@ func (p *Provider) Create() error { } } cache.ResourcePool = pool.Name() + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "Resource Pool Found") datastoreRefrence := datastore.Reference() folderReference := folder.Reference() @@ -145,6 +150,7 @@ func (p *Provider) Create() error { return fmt.Errorf("error waiting for task: %v", err) } p.done() + p.updateProgressingCondition(*p.Environment.DeepCopy(), cache, "v1alpha1.Creating", "VM Cloned") newVm := object.NewVirtualMachine(p.vsphereClient.Client, info.Result.(types.ManagedObjectReference)) diff --git a/pkg/provider/vsphere/status.go b/pkg/provider/vsphere/status.go index 4d06bd794..8c8e4b622 100644 --- a/pkg/provider/vsphere/status.go +++ b/pkg/provider/vsphere/status.go @@ -43,6 +43,10 @@ func (p *Provider) Status() (string, error) { return "", err } + if len(env.Status.Conditions) == 0 { + return "", nil + } + return env.Status.Conditions[0].Type, nil } diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index 365b7d716..000000000 --- a/tests/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. -# -# 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. - -GO_CMD ?= go - -AWS_ENV_FILE ?= $(CURDIR)/tests/test_aws.yml -VSPHERE_ENV_FILE ?= $(CURDIR)/tests/test_vsphere.yml -LOG_ARTIFACTS ?= $(CURDIR)/e2e_logs - -.PHONY: e2e-aws e2e-vsphere -e2e-aws: - @echo "Running AWS e2e tests" - $(GO_CMD) test -v $(CURDIR)/tests -args \ - -env-file=$(AWS_ENV_FILE)\ - -log-artifacts=$(LOG_ARTIFACTS) \ - -ginkgo.focus="AWS" \ - -test.timeout=1h \ - -ginkgo.v - -e2e-vsphere: - @echo "Running VSPHERE e2e tests" - $(GO_CMD) test -v $(CURDIR)/tests -args \ - -env-file=$(VSPHERE_ENV_FILE)\ - -log-artifacts=$(LOG_ARTIFACTS) \ - -ginkgo.focus="VSPHERE" \ - -test.timeout=1h \ - -ginkgo.v diff --git a/tests/aws_test.go b/tests/aws_test.go index 302d5b426..cbc124a24 100644 --- a/tests/aws_test.go +++ b/tests/aws_test.go @@ -52,13 +52,13 @@ var _ = Describe("AWS", func() { BeforeAll(func(ctx context.Context) { // Read the config file var err error - opts.cfg, err = jyaml.UnmarshalFromFile[v1alpha1.Environment](*EnvFile) + opts.cfg, err = jyaml.UnmarshalFromFile[v1alpha1.Environment](EnvFile) Expect(err).ToNot(HaveOccurred()) // Set unique name for the environment opts.cfg.Name = opts.cfg.Name + "-" + common.GenerateUID() // set cache path - opts.cachePath = *LogArtifactDir + opts.cachePath = LogArtifactDir // set cache file opts.cachefile = filepath.Join(opts.cachePath, opts.cfg.Name+".yaml") // Create cachedir directory diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 85edcfc9c..3ad6988e6 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -17,41 +17,42 @@ package e2e import ( - "flag" - "log" "os" "testing" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/NVIDIA/k8s-test-infra/pkg/framework" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" ) var ( - LogArtifactDir = flag.String("log-artifacts", "", "Directory to store logs") - EnvFile = flag.String("env-file", "", "Environment file to use") + LogArtifactDir string + EnvFile string + cwd string ) -func TestMain(m *testing.M) { - // Register test flags, then parse flags. - framework.RegisterClusterFlags(flag.CommandLine) - flag.Parse() - - // check if flags are set and if not cancel the test run - if *EnvFile == "" { - log.Fatal("Required flags not set. Please set -env-file") - } +func TestMain(t *testing.T) { + suiteName := "E2E Holodeck" - os.Exit(m.Run()) + RegisterFailHandler(Fail) + RunSpecs(t, + suiteName, + ) } -func TestE2E(t *testing.T) { - gomega.RegisterFailHandler(ginkgo.Fail) - // Run tests through the Ginkgo runner with output to console + JUnit for Jenkins - suiteConfig, reporterConfig := ginkgo.GinkgoConfiguration() - // Randomize specs as well as suites - suiteConfig.RandomizeAllSpecs = true +// cleanup cleans up the test environment +func getTestEnv() { + var err error - ginkgo.RunSpecs(t, "nvidia holodeck e2e suite", suiteConfig, reporterConfig) + LogArtifactDir = os.Getenv("LOG_ARTIFACT_DIR") + EnvFile = os.Getenv("ENV_FILE") + + // Get current working directory + cwd, err = os.Getwd() + Expect(err).NotTo(HaveOccurred()) } + +// BeforeSuite runs before the test suite +var _ = BeforeSuite(func() { + // Init + getTestEnv() +}) diff --git a/tests/vsphere_test.go b/tests/vsphere_test.go index a4b1357d7..a8810255f 100644 --- a/tests/vsphere_test.go +++ b/tests/vsphere_test.go @@ -52,13 +52,13 @@ var _ = Describe("VSPHERE", func() { BeforeAll(func(ctx context.Context) { // Read the config file var err error - opts.cfg, err = jyaml.UnmarshalFromFile[v1alpha1.Environment](*EnvFile) + opts.cfg, err = jyaml.UnmarshalFromFile[v1alpha1.Environment](EnvFile) Expect(err).ToNot(HaveOccurred()) // Set unique name for the environment opts.cfg.Name = opts.cfg.Name + "-" + common.GenerateUID() // set cache path - opts.cachePath = *LogArtifactDir + opts.cachePath = LogArtifactDir // set cache file opts.cachefile = filepath.Join(opts.cachePath, opts.cfg.Name+".yaml") // Create cachedir directory