github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/ready.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 "time" 23 24 "github.com/fatih/color" 25 "github.com/minio/cli" 26 json "github.com/minio/colorjson" 27 "github.com/minio/madmin-go/v3" 28 "github.com/minio/mc/pkg/probe" 29 ) 30 31 const ( 32 healthCheckInterval = 5 * time.Second 33 ) 34 35 var readyFlags = []cli.Flag{ 36 cli.BoolFlag{ 37 Name: "cluster-read", 38 Usage: "check if the cluster has enough read quorum", 39 }, 40 cli.BoolFlag{ 41 Name: "maintenance", 42 Usage: "check if the cluster is taken down for maintenance", 43 }, 44 } 45 46 // Checks if the cluster is ready or not 47 var readyCmd = cli.Command{ 48 Name: "ready", 49 Usage: "checks if the cluster is ready or not", 50 Action: mainReady, 51 OnUsageError: onUsageError, 52 Before: setGlobalsFromContext, 53 Flags: append(readyFlags, globalFlags...), 54 CustomHelpTemplate: `NAME: 55 {{.HelpName}} - {{.Usage}} 56 57 USAGE: 58 {{.HelpName}} [FLAGS] TARGET 59 {{if .VisibleFlags}} 60 FLAGS: 61 {{range .VisibleFlags}}{{.}} 62 {{end}}{{end}} 63 EXAMPLES: 64 1. Check if the cluster is ready or not 65 {{.Prompt}} {{.HelpName}} myminio 66 67 2. Check if the cluster has enough read quorum 68 {{.Prompt}} {{.HelpName}} myminio --cluster-read 69 70 3. Check if the cluster is taken down for maintenance 71 {{.Prompt}} {{.HelpName}} myminio --maintenance 72 `, 73 } 74 75 type readyMessage struct { 76 Healthy bool `json:"healthy"` 77 MaintenanceMode bool `json:"maintenanceMode"` 78 WriteQuorum int `json:"writeQuorum"` 79 HealingDrives int `json:"healingDrives"` 80 81 Err error `json:"error"` 82 } 83 84 func (r readyMessage) String() string { 85 switch { 86 case r.Healthy: 87 return color.GreenString("The cluster is ready") 88 case r.Err != nil: 89 return color.RedString("The cluster is unreachable: " + r.Err.Error()) 90 default: 91 return color.RedString("The cluster is not ready") 92 } 93 } 94 95 // JSON jsonified ready result 96 func (r readyMessage) JSON() string { 97 jsonMessageBytes, e := json.MarshalIndent(r, "", " ") 98 fatalIf(probe.NewError(e), "Unable to marshal into JSON.") 99 return string(jsonMessageBytes) 100 } 101 102 // mainReady - main handler for mc ready command. 103 func mainReady(cliCtx *cli.Context) error { 104 if !cliCtx.Args().Present() { 105 exitCode := 1 106 showCommandHelpAndExit(cliCtx, exitCode) 107 } 108 109 // Set command flags from context. 110 clusterRead := cliCtx.Bool("cluster-read") 111 maintenance := cliCtx.Bool("maintenance") 112 113 ctx, cancelClusterReady := context.WithCancel(globalContext) 114 defer cancelClusterReady() 115 aliasedURL := cliCtx.Args().Get(0) 116 117 anonClient, err := newAnonymousClient(aliasedURL) 118 fatalIf(err.Trace(aliasedURL), "Couldn't construct anonymous client for `"+aliasedURL+"`.") 119 120 healthOpts := madmin.HealthOpts{ 121 ClusterRead: clusterRead, 122 Maintenance: maintenance, 123 } 124 125 timer := time.NewTimer(0) 126 defer timer.Stop() 127 128 for { 129 select { 130 case <-ctx.Done(): 131 return nil 132 case <-timer.C: 133 healthResult, hErr := anonClient.Healthy(ctx, healthOpts) 134 printMsg(readyMessage{ 135 Healthy: healthResult.Healthy, 136 MaintenanceMode: healthResult.MaintenanceMode, 137 WriteQuorum: healthResult.WriteQuorum, 138 HealingDrives: healthResult.HealingDrives, 139 Err: hErr, 140 }) 141 if healthResult.Healthy { 142 return nil 143 } 144 timer.Reset(healthCheckInterval) 145 } 146 } 147 }