go.etcd.io/etcd@v3.3.27+incompatible/etcdctl/ctlv3/command/ep_command.go (about) 1 // Copyright 2015 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package command 16 17 import ( 18 "fmt" 19 "os" 20 "sync" 21 "time" 22 23 v3 "github.com/coreos/etcd/clientv3" 24 "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes" 25 "github.com/coreos/etcd/pkg/flags" 26 27 "github.com/spf13/cobra" 28 ) 29 30 var epClusterEndpoints bool 31 var epHashKVRev int64 32 33 // NewEndpointCommand returns the cobra command for "endpoint". 34 func NewEndpointCommand() *cobra.Command { 35 ec := &cobra.Command{ 36 Use: "endpoint <subcommand>", 37 Short: "Endpoint related commands", 38 } 39 40 ec.PersistentFlags().BoolVar(&epClusterEndpoints, "cluster", false, "use all endpoints from the cluster member list") 41 ec.AddCommand(newEpHealthCommand()) 42 ec.AddCommand(newEpStatusCommand()) 43 ec.AddCommand(newEpHashKVCommand()) 44 45 return ec 46 } 47 48 func newEpHealthCommand() *cobra.Command { 49 cmd := &cobra.Command{ 50 Use: "health", 51 Short: "Checks the healthiness of endpoints specified in `--endpoints` flag", 52 Run: epHealthCommandFunc, 53 } 54 55 return cmd 56 } 57 58 func newEpStatusCommand() *cobra.Command { 59 return &cobra.Command{ 60 Use: "status", 61 Short: "Prints out the status of endpoints specified in `--endpoints` flag", 62 Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint. 63 The items in the lists are endpoint, ID, version, db size, is leader, raft term, raft index. 64 `, 65 Run: epStatusCommandFunc, 66 } 67 } 68 69 func newEpHashKVCommand() *cobra.Command { 70 hc := &cobra.Command{ 71 Use: "hashkv", 72 Short: "Prints the KV history hash for each endpoint in --endpoints", 73 Run: epHashKVCommandFunc, 74 } 75 hc.PersistentFlags().Int64Var(&epHashKVRev, "rev", 0, "maximum revision to hash (default: all revisions)") 76 return hc 77 } 78 79 type epHealth struct { 80 Ep string `json:"endpoint"` 81 Health bool `json:"health"` 82 Took string `json:"took"` 83 Error string `json:"error,omitempty"` 84 } 85 86 // epHealthCommandFunc executes the "endpoint-health" command. 87 func epHealthCommandFunc(cmd *cobra.Command, args []string) { 88 flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags()) 89 initDisplayFromCmd(cmd) 90 91 sec := secureCfgFromCmd(cmd) 92 dt := dialTimeoutFromCmd(cmd) 93 ka := keepAliveTimeFromCmd(cmd) 94 kat := keepAliveTimeoutFromCmd(cmd) 95 auth := authCfgFromCmd(cmd) 96 cfgs := []*v3.Config{} 97 for _, ep := range endpointsFromCluster(cmd) { 98 cfg, err := newClientCfg([]string{ep}, dt, ka, kat, sec, auth) 99 if err != nil { 100 ExitWithError(ExitBadArgs, err) 101 } 102 cfgs = append(cfgs, cfg) 103 } 104 105 var wg sync.WaitGroup 106 hch := make(chan epHealth, len(cfgs)) 107 for _, cfg := range cfgs { 108 wg.Add(1) 109 go func(cfg *v3.Config) { 110 defer wg.Done() 111 ep := cfg.Endpoints[0] 112 cli, err := v3.New(*cfg) 113 if err != nil { 114 hch <- epHealth{Ep: ep, Health: false, Error: err.Error()} 115 return 116 } 117 st := time.Now() 118 // get a random key. As long as we can get the response without an error, the 119 // endpoint is health. 120 ctx, cancel := commandCtx(cmd) 121 _, err = cli.Get(ctx, "health") 122 cancel() 123 eh := epHealth{Ep: ep, Health: false, Took: time.Since(st).String()} 124 // permission denied is OK since proposal goes through consensus to get it 125 if err == nil || err == rpctypes.ErrPermissionDenied { 126 eh.Health = true 127 } else { 128 eh.Error = err.Error() 129 } 130 hch <- eh 131 }(cfg) 132 } 133 134 wg.Wait() 135 close(hch) 136 137 errs := false 138 healthList := []epHealth{} 139 for h := range hch { 140 healthList = append(healthList, h) 141 if h.Error != "" { 142 errs = true 143 } 144 } 145 display.EndpointHealth(healthList) 146 if errs { 147 ExitWithError(ExitError, fmt.Errorf("unhealthy cluster")) 148 } 149 } 150 151 type epStatus struct { 152 Ep string `json:"Endpoint"` 153 Resp *v3.StatusResponse `json:"Status"` 154 } 155 156 func epStatusCommandFunc(cmd *cobra.Command, args []string) { 157 c := mustClientFromCmd(cmd) 158 159 statusList := []epStatus{} 160 var err error 161 for _, ep := range endpointsFromCluster(cmd) { 162 ctx, cancel := commandCtx(cmd) 163 resp, serr := c.Status(ctx, ep) 164 cancel() 165 if serr != nil { 166 err = serr 167 fmt.Fprintf(os.Stderr, "Failed to get the status of endpoint %s (%v)\n", ep, serr) 168 continue 169 } 170 statusList = append(statusList, epStatus{Ep: ep, Resp: resp}) 171 } 172 173 display.EndpointStatus(statusList) 174 175 if err != nil { 176 os.Exit(ExitError) 177 } 178 } 179 180 type epHashKV struct { 181 Ep string `json:"Endpoint"` 182 Resp *v3.HashKVResponse `json:"HashKV"` 183 } 184 185 func epHashKVCommandFunc(cmd *cobra.Command, args []string) { 186 c := mustClientFromCmd(cmd) 187 188 hashList := []epHashKV{} 189 var err error 190 for _, ep := range endpointsFromCluster(cmd) { 191 ctx, cancel := commandCtx(cmd) 192 resp, serr := c.HashKV(ctx, ep, epHashKVRev) 193 cancel() 194 if serr != nil { 195 err = serr 196 fmt.Fprintf(os.Stderr, "Failed to get the hash of endpoint %s (%v)\n", ep, serr) 197 continue 198 } 199 hashList = append(hashList, epHashKV{Ep: ep, Resp: resp}) 200 } 201 202 display.EndpointHashKV(hashList) 203 204 if err != nil { 205 ExitWithError(ExitError, err) 206 } 207 } 208 209 func endpointsFromCluster(cmd *cobra.Command) []string { 210 if !epClusterEndpoints { 211 endpoints, err := cmd.Flags().GetStringSlice("endpoints") 212 if err != nil { 213 ExitWithError(ExitError, err) 214 } 215 return endpoints 216 } 217 218 sec := secureCfgFromCmd(cmd) 219 dt := dialTimeoutFromCmd(cmd) 220 ka := keepAliveTimeFromCmd(cmd) 221 kat := keepAliveTimeoutFromCmd(cmd) 222 eps, err := endpointsFromCmd(cmd) 223 if err != nil { 224 ExitWithError(ExitError, err) 225 } 226 // exclude auth for not asking needless password (MemberList() doesn't need authentication) 227 228 cfg, err := newClientCfg(eps, dt, ka, kat, sec, nil) 229 if err != nil { 230 ExitWithError(ExitError, err) 231 } 232 c, err := v3.New(*cfg) 233 if err != nil { 234 ExitWithError(ExitError, err) 235 } 236 237 ctx, cancel := commandCtx(cmd) 238 defer func() { 239 c.Close() 240 cancel() 241 }() 242 membs, err := c.MemberList(ctx) 243 if err != nil { 244 err = fmt.Errorf("failed to fetch endpoints from etcd cluster member list: %v", err) 245 ExitWithError(ExitError, err) 246 } 247 248 ret := []string{} 249 for _, m := range membs.Members { 250 ret = append(ret, m.ClientURLs...) 251 } 252 return ret 253 }