Skip to content

Commit 85ab16a

Browse files
committed
ROSAENG-61173 | test: complete delete cluster command coverage
1 parent 6ef37e7 commit 85ab16a

2 files changed

Lines changed: 275 additions & 6 deletions

File tree

cmd/dlt/cluster/cmd.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ var args struct {
4141
mode string
4242
}
4343

44+
var confirmDelete = confirm.Confirm
45+
46+
var runUninstallLogs = func(clusterKey string) {
47+
uninstallLogs.Cmd.Run(uninstallLogs.Cmd, []string{clusterKey})
48+
}
49+
4450
var Cmd = &cobra.Command{
4551
Use: "cluster",
4652
Short: "Delete cluster",
@@ -76,27 +82,34 @@ func init() {
7682
)
7783
}
7884

79-
func run(_ *cobra.Command, _ []string) {
85+
func run(cmd *cobra.Command, argv []string) {
8086
r := rosa.NewRuntime().WithAWS().WithOCM()
8187
defer r.Cleanup()
8288

89+
err := runWithRuntime(r, cmd, argv)
90+
if err != nil {
91+
r.Reporter.Errorf("%s", err)
92+
os.Exit(1)
93+
}
94+
}
95+
96+
func runWithRuntime(r *rosa.Runtime, _ *cobra.Command, _ []string) error {
8397
clusterKey := r.GetClusterKey()
8498

8599
if args.bestEffort {
86100
r.Reporter.Warnf("Deleting cluster '%s' with 'best effort' means that certain resources may be left behind"+
87101
" in AWS account '%s'. These resources will need to be deleted manually.", clusterKey, r.Creator.AccountID)
88102
}
89103

90-
if !confirm.Confirm("delete cluster %s", clusterKey) {
91-
os.Exit(0)
104+
if !confirmDelete("delete cluster %s", clusterKey) {
105+
return nil
92106
}
93107

94108
cluster := r.FetchCluster()
95109

96110
err := handleClusterDelete(r, cluster, clusterKey, args.bestEffort)
97111
if err != nil {
98-
r.Reporter.Errorf("%s", err)
99-
os.Exit(1)
112+
return err
100113
}
101114

102115
if cluster.AWS().STS().RoleARN() != "" {
@@ -122,13 +135,15 @@ func run(_ *cobra.Command, _ []string) {
122135
}
123136
if args.watch {
124137
arguments.DisableRegionDeprecationWarning = true // disable region deprecation warning
125-
uninstallLogs.Cmd.Run(uninstallLogs.Cmd, []string{clusterKey})
138+
runUninstallLogs(clusterKey)
126139
arguments.DisableRegionDeprecationWarning = false // enable region deprecation again
127140
} else {
128141
r.Reporter.Infof("To watch your cluster uninstallation logs, run 'rosa logs uninstall -c %s --watch'",
129142
clusterKey,
130143
)
131144
}
145+
146+
return nil
132147
}
133148

134149
func handleClusterDelete(r *rosa.Runtime, cluster *cmv1.Cluster, clusterKey string, bestEffort bool) error {

cmd/dlt/cluster/cmd_test.go

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
1010
. "github.com/openshift-online/ocm-sdk-go/testing"
1111

12+
"github.com/openshift/rosa/pkg/arguments"
13+
"github.com/openshift/rosa/pkg/interactive"
1214
"github.com/openshift/rosa/pkg/test"
1315
)
1416

@@ -21,6 +23,258 @@ var _ = Describe("Delete cluster", func() {
2123
BeforeEach(func() {
2224
t = test.NewTestRuntime()
2325
clusterId = test.MockClusterID
26+
args.bestEffort = false
27+
args.watch = false
28+
interactive.SetEnabled(false)
29+
arguments.DisableRegionDeprecationWarning = false
30+
})
31+
32+
Context("runWithRuntime", func() {
33+
var argv []string
34+
35+
BeforeEach(func() {
36+
argv = []string{}
37+
args.bestEffort = false
38+
args.watch = false
39+
confirmDelete = func(string, ...interface{}) bool { return true }
40+
runUninstallLogs = func(string) {}
41+
interactive.SetEnabled(false)
42+
arguments.DisableRegionDeprecationWarning = false
43+
})
44+
45+
It("runs the non-STS happy path and prints the uninstall log hint", func() {
46+
clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) {
47+
c.State(cmv1.ClusterStateReady)
48+
c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS()))
49+
})
50+
t.SetCluster(clusterId, clusterReady)
51+
52+
statusBody := fmt.Sprintf(`{
53+
"kind": "ClusterStatus",
54+
"id": "%s",
55+
"state": "ready"
56+
}`, clusterId)
57+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody))
58+
t.ApiServer.AppendHandlers(RespondWithJSON(
59+
http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady})))
60+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, ""))
61+
62+
stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv)
63+
Expect(err).NotTo(HaveOccurred())
64+
Expect(stderr).To(BeEmpty())
65+
Expect(stdout).To(ContainSubstring("will start uninstalling"))
66+
Expect(stdout).To(ContainSubstring("rosa logs uninstall -c"))
67+
Expect(stdout).To(ContainSubstring("--watch"))
68+
})
69+
70+
It("returns cleanly when deletion is not confirmed", func() {
71+
clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) {
72+
c.State(cmv1.ClusterStateReady)
73+
c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS()))
74+
})
75+
t.SetCluster(clusterId, clusterReady)
76+
confirmDelete = func(string, ...interface{}) bool { return false }
77+
78+
stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv)
79+
Expect(err).NotTo(HaveOccurred())
80+
Expect(stderr).To(BeEmpty())
81+
Expect(stdout).To(BeEmpty())
82+
})
83+
84+
It("prints the best-effort warning and passes the flag through", func() {
85+
clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) {
86+
c.State(cmv1.ClusterStateReady)
87+
c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS()))
88+
})
89+
t.SetCluster(clusterId, clusterReady)
90+
args.bestEffort = true
91+
92+
statusBody := fmt.Sprintf(`{
93+
"kind": "ClusterStatus",
94+
"id": "%s",
95+
"state": "ready"
96+
}`, clusterId)
97+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody))
98+
t.ApiServer.AppendHandlers(RespondWithJSON(
99+
http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady})))
100+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, ""))
101+
102+
stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv)
103+
Expect(err).NotTo(HaveOccurred())
104+
Expect(stderr).To(ContainSubstring("best effort"))
105+
Expect(stderr).To(ContainSubstring("certain resources may be left behind"))
106+
Expect(stdout).To(ContainSubstring("will start uninstalling"))
107+
})
108+
109+
It("prints STS cleanup guidance for clusters with operator roles", func() {
110+
clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) {
111+
c.State(cmv1.ClusterStateReady)
112+
c.AWS(cmv1.NewAWS().STS(
113+
cmv1.NewSTS().
114+
RoleARN("arn:aws:iam::123456789012:role/Installer").
115+
OIDCEndpointURL("https://oidc.example.com").
116+
OperatorRolePrefix("my-prefix").
117+
OperatorIAMRoles(
118+
cmv1.NewOperatorIAMRole().
119+
Name("ebs-cloud-credentials").
120+
Namespace("openshift-cluster-csi-drivers").
121+
RoleARN("arn:aws:iam::123456789012:role/op-role"),
122+
),
123+
))
124+
})
125+
t.SetCluster(clusterId, clusterReady)
126+
127+
statusBody := fmt.Sprintf(`{
128+
"kind": "ClusterStatus",
129+
"id": "%s",
130+
"state": "ready"
131+
}`, clusterId)
132+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody))
133+
t.ApiServer.AppendHandlers(RespondWithJSON(
134+
http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady})))
135+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, ""))
136+
137+
stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv)
138+
Expect(err).NotTo(HaveOccurred())
139+
Expect(stderr).To(BeEmpty())
140+
Expect(stdout).To(ContainSubstring("Operator IAM Roles:"))
141+
Expect(stdout).To(ContainSubstring("arn:aws:iam::123456789012:role/op-role"))
142+
Expect(stdout).To(ContainSubstring("OIDC Provider : https://oidc.example.com"))
143+
Expect(stdout).To(ContainSubstring("rosa delete operator-roles -c"))
144+
Expect(stdout).To(ContainSubstring("rosa delete oidc-provider -c"))
145+
})
146+
147+
It("prints STS cleanup guidance without operator role output when none remain", func() {
148+
clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) {
149+
c.State(cmv1.ClusterStateReady)
150+
c.AWS(cmv1.NewAWS().STS(
151+
cmv1.NewSTS().
152+
RoleARN("arn:aws:iam::123456789012:role/Installer").
153+
OIDCEndpointURL("https://oidc-no-roles.example.com"),
154+
))
155+
})
156+
t.SetCluster(clusterId, clusterReady)
157+
158+
statusBody := fmt.Sprintf(`{
159+
"kind": "ClusterStatus",
160+
"id": "%s",
161+
"state": "ready"
162+
}`, clusterId)
163+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody))
164+
t.ApiServer.AppendHandlers(RespondWithJSON(
165+
http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady})))
166+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, ""))
167+
168+
stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv)
169+
Expect(err).NotTo(HaveOccurred())
170+
Expect(stderr).To(BeEmpty())
171+
Expect(stdout).NotTo(ContainSubstring("Operator IAM Roles:"))
172+
Expect(stdout).To(ContainSubstring("OIDC Provider : https://oidc-no-roles.example.com"))
173+
Expect(stdout).To(ContainSubstring("rosa delete operator-roles -c"))
174+
Expect(stdout).To(ContainSubstring("rosa delete oidc-provider -c"))
175+
})
176+
177+
It("runs uninstall logs when watch is enabled and restores the deprecation warning flag", func() {
178+
clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) {
179+
c.State(cmv1.ClusterStateReady)
180+
c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS()))
181+
})
182+
t.SetCluster(clusterId, clusterReady)
183+
args.watch = true
184+
185+
statusBody := fmt.Sprintf(`{
186+
"kind": "ClusterStatus",
187+
"id": "%s",
188+
"state": "ready"
189+
}`, clusterId)
190+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody))
191+
t.ApiServer.AppendHandlers(RespondWithJSON(
192+
http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady})))
193+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, ""))
194+
195+
var watchedClusterKey string
196+
var sawDisabledWarning bool
197+
runUninstallLogs = func(clusterKey string) {
198+
watchedClusterKey = clusterKey
199+
sawDisabledWarning = arguments.DisableRegionDeprecationWarning
200+
}
201+
202+
stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv)
203+
Expect(err).NotTo(HaveOccurred())
204+
Expect(stderr).To(BeEmpty())
205+
Expect(stdout).To(ContainSubstring("will start uninstalling"))
206+
Expect(watchedClusterKey).To(Equal(clusterId))
207+
Expect(sawDisabledWarning).To(BeTrue())
208+
Expect(arguments.DisableRegionDeprecationWarning).To(BeFalse())
209+
Expect(stdout).NotTo(ContainSubstring("rosa logs uninstall -c"))
210+
})
211+
212+
It("returns the already-uninstalling path through the command wrapper", func() {
213+
clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) {
214+
c.State(cmv1.ClusterStateReady)
215+
c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS()))
216+
})
217+
t.SetCluster(clusterId, clusterReady)
218+
219+
statusBody := fmt.Sprintf(`{
220+
"kind": "ClusterStatus",
221+
"id": "%s",
222+
"state": "uninstalling"
223+
}`, clusterId)
224+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody))
225+
226+
stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv)
227+
Expect(err).NotTo(HaveOccurred())
228+
Expect(stderr).To(BeEmpty())
229+
Expect(stdout).To(ContainSubstring("already uninstalling"))
230+
Expect(stdout).To(ContainSubstring("rosa logs uninstall -c"))
231+
})
232+
233+
It("returns an error from GetClusterState through the command wrapper", func() {
234+
clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) {
235+
c.State(cmv1.ClusterStateReady)
236+
c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS()))
237+
})
238+
t.SetCluster(clusterId, clusterReady)
239+
240+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusInternalServerError, ""))
241+
242+
stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv)
243+
Expect(err).To(HaveOccurred())
244+
Expect(stderr).To(BeEmpty())
245+
Expect(stdout).To(BeEmpty())
246+
Expect(err.Error()).To(ContainSubstring("expected response content type"))
247+
})
248+
249+
It("returns a delete error through the command wrapper", func() {
250+
clusterReady := test.MockCluster(func(c *cmv1.ClusterBuilder) {
251+
c.State(cmv1.ClusterStateReady)
252+
c.AWS(cmv1.NewAWS().STS(cmv1.NewSTS()))
253+
})
254+
t.SetCluster(clusterId, clusterReady)
255+
256+
statusBody := fmt.Sprintf(`{
257+
"kind": "ClusterStatus",
258+
"id": "%s",
259+
"state": "ready"
260+
}`, clusterId)
261+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, statusBody))
262+
t.ApiServer.AppendHandlers(RespondWithJSON(
263+
http.StatusOK, test.FormatClusterList([]*cmv1.Cluster{clusterReady})))
264+
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusForbidden, `{
265+
"kind": "Error",
266+
"id": "403",
267+
"href": "/api/clusters_mgmt/v1/errors/403",
268+
"code": "CLUSTERS-MGMT-403",
269+
"reason": "forbidden"
270+
}`))
271+
272+
stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, t.RosaRuntime, Cmd, &argv)
273+
Expect(err).To(HaveOccurred())
274+
Expect(stderr).To(BeEmpty())
275+
Expect(stdout).To(BeEmpty())
276+
Expect(err.Error()).To(ContainSubstring("forbidden"))
277+
})
24278
})
25279

26280
Context("handleClusterDelete", func() {

0 commit comments

Comments
 (0)