Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add admin failover for managed domains #3154

Merged
merged 7 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions tools/cli/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,20 @@ func newAdminClusterCommands() []cli.Command {
AdminDescribeCluster(c)
},
},
{
Name: "failover",
Aliases: []string{"fo"},
Usage: "Failover domains with domain data IsManagedByCadence=true to target cluster",
Flags: []cli.Flag{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this command should only be executed by cadence team. Should the command also takes in a security token and verify its content before actually execution?

Just an idea to explore. I understand this is not easy as the underlying domain failover command doesn't require security token.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. But security token on open sourced repo is merely support on server API.
Internally we will be protected by the auth feature.

cli.StringFlag{
Name: FlagActiveClusterNameWithAlias,
Usage: "Target active cluster name",
},
},
Action: func(c *cli.Context) {
newDomainCLI(c, false).FailoverDomains(c)
},
},
}
}

Expand Down
32 changes: 32 additions & 0 deletions tools/cli/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,38 @@ func (s *cliAppSuite) TestAdminAddSearchAttribute() {
s.Nil(err)
}

func (s *cliAppSuite) TestAdminFailover() {
var listDomainsResponse = &serverShared.ListDomainsResponse{
Domains: []*serverShared.DescribeDomainResponse{
{
DomainInfo: &serverShared.DomainInfo{
Name: common.StringPtr("test-domain"),
Description: common.StringPtr("a test domain"),
OwnerEmail: common.StringPtr("[email protected]"),
Data: map[string]string{
managedFailoverDomainData: "true",
},
},
ReplicationConfiguration: &serverShared.DomainReplicationConfiguration{
ActiveClusterName: common.StringPtr("active"),
Clusters: []*serverShared.ClusterReplicationConfiguration{
{
ClusterName: common.StringPtr("active"),
},
{
ClusterName: common.StringPtr("standby"),
},
},
},
},
},
}
s.serverFrontendClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return(listDomainsResponse, nil).Times(1)
s.serverFrontendClient.EXPECT().UpdateDomain(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
err := s.app.Run([]string{"", "admin", "cl", "fo", "--ac", "standby"})
s.Nil(err)
}

func (s *cliAppSuite) TestDescribeTaskList() {
resp := describeTaskListResponse
s.clientFrontendClient.EXPECT().DescribeTaskList(gomock.Any(), gomock.Any(), callOptions...).Return(resp, nil)
Expand Down
2 changes: 2 additions & 0 deletions tools/cli/defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ const (
showErrorStackEnv = `CADENCE_CLI_SHOW_STACKS`

searchAttrInputSeparator = "|"

managedFailoverDomainData = "IsManagedByCadence"
yycptt marked this conversation as resolved.
Show resolved Hide resolved
)

var envKeysForUserName = []string{
Expand Down
79 changes: 79 additions & 0 deletions tools/cli/domainCommands.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"fmt"
"os"
"strconv"
"strings"
"time"

"github.com/olekukonko/tablewriter"
Expand Down Expand Up @@ -282,6 +283,72 @@ func (d *domainCLIImpl) UpdateDomain(c *cli.Context) {
}
}

// FailoverDomains is used for managed failover all domains with domain data IsManagedByCadence=true
func (d *domainCLIImpl) FailoverDomains(c *cli.Context) {
targetCluster := getRequiredOption(c, FlagActiveClusterName)

domains := d.getAllDomains()
shouldFailover := func(domain *shared.DescribeDomainResponse) bool {
isDomainNotActiveInTargetCluster := domain.ReplicationConfiguration.GetActiveClusterName() != targetCluster
return isDomainNotActiveInTargetCluster && isDomainFailoverManagedByCadence(domain)
}
var succeedDomains []string
var failedDomains []string
for _, domain := range domains {
yycptt marked this conversation as resolved.
Show resolved Hide resolved
if shouldFailover(domain) {
domainName := domain.GetDomainInfo().GetName()
err := d.failover(c, domainName, targetCluster)
if err != nil {
printError(fmt.Sprintf("Failed failover domain: %s\n", domainName), err)
failedDomains = append(failedDomains, domainName)
} else {
fmt.Printf("Success failover domain: %s\n", domainName)
succeedDomains = append(succeedDomains, domainName)
}
}
}
fmt.Printf("Succeed %d: %v\n", len(succeedDomains), succeedDomains)
fmt.Printf("Failed %d: %v\n", len(failedDomains), failedDomains)
}

func (d *domainCLIImpl) getAllDomains() []*shared.DescribeDomainResponse {
var res []*shared.DescribeDomainResponse
pagesize := int32(200)
var token []byte
for more := true; more; more = len(token) > 0 {
listRequest := &shared.ListDomainsRequest{
PageSize: common.Int32Ptr(pagesize),
NextPageToken: token,
}
listResp, err := d.listDomains(context.Background(), listRequest)
yycptt marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
ErrorAndExit("Error when list domains info", err)
}
token = listResp.GetNextPageToken()
res = append(res, listResp.GetDomains()...)
}
return res
}

func isDomainFailoverManagedByCadence(domain *shared.DescribeDomainResponse) bool {
domainData := domain.DomainInfo.GetData()
return strings.ToLower(strings.TrimSpace(domainData[managedFailoverDomainData])) == "true"
}

func (d *domainCLIImpl) failover(c *cli.Context, domainName string, targetCluster string) error {
replicationConfig := &shared.DomainReplicationConfiguration{
ActiveClusterName: common.StringPtr(targetCluster),
}
updateRequest := &shared.UpdateDomainRequest{
Name: common.StringPtr(domainName),
ReplicationConfiguration: replicationConfig,
}
ctx, cancel := newContext(c)
defer cancel()
_, err := d.updateDomain(ctx, updateRequest)
return err
}

// DescribeDomain updates a domain
func (d *domainCLIImpl) DescribeDomain(c *cli.Context) {
domainName := c.GlobalString(FlagDomain)
Expand Down Expand Up @@ -349,6 +416,18 @@ func (d *domainCLIImpl) DescribeDomain(c *cli.Context) {
}
}

func (d *domainCLIImpl) listDomains(
ctx context.Context,
request *shared.ListDomainsRequest,
) (*shared.ListDomainsResponse, error) {

if d.frontendClient != nil {
return d.frontendClient.ListDomains(ctx, request)
}

return d.domainHandler.ListDomains(ctx, request)
}

func (d *domainCLIImpl) registerDomain(
ctx context.Context,
request *shared.RegisterDomainRequest,
Expand Down
8 changes: 6 additions & 2 deletions tools/cli/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,8 +509,7 @@ func mapKeysToArray(m map[string]string) []string {
return out
}

// ErrorAndExit print easy to understand error msg first then error detail in a new line
func ErrorAndExit(msg string, err error) {
func printError(msg string, err error) {
if err != nil {
fmt.Printf("%s %s\n%s %+v\n", colorRed("Error:"), msg, colorMagenta("Error Details:"), err)
if os.Getenv(showErrorStackEnv) != `` {
Expand All @@ -522,6 +521,11 @@ func ErrorAndExit(msg string, err error) {
} else {
fmt.Printf("%s %s\n", colorRed("Error:"), msg)
}
}

// ErrorAndExit print easy to understand error msg first then error detail in a new line
func ErrorAndExit(msg string, err error) {
printError(msg, err)
osExit(1)
}

Expand Down