Skip to content

Commit 7ad3bde

Browse files
committed
CI: check if PowerVS instances are cleaned up in workspace before creating a cluster
1 parent 3e538a5 commit 7ad3bde

File tree

3 files changed

+128
-10
lines changed

3 files changed

+128
-10
lines changed

test/e2e/e2e_test.go

+19-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,18 @@ import (
3333
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
3434
"sigs.k8s.io/cluster-api/util"
3535

36+
"sigs.k8s.io/cluster-api-provider-ibmcloud/test/helpers"
37+
3638
. "github.com/onsi/ginkgo/v2"
3739
. "github.com/onsi/gomega"
3840
)
3941

42+
const (
43+
powervsRemediationFlavor = "powervs-md-remediation"
44+
kubernetesVersion = "KUBERNETES_VERSION"
45+
serviceInstanceID = "IBMPOWERVS_SERVICE_INSTANCE_ID"
46+
)
47+
4048
var _ = Describe("Workload cluster creation", func() {
4149
var (
4250
ctx = context.TODO()
@@ -55,7 +63,7 @@ var _ = Describe("Workload cluster creation", func() {
5563
Expect(bootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. bootstrapClusterProxy can't be nil when calling %s spec", specName)
5664
Expect(os.MkdirAll(artifactFolder, 0750)).To(Succeed(), "Invalid argument. artifactFolder can't be created for %s spec", specName)
5765

58-
Expect(e2eConfig.Variables).To(HaveKey(KubernetesVersion))
66+
Expect(e2eConfig.Variables).To(HaveKey(kubernetesVersion))
5967

6068
clusterName = fmt.Sprintf("capibm-e2e-%s", util.RandomString(6))
6169

@@ -70,6 +78,13 @@ var _ = Describe("Workload cluster creation", func() {
7078
// Path to the CNI file is defined in the config
7179
Expect(e2eConfig.Variables).To(HaveKey(capi_e2e.CNIPath), "Missing %s variable in the config", capi_e2e.CNIPath)
7280
cniPath = e2eConfig.GetVariable(capi_e2e.CNIPath)
81+
82+
if flavor == powervsRemediationFlavor {
83+
Expect(e2eConfig.Variables).To(HaveKey(serviceInstanceID))
84+
if err := helpers.CheckPowerVSInstances(e2eConfig.GetVariable(serviceInstanceID)); err != nil {
85+
os.Exit(1)
86+
}
87+
}
7388
})
7489

7590
AfterEach(func() {
@@ -100,7 +115,7 @@ var _ = Describe("Workload cluster creation", func() {
100115
Flavor: flavor,
101116
Namespace: namespace.Name,
102117
ClusterName: clusterName,
103-
KubernetesVersion: e2eConfig.GetVariable(KubernetesVersion),
118+
KubernetesVersion: e2eConfig.GetVariable(kubernetesVersion),
104119
ControlPlaneMachineCount: ptr.To(int64(1)),
105120
WorkerMachineCount: ptr.To(int64(1)),
106121
},
@@ -121,7 +136,7 @@ var _ = Describe("Workload cluster creation", func() {
121136
Flavor: flavor,
122137
Namespace: namespace.Name,
123138
ClusterName: clusterName,
124-
KubernetesVersion: e2eConfig.GetVariable(KubernetesVersion),
139+
KubernetesVersion: e2eConfig.GetVariable(kubernetesVersion),
125140
ControlPlaneMachineCount: ptr.To(int64(1)),
126141
WorkerMachineCount: ptr.To(int64(3)),
127142
},
@@ -146,7 +161,7 @@ var _ = Describe("Workload cluster creation", func() {
146161
Flavor: flavor,
147162
Namespace: namespace.Name,
148163
ClusterName: clusterName,
149-
KubernetesVersion: e2eConfig.GetVariable(KubernetesVersion),
164+
KubernetesVersion: e2eConfig.GetVariable(kubernetesVersion),
150165
ControlPlaneMachineCount: ptr.To(int64(3)),
151166
WorkerMachineCount: ptr.To(int64(1)),
152167
},

test/e2e/suite_test.go

-6
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,6 @@ import (
4343
. "github.com/onsi/gomega"
4444
)
4545

46-
const (
47-
KubernetesVersion = "KUBERNETES_VERSION"
48-
CNIPath = "CNI"
49-
CNIResources = "CNI_RESOURCES"
50-
)
51-
5246
// Test suite flags.
5347
var (
5448
// configPath is the path to the e2e config file.

test/helpers/powervs.go

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package helpers
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"os"
23+
"strings"
24+
"time"
25+
26+
"github.com/IBM-Cloud/power-go-client/clients/instance"
27+
"github.com/IBM-Cloud/power-go-client/ibmpisession"
28+
29+
"k8s.io/apimachinery/pkg/util/wait"
30+
31+
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/authenticator"
32+
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/utils"
33+
)
34+
35+
var (
36+
pollingInterval = time.Second * 30
37+
powerVSInstanceDeletionTimeout = time.Minute * 10
38+
)
39+
40+
const (
41+
powervsInstanceDoesNotExist = "pvm-instance does not exist"
42+
powervsInstanceNotFound = "could not be found"
43+
powervsInstanceStateDeleting = "deleting"
44+
)
45+
46+
// CheckPowerVSInstances verifies if the Powervs instances are deleted
47+
// before proceeding with the next cluster creation.
48+
func CheckPowerVSInstances(serviceInstanceID string) error {
49+
pclient, err := getPowerVSInstanceClient(serviceInstanceID)
50+
if err != nil {
51+
return err
52+
}
53+
54+
instances, err := pclient.GetAll()
55+
if err != nil {
56+
return err
57+
}
58+
59+
for _, ins := range instances.PvmInstances {
60+
err = wait.PollUntilContextTimeout(context.Background(), pollingInterval, powerVSInstanceDeletionTimeout, false, func(_ context.Context) (done bool, err error) {
61+
instance, err := pclient.Get(*ins.PvmInstanceID)
62+
if err != nil {
63+
if strings.Contains(err.Error(), powervsInstanceNotFound) || strings.Contains(err.Error(), powervsInstanceDoesNotExist) {
64+
return true, nil
65+
}
66+
return false, err
67+
}
68+
69+
if instance.TaskState == powervsInstanceStateDeleting {
70+
return false, nil
71+
}
72+
return false, nil
73+
})
74+
if err != nil {
75+
return err
76+
}
77+
}
78+
return nil
79+
}
80+
81+
func getPowerVSInstanceClient(serviceInstanceID string) (*instance.IBMPIInstanceClient, error) {
82+
auth, err := authenticator.GetAuthenticator()
83+
if err != nil {
84+
return nil, err
85+
}
86+
87+
zone := os.Getenv("IBMPOWERVS_ZONE")
88+
if zone == "" {
89+
return nil, fmt.Errorf("IBMPOWERVS_ZONE is not set")
90+
}
91+
92+
account, err := utils.GetAccount(auth)
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
piOptions := ibmpisession.IBMPIOptions{
98+
Authenticator: auth,
99+
UserAccount: account,
100+
Zone: zone,
101+
Debug: true,
102+
}
103+
104+
session, err := ibmpisession.NewIBMPISession(&piOptions)
105+
if err != nil {
106+
return nil, err
107+
}
108+
return instance.NewIBMPIInstanceClient(context.Background(), session, serviceInstanceID), nil
109+
}

0 commit comments

Comments
 (0)