vitess.io/vitess@v0.16.2/go/cmd/vtctldclient/command/validate.go (about) 1 /* 2 Copyright 2021 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package command 18 19 import ( 20 "errors" 21 "fmt" 22 "strings" 23 24 "github.com/spf13/cobra" 25 26 "vitess.io/vitess/go/cmd/vtctldclient/cli" 27 "vitess.io/vitess/go/vt/topo/topoproto" 28 29 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 30 ) 31 32 var ( 33 // Validate makes a Validate gRPC call to a vtctld. 34 Validate = &cobra.Command{ 35 Use: "Validate [--ping-tablets]", 36 Short: "Validates that all nodes reachable from the global replication graph, as well as all tablets in discoverable cells, are consistent.", 37 DisableFlagsInUseLine: true, 38 Args: cobra.NoArgs, 39 RunE: commandValidate, 40 } 41 // ValidateKeyspace makes a ValidateKeyspace gRPC call to a vtctld. 42 ValidateKeyspace = &cobra.Command{ 43 Use: "ValidateKeyspace [--ping-tablets] <keyspace>", 44 Short: "Validates that all nodes reachable from the specified keyspace are consistent.", 45 DisableFlagsInUseLine: true, 46 Args: cobra.ExactArgs(1), 47 RunE: commandValidateKeyspace, 48 } 49 // ValidateShard makes a ValidateShard gRPC call to a vtctld. 50 ValidateShard = &cobra.Command{ 51 Use: "ValidateShard [--ping-tablets] <keyspace/shard>", 52 Short: "Validates that all nodes reachable from the specified shard are consistent.", 53 DisableFlagsInUseLine: true, 54 Args: cobra.ExactArgs(1), 55 RunE: commandValidateShard, 56 } 57 ) 58 59 var validateOptions = struct { 60 PingTablets bool 61 }{} 62 63 func commandValidate(cmd *cobra.Command, args []string) error { 64 cli.FinishedParsing(cmd) 65 66 resp, err := client.Validate(commandCtx, &vtctldatapb.ValidateRequest{ 67 PingTablets: validateOptions.PingTablets, 68 }) 69 if err != nil { 70 return err 71 } 72 73 buf := &strings.Builder{} 74 if err := consumeValidationResults(resp, buf); err != nil { 75 fmt.Printf("Validation results:\n%s", buf.String() /* note: this should have a trailing newline already */) 76 return err 77 } 78 79 fmt.Println("Validation complete; no issues found.") 80 return nil 81 } 82 83 var validateKeyspaceOptions = struct { 84 PingTablets bool 85 }{} 86 87 func commandValidateKeyspace(cmd *cobra.Command, args []string) error { 88 cli.FinishedParsing(cmd) 89 90 keyspace := cmd.Flags().Arg(0) 91 resp, err := client.ValidateKeyspace(commandCtx, &vtctldatapb.ValidateKeyspaceRequest{ 92 Keyspace: keyspace, 93 PingTablets: validateKeyspaceOptions.PingTablets, 94 }) 95 if err != nil { 96 return err 97 } 98 99 buf := &strings.Builder{} 100 if err := consumeKeyspaceValidationResults(keyspace, resp, buf); err != nil { 101 fmt.Printf("Validation results:\n%s", buf.String() /* note: this should have a trailing newline already */) 102 return err 103 } 104 105 fmt.Printf("Validation of %s complete; no issues found.\n", keyspace) 106 return nil 107 } 108 109 var validateShardOptions = struct { 110 PingTablets bool 111 }{} 112 113 func commandValidateShard(cmd *cobra.Command, args []string) error { 114 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 115 if err != nil { 116 return fmt.Errorf("could not parse <keyspace/shard> from %s: %w", cmd.Flags().Arg(0), err) 117 } 118 119 cli.FinishedParsing(cmd) 120 121 resp, err := client.ValidateShard(commandCtx, &vtctldatapb.ValidateShardRequest{ 122 Keyspace: keyspace, 123 Shard: shard, 124 PingTablets: validateShardOptions.PingTablets, 125 }) 126 if err != nil { 127 return err 128 } 129 130 buf := &strings.Builder{} 131 if err := consumeShardValidationResults(keyspace, shard, resp, buf); err != nil { 132 fmt.Printf("Validation results:\n%s", buf.String() /* note: this should have a trailing newline already */) 133 return err 134 } 135 136 fmt.Printf("Validation of %s/%s complete; no issues found.\n", keyspace, shard) 137 return nil 138 } 139 140 func consumeValidationResults(resp *vtctldatapb.ValidateResponse, buf *strings.Builder) error { 141 for _, result := range resp.Results { 142 fmt.Fprintf(buf, "- %s\n", result) 143 } 144 145 for keyspace, keyspaceResults := range resp.ResultsByKeyspace { 146 buf2 := &strings.Builder{} 147 if err := consumeKeyspaceValidationResults(keyspace, keyspaceResults, buf2); err != nil { 148 fmt.Fprintf(buf, "\nFor %s:\n%s", keyspace, buf2.String() /* note: this should have a trailing newline */) 149 } 150 } 151 152 if buf.Len() > 0 { 153 return errors.New("some issues were found during validation; see above for details") 154 } 155 156 return nil 157 } 158 159 func consumeKeyspaceValidationResults(keyspace string, resp *vtctldatapb.ValidateKeyspaceResponse, buf *strings.Builder) error { 160 for _, result := range resp.Results { 161 fmt.Fprintf(buf, "- %s\n", result) 162 } 163 164 for shard, shardResults := range resp.ResultsByShard { 165 if len(shardResults.Results) == 0 { 166 continue 167 } 168 169 fmt.Fprintf(buf, "\nFor %s/%s:\n", keyspace, shard) 170 _ = consumeShardValidationResults(keyspace, shard, shardResults, buf) 171 } 172 173 if buf.Len() > 0 { 174 return fmt.Errorf("keyspace %s had validation issues; see above for details", keyspace) 175 } 176 177 return nil 178 } 179 180 func consumeShardValidationResults(keyspace string, shard string, resp *vtctldatapb.ValidateShardResponse, buf *strings.Builder) error { 181 for _, result := range resp.Results { 182 fmt.Fprintf(buf, "- %s\n", result) 183 } 184 185 if buf.Len() > 0 { 186 return fmt.Errorf("shard %s/%s had validation issues; see above for details", keyspace, shard) 187 } 188 189 return nil 190 } 191 192 func init() { 193 pingTabletsName := "ping-tablets" 194 pingTabletsShort := "p" 195 pingTabletsDefault := false 196 pingTabletsUsage := "Indicates whether all tablets should be pinged during the validation process." 197 198 Validate.Flags().BoolVarP(&validateOptions.PingTablets, pingTabletsName, pingTabletsShort, pingTabletsDefault, pingTabletsUsage) 199 ValidateKeyspace.Flags().BoolVarP(&validateKeyspaceOptions.PingTablets, pingTabletsName, pingTabletsShort, pingTabletsDefault, pingTabletsUsage) 200 ValidateShard.Flags().BoolVarP(&validateShardOptions.PingTablets, pingTabletsName, pingTabletsShort, pingTabletsDefault, pingTabletsUsage) 201 202 Root.AddCommand(Validate) 203 Root.AddCommand(ValidateKeyspace) 204 Root.AddCommand(ValidateShard) 205 }