github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/replicate-reset-status.go (about) 1 // Copyright (c) 2015-2022 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "context" 22 "fmt" 23 24 humanize "github.com/dustin/go-humanize" 25 "github.com/fatih/color" 26 "github.com/minio/cli" 27 json "github.com/minio/colorjson" 28 "github.com/minio/mc/pkg/probe" 29 "github.com/minio/minio-go/v7/pkg/replication" 30 "github.com/minio/pkg/v2/console" 31 ) 32 33 var replicateResyncStatusFlags = []cli.Flag{ 34 cli.StringFlag{ 35 Name: "remote-bucket", 36 Usage: "remote bucket ARN", 37 }, 38 } 39 40 var replicateResyncStatusCmd = cli.Command{ 41 Name: "status", 42 Usage: "status of replication recovery", 43 Action: mainreplicateResyncStatus, 44 OnUsageError: onUsageError, 45 Before: setGlobalsFromContext, 46 Flags: append(globalFlags, replicateResyncStatusFlags...), 47 CustomHelpTemplate: `NAME: 48 {{.HelpName}} - {{.Usage}} 49 50 USAGE: 51 {{.HelpName}} TARGET 52 53 FLAGS: 54 {{range .VisibleFlags}}{{.}} 55 {{end}} 56 EXAMPLES: 57 1. Status of replication resync in bucket "mybucket" under alias "myminio" for all targets. 58 {{.Prompt}} {{.HelpName}} myminio/mybucket 59 60 2. Status of replication resync in bucket "mybucket" under specific remote bucket target. 61 {{.Prompt}} {{.HelpName}} myminio/mybucket --remote-bucket "arn:minio:replication::xxx:mybucket" 62 `, 63 } 64 65 // checkreplicateResyncStatusSyntax - validate all the passed arguments 66 func checkreplicateResyncStatusSyntax(ctx *cli.Context) { 67 if len(ctx.Args()) != 1 { 68 showCommandHelpAndExit(ctx, 1) // last argument is exit code 69 } 70 } 71 72 type replicateResyncStatusMessage struct { 73 Op string `json:"op"` 74 URL string `json:"url"` 75 ResyncTargetsInfo replication.ResyncTargetsInfo `json:"resyncInfo"` 76 Status string `json:"status"` 77 TargetArn string `json:"targetArn"` 78 } 79 80 func (r replicateResyncStatusMessage) JSON() string { 81 r.Status = "success" 82 jsonMessageBytes, e := json.MarshalIndent(r, "", " ") 83 fatalIf(probe.NewError(e), "Unable to marshal into JSON.") 84 return string(jsonMessageBytes) 85 } 86 87 func (r replicateResyncStatusMessage) String() string { 88 if len(r.ResyncTargetsInfo.Targets) == 0 { 89 return console.Colorize("replicateResyncStatusWarn", "No replication resync status available.") 90 } 91 coloredDot := console.Colorize("Headers", dot) 92 var rows string 93 rows += console.Colorize("TDetail", "Resync status summary:") 94 95 for _, st := range r.ResyncTargetsInfo.Targets { 96 rows += "\n" 97 rows += console.Colorize("replicateResyncStatusMsg", newPrettyTable(" | ", 98 Field{"ARN", 120}, 99 ).buildRow(fmt.Sprintf("%s %s", coloredDot, st.Arn))) 100 rows += "\n" 101 rows += console.Colorize("TDetail", " Status: ") 102 rows += console.Colorize(st.ResyncStatus, st.ResyncStatus) 103 rows += "\n" 104 105 maxLen := 15 106 theme := []string{"Replicated", "Failed"} 107 rows += console.Colorize("THeaders", newPrettyTable(" | ", 108 Field{"Status", 21}, 109 Field{"Size", maxLen}, 110 Field{"Count", maxLen}, 111 ).buildRow(" Replication Status", "Size (Bytes)", "Count")) 112 rows += "\n" 113 rows += console.Colorize(theme[0], newPrettyTable(" | ", 114 Field{"Status", 21}, 115 Field{"Size", maxLen}, 116 Field{"Count", maxLen}, 117 ).buildRow(" Replicated", humanize.IBytes(uint64(st.ReplicatedSize)), humanize.Comma(int64(st.ReplicatedCount)))) 118 rows += "\n" 119 rows += console.Colorize(theme[0], newPrettyTable(" | ", 120 Field{"Status", 21}, 121 Field{"Size", maxLen}, 122 Field{"Count", maxLen}, 123 ).buildRow(" Failed", humanize.IBytes(uint64(st.FailedSize)), humanize.Comma(int64(st.FailedCount)))) 124 rows += "\n" 125 } 126 return rows 127 } 128 129 func mainreplicateResyncStatus(cliCtx *cli.Context) error { 130 ctx, cancelreplicateResyncStatus := context.WithCancel(globalContext) 131 defer cancelreplicateResyncStatus() 132 133 console.SetColor("replicateResyncStatusWarn", color.New(color.FgHiYellow)) 134 console.SetColor("replicateResyncStatusMsg", color.New(color.FgGreen)) 135 console.SetColor("Headers", color.New(color.FgGreen, color.Bold)) 136 console.SetColor("THeaders", color.New(color.Bold, color.FgCyan)) 137 138 console.SetColor("TDetail", color.New(color.FgWhite, color.Bold)) 139 console.SetColor("Ongoing", color.New(color.Bold, color.FgYellow)) 140 console.SetColor("Failed", color.New(color.Bold, color.FgRed)) 141 console.SetColor("Completed", color.New(color.Bold, color.FgGreen)) 142 143 checkreplicateResyncStatusSyntax(cliCtx) 144 145 // Get the alias parameter from cli 146 args := cliCtx.Args() 147 aliasedURL := args.Get(0) 148 // Create a new Client 149 client, err := newClient(aliasedURL) 150 fatalIf(err, "Unable to initialize connection.") 151 152 rinfo, err := client.ReplicationResyncStatus(ctx, cliCtx.String("remote-bucket")) 153 fatalIf(err.Trace(args...), "Unable to get replication resync status") 154 printMsg(replicateResyncStatusMessage{ 155 Op: cliCtx.Command.Name, 156 URL: aliasedURL, 157 ResyncTargetsInfo: rinfo, 158 TargetArn: cliCtx.String("remote-bucket"), 159 }) 160 return nil 161 }