vitess.io/vitess@v0.16.2/go/test/endtoend/cluster/vtctlclient_process.go (about) 1 /* 2 Copyright 2019 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 cluster 18 19 import ( 20 "fmt" 21 "os/exec" 22 "strings" 23 "time" 24 25 "vitess.io/vitess/go/vt/vterrors" 26 27 "vitess.io/vitess/go/vt/log" 28 ) 29 30 // VtctlClientProcess is a generic handle for a running vtctlclient command . 31 // It can be spawned manually 32 type VtctlClientProcess struct { 33 Name string 34 Binary string 35 Server string 36 TempDirectory string 37 ZoneName string 38 VtctlClientMajorVersion int 39 } 40 41 // VtctlClientParams encapsulated params to provide if non-default 42 type VtctlClientParams struct { 43 DDLStrategy string 44 MigrationContext string 45 SkipPreflight bool 46 UUIDList string 47 CallerID string 48 } 49 50 // InitShardPrimary executes vtctlclient command to make specified tablet the primary for the shard. 51 func (vtctlclient *VtctlClientProcess) InitShardPrimary(Keyspace string, Shard string, Cell string, TabletUID int) (err error) { 52 output, err := vtctlclient.ExecuteCommandWithOutput( 53 "InitShardPrimary", "--", 54 "--force", "--wait_replicas_timeout", "31s", 55 fmt.Sprintf("%s/%s", Keyspace, Shard), 56 fmt.Sprintf("%s-%d", Cell, TabletUID)) 57 if err != nil { 58 log.Errorf("error in InitShardPrimary output %s, err %s", output, err.Error()) 59 } 60 return err 61 } 62 63 // InitializeShard executes vtctlclient command to make specified tablet the primary for the shard. 64 func (vtctlclient *VtctlClientProcess) InitializeShard(Keyspace string, Shard string, Cell string, TabletUID int) (err error) { 65 output, err := vtctlclient.ExecuteCommandWithOutput( 66 "PlannedReparentShard", "--", 67 "--keyspace_shard", fmt.Sprintf("%s/%s", Keyspace, Shard), 68 "--wait_replicas_timeout", "31s", 69 "--new_primary", fmt.Sprintf("%s-%d", Cell, TabletUID)) 70 if err != nil { 71 log.Errorf("error in PlannedReparentShard output %s, err %s", output, err.Error()) 72 } 73 return err 74 } 75 76 // ApplySchemaWithOutput applies SQL schema to the keyspace 77 func (vtctlclient *VtctlClientProcess) ApplySchemaWithOutput(Keyspace string, SQL string, params VtctlClientParams) (result string, err error) { 78 args := []string{ 79 "ApplySchema", "--", 80 "--sql", SQL, 81 } 82 if params.MigrationContext != "" { 83 args = append(args, "--migration_context", params.MigrationContext) 84 } 85 if params.DDLStrategy != "" { 86 args = append(args, "--ddl_strategy", params.DDLStrategy) 87 } 88 if params.UUIDList != "" { 89 args = append(args, "--uuid_list", params.UUIDList) 90 } 91 if params.SkipPreflight { 92 args = append(args, "--skip_preflight") 93 } 94 95 if params.CallerID != "" { 96 args = append(args, "--caller_id", params.CallerID) 97 } 98 args = append(args, Keyspace) 99 return vtctlclient.ExecuteCommandWithOutput(args...) 100 } 101 102 // ApplySchema applies SQL schema to the keyspace 103 func (vtctlclient *VtctlClientProcess) ApplySchema(Keyspace string, SQL string) error { 104 message, err := vtctlclient.ApplySchemaWithOutput(Keyspace, SQL, VtctlClientParams{DDLStrategy: "direct -allow-zero-in-date", SkipPreflight: true}) 105 106 return vterrors.Wrap(err, message) 107 } 108 109 // ApplyVSchema applies vitess schema (JSON format) to the keyspace 110 func (vtctlclient *VtctlClientProcess) ApplyVSchema(Keyspace string, JSON string) (err error) { 111 return vtctlclient.ExecuteCommand( 112 "ApplyVSchema", "--", 113 "--vschema", JSON, 114 Keyspace, 115 ) 116 } 117 118 // ApplyRoutingRules does it 119 func (vtctlclient *VtctlClientProcess) ApplyRoutingRules(JSON string) (err error) { 120 return vtctlclient.ExecuteCommand("ApplyRoutingRules", "--", "--rules", JSON) 121 } 122 123 // ApplyRoutingRules does it 124 func (vtctlclient *VtctlClientProcess) ApplyShardRoutingRules(JSON string) (err error) { 125 return vtctlclient.ExecuteCommand("ApplyShardRoutingRules", "--", "--rules", JSON) 126 } 127 128 // OnlineDDLShowRecent responds with recent schema migration list 129 func (vtctlclient *VtctlClientProcess) OnlineDDLShowRecent(Keyspace string) (result string, err error) { 130 return vtctlclient.ExecuteCommandWithOutput( 131 "OnlineDDL", 132 Keyspace, 133 "show", 134 "recent", 135 ) 136 } 137 138 // OnlineDDLCancelMigration cancels a given migration uuid 139 func (vtctlclient *VtctlClientProcess) OnlineDDLCancelMigration(Keyspace, uuid string) (result string, err error) { 140 return vtctlclient.ExecuteCommandWithOutput( 141 "OnlineDDL", 142 Keyspace, 143 "cancel", 144 uuid, 145 ) 146 } 147 148 // OnlineDDLCancelAllMigrations cancels all migrations for a keyspace 149 func (vtctlclient *VtctlClientProcess) OnlineDDLCancelAllMigrations(Keyspace string) (result string, err error) { 150 return vtctlclient.ExecuteCommandWithOutput( 151 "OnlineDDL", 152 Keyspace, 153 "cancel-all", 154 ) 155 } 156 157 // OnlineDDLRetryMigration retries a given migration uuid 158 func (vtctlclient *VtctlClientProcess) OnlineDDLRetryMigration(Keyspace, uuid string) (result string, err error) { 159 return vtctlclient.ExecuteCommandWithOutput( 160 "OnlineDDL", 161 Keyspace, 162 "retry", 163 uuid, 164 ) 165 } 166 167 // OnlineDDLRevertMigration reverts a given migration uuid 168 func (vtctlclient *VtctlClientProcess) OnlineDDLRevertMigration(Keyspace, uuid string) (result string, err error) { 169 return vtctlclient.ExecuteCommandWithOutput( 170 "OnlineDDL", 171 Keyspace, 172 "revert", 173 uuid, 174 ) 175 } 176 177 // VExec runs a VExec query 178 func (vtctlclient *VtctlClientProcess) VExec(Keyspace, workflow, query string) (result string, err error) { 179 return vtctlclient.ExecuteCommandWithOutput( 180 "VExec", 181 fmt.Sprintf("%s.%s", Keyspace, workflow), 182 query, 183 ) 184 } 185 186 // ExecuteCommand executes any vtctlclient command 187 func (vtctlclient *VtctlClientProcess) ExecuteCommand(args ...string) (err error) { 188 output, err := vtctlclient.ExecuteCommandWithOutput(args...) 189 if output != "" { 190 if err != nil { 191 log.Errorf("Output:\n%v", output) 192 } 193 } 194 return err 195 } 196 197 // ExecuteCommandWithOutput executes any vtctlclient command and returns output 198 func (vtctlclient *VtctlClientProcess) ExecuteCommandWithOutput(args ...string) (string, error) { 199 var resultByte []byte 200 var resultStr string 201 var err error 202 retries := 10 203 retryDelay := 1 * time.Second 204 pArgs := []string{"--server", vtctlclient.Server} 205 if *isCoverage { 206 pArgs = append(pArgs, "--test.coverprofile="+getCoveragePath("vtctlclient-"+args[0]+".out"), "--test.v") 207 } 208 pArgs = append(pArgs, args...) 209 for i := 1; i <= retries; i++ { 210 tmpProcess := exec.Command( 211 vtctlclient.Binary, 212 filterDoubleDashArgs(pArgs, vtctlclient.VtctlClientMajorVersion)..., 213 ) 214 log.Infof("Executing vtctlclient with command: %v (attempt %d of %d)", strings.Join(tmpProcess.Args, " "), i, retries) 215 resultByte, err = tmpProcess.CombinedOutput() 216 resultStr = string(resultByte) 217 if err == nil || !shouldRetry(resultStr) { 218 break 219 } 220 time.Sleep(retryDelay) 221 } 222 return filterResultWhenRunsForCoverage(resultStr), err 223 } 224 225 // VtctlClientProcessInstance returns a VtctlProcess handle for vtctlclient process 226 // configured with the given Config. 227 func VtctlClientProcessInstance(hostname string, grpcPort int, tmpDirectory string) *VtctlClientProcess { 228 version, err := GetMajorVersion("vtctl") // `vtctlclient` does not have a --version flag, so we assume both vtctl/vtctlclient have the same version 229 if err != nil { 230 log.Warningf("failed to get major vtctlclient version; interop with CLI changes for VEP-4 may not work: %s", err) 231 } 232 233 vtctlclient := &VtctlClientProcess{ 234 Name: "vtctlclient", 235 Binary: "vtctlclient", 236 Server: fmt.Sprintf("%s:%d", hostname, grpcPort), 237 TempDirectory: tmpDirectory, 238 VtctlClientMajorVersion: version, 239 } 240 return vtctlclient 241 } 242 243 // InitTablet initializes a tablet 244 func (vtctlclient *VtctlClientProcess) InitTablet(tablet *Vttablet, cell string, keyspaceName string, hostname string, shardName string) error { 245 tabletType := "replica" 246 if tablet.Type == "rdonly" { 247 tabletType = "rdonly" 248 } 249 args := []string{"InitTablet", "--", "--hostname", hostname, 250 "--port", fmt.Sprintf("%d", tablet.HTTPPort), "--allow_update", "--parent", 251 "--keyspace", keyspaceName, 252 "--shard", shardName} 253 if tablet.MySQLPort > 0 { 254 args = append(args, "--mysql_port", fmt.Sprintf("%d", tablet.MySQLPort)) 255 } 256 if tablet.GrpcPort > 0 { 257 args = append(args, "--grpc_port", fmt.Sprintf("%d", tablet.GrpcPort)) 258 } 259 args = append(args, fmt.Sprintf("%s-%010d", cell, tablet.TabletUID), tabletType) 260 return vtctlclient.ExecuteCommand(args...) 261 } 262 263 // shouldRetry tells us if the command should be retried based on the results/output -- meaning that it 264 // is likely an ephemeral or recoverable issue that is likely to succeed when retried. 265 func shouldRetry(cmdResults string) bool { 266 return strings.Contains(cmdResults, "Deadlock found when trying to get lock; try restarting transaction") 267 }