vitess.io/vitess@v0.16.2/go/test/endtoend/cluster/cluster_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 "context" 21 "errors" 22 "flag" 23 "fmt" 24 "io" 25 "math/rand" 26 "net" 27 "os" 28 "os/exec" 29 "os/signal" 30 "path" 31 "regexp" 32 "strconv" 33 "strings" 34 "sync" 35 "syscall" 36 "testing" 37 "time" 38 39 "vitess.io/vitess/go/json2" 40 "vitess.io/vitess/go/mysql" 41 "vitess.io/vitess/go/sqltypes" 42 "vitess.io/vitess/go/test/endtoend/filelock" 43 "vitess.io/vitess/go/vt/grpcclient" 44 "vitess.io/vitess/go/vt/log" 45 "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" 46 "vitess.io/vitess/go/vt/vtgate/vtgateconn" 47 "vitess.io/vitess/go/vt/vttablet/tabletconn" 48 49 querypb "vitess.io/vitess/go/vt/proto/query" 50 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 51 52 // Ensure dialers are registered (needed by ExecOnTablet and ExecOnVTGate). 53 _ "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" 54 _ "vitess.io/vitess/go/vt/vttablet/grpctabletconn" 55 ) 56 57 // DefaultCell : If no cell name is passed, then use following 58 const ( 59 DefaultCell = "zone1" 60 DefaultStartPort = 6700 61 ) 62 63 var ( 64 keepData = flag.Bool("keep-data", true, "don't delete the per-test VTDATAROOT subfolders") 65 topoFlavor = flag.String("topo-flavor", "etcd2", "choose a topo server from etcd2, zk2 or consul") 66 isCoverage = flag.Bool("is-coverage", false, "whether coverage is required") 67 forceVTDATAROOT = flag.String("force-vtdataroot", "", "force path for VTDATAROOT, which may already be populated") 68 forcePortStart = flag.Int("force-port-start", 0, "force assigning ports based on this seed") 69 forceBaseTabletUID = flag.Int("force-base-tablet-uid", 0, "force assigning tablet ports based on this seed") 70 partialKeyspace = flag.Bool("partial-keyspace", false, "add a second keyspace for sharded tests and mark first shard as moved to this keyspace in the shard routing rules") 71 72 // PerfTest controls whether to run the slower end-to-end tests that check the system's performance 73 PerfTest = flag.Bool("perf-test", false, "include end-to-end performance tests") 74 ) 75 76 // LocalProcessCluster Testcases need to use this to iniate a cluster 77 type LocalProcessCluster struct { 78 Keyspaces []Keyspace 79 Cell string 80 DefaultCharset string 81 BaseTabletUID int 82 Hostname string 83 TopoFlavor string 84 TopoPort int 85 TmpDirectory string 86 OriginalVTDATAROOT string 87 CurrentVTDATAROOT string 88 ReusingVTDATAROOT bool 89 90 VtgateMySQLPort int 91 VtgateGrpcPort int 92 VtctldHTTPPort int 93 94 // major version numbers 95 VtTabletMajorVersion int 96 VtctlMajorVersion int 97 98 // standalone executable 99 VtctlclientProcess VtctlClientProcess 100 VtctldClientProcess VtctldClientProcess 101 VtctlProcess VtctlProcess 102 103 // background executable processes 104 TopoProcess TopoProcess 105 VtctldProcess VtctldProcess 106 VtgateProcess VtgateProcess 107 VtbackupProcess VtbackupProcess 108 VTOrcProcesses []*VTOrcProcess 109 110 nextPortForProcess int 111 112 // Extra arguments for vtTablet 113 VtTabletExtraArgs []string 114 115 // Extra arguments for vtGate 116 VtGateExtraArgs []string 117 VtGatePlannerVersion plancontext.PlannerVersion 118 119 VtctldExtraArgs []string 120 121 // mutex added to handle the parallel teardowns 122 mx *sync.Mutex 123 teardownCompleted bool 124 125 context.Context 126 context.CancelFunc 127 128 HasPartialKeyspaces bool 129 } 130 131 // Vttablet stores the properties needed to start a vttablet process 132 type Vttablet struct { 133 Type string 134 TabletUID int 135 HTTPPort int 136 GrpcPort int 137 MySQLPort int 138 Alias string 139 Cell string 140 141 // background executable processes 142 MysqlctlProcess MysqlctlProcess 143 MysqlctldProcess MysqlctldProcess 144 VttabletProcess *VttabletProcess 145 VtgrProcess *VtgrProcess 146 } 147 148 // Keyspace : Cluster accepts keyspace to launch it 149 type Keyspace struct { 150 Name string 151 SchemaSQL string 152 VSchema string 153 Shards []Shard 154 } 155 156 // Shard with associated vttablets 157 type Shard struct { 158 Name string 159 Vttablets []*Vttablet 160 } 161 162 // PrimaryTablet get the 1st tablet which is always elected as primary 163 func (shard *Shard) PrimaryTablet() *Vttablet { 164 return shard.Vttablets[0] 165 } 166 167 // Rdonly get the last tablet which is rdonly 168 func (shard *Shard) Rdonly() *Vttablet { 169 for idx, tablet := range shard.Vttablets { 170 if tablet.Type == "rdonly" { 171 return shard.Vttablets[idx] 172 } 173 } 174 return nil 175 } 176 177 // Replica get the last but one tablet which is replica 178 // Mostly we have either 3 tablet setup [primary, replica, rdonly] 179 func (shard *Shard) Replica() *Vttablet { 180 for idx, tablet := range shard.Vttablets { 181 if tablet.Type == "replica" && idx > 0 { 182 return shard.Vttablets[idx] 183 } 184 } 185 return nil 186 } 187 188 // CtrlCHandler handles the teardown for the ctrl-c. 189 func (cluster *LocalProcessCluster) CtrlCHandler() { 190 cluster.Context, cluster.CancelFunc = context.WithCancel(context.Background()) 191 192 c := make(chan os.Signal, 2) 193 signal.Notify(c, os.Interrupt, syscall.SIGTERM) 194 select { 195 case <-c: 196 cluster.Teardown() 197 os.Exit(0) 198 case <-cluster.Done(): 199 } 200 } 201 202 // StartTopo starts topology server 203 func (cluster *LocalProcessCluster) StartTopo() (err error) { 204 if cluster.Cell == "" { 205 cluster.Cell = DefaultCell 206 } 207 208 topoFlavor = cluster.TopoFlavorString() 209 cluster.TopoPort = cluster.GetAndReservePort() 210 cluster.TmpDirectory = path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/tmp_%d", cluster.GetAndReservePort())) 211 cluster.TopoProcess = *TopoProcessInstance(cluster.TopoPort, cluster.GetAndReservePort(), cluster.Hostname, *topoFlavor, "global") 212 213 log.Infof("Starting topo server %v on port: %d", *topoFlavor, cluster.TopoPort) 214 if err = cluster.TopoProcess.Setup(*topoFlavor, cluster); err != nil { 215 log.Error(err.Error()) 216 return 217 } 218 219 if *topoFlavor == "etcd2" { 220 log.Info("Creating global and cell topo dirs") 221 if err = cluster.TopoProcess.ManageTopoDir("mkdir", "/vitess/global"); err != nil { 222 log.Error(err.Error()) 223 return 224 } 225 226 if err = cluster.TopoProcess.ManageTopoDir("mkdir", "/vitess/"+cluster.Cell); err != nil { 227 log.Error(err.Error()) 228 return 229 } 230 } 231 232 if !cluster.ReusingVTDATAROOT { 233 cluster.VtctlProcess = *VtctlProcessInstance(cluster.TopoProcess.Port, cluster.Hostname) 234 if err = cluster.VtctlProcess.AddCellInfo(cluster.Cell); err != nil { 235 log.Error(err) 236 return 237 } 238 cluster.VtctlProcess.LogDir = cluster.TmpDirectory 239 } 240 241 cluster.VtctldProcess = *VtctldProcessInstance(cluster.GetAndReservePort(), cluster.GetAndReservePort(), 242 cluster.TopoProcess.Port, cluster.Hostname, cluster.TmpDirectory) 243 log.Infof("Starting vtctld server on port: %d", cluster.VtctldProcess.Port) 244 cluster.VtctldHTTPPort = cluster.VtctldProcess.Port 245 if err = cluster.VtctldProcess.Setup(cluster.Cell, cluster.VtctldExtraArgs...); err != nil { 246 log.Error(err.Error()) 247 return 248 } 249 250 cluster.VtctlclientProcess = *VtctlClientProcessInstance("localhost", cluster.VtctldProcess.GrpcPort, cluster.TmpDirectory) 251 return 252 } 253 254 // StartVTOrc starts a VTOrc instance 255 func (cluster *LocalProcessCluster) StartVTOrc(keyspace string) error { 256 // Start vtorc 257 vtorcProcess := cluster.NewVTOrcProcess(VTOrcConfiguration{}) 258 err := vtorcProcess.Setup() 259 if err != nil { 260 log.Error(err.Error()) 261 return err 262 } 263 if keyspace != "" { 264 vtorcProcess.ExtraArgs = append(vtorcProcess.ExtraArgs, fmt.Sprintf(`--clusters_to_watch="%s"`, keyspace)) 265 } 266 cluster.VTOrcProcesses = append(cluster.VTOrcProcesses, vtorcProcess) 267 return nil 268 } 269 270 // StartUnshardedKeyspace starts unshared keyspace with shard name as "0" 271 func (cluster *LocalProcessCluster) StartUnshardedKeyspace(keyspace Keyspace, replicaCount int, rdonly bool) error { 272 return cluster.StartKeyspace(keyspace, []string{"0"}, replicaCount, rdonly) 273 } 274 275 func (cluster *LocalProcessCluster) startPartialKeyspace(keyspace Keyspace, shardNames []string, movedShard string, replicaCount int, rdonly bool, customizers ...any) (err error) { 276 277 cluster.HasPartialKeyspaces = true 278 routedKeyspace := &Keyspace{ 279 Name: fmt.Sprintf("%s_routed", keyspace.Name), 280 SchemaSQL: keyspace.SchemaSQL, 281 VSchema: keyspace.VSchema, 282 } 283 284 err = cluster.startKeyspace(*routedKeyspace, shardNames, replicaCount, rdonly, customizers...) 285 if err != nil { 286 return err 287 } 288 shardRoutingRulesTemplate := `{"rules":[{"from_keyspace":"%s","to_keyspace":"%s","shards":"%s"}]}` 289 shardRoutingRules := fmt.Sprintf(shardRoutingRulesTemplate, keyspace.Name, routedKeyspace.Name, movedShard) 290 cmd := exec.Command("vtctldclient", "--server", 291 net.JoinHostPort("localhost", strconv.Itoa(cluster.VtctldProcess.GrpcPort)), 292 "ApplyShardRoutingRules", "--rules", shardRoutingRules) 293 _, err = cmd.Output() 294 if err != nil { 295 return err 296 } 297 return nil 298 } 299 300 func (cluster *LocalProcessCluster) StartKeyspace(keyspace Keyspace, shardNames []string, replicaCount int, rdonly bool, customizers ...any) (err error) { 301 err = cluster.startKeyspace(keyspace, shardNames, replicaCount, rdonly, customizers...) 302 if err != nil { 303 return err 304 } 305 306 if *partialKeyspace && len(shardNames) > 1 { 307 movedShard := shardNames[0] 308 return cluster.startPartialKeyspace(keyspace, shardNames, movedShard, replicaCount, rdonly, customizers...) 309 } 310 return nil 311 } 312 313 // StartKeyspace starts required number of shard and the corresponding tablets 314 // keyspace : struct containing keyspace name, Sqlschema to apply, VSchema to apply 315 // shardName : list of shard names 316 // replicaCount: total number of replicas excluding shard primary and rdonly 317 // rdonly: whether readonly tablets needed 318 // customizers: functions like "func(*VttabletProcess)" that can modify settings of various objects 319 // after they're created. 320 func (cluster *LocalProcessCluster) startKeyspace(keyspace Keyspace, shardNames []string, replicaCount int, rdonly bool, customizers ...any) (err error) { 321 totalTabletsRequired := replicaCount + 1 // + 1 is for primary 322 if rdonly { 323 totalTabletsRequired = totalTabletsRequired + 1 // + 1 for rdonly 324 } 325 326 log.Infof("Starting keyspace: %v", keyspace.Name) 327 if !cluster.ReusingVTDATAROOT { 328 _ = cluster.VtctlProcess.CreateKeyspace(keyspace.Name) 329 } 330 var mysqlctlProcessList []*exec.Cmd 331 for _, shardName := range shardNames { 332 shard := &Shard{ 333 Name: shardName, 334 } 335 log.Infof("Starting shard: %v", shardName) 336 mysqlctlProcessList = []*exec.Cmd{} 337 for i := 0; i < totalTabletsRequired; i++ { 338 // instantiate vttablet object with reserved ports 339 tabletUID := cluster.GetAndReserveTabletUID() 340 tablet := &Vttablet{ 341 TabletUID: tabletUID, 342 Type: "replica", 343 HTTPPort: cluster.GetAndReservePort(), 344 GrpcPort: cluster.GetAndReservePort(), 345 MySQLPort: cluster.GetAndReservePort(), 346 Alias: fmt.Sprintf("%s-%010d", cluster.Cell, tabletUID), 347 } 348 if i == 0 { // Make the first one as primary 349 tablet.Type = "primary" 350 } else if i == totalTabletsRequired-1 && rdonly { // Make the last one as rdonly if rdonly flag is passed 351 tablet.Type = "rdonly" 352 } 353 // Start Mysqlctl process 354 log.Infof("Starting mysqlctl for table uid %d, mysql port %d", tablet.TabletUID, tablet.MySQLPort) 355 tablet.MysqlctlProcess = *MysqlCtlProcessInstanceOptionalInit(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory, !cluster.ReusingVTDATAROOT) 356 proc, err := tablet.MysqlctlProcess.StartProcess() 357 if err != nil { 358 log.Errorf("error starting mysqlctl process: %v, %v", tablet.MysqlctldProcess, err) 359 return err 360 } 361 mysqlctlProcessList = append(mysqlctlProcessList, proc) 362 363 // start vttablet process 364 tablet.VttabletProcess = VttabletProcessInstance( 365 tablet.HTTPPort, 366 tablet.GrpcPort, 367 tablet.TabletUID, 368 cluster.Cell, 369 shardName, 370 keyspace.Name, 371 cluster.VtctldProcess.Port, 372 tablet.Type, 373 cluster.TopoProcess.Port, 374 cluster.Hostname, 375 cluster.TmpDirectory, 376 cluster.VtTabletExtraArgs, 377 cluster.DefaultCharset) 378 tablet.Alias = tablet.VttabletProcess.TabletPath 379 if cluster.ReusingVTDATAROOT { 380 tablet.VttabletProcess.ServingStatus = "SERVING" 381 } 382 shard.Vttablets = append(shard.Vttablets, tablet) 383 // Apply customizations 384 for _, customizer := range customizers { 385 if f, ok := customizer.(func(*VttabletProcess)); ok { 386 f(tablet.VttabletProcess) 387 } else { 388 return fmt.Errorf("type mismatch on customizer: %T", customizer) 389 } 390 } 391 } 392 393 // wait till all mysqlctl is instantiated 394 for _, proc := range mysqlctlProcessList { 395 if err = proc.Wait(); err != nil { 396 log.Errorf("unable to start mysql process %v: %v", proc, err) 397 return err 398 } 399 } 400 for _, tablet := range shard.Vttablets { 401 log.Infof("Starting vttablet for tablet uid %d, grpc port %d", tablet.TabletUID, tablet.GrpcPort) 402 403 if err = tablet.VttabletProcess.Setup(); err != nil { 404 log.Errorf("error starting vttablet for tablet uid %d, grpc port %d: %v", tablet.TabletUID, tablet.GrpcPort, err) 405 return 406 } 407 } 408 409 // Make first tablet as primary 410 if err = cluster.VtctlclientProcess.InitializeShard(keyspace.Name, shardName, cluster.Cell, shard.Vttablets[0].TabletUID); err != nil { 411 log.Errorf("error running InitializeShard on keyspace %v, shard %v: %v", keyspace.Name, shardName, err) 412 return 413 } 414 keyspace.Shards = append(keyspace.Shards, *shard) 415 } 416 // if the keyspace is present then append the shard info 417 existingKeyspace := false 418 for idx, ks := range cluster.Keyspaces { 419 if ks.Name == keyspace.Name { 420 cluster.Keyspaces[idx].Shards = append(cluster.Keyspaces[idx].Shards, keyspace.Shards...) 421 existingKeyspace = true 422 } 423 } 424 if !existingKeyspace { 425 cluster.Keyspaces = append(cluster.Keyspaces, keyspace) 426 } 427 428 // Apply Schema SQL 429 if keyspace.SchemaSQL != "" { 430 if err = cluster.VtctlclientProcess.ApplySchema(keyspace.Name, keyspace.SchemaSQL); err != nil { 431 log.Errorf("error applying schema: %v, %v", keyspace.SchemaSQL, err) 432 return 433 } 434 } 435 436 // Apply VSchema 437 if keyspace.VSchema != "" { 438 if err = cluster.VtctlclientProcess.ApplyVSchema(keyspace.Name, keyspace.VSchema); err != nil { 439 log.Errorf("error applying vschema: %v, %v", keyspace.VSchema, err) 440 return 441 } 442 } 443 444 log.Infof("Done creating keyspace: %v ", keyspace.Name) 445 446 err = cluster.StartVTOrc(keyspace.Name) 447 if err != nil { 448 log.Errorf("Error starting VTOrc - %v", err) 449 return err 450 } 451 452 return 453 } 454 455 // StartUnshardedKeyspaceLegacy starts unshared keyspace with shard name as "0" 456 func (cluster *LocalProcessCluster) StartUnshardedKeyspaceLegacy(keyspace Keyspace, replicaCount int, rdonly bool) error { 457 return cluster.StartKeyspaceLegacy(keyspace, []string{"0"}, replicaCount, rdonly) 458 } 459 460 // StartKeyspaceLegacy starts required number of shard and the corresponding tablets 461 // keyspace : struct containing keyspace name, Sqlschema to apply, VSchema to apply 462 // shardName : list of shard names 463 // replicaCount: total number of replicas excluding shard primary and rdonly 464 // rdonly: whether readonly tablets needed 465 // customizers: functions like "func(*VttabletProcess)" that can modify settings of various objects 466 // after they're created. 467 func (cluster *LocalProcessCluster) StartKeyspaceLegacy(keyspace Keyspace, shardNames []string, replicaCount int, rdonly bool, customizers ...any) (err error) { 468 totalTabletsRequired := replicaCount + 1 // + 1 is for primary 469 if rdonly { 470 totalTabletsRequired = totalTabletsRequired + 1 // + 1 for rdonly 471 } 472 473 log.Infof("Starting keyspace: %v", keyspace.Name) 474 if !cluster.ReusingVTDATAROOT { 475 _ = cluster.VtctlProcess.CreateKeyspace(keyspace.Name) 476 } 477 var mysqlctlProcessList []*exec.Cmd 478 for _, shardName := range shardNames { 479 shard := &Shard{ 480 Name: shardName, 481 } 482 log.Infof("Starting shard: %v", shardName) 483 mysqlctlProcessList = []*exec.Cmd{} 484 for i := 0; i < totalTabletsRequired; i++ { 485 // instantiate vttablet object with reserved ports 486 tabletUID := cluster.GetAndReserveTabletUID() 487 tablet := &Vttablet{ 488 TabletUID: tabletUID, 489 Type: "replica", 490 HTTPPort: cluster.GetAndReservePort(), 491 GrpcPort: cluster.GetAndReservePort(), 492 MySQLPort: cluster.GetAndReservePort(), 493 Alias: fmt.Sprintf("%s-%010d", cluster.Cell, tabletUID), 494 } 495 if i == 0 { // Make the first one as primary 496 tablet.Type = "primary" 497 } else if i == totalTabletsRequired-1 && rdonly { // Make the last one as rdonly if rdonly flag is passed 498 tablet.Type = "rdonly" 499 } 500 // Start Mysqlctl process 501 log.Infof("Starting mysqlctl for table uid %d, mysql port %d", tablet.TabletUID, tablet.MySQLPort) 502 tablet.MysqlctlProcess = *MysqlCtlProcessInstanceOptionalInit(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory, !cluster.ReusingVTDATAROOT) 503 proc, err := tablet.MysqlctlProcess.StartProcess() 504 if err != nil { 505 log.Errorf("error starting mysqlctl process: %v, %v", tablet.MysqlctldProcess, err) 506 return err 507 } 508 mysqlctlProcessList = append(mysqlctlProcessList, proc) 509 510 // start vttablet process 511 tablet.VttabletProcess = VttabletProcessInstance( 512 tablet.HTTPPort, 513 tablet.GrpcPort, 514 tablet.TabletUID, 515 cluster.Cell, 516 shardName, 517 keyspace.Name, 518 cluster.VtctldProcess.Port, 519 tablet.Type, 520 cluster.TopoProcess.Port, 521 cluster.Hostname, 522 cluster.TmpDirectory, 523 cluster.VtTabletExtraArgs, 524 cluster.DefaultCharset) 525 tablet.Alias = tablet.VttabletProcess.TabletPath 526 if cluster.ReusingVTDATAROOT { 527 tablet.VttabletProcess.ServingStatus = "SERVING" 528 } 529 shard.Vttablets = append(shard.Vttablets, tablet) 530 // Apply customizations 531 for _, customizer := range customizers { 532 if f, ok := customizer.(func(*VttabletProcess)); ok { 533 f(tablet.VttabletProcess) 534 } else { 535 return fmt.Errorf("type mismatch on customizer: %T", customizer) 536 } 537 } 538 } 539 540 // wait till all mysqlctl is instantiated 541 for _, proc := range mysqlctlProcessList { 542 if err = proc.Wait(); err != nil { 543 log.Errorf("unable to start mysql process %v: %v", proc, err) 544 return err 545 } 546 } 547 for _, tablet := range shard.Vttablets { 548 if !cluster.ReusingVTDATAROOT { 549 if _, err = tablet.VttabletProcess.QueryTablet(fmt.Sprintf("create database vt_%s", keyspace.Name), keyspace.Name, false); err != nil { 550 log.Errorf("error creating database for keyspace %v: %v", keyspace.Name, err) 551 return 552 } 553 } 554 555 log.Infof("Starting vttablet for tablet uid %d, grpc port %d", tablet.TabletUID, tablet.GrpcPort) 556 557 if err = tablet.VttabletProcess.Setup(); err != nil { 558 log.Errorf("error starting vttablet for tablet uid %d, grpc port %d: %v", tablet.TabletUID, tablet.GrpcPort, err) 559 return 560 } 561 } 562 563 // Make first tablet as primary 564 if err = cluster.VtctlclientProcess.InitShardPrimary(keyspace.Name, shardName, cluster.Cell, shard.Vttablets[0].TabletUID); err != nil { 565 log.Errorf("error running ISM on keyspace %v, shard %v: %v", keyspace.Name, shardName, err) 566 return 567 } 568 keyspace.Shards = append(keyspace.Shards, *shard) 569 } 570 // if the keyspace is present then append the shard info 571 existingKeyspace := false 572 for idx, ks := range cluster.Keyspaces { 573 if ks.Name == keyspace.Name { 574 cluster.Keyspaces[idx].Shards = append(cluster.Keyspaces[idx].Shards, keyspace.Shards...) 575 existingKeyspace = true 576 } 577 } 578 if !existingKeyspace { 579 cluster.Keyspaces = append(cluster.Keyspaces, keyspace) 580 } 581 582 // Apply Schema SQL 583 if keyspace.SchemaSQL != "" { 584 if err = cluster.VtctlclientProcess.ApplySchema(keyspace.Name, keyspace.SchemaSQL); err != nil { 585 log.Errorf("error applying schema: %v, %v", keyspace.SchemaSQL, err) 586 return 587 } 588 } 589 590 // Apply VSchema 591 if keyspace.VSchema != "" { 592 if err = cluster.VtctlclientProcess.ApplyVSchema(keyspace.Name, keyspace.VSchema); err != nil { 593 log.Errorf("error applying vschema: %v, %v", keyspace.VSchema, err) 594 return 595 } 596 } 597 598 log.Infof("Done creating keyspace: %v ", keyspace.Name) 599 return 600 } 601 602 // SetupCluster creates the skeleton for a cluster by creating keyspace 603 // shards and initializing tablets and mysqlctl processes. 604 // This does not start any process and user have to explicitly start all 605 // the required services (ex topo, vtgate, mysql and vttablet) 606 func (cluster *LocalProcessCluster) SetupCluster(keyspace *Keyspace, shards []Shard) (err error) { 607 log.Infof("Starting keyspace: %v", keyspace.Name) 608 609 if !cluster.ReusingVTDATAROOT { 610 // Create Keyspace 611 err = cluster.VtctlProcess.CreateKeyspace(keyspace.Name) 612 if err != nil { 613 log.Error(err) 614 return 615 } 616 } 617 618 // Create shard 619 for _, shard := range shards { 620 for _, tablet := range shard.Vttablets { 621 // Setup MysqlctlProcess 622 tablet.MysqlctlProcess = *MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory) 623 // Setup VttabletProcess 624 tablet.VttabletProcess = VttabletProcessInstance( 625 tablet.HTTPPort, 626 tablet.GrpcPort, 627 tablet.TabletUID, 628 tablet.Cell, 629 shard.Name, 630 keyspace.Name, 631 cluster.VtctldProcess.Port, 632 tablet.Type, 633 cluster.TopoProcess.Port, 634 cluster.Hostname, 635 cluster.TmpDirectory, 636 cluster.VtTabletExtraArgs, 637 cluster.DefaultCharset) 638 } 639 640 keyspace.Shards = append(keyspace.Shards, shard) 641 } 642 643 // if the keyspace is present then append the shard info 644 existingKeyspace := false 645 for idx, ks := range cluster.Keyspaces { 646 if ks.Name == keyspace.Name { 647 cluster.Keyspaces[idx].Shards = append(cluster.Keyspaces[idx].Shards, keyspace.Shards...) 648 existingKeyspace = true 649 } 650 } 651 if !existingKeyspace { 652 cluster.Keyspaces = append(cluster.Keyspaces, *keyspace) 653 } 654 655 log.Infof("Done launching keyspace: %v", keyspace.Name) 656 return err 657 } 658 659 // StartVtgate starts vtgate 660 func (cluster *LocalProcessCluster) StartVtgate() (err error) { 661 if cluster.HasPartialKeyspaces { 662 cluster.VtGateExtraArgs = append(cluster.VtGateExtraArgs, "--enable-partial-keyspace-migration") 663 } 664 vtgateInstance := *cluster.NewVtgateInstance() 665 cluster.VtgateProcess = vtgateInstance 666 log.Infof("Starting vtgate on port %d", vtgateInstance.Port) 667 log.Infof("Vtgate started, connect to mysql using : mysql -h 127.0.0.1 -P %d", cluster.VtgateMySQLPort) 668 return cluster.VtgateProcess.Setup() 669 } 670 671 // NewVtgateInstance returns an instance of vtgateprocess 672 func (cluster *LocalProcessCluster) NewVtgateInstance() *VtgateProcess { 673 vtgateHTTPPort := cluster.GetAndReservePort() 674 cluster.VtgateGrpcPort = cluster.GetAndReservePort() 675 cluster.VtgateMySQLPort = cluster.GetAndReservePort() 676 vtgateProcInstance := VtgateProcessInstance( 677 vtgateHTTPPort, 678 cluster.VtgateGrpcPort, 679 cluster.VtgateMySQLPort, 680 cluster.Cell, 681 cluster.Cell, 682 cluster.Hostname, 683 "PRIMARY,REPLICA", 684 cluster.TopoProcess.Port, 685 cluster.TmpDirectory, 686 cluster.VtGateExtraArgs, 687 cluster.VtGatePlannerVersion) 688 return vtgateProcInstance 689 } 690 691 // NewBareCluster instantiates a new cluster and does not assume existence of any of the vitess processes 692 func NewBareCluster(cell string, hostname string) *LocalProcessCluster { 693 cluster := &LocalProcessCluster{Cell: cell, Hostname: hostname, mx: new(sync.Mutex), DefaultCharset: "utf8mb4"} 694 go cluster.CtrlCHandler() 695 696 cluster.OriginalVTDATAROOT = os.Getenv("VTDATAROOT") 697 cluster.CurrentVTDATAROOT = path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("vtroot_%d", cluster.GetAndReservePort())) 698 cluster.VtGatePlannerVersion = defaultVtGatePlannerVersion 699 if *forceVTDATAROOT != "" { 700 cluster.CurrentVTDATAROOT = *forceVTDATAROOT 701 } 702 if _, err := os.Stat(cluster.CurrentVTDATAROOT); err == nil { 703 // path/to/whatever exists 704 cluster.ReusingVTDATAROOT = true 705 } else { 706 _ = createDirectory(cluster.CurrentVTDATAROOT, 0700) 707 } 708 _ = os.Setenv("VTDATAROOT", cluster.CurrentVTDATAROOT) 709 log.Infof("Created cluster on %s. ReusingVTDATAROOT=%v", cluster.CurrentVTDATAROOT, cluster.ReusingVTDATAROOT) 710 711 rand.Seed(time.Now().UTC().UnixNano()) 712 return cluster 713 } 714 715 // NewCluster instantiates a new cluster 716 func NewCluster(cell string, hostname string) *LocalProcessCluster { 717 cluster := NewBareCluster(cell, hostname) 718 719 err := cluster.populateVersionInfo() 720 if err != nil { 721 log.Errorf("Error populating version information - %v", err) 722 } 723 return cluster 724 } 725 726 // populateVersionInfo is used to populate the version information for the binaries used to setup the cluster. 727 func (cluster *LocalProcessCluster) populateVersionInfo() error { 728 var err error 729 cluster.VtTabletMajorVersion, err = GetMajorVersion("vttablet") 730 if err != nil { 731 return err 732 } 733 cluster.VtctlMajorVersion, err = GetMajorVersion("vtctl") 734 return err 735 } 736 737 func GetMajorVersion(binaryName string) (int, error) { 738 version, err := exec.Command(binaryName, "--version").Output() 739 if err != nil { 740 return 0, err 741 } 742 versionRegex := regexp.MustCompile(`Version: ([0-9]+)\.([0-9]+)\.([0-9]+)`) 743 v := versionRegex.FindStringSubmatch(string(version)) 744 if len(v) != 4 { 745 return 0, fmt.Errorf("could not parse server version from: %s", version) 746 } 747 if err != nil { 748 return 0, fmt.Errorf("could not parse server version from: %s", version) 749 } 750 return strconv.Atoi(v[1]) 751 } 752 753 // RestartVtgate starts vtgate with updated configs 754 func (cluster *LocalProcessCluster) RestartVtgate() (err error) { 755 err = cluster.VtgateProcess.TearDown() 756 if err != nil { 757 log.Errorf("error stopping vtgate %v: %v", cluster.VtgateProcess, err) 758 return 759 } 760 err = cluster.StartVtgate() 761 if err != nil { 762 log.Errorf("error starting vtgate %v: %v", cluster.VtgateProcess, err) 763 return 764 } 765 return err 766 } 767 768 // WaitForTabletsToHealthyInVtgate waits for all tablets in all shards to be seen as 769 // healthy and serving in vtgate. 770 // For each shard: 771 // - It must have 1 (and only 1) healthy primary tablet so we always wait for that 772 // - For replica and rdonly tablets, which are optional, we wait for as many as we 773 // should have based on how the cluster was defined. 774 func (cluster *LocalProcessCluster) WaitForTabletsToHealthyInVtgate() (err error) { 775 for _, keyspace := range cluster.Keyspaces { 776 for _, shard := range keyspace.Shards { 777 rdonlyTabletCount, replicaTabletCount := 0, 0 778 for _, tablet := range shard.Vttablets { 779 switch strings.ToLower(tablet.Type) { 780 case "replica": 781 replicaTabletCount++ 782 case "rdonly": 783 rdonlyTabletCount++ 784 } 785 } 786 if err = cluster.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", keyspace.Name, shard.Name), 1, 2*time.Minute); err != nil { 787 return err 788 } 789 if replicaTabletCount > 0 { 790 err = cluster.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspace.Name, shard.Name), replicaTabletCount, 2*time.Minute) 791 } 792 if rdonlyTabletCount > 0 { 793 err = cluster.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspace.Name, shard.Name), rdonlyTabletCount, 2*time.Minute) 794 } 795 if err != nil { 796 return err 797 } 798 } 799 } 800 return nil 801 } 802 803 // ExecOnTablet executes a query on the local cluster Vttablet and returns the 804 // result. 805 func (cluster *LocalProcessCluster) ExecOnTablet(ctx context.Context, vttablet *Vttablet, sql string, binds map[string]any, opts *querypb.ExecuteOptions) (*sqltypes.Result, error) { 806 bindvars, err := sqltypes.BuildBindVariables(binds) 807 if err != nil { 808 return nil, err 809 } 810 811 tablet, err := cluster.VtctlclientGetTablet(vttablet) 812 if err != nil { 813 return nil, err 814 } 815 816 conn, err := tabletconn.GetDialer()(tablet, grpcclient.FailFast(false)) 817 if err != nil { 818 return nil, err 819 } 820 defer conn.Close(ctx) 821 822 txID, reservedID := 0, 0 823 824 return conn.Execute(ctx, &querypb.Target{ 825 Keyspace: tablet.Keyspace, 826 Shard: tablet.Shard, 827 TabletType: tablet.Type, 828 }, sql, bindvars, int64(txID), int64(reservedID), opts) 829 } 830 831 // ExecOnVTGate executes a query on a local cluster VTGate with the provided 832 // target, bindvars, and execute options. 833 func (cluster *LocalProcessCluster) ExecOnVTGate(ctx context.Context, addr string, target string, sql string, binds map[string]any, opts *querypb.ExecuteOptions) (*sqltypes.Result, error) { 834 bindvars, err := sqltypes.BuildBindVariables(binds) 835 if err != nil { 836 return nil, err 837 } 838 839 conn, err := vtgateconn.Dial(ctx, addr) 840 if err != nil { 841 return nil, err 842 } 843 844 session := conn.Session(target, opts) 845 defer conn.Close() 846 847 return session.Execute(ctx, sql, bindvars) 848 } 849 850 // StreamTabletHealth invokes a HealthStream on a local cluster Vttablet and 851 // returns the responses. It returns an error if the stream ends with fewer than 852 // `count` responses. 853 func (cluster *LocalProcessCluster) StreamTabletHealth(ctx context.Context, vttablet *Vttablet, count int) (responses []*querypb.StreamHealthResponse, err error) { 854 tablet, err := cluster.VtctlclientGetTablet(vttablet) 855 if err != nil { 856 return nil, err 857 } 858 859 conn, err := tabletconn.GetDialer()(tablet, grpcclient.FailFast(false)) 860 if err != nil { 861 return nil, err 862 } 863 864 i := 0 865 err = conn.StreamHealth(ctx, func(shr *querypb.StreamHealthResponse) error { 866 responses = append(responses, shr) 867 868 i++ 869 if i >= count { 870 return io.EOF 871 } 872 873 return nil 874 }) 875 876 switch { 877 case err != nil: 878 return nil, err 879 case len(responses) < count: 880 return nil, errors.New("stream ended early") 881 } 882 883 return responses, nil 884 } 885 886 func (cluster *LocalProcessCluster) VtctlclientGetTablet(tablet *Vttablet) (*topodatapb.Tablet, error) { 887 result, err := cluster.VtctlclientProcess.ExecuteCommandWithOutput("GetTablet", "--", tablet.Alias) 888 if err != nil { 889 return nil, err 890 } 891 892 var ti topodatapb.Tablet 893 if err := json2.Unmarshal([]byte(result), &ti); err != nil { 894 return nil, err 895 } 896 897 return &ti, nil 898 } 899 900 // Teardown brings down the cluster by invoking teardown for individual processes 901 func (cluster *LocalProcessCluster) Teardown() { 902 PanicHandler(nil) 903 cluster.mx.Lock() 904 defer cluster.mx.Unlock() 905 if cluster.teardownCompleted { 906 return 907 } 908 if cluster.CancelFunc != nil { 909 cluster.CancelFunc() 910 } 911 if err := cluster.VtgateProcess.TearDown(); err != nil { 912 log.Errorf("Error in vtgate teardown: %v", err) 913 } 914 915 for _, vtorcProcess := range cluster.VTOrcProcesses { 916 if err := vtorcProcess.TearDown(); err != nil { 917 log.Errorf("Error in vtorc teardown: %v", err) 918 } 919 } 920 921 var mysqlctlProcessList []*exec.Cmd 922 var mysqlctlTabletUIDs []int 923 for _, keyspace := range cluster.Keyspaces { 924 for _, shard := range keyspace.Shards { 925 for _, tablet := range shard.Vttablets { 926 if tablet.MysqlctlProcess.TabletUID > 0 { 927 if proc, err := tablet.MysqlctlProcess.StopProcess(); err != nil { 928 log.Errorf("Error in mysqlctl teardown: %v", err) 929 } else { 930 mysqlctlProcessList = append(mysqlctlProcessList, proc) 931 mysqlctlTabletUIDs = append(mysqlctlTabletUIDs, tablet.MysqlctlProcess.TabletUID) 932 } 933 } 934 if tablet.MysqlctldProcess.TabletUID > 0 { 935 if err := tablet.MysqlctldProcess.Stop(); err != nil { 936 log.Errorf("Error in mysqlctl teardown: %v", err) 937 } 938 } 939 940 if err := tablet.VttabletProcess.TearDown(); err != nil { 941 log.Errorf("Error in vttablet teardown: %v", err) 942 } 943 } 944 } 945 } 946 947 // On the CI it was noticed that MySQL shutdown hangs sometimes and 948 // on local investigation it was waiting on SEMI_SYNC acks for an internal command 949 // of Vitess even after closing the socket file. 950 // To prevent this process for hanging for 5 minutes, we will add a 30-second timeout. 951 cluster.waitForMySQLProcessToExit(mysqlctlProcessList, mysqlctlTabletUIDs) 952 953 if err := cluster.VtctldProcess.TearDown(); err != nil { 954 log.Errorf("Error in vtctld teardown: %v", err) 955 } 956 957 if err := cluster.TopoProcess.TearDown(cluster.Cell, cluster.OriginalVTDATAROOT, cluster.CurrentVTDATAROOT, *keepData, *topoFlavor); err != nil { 958 log.Errorf("Error in topo server teardown: %v", err) 959 } 960 961 // reset the VTDATAROOT path. 962 os.Setenv("VTDATAROOT", cluster.OriginalVTDATAROOT) 963 964 cluster.teardownCompleted = true 965 } 966 967 func (cluster *LocalProcessCluster) waitForMySQLProcessToExit(mysqlctlProcessList []*exec.Cmd, mysqlctlTabletUIDs []int) { 968 wg := sync.WaitGroup{} 969 for i, cmd := range mysqlctlProcessList { 970 wg.Add(1) 971 go func(cmd *exec.Cmd, tabletUID int) { 972 defer func() { 973 wg.Done() 974 }() 975 exit := make(chan error) 976 go func() { 977 exit <- cmd.Wait() 978 }() 979 select { 980 case <-time.After(30 * time.Second): 981 break 982 case err := <-exit: 983 if err == nil { 984 return 985 } 986 log.Errorf("Error in mysqlctl teardown wait: %v", err) 987 break 988 } 989 pidFile := path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d/mysql.pid", tabletUID)) 990 pidBytes, err := os.ReadFile(pidFile) 991 if err != nil { 992 // We can't read the file which means the PID file does not exist 993 // The server must have stopped 994 return 995 } 996 pid, err := strconv.Atoi(strings.TrimSpace(string(pidBytes))) 997 if err != nil { 998 log.Errorf("Error in conversion to integer: %v", err) 999 return 1000 } 1001 err = syscall.Kill(pid, syscall.SIGKILL) 1002 if err != nil { 1003 log.Errorf("Error in killing process: %v", err) 1004 } 1005 }(cmd, mysqlctlTabletUIDs[i]) 1006 } 1007 wg.Wait() 1008 } 1009 1010 // StartVtbackup starts a vtbackup 1011 func (cluster *LocalProcessCluster) StartVtbackup(newInitDBFile string, initialBackup bool, 1012 keyspace string, shard string, cell string, extraArgs ...string) error { 1013 log.Info("Starting vtbackup") 1014 cluster.VtbackupProcess = *VtbackupProcessInstance( 1015 cluster.GetAndReserveTabletUID(), 1016 cluster.GetAndReservePort(), 1017 newInitDBFile, 1018 keyspace, 1019 shard, 1020 cell, 1021 cluster.Hostname, 1022 cluster.TmpDirectory, 1023 cluster.TopoPort, 1024 initialBackup) 1025 cluster.VtbackupProcess.ExtraArgs = extraArgs 1026 return cluster.VtbackupProcess.Setup() 1027 1028 } 1029 1030 // GetAndReservePort gives port for required process 1031 func (cluster *LocalProcessCluster) GetAndReservePort() int { 1032 if cluster.nextPortForProcess == 0 { 1033 if *forcePortStart > 0 { 1034 cluster.nextPortForProcess = *forcePortStart 1035 } else { 1036 cluster.nextPortForProcess = getPort() 1037 } 1038 } 1039 for { 1040 cluster.nextPortForProcess = cluster.nextPortForProcess + 1 1041 log.Infof("Attempting to reserve port: %v", cluster.nextPortForProcess) 1042 ln, err := net.Listen("tcp", fmt.Sprintf(":%v", cluster.nextPortForProcess)) 1043 1044 if err != nil { 1045 log.Errorf("Can't listen on port %v: %s, trying next port", cluster.nextPortForProcess, err) 1046 continue 1047 } 1048 1049 log.Infof("Port %v is available, reserving..", cluster.nextPortForProcess) 1050 ln.Close() 1051 break 1052 } 1053 return cluster.nextPortForProcess 1054 } 1055 1056 // portFileTimeout determines when we see the content of a port file as 1057 // stale. After this time, we assume we can start with the default base 1058 // port again. 1059 const portFileTimeout = 1 * time.Hour 1060 1061 // getPort checks if we have recent used port info in /tmp/todaytime.port 1062 // If no, then use a random port and save that port + 200 in the above file 1063 // If yes, then return that port, and save port + 200 in the same file 1064 // here, assumptions is 200 ports might be consumed for all tests in a package 1065 func getPort() int { 1066 portFile, err := os.OpenFile(path.Join(os.TempDir(), "endtoend.port"), os.O_CREATE|os.O_RDWR, 0644) 1067 if err != nil { 1068 panic(err) 1069 } 1070 1071 filelock.Lock(portFile) 1072 defer filelock.Unlock(portFile) 1073 1074 fileInfo, err := portFile.Stat() 1075 if err != nil { 1076 panic(err) 1077 } 1078 1079 portBytes, err := io.ReadAll(portFile) 1080 if err != nil { 1081 panic(err) 1082 } 1083 1084 var port int 1085 if len(portBytes) == 0 || time.Now().After(fileInfo.ModTime().Add(portFileTimeout)) { 1086 port = getVtStartPort() 1087 } else { 1088 parsedPort, err := strconv.ParseInt(string(portBytes), 10, 64) 1089 if err != nil { 1090 panic(err) 1091 } 1092 port = int(parsedPort) 1093 } 1094 1095 portFile.Truncate(0) 1096 portFile.Seek(0, 0) 1097 portFile.WriteString(fmt.Sprintf("%v", port+200)) 1098 portFile.Close() 1099 return port 1100 } 1101 1102 // GetAndReserveTabletUID gives tablet uid 1103 func (cluster *LocalProcessCluster) GetAndReserveTabletUID() int { 1104 if cluster.BaseTabletUID == 0 { 1105 if *forceBaseTabletUID > 0 { 1106 cluster.BaseTabletUID = *forceBaseTabletUID 1107 } else { 1108 cluster.BaseTabletUID = getRandomNumber(10000, 0) 1109 } 1110 } 1111 cluster.BaseTabletUID = cluster.BaseTabletUID + 1 1112 return cluster.BaseTabletUID 1113 } 1114 1115 func getRandomNumber(maxNumber int32, baseNumber int) int { 1116 return int(rand.Int31n(maxNumber)) + baseNumber 1117 } 1118 1119 func getVtStartPort() int { 1120 osVtPort := os.Getenv("VTPORTSTART") 1121 if osVtPort != "" { 1122 cport, err := strconv.Atoi(osVtPort) 1123 if err == nil { 1124 return cport 1125 } 1126 } 1127 return DefaultStartPort 1128 } 1129 1130 // NewVttabletInstance creates a new vttablet object 1131 func (cluster *LocalProcessCluster) NewVttabletInstance(tabletType string, UID int, cell string) *Vttablet { 1132 if UID == 0 { 1133 UID = cluster.GetAndReserveTabletUID() 1134 } 1135 if cell == "" { 1136 cell = cluster.Cell 1137 } 1138 return &Vttablet{ 1139 TabletUID: UID, 1140 HTTPPort: cluster.GetAndReservePort(), 1141 GrpcPort: cluster.GetAndReservePort(), 1142 MySQLPort: cluster.GetAndReservePort(), 1143 Type: tabletType, 1144 Cell: cell, 1145 Alias: fmt.Sprintf("%s-%010d", cell, UID), 1146 } 1147 } 1148 1149 // NewVTOrcProcess creates a new VTOrcProcess object 1150 func (cluster *LocalProcessCluster) NewVTOrcProcess(config VTOrcConfiguration) *VTOrcProcess { 1151 base := VtctlProcessInstance(cluster.TopoProcess.Port, cluster.Hostname) 1152 base.Binary = "vtorc" 1153 return &VTOrcProcess{ 1154 VtctlProcess: *base, 1155 LogDir: cluster.TmpDirectory, 1156 Config: config, 1157 WebPort: cluster.GetAndReservePort(), 1158 Port: cluster.GetAndReservePort(), 1159 } 1160 } 1161 1162 // NewVtgrProcess creates a new VtgrProcess object 1163 func (cluster *LocalProcessCluster) NewVtgrProcess(clusters []string, config string, grPort int) *VtgrProcess { 1164 base := VtctlProcessInstance(cluster.TopoProcess.Port, cluster.Hostname) 1165 base.Binary = "vtgr" 1166 return &VtgrProcess{ 1167 VtctlProcess: *base, 1168 LogDir: cluster.TmpDirectory, 1169 clusters: clusters, 1170 config: config, 1171 grPort: grPort, 1172 } 1173 } 1174 1175 // VtprocessInstanceFromVttablet creates a new vttablet object 1176 func (cluster *LocalProcessCluster) VtprocessInstanceFromVttablet(tablet *Vttablet, shardName string, ksName string) *VttabletProcess { 1177 return VttabletProcessInstance( 1178 tablet.HTTPPort, 1179 tablet.GrpcPort, 1180 tablet.TabletUID, 1181 cluster.Cell, 1182 shardName, 1183 ksName, 1184 cluster.VtctldProcess.Port, 1185 tablet.Type, 1186 cluster.TopoProcess.Port, 1187 cluster.Hostname, 1188 cluster.TmpDirectory, 1189 cluster.VtTabletExtraArgs, 1190 cluster.DefaultCharset) 1191 } 1192 1193 // StartVttablet starts a new tablet 1194 func (cluster *LocalProcessCluster) StartVttablet(tablet *Vttablet, servingStatus string, 1195 supportBackup bool, cell string, keyspaceName string, hostname string, shardName string) error { 1196 tablet.VttabletProcess = VttabletProcessInstance( 1197 tablet.HTTPPort, 1198 tablet.GrpcPort, 1199 tablet.TabletUID, 1200 cell, 1201 shardName, 1202 keyspaceName, 1203 cluster.VtctldProcess.Port, 1204 tablet.Type, 1205 cluster.TopoProcess.Port, 1206 hostname, 1207 cluster.TmpDirectory, 1208 cluster.VtTabletExtraArgs, 1209 cluster.DefaultCharset) 1210 1211 tablet.VttabletProcess.SupportsBackup = supportBackup 1212 tablet.VttabletProcess.ServingStatus = servingStatus 1213 return tablet.VttabletProcess.Setup() 1214 } 1215 1216 // TopoFlavorString returns the topo flavor 1217 func (cluster *LocalProcessCluster) TopoFlavorString() *string { 1218 if cluster.TopoFlavor != "" { 1219 return &cluster.TopoFlavor 1220 } 1221 return topoFlavor 1222 } 1223 1224 func getCoveragePath(fileName string) string { 1225 covDir := os.Getenv("COV_DIR") 1226 if covDir == "" { 1227 covDir = os.TempDir() 1228 } 1229 return path.Join(covDir, fileName) 1230 } 1231 1232 // PrintMysqlctlLogFiles prints all the log files associated with the mysqlctl binary 1233 func (cluster *LocalProcessCluster) PrintMysqlctlLogFiles() { 1234 logDir := cluster.TmpDirectory 1235 files, _ := os.ReadDir(logDir) 1236 for _, fileInfo := range files { 1237 if !fileInfo.IsDir() && strings.Contains(fileInfo.Name(), "mysqlctl") { 1238 log.Errorf("Printing the log file - " + fileInfo.Name()) 1239 logOut, _ := os.ReadFile(path.Join(logDir, fileInfo.Name())) 1240 log.Errorf(string(logOut)) 1241 } 1242 } 1243 } 1244 1245 // GetVTParams returns mysql.ConnParams with host and port only for regular tests enabling global routing, 1246 // and also dbname if we are testing for a cluster with a partially moved keyspace 1247 func (cluster *LocalProcessCluster) GetVTParams(dbname string) mysql.ConnParams { 1248 params := mysql.ConnParams{ 1249 Host: cluster.Hostname, 1250 Port: cluster.VtgateMySQLPort, 1251 } 1252 if cluster.HasPartialKeyspaces { 1253 params.DbName = dbname 1254 } 1255 return params 1256 } 1257 1258 // DisableVTOrcRecoveries stops all VTOrcs from running any recoveries 1259 func (cluster *LocalProcessCluster) DisableVTOrcRecoveries(t *testing.T) { 1260 for _, vtorc := range cluster.VTOrcProcesses { 1261 vtorc.DisableGlobalRecoveries(t) 1262 } 1263 } 1264 1265 // EnableVTOrcRecoveries allows all VTOrcs to run any recoveries 1266 func (cluster *LocalProcessCluster) EnableVTOrcRecoveries(t *testing.T) { 1267 for _, vtorc := range cluster.VTOrcProcesses { 1268 vtorc.EnableGlobalRecoveries(t) 1269 } 1270 }