Skip to content

Commit a225226

Browse files
authored
[CLOUDGA-26754] Describe GCP Backup Replication (#317)
Co-authored-by: pranavbansal <pranav.bansal@yugabyte.com>
1 parent 9b114f2 commit a225226

15 files changed

Lines changed: 1186 additions & 11 deletions

cmd/backup_replication_test.go

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
// Licensed to Yugabyte, Inc. under one or more contributor license
2+
// agreements. See the NOTICE file distributed with this work for
3+
// additional information regarding copyright ownership. Yugabyte
4+
// licenses this file to you under the Apache License, Version 2.0
5+
// (the "License"); you may not use this file except in compliance
6+
// with the License. You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing,
10+
// software distributed under the License is distributed on an
11+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12+
// KIND, either express or implied. See the License for the
13+
// specific language governing permissions and limitations
14+
// under the License.
15+
16+
package cmd_test
17+
18+
import (
19+
"fmt"
20+
"net/http"
21+
"os"
22+
"os/exec"
23+
24+
. "github.com/onsi/ginkgo/v2"
25+
. "github.com/onsi/gomega"
26+
"github.com/onsi/gomega/gbytes"
27+
"github.com/onsi/gomega/gexec"
28+
"github.com/onsi/gomega/ghttp"
29+
openapi "github.com/yugabyte/yugabytedb-managed-go-client-internal"
30+
)
31+
32+
var _ = Describe("Backup Replication", func() {
33+
34+
var (
35+
server *ghttp.Server
36+
statusCode int
37+
args []string
38+
responseAccount openapi.AccountResponse
39+
responseProject openapi.AccountResponse
40+
responseListClusters openapi.ClusterListResponse
41+
responseBackupReplication openapi.GCPBackupReplicationResponse
42+
responseBackupReplicationDisabled openapi.GCPBackupReplicationResponse
43+
)
44+
45+
BeforeEach(func() {
46+
args = os.Args
47+
os.Args = []string{}
48+
var err error
49+
server, err = newGhttpServer(responseAccount, responseProject)
50+
Expect(err).ToNot(HaveOccurred())
51+
os.Setenv("YBM_HOST", fmt.Sprintf("http://%s", server.Addr()))
52+
os.Setenv("YBM_APIKEY", "test-token")
53+
os.Setenv("YBM_FF_BACKUP_REPLICATION_GCP_TARGET", "true")
54+
statusCode = 200
55+
err = loadJson("./test/fixtures/list-clusters.json", &responseListClusters)
56+
Expect(err).ToNot(HaveOccurred())
57+
server.AppendHandlers(
58+
ghttp.CombineHandlers(
59+
ghttp.VerifyRequest(http.MethodGet, "/api/public/v1/accounts/340af43a-8a7c-4659-9258-4876fd6a207b/projects/78d4459c-0f45-47a5-899a-45ddf43eba6e/clusters"),
60+
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseListClusters),
61+
),
62+
)
63+
})
64+
65+
Describe("When describing GCP backup replication", func() {
66+
Context("with cluster-name not set", func() {
67+
It("should throw error, cluster-name not set", func() {
68+
cmd := exec.Command(compiledCLIPath, "cluster", "backup-replication", "gcp", "describe")
69+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
70+
71+
Expect(err).NotTo(HaveOccurred())
72+
session.Wait(2)
73+
Expect(session.Err).Should(gbytes.Say(`\bError: required flag\(s\) "cluster-name" not set\b`))
74+
session.Kill()
75+
})
76+
})
77+
78+
Context("with valid cluster-name and active replication", func() {
79+
It("should show backup replication configuration in table format", func() {
80+
err := loadJson("./test/fixtures/gcp-backup-replication.json", &responseBackupReplication)
81+
Expect(err).ToNot(HaveOccurred())
82+
83+
statusCode = 200
84+
server.AppendHandlers(
85+
ghttp.CombineHandlers(
86+
ghttp.VerifyRequest(http.MethodGet, "/api/public/v1/accounts/340af43a-8a7c-4659-9258-4876fd6a207b/projects/78d4459c-0f45-47a5-899a-45ddf43eba6e/clusters/5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8/backup-replication/gcp"),
87+
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseBackupReplication),
88+
),
89+
)
90+
91+
cmd := exec.Command(compiledCLIPath, "cluster", "backup-replication", "gcp", "describe",
92+
"--cluster-name", "stunning-sole",
93+
)
94+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
95+
96+
Expect(err).NotTo(HaveOccurred())
97+
session.Wait(2)
98+
Expect(session.Out).Should(gbytes.Say(`Overall State: ENABLED`))
99+
Expect(session.Out).Should(gbytes.Say(`Region`))
100+
Expect(session.Out).Should(gbytes.Say(`Config State`))
101+
Expect(session.Out).Should(gbytes.Say(`Bucket Name`))
102+
Expect(session.Out).Should(gbytes.Say(`Latest Operation Status`))
103+
Expect(session.Out).Should(gbytes.Say(`Last Run`))
104+
Expect(session.Out).Should(gbytes.Say(`Next Run`))
105+
Expect(session.Out).Should(gbytes.Say(`us-west1`))
106+
Expect(session.Out).Should(gbytes.Say(`backup-bucket-us-west1`))
107+
Expect(session.Out).Should(gbytes.Say(`SUCCESS`))
108+
Expect(session.Out).Should(gbytes.Say(`2024-01-15,09:30`))
109+
Expect(session.Out).Should(gbytes.Say(`2024-01-15,10:00`))
110+
Expect(session.Out).Should(gbytes.Say(`us-east1`))
111+
Expect(session.Out).Should(gbytes.Say(`backup-bucket-us-east1`))
112+
Expect(session.Out).Should(gbytes.Say(`IN_PROGRESS`))
113+
Expect(session.Out).Should(gbytes.Say(`N/A`))
114+
Expect(session.Out).Should(gbytes.Say(`2024-01-15,11:00`))
115+
Expect(server.ReceivedRequests()).Should(HaveLen(4))
116+
session.Kill()
117+
})
118+
119+
It("should show backup replication configuration in json format", func() {
120+
err := loadJson("./test/fixtures/gcp-backup-replication.json", &responseBackupReplication)
121+
Expect(err).ToNot(HaveOccurred())
122+
123+
statusCode = 200
124+
server.AppendHandlers(
125+
ghttp.CombineHandlers(
126+
ghttp.VerifyRequest(http.MethodGet, "/api/public/v1/accounts/340af43a-8a7c-4659-9258-4876fd6a207b/projects/78d4459c-0f45-47a5-899a-45ddf43eba6e/clusters/5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8/backup-replication/gcp"),
127+
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseBackupReplication),
128+
),
129+
)
130+
131+
cmd := exec.Command(compiledCLIPath, "cluster", "backup-replication", "gcp", "describe",
132+
"--cluster-name", "stunning-sole",
133+
"--output", "json",
134+
)
135+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
136+
137+
Expect(err).NotTo(HaveOccurred())
138+
session.Wait(2)
139+
Expect(session.Out).Should(gbytes.Say(`"info"`))
140+
Expect(session.Out).Should(gbytes.Say(`"region_configs"`))
141+
Expect(session.Out).Should(gbytes.Say(`"state":"ENABLED"`))
142+
Expect(session.Out).Should(gbytes.Say(`"region":"us-west1"`))
143+
Expect(session.Out).Should(gbytes.Say(`"target":"backup-bucket-us-west1"`))
144+
Expect(session.Out).Should(gbytes.Say(`"region":"us-east1"`))
145+
Expect(session.Out).Should(gbytes.Say(`"target":"backup-bucket-us-east1"`))
146+
Expect(server.ReceivedRequests()).Should(HaveLen(4))
147+
session.Kill()
148+
})
149+
150+
It("should show backup replication configuration in pretty format", func() {
151+
err := loadJson("./test/fixtures/gcp-backup-replication.json", &responseBackupReplication)
152+
Expect(err).ToNot(HaveOccurred())
153+
154+
statusCode = 200
155+
server.AppendHandlers(
156+
ghttp.CombineHandlers(
157+
ghttp.VerifyRequest(http.MethodGet, "/api/public/v1/accounts/340af43a-8a7c-4659-9258-4876fd6a207b/projects/78d4459c-0f45-47a5-899a-45ddf43eba6e/clusters/5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8/backup-replication/gcp"),
158+
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseBackupReplication),
159+
),
160+
)
161+
162+
cmd := exec.Command(compiledCLIPath, "cluster", "backup-replication", "gcp", "describe",
163+
"--cluster-name", "stunning-sole",
164+
"--output", "pretty",
165+
)
166+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
167+
168+
Expect(err).NotTo(HaveOccurred())
169+
session.Wait(2)
170+
Expect(session.Out).Should(gbytes.Say(`"info"`))
171+
Expect(session.Out).Should(gbytes.Say(`"region_configs"`))
172+
Expect(session.Out).Should(gbytes.Say(`"state": "ENABLED"`))
173+
Expect(session.Out).Should(gbytes.Say(`"region": "us-west1"`))
174+
Expect(session.Out).Should(gbytes.Say(`"target": "backup-bucket-us-west1"`))
175+
Expect(session.Out).Should(gbytes.Say(`"region": "us-east1"`))
176+
Expect(session.Out).Should(gbytes.Say(`"target": "backup-bucket-us-east1"`))
177+
Expect(server.ReceivedRequests()).Should(HaveLen(4))
178+
session.Kill()
179+
})
180+
})
181+
182+
Context("with disabled replication", func() {
183+
It("should show DISABLED state when no active reports", func() {
184+
err := loadJson("./test/fixtures/gcp-backup-replication-disabled.json", &responseBackupReplicationDisabled)
185+
Expect(err).ToNot(HaveOccurred())
186+
187+
statusCode = 200
188+
server.AppendHandlers(
189+
ghttp.CombineHandlers(
190+
ghttp.VerifyRequest(http.MethodGet, "/api/public/v1/accounts/340af43a-8a7c-4659-9258-4876fd6a207b/projects/78d4459c-0f45-47a5-899a-45ddf43eba6e/clusters/5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8/backup-replication/gcp"),
191+
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseBackupReplicationDisabled),
192+
),
193+
)
194+
195+
cmd := exec.Command(compiledCLIPath, "cluster", "backup-replication", "gcp", "describe",
196+
"--cluster-name", "stunning-sole",
197+
)
198+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
199+
200+
Expect(err).NotTo(HaveOccurred())
201+
session.Wait(2)
202+
Expect(session.Out).Should(gbytes.Say(`Overall State: DISABLED`))
203+
Expect(session.Out).Should(gbytes.Say(`DISABLED`))
204+
Expect(session.Out).Should(gbytes.Say(`backup-bucket-us-west1`))
205+
Expect(server.ReceivedRequests()).Should(HaveLen(4))
206+
session.Kill()
207+
})
208+
})
209+
210+
Context("with show-all flag", func() {
211+
It("should show active and expired configurations in table format", func() {
212+
err := loadJson("./test/fixtures/gcp-backup-replication.json", &responseBackupReplication)
213+
Expect(err).ToNot(HaveOccurred())
214+
215+
statusCode = 200
216+
server.AppendHandlers(
217+
ghttp.CombineHandlers(
218+
ghttp.VerifyRequest(http.MethodGet, "/api/public/v1/accounts/340af43a-8a7c-4659-9258-4876fd6a207b/projects/78d4459c-0f45-47a5-899a-45ddf43eba6e/clusters/5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8/backup-replication/gcp"),
219+
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseBackupReplication),
220+
),
221+
)
222+
223+
cmd := exec.Command(compiledCLIPath, "cluster", "backup-replication", "gcp", "describe",
224+
"--cluster-name", "stunning-sole",
225+
"--show-all",
226+
)
227+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
228+
229+
Expect(err).NotTo(HaveOccurred())
230+
session.Wait(2)
231+
Expect(session.Out).Should(gbytes.Say(`=== Active Configurations ===`))
232+
Expect(session.Out).Should(gbytes.Say(`Region`))
233+
Expect(session.Out).Should(gbytes.Say(`Config State`))
234+
Expect(session.Out).Should(gbytes.Say(`Bucket Name`))
235+
Expect(session.Out).Should(gbytes.Say(`Latest Operation Status`))
236+
Expect(session.Out).Should(gbytes.Say(`Last Run`))
237+
Expect(session.Out).Should(gbytes.Say(`Next Run`))
238+
Expect(session.Out).Should(gbytes.Say(`us-west1`))
239+
Expect(session.Out).Should(gbytes.Say(`backup-bucket-us-west1`))
240+
Expect(session.Out).Should(gbytes.Say(`SUCCESS`))
241+
Expect(session.Out).Should(gbytes.Say(`us-east1`))
242+
Expect(session.Out).Should(gbytes.Say(`backup-bucket-us-east1`))
243+
Expect(session.Out).Should(gbytes.Say(`IN_PROGRESS`))
244+
Expect(session.Out).Should(gbytes.Say(`=== Configurations Set for Expiry ===`))
245+
Expect(session.Out).Should(gbytes.Say(`Expiry Time`))
246+
Expect(session.Out).Should(gbytes.Say(`2024-01-15,08:00`))
247+
Expect(session.Out).Should(gbytes.Say(`2024-01-15,09:00`))
248+
Expect(server.ReceivedRequests()).Should(HaveLen(4))
249+
session.Kill()
250+
})
251+
252+
It("should show backup replication configuration in json format", func() {
253+
err := loadJson("./test/fixtures/gcp-backup-replication.json", &responseBackupReplication)
254+
Expect(err).ToNot(HaveOccurred())
255+
256+
statusCode = 200
257+
server.AppendHandlers(
258+
ghttp.CombineHandlers(
259+
ghttp.VerifyRequest(http.MethodGet, "/api/public/v1/accounts/340af43a-8a7c-4659-9258-4876fd6a207b/projects/78d4459c-0f45-47a5-899a-45ddf43eba6e/clusters/5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8/backup-replication/gcp"),
260+
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseBackupReplication),
261+
),
262+
)
263+
264+
cmd := exec.Command(compiledCLIPath, "cluster", "backup-replication", "gcp", "describe",
265+
"--cluster-name", "stunning-sole",
266+
"--output", "json",
267+
"--show-all",
268+
)
269+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
270+
271+
Expect(err).NotTo(HaveOccurred())
272+
session.Wait(2)
273+
Expect(session.Out).Should(gbytes.Say(`"info"`))
274+
Expect(session.Out).Should(gbytes.Say(`"region_configs"`))
275+
Expect(session.Out).Should(gbytes.Say(`"state":"ENABLED"`))
276+
Expect(session.Out).Should(gbytes.Say(`"region":"us-west1"`))
277+
Expect(session.Out).Should(gbytes.Say(`"target":"backup-bucket-us-west1"`))
278+
Expect(session.Out).Should(gbytes.Say(`"region":"us-east1"`))
279+
Expect(session.Out).Should(gbytes.Say(`"target":"backup-bucket-us-east1"`))
280+
Expect(server.ReceivedRequests()).Should(HaveLen(4))
281+
session.Kill()
282+
})
283+
284+
It("should show backup replication configuration in pretty format", func() {
285+
err := loadJson("./test/fixtures/gcp-backup-replication.json", &responseBackupReplication)
286+
Expect(err).ToNot(HaveOccurred())
287+
288+
statusCode = 200
289+
server.AppendHandlers(
290+
ghttp.CombineHandlers(
291+
ghttp.VerifyRequest(http.MethodGet, "/api/public/v1/accounts/340af43a-8a7c-4659-9258-4876fd6a207b/projects/78d4459c-0f45-47a5-899a-45ddf43eba6e/clusters/5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8/backup-replication/gcp"),
292+
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseBackupReplication),
293+
),
294+
)
295+
296+
cmd := exec.Command(compiledCLIPath, "cluster", "backup-replication", "gcp", "describe",
297+
"--cluster-name", "stunning-sole",
298+
"--output", "pretty",
299+
"--show-all",
300+
)
301+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
302+
303+
Expect(err).NotTo(HaveOccurred())
304+
session.Wait(2)
305+
Expect(session.Out).Should(gbytes.Say(`"info"`))
306+
Expect(session.Out).Should(gbytes.Say(`"region_configs"`))
307+
Expect(session.Out).Should(gbytes.Say(`"state": "ENABLED"`))
308+
Expect(session.Out).Should(gbytes.Say(`"region": "us-west1"`))
309+
Expect(session.Out).Should(gbytes.Say(`"target": "backup-bucket-us-west1"`))
310+
Expect(session.Out).Should(gbytes.Say(`"region": "us-east1"`))
311+
Expect(session.Out).Should(gbytes.Say(`"target": "backup-bucket-us-east1"`))
312+
Expect(server.ReceivedRequests()).Should(HaveLen(4))
313+
session.Kill()
314+
})
315+
})
316+
})
317+
318+
AfterEach(func() {
319+
os.Args = args
320+
server.Close()
321+
})
322+
323+
})
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to Yugabyte, Inc. under one or more contributor license
2+
// agreements. See the NOTICE file distributed with this work for
3+
// additional information regarding copyright ownership. Yugabyte
4+
// licenses this file to you under the Apache License, Version 2.0
5+
// (the "License"); you may not use this file except in compliance
6+
// with the License. You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing,
10+
// software distributed under the License is distributed on an
11+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12+
// KIND, either express or implied. See the License for the
13+
// specific language governing permissions and limitations
14+
// under the License.
15+
16+
package backupreplication
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
"github.com/yugabyte/ybm-cli/cmd/cluster/backup-replication/gcp"
21+
)
22+
23+
var BackupReplicationCmd = &cobra.Command{
24+
Use: "backup-replication",
25+
Short: "Manage backup replication configuration for a cluster",
26+
Long: "Manage backup replication configuration for a cluster. Supports cloud provider GCP.",
27+
Run: func(cmd *cobra.Command, args []string) {
28+
cmd.Help()
29+
},
30+
}
31+
32+
func init() {
33+
BackupReplicationCmd.AddCommand(gcp.GcpCmd)
34+
}

0 commit comments

Comments
 (0)