vitess.io/vitess@v0.16.2/go/test/endtoend/cluster/mysqlctld_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 "fmt" 22 "os" 23 "os/exec" 24 "path" 25 "strings" 26 "time" 27 28 "vitess.io/vitess/go/mysql" 29 "vitess.io/vitess/go/vt/log" 30 ) 31 32 // MysqlctldProcess is a generic handle for a running mysqlctld command . 33 // It can be spawned manually 34 type MysqlctldProcess struct { 35 Name string 36 Binary string 37 LogDirectory string 38 Password string 39 TabletUID int 40 MySQLPort int 41 InitDBFile string 42 ExtraArgs []string 43 process *exec.Cmd 44 exit chan error 45 InitMysql bool 46 exitSignalReceived bool 47 } 48 49 // InitDb executes mysqlctld command to add cell info 50 func (mysqlctld *MysqlctldProcess) InitDb() (err error) { 51 tmpProcess := exec.Command( 52 mysqlctld.Binary, 53 "--log_dir", mysqlctld.LogDirectory, 54 "--tablet_uid", fmt.Sprintf("%d", mysqlctld.TabletUID), 55 "--mysql_port", fmt.Sprintf("%d", mysqlctld.MySQLPort), 56 "--init_db_sql_file", mysqlctld.InitDBFile, 57 ) 58 return tmpProcess.Run() 59 } 60 61 // Start starts the mysqlctld and returns the error. 62 func (mysqlctld *MysqlctldProcess) Start() error { 63 if mysqlctld.process != nil { 64 return fmt.Errorf("process is already running") 65 } 66 _ = createDirectory(mysqlctld.LogDirectory, 0700) 67 tempProcess := exec.Command( 68 mysqlctld.Binary, 69 "--log_dir", mysqlctld.LogDirectory, 70 "--tablet_uid", fmt.Sprintf("%d", mysqlctld.TabletUID), 71 "--mysql_port", fmt.Sprintf("%d", mysqlctld.MySQLPort), 72 ) 73 74 tempProcess.Args = append(tempProcess.Args, mysqlctld.ExtraArgs...) 75 76 if mysqlctld.InitMysql { 77 tempProcess.Args = append(tempProcess.Args, 78 "--init_db_sql_file", mysqlctld.InitDBFile) 79 } 80 81 errFile, _ := os.Create(path.Join(mysqlctld.LogDirectory, "mysqlctld-stderr.txt")) 82 tempProcess.Stderr = errFile 83 84 tempProcess.Env = append(tempProcess.Env, os.Environ()...) 85 tempProcess.Stdout = os.Stdout 86 tempProcess.Stderr = os.Stderr 87 88 log.Infof("%v", strings.Join(tempProcess.Args, " ")) 89 90 err := tempProcess.Start() 91 if err != nil { 92 return err 93 } 94 95 mysqlctld.process = tempProcess 96 97 mysqlctld.exit = make(chan error) 98 go func(mysqlctld *MysqlctldProcess) { 99 err := mysqlctld.process.Wait() 100 if !mysqlctld.exitSignalReceived { 101 fmt.Printf("mysqlctld stopped unexpectedly, tabletUID %v, mysql port %v, PID %v\n", mysqlctld.TabletUID, mysqlctld.MySQLPort, mysqlctld.process.Process.Pid) 102 } 103 mysqlctld.process = nil 104 mysqlctld.exitSignalReceived = false 105 mysqlctld.exit <- err 106 }(mysqlctld) 107 108 timeout := time.Now().Add(60 * time.Second) 109 for time.Now().Before(timeout) { 110 if mysqlctld.IsHealthy() { 111 return nil 112 } 113 select { 114 case err := <-mysqlctld.exit: 115 return fmt.Errorf("process '%s' exited prematurely (err: %s)", mysqlctld.Name, err) 116 default: 117 time.Sleep(300 * time.Millisecond) 118 } 119 } 120 121 return fmt.Errorf("process '%s' timed out after 60s (err: %s)", mysqlctld.Name, mysqlctld.Stop()) 122 123 } 124 125 // Stop executes mysqlctld command to stop mysql instance 126 func (mysqlctld *MysqlctldProcess) Stop() error { 127 // if mysqlctld.process == nil || mysqlctld.exit == nil { 128 // return nil 129 // } 130 mysqlctld.exitSignalReceived = true 131 tmpProcess := exec.Command( 132 "mysqlctl", 133 "--tablet_uid", fmt.Sprintf("%d", mysqlctld.TabletUID), 134 ) 135 tmpProcess.Args = append(tmpProcess.Args, mysqlctld.ExtraArgs...) 136 tmpProcess.Args = append(tmpProcess.Args, "shutdown") 137 return tmpProcess.Run() 138 } 139 140 // CleanupFiles clean the mysql files to make sure we can start the same process again 141 func (mysqlctld *MysqlctldProcess) CleanupFiles(tabletUID int) { 142 os.RemoveAll(path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d", tabletUID))) 143 } 144 145 // MysqlCtldProcessInstance returns a Mysqlctld handle for mysqlctld process 146 // configured with the given Config. 147 func MysqlCtldProcessInstance(tabletUID int, mySQLPort int, tmpDirectory string) *MysqlctldProcess { 148 mysqlctld := &MysqlctldProcess{ 149 Name: "mysqlctld", 150 Binary: "mysqlctld", 151 LogDirectory: tmpDirectory, 152 InitDBFile: path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"), 153 } 154 mysqlctld.MySQLPort = mySQLPort 155 mysqlctld.TabletUID = tabletUID 156 mysqlctld.InitMysql = true 157 return mysqlctld 158 } 159 160 // IsHealthy gives the health status of mysql. 161 func (mysqlctld *MysqlctldProcess) IsHealthy() bool { 162 socketFile := path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d", mysqlctld.TabletUID), "/mysql.sock") 163 params := NewConnParams(0, mysqlctld.Password, socketFile, "") 164 _, err := mysql.Connect(context.Background(), ¶ms) 165 return err == nil 166 }