Skip to content

Commit

Permalink
Support Failover, Restart, Pause and Resume
Browse files Browse the repository at this point in the history
  • Loading branch information
posriniv committed Oct 22, 2024
1 parent 19b59de commit a0b1fd8
Show file tree
Hide file tree
Showing 5 changed files with 447 additions and 0 deletions.
129 changes: 129 additions & 0 deletions cmd/cluster/dr/failover_dr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// 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"
"strconv"
"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 failoverDrCmd = &cobra.Command{
Use: "failover",
Short: "Failover DR for a cluster",
Long: `Failover 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")
safetimes, _ := cmd.Flags().GetStringArray("safetimes")
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()
}

safetimesMap := map[string]int64{}
for _, safetimesString := range safetimes {
for _, safetime := range strings.Split(safetimesString, ",") {
kvp := strings.Split(safetime, "=")
if len(kvp) != 2 {
logrus.Fatalln("Incorrect format in safetime")
}
database := kvp[0]
if databaseId, exists := dbNameToIdMap[database]; exists {
safetimeInMinString := kvp[1]
safetimeInMin, err := strconv.Atoi(safetimeInMinString)
if err != nil {
logrus.Fatalln("Error:", err)
}
safetimesMap[databaseId] = int64(safetimeInMin)
} else {
logrus.Fatalf("The database %s doesn't exist", database)
}
}
}

drFailoverRequest := ybmclient.NewDrFailoverRequestWithDefaults()
if len(safetimes) != 0 {
drFailoverRequest.SetNamespaceSafeTimes(safetimesMap)
}
response, err := authApi.FailoverXClusterDr(clusterId, drId).DrFailoverRequest(*drFailoverRequest).Execute()
if err != nil {
logrus.Debugf("Full HTTP response: %v", response)
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}

msg := fmt.Sprintf("Failover 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_FAILOVER, []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("Failover 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(failoverDrCmd)
failoverDrCmd.Flags().String("dr-name", "", "[REQUIRED] Name of the DR configuration.")
failoverDrCmd.MarkFlagRequired("dr-name")
failoverDrCmd.Flags().StringArray("safetimes", []string{}, "[OPTIONAL] Safetimes of the DR configuation. Please provide key value pairs <db-name-1>=<safe-time-in-min>,<db-name-2>=<safetime-in-min>.")
}
95 changes: 95 additions & 0 deletions cmd/cluster/dr/pause_dr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// 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 pauseDrCmd = &cobra.Command{
Use: "pause",
Short: "Pause DR for a cluster",
Long: `Pause 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")
durationInMin, _ := cmd.Flags().GetInt32("duration")
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)
}

pauseDrRequest := ybmclient.NewPauseDrRequestWithDefaults()
pauseDrRequest.SetDurationMinutes(durationInMin)
response, err := authApi.PauseXClusterDr(clusterId, drId).PauseDrRequest(*pauseDrRequest).Execute()
if err != nil {
logrus.Debugf("Full HTTP response: %v", response)
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}

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

if viper.GetBool("wait") {
returnStatus, err := authApi.WaitForTaskCompletion(clusterId, ybmclient.ENTITYTYPEENUM_CLUSTER, ybmclient.TASKTYPEENUM_DR_PAUSE, []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 is paused successfully\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(pauseDrCmd)
pauseDrCmd.Flags().String("dr-name", "", "[REQUIRED] Name of the DR configuration.")
pauseDrCmd.MarkFlagRequired("dr-name")
pauseDrCmd.Flags().Int32("duration", 60, "[OPTIONAL] Duration in minutes.")
}
116 changes: 116 additions & 0 deletions cmd/cluster/dr/restart_dr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// 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 restartDrCmd = &cobra.Command{
Use: "restart",
Short: "Restart DR for a cluster",
Long: `Restart 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)
}
}
}
restartDrRequest := ybmclient.NewDrRestartRequestWithDefaults()
if len(databaseIds) != 0 {
restartDrRequest.SetDatabaseIds(databaseIds)
}
response, err := authApi.RestartXClusterDr(clusterId, drId).DrRestartRequest(*restartDrRequest).Execute()
if err != nil {
logrus.Debugf("Full HTTP response: %v", response)
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}

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

if viper.GetBool("wait") {
returnStatus, err := authApi.WaitForTaskCompletion(clusterId, ybmclient.ENTITYTYPEENUM_CLUSTER, ybmclient.TASKTYPEENUM_DR_RESTART, []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 is restartd successfully\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(restartDrCmd)
restartDrCmd.Flags().String("dr-name", "", "[REQUIRED] Name of the DR configuration.")
restartDrCmd.MarkFlagRequired("dr-name")
restartDrCmd.Flags().StringArray("databases", []string{}, "[OPTIONAL] Databases to be restarted.")
}
Loading

0 comments on commit a0b1fd8

Please sign in to comment.