Skip to content

Commit

Permalink
Support update and switchover DR (#260)
Browse files Browse the repository at this point in the history
  • Loading branch information
posriniv authored Oct 22, 2024
1 parent 417f2a0 commit 19b59de
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 7 deletions.
13 changes: 8 additions & 5 deletions cmd/cluster/dr/create_dr.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package dr
import (
"fmt"
"os"
"strings"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -59,11 +60,13 @@ var createDrCmd = &cobra.Command{
dbNameToIdMap[namespace.GetName()] = namespace.GetId()
}
databaseIds := []string{}
for _, database := range databases {
if databaseId, exists := dbNameToIdMap[database]; exists {
databaseIds = append(databaseIds, databaseId)
} else {
logrus.Fatalf("The database %s doesn't exist", database)
for _, databaseString := range databases {
for _, database := range strings.Split(databaseString, ",") {
if databaseId, exists := dbNameToIdMap[database]; exists {
databaseIds = append(databaseIds, databaseId)
} else {
logrus.Fatalf("The database %s doesn't exist", database)
}
}
}
createDrRequest := ybmclient.NewCreateXClusterDrRequest(*ybmclient.NewXClusterDrSpec(drName, targetClusterId, databaseIds))
Expand Down
4 changes: 2 additions & 2 deletions cmd/cluster/dr/dr.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ var ClusterName string

var DrCmd = &cobra.Command{
Use: "dr",
Short: "Create DR for a cluster.",
Long: "Create DR for a cluster.",
Short: "Manage DR for a cluster.",
Long: "Manage DR for a cluster.",
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
Expand Down
91 changes: 91 additions & 0 deletions cmd/cluster/dr/switchover_dr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Licensed to Yugabyte, Inc. under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership. Yugabyte
// licenses this file to you 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.

package dr

import (
"fmt"
"os"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
ybmAuthClient "github.com/yugabyte/ybm-cli/internal/client"
"github.com/yugabyte/ybm-cli/internal/formatter"
ybmclient "github.com/yugabyte/yugabytedb-managed-go-client-internal"
)

var switchoverDrCmd = &cobra.Command{
Use: "switchover",
Short: "Switchover DR for a cluster",
Long: `Switchover DR for a cluster`,
Run: func(cmd *cobra.Command, args []string) {
authApi, err := ybmAuthClient.NewAuthApiClient()
if err != nil {
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}
authApi.GetInfo("", "")

drName, _ := cmd.Flags().GetString("dr-name")
clusterId, err := authApi.GetClusterIdByName(ClusterName)
if err != nil {
logrus.Fatalf("Could not get cluster data: %s", ybmAuthClient.GetApiErrorDetails(err))
}
drId, err := authApi.GetDrIdByName(clusterId, drName)
if err != nil {
logrus.Fatal(err)
}

response, err := authApi.SwitchoverXClusterDr(clusterId, drId).Execute()
if err != nil {
logrus.Debugf("Full HTTP response: %v", response)
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}

msg := fmt.Sprintf("Switchover is in progress for the DR %s ", formatter.Colorize(drName, formatter.GREEN_COLOR))

if viper.GetBool("wait") {
returnStatus, err := authApi.WaitForTaskCompletion(clusterId, ybmclient.ENTITYTYPEENUM_CLUSTER, ybmclient.TASKTYPEENUM_DR_SWITCHOVER, []string{"FAILED", "SUCCEEDED"}, msg)
if err != nil {
logrus.Fatalf("error when getting task status: %s", err)
}
if returnStatus != "SUCCEEDED" {
logrus.Fatalf("Operation failed with error: %s", returnStatus)
}
fmt.Printf("Switchover for DR config %s is successful\n", formatter.Colorize(drName, formatter.GREEN_COLOR))

drGetResp, r, err := authApi.GetXClusterDr(clusterId, drId).Execute()
if err != nil {
logrus.Debugf("Full HTTP response: %v", r)
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}
drCtx := formatter.Context{
Output: os.Stdout,
Format: formatter.NewDrFormat(viper.GetString("output")),
}

formatter.DrWrite(drCtx, []ybmclient.XClusterDrData{drGetResp.GetData()}, *authApi)
} else {
fmt.Println(msg)
}

},
}

func init() {
DrCmd.AddCommand(switchoverDrCmd)
switchoverDrCmd.Flags().String("dr-name", "", "[REQUIRED] Name of the DR configuration.")
switchoverDrCmd.MarkFlagRequired("dr-name")
}
117 changes: 117 additions & 0 deletions cmd/cluster/dr/update_dr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Licensed to Yugabyte, Inc. under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership. Yugabyte
// licenses this file to you 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.

package dr

import (
"fmt"
"os"
"strings"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
ybmAuthClient "github.com/yugabyte/ybm-cli/internal/client"
"github.com/yugabyte/ybm-cli/internal/formatter"
ybmclient "github.com/yugabyte/yugabytedb-managed-go-client-internal"
)

var updateDrCmd = &cobra.Command{
Use: "update",
Short: "Update DR for a cluster",
Long: `Update DR for a cluster`,
Run: func(cmd *cobra.Command, args []string) {
authApi, err := ybmAuthClient.NewAuthApiClient()
if err != nil {
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}
authApi.GetInfo("", "")

drName, _ := cmd.Flags().GetString("dr-name")
databases, _ := cmd.Flags().GetStringArray("databases")
clusterId, err := authApi.GetClusterIdByName(ClusterName)
if err != nil {
logrus.Fatalf("Could not get cluster data: %s", ybmAuthClient.GetApiErrorDetails(err))
}
drId, err := authApi.GetDrIdByName(clusterId, drName)
if err != nil {
logrus.Fatal(err)
}
namespacesResp, r, err := authApi.GetClusterNamespaces(clusterId).Execute()
if err != nil {
logrus.Debugf("Full HTTP response: %v", r)
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}
dbNameToIdMap := map[string]string{}
for _, namespace := range namespacesResp.Data {
dbNameToIdMap[namespace.GetName()] = namespace.GetId()
}
databaseIds := []string{}
for _, databaseString := range databases {
for _, database := range strings.Split(databaseString, ",") {
if databaseId, exists := dbNameToIdMap[database]; exists {
databaseIds = append(databaseIds, databaseId)
} else {
logrus.Fatalf("The database %s doesn't exist", database)
}
}
}

updateDrRequest := ybmclient.NewEditXClusterDrRequest(*ybmclient.NewEditXClusterDrSpec(databaseIds))
drResp, response, err := authApi.EditXClusterDr(clusterId, drId).EditXClusterDrRequest(*updateDrRequest).Execute()
if err != nil {
logrus.Debugf("Full HTTP response: %v", response)
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}

msg := fmt.Sprintf("DR config %s is being updated", formatter.Colorize(drName, formatter.GREEN_COLOR))

if viper.GetBool("wait") {
returnStatus, err := authApi.WaitForTaskCompletion(clusterId, ybmclient.ENTITYTYPEENUM_CLUSTER, ybmclient.TASKTYPEENUM_EDIT_DR, []string{"FAILED", "SUCCEEDED"}, msg)
if err != nil {
logrus.Fatalf("error when getting task status: %s", err)
}
if returnStatus != "SUCCEEDED" {
logrus.Fatalf("Operation failed with error: %s", returnStatus)
}
fmt.Printf("DR config %s has been updated\n", formatter.Colorize(drName, formatter.GREEN_COLOR))

drGetResp, r, err := authApi.GetXClusterDr(clusterId, drId).Execute()
if err != nil {
logrus.Debugf("Full HTTP response: %v", r)
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}
drResp = drGetResp
} else {
fmt.Println(msg)
}

drCtx := formatter.Context{
Output: os.Stdout,
Format: formatter.NewDrFormat(viper.GetString("output")),
}

formatter.DrWrite(drCtx, []ybmclient.XClusterDrData{drResp.GetData()}, *authApi)

},
}

func init() {
DrCmd.AddCommand(updateDrCmd)
updateDrCmd.Flags().String("dr-name", "", "[REQUIRED] Name of the DR configuration.")
updateDrCmd.MarkFlagRequired("dr-name")
updateDrCmd.Flags().StringArray("databases", []string{}, "[REQUIRED] Databases to be replicated.")
updateDrCmd.MarkFlagRequired("databases")
}
2 changes: 2 additions & 0 deletions cmd/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ var _ = Describe("Cluster", func() {
})
Context("with a valid Api token and default output table", func() {
It("should return list of cluster", func() {
os.Setenv("YBM_FF_CONNECTION_POOLING", "true")
cmd := exec.Command(compiledCLIPath, "cluster", "list")
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())
Expand All @@ -218,6 +219,7 @@ stunning-sole Dedicated 2.16.0.1-b7 ACTIVE 💚 AWS us-we
})

It("should return detailed summary of cluster if cluster-name is specified", func() {
os.Setenv("YBM_FF_CONNECTION_POOLING", "true")
statusCode = 200
err := loadJson("./test/fixtures/allow-list.json", &responseNetworkAllowList)
Expect(err).ToNot(HaveOccurred())
Expand Down
8 changes: 8 additions & 0 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1674,6 +1674,14 @@ func (a *AuthApiClient) CreateXClusterDr(clusterId string) ybmclient.ApiCreateXC
return a.ApiClient.XclusterDrApi.CreateXClusterDr(a.ctx, a.AccountID, a.ProjectID, clusterId)
}

func (a *AuthApiClient) EditXClusterDr(clusterId string, drId string) ybmclient.ApiEditXClusterDrRequest {
return a.ApiClient.XclusterDrApi.EditXClusterDr(a.ctx, a.AccountID, a.ProjectID, clusterId, drId)
}

func (a *AuthApiClient) SwitchoverXClusterDr(clusterId string, drId string) ybmclient.ApiSwitchoverRequest {
return a.ApiClient.XclusterDrApi.Switchover(a.ctx, a.AccountID, a.ProjectID, clusterId, drId)
}

func (a *AuthApiClient) GetXClusterDr(clusterId string, drId string) ybmclient.ApiGetXClusterDrRequest {
return a.ApiClient.XclusterDrApi.GetXClusterDr(a.ctx, a.AccountID, a.ProjectID, clusterId, drId)
}
Expand Down

0 comments on commit 19b59de

Please sign in to comment.