vitess.io/vitess@v0.16.2/go/test/endtoend/cluster/vtctld_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 "net/http" 22 "os" 23 "os/exec" 24 "path" 25 "strings" 26 "syscall" 27 "time" 28 29 "vitess.io/vitess/go/vt/log" 30 ) 31 32 // VtctldProcess is a generic handle for a running vtctld . 33 // It can be spawned manually 34 type VtctldProcess struct { 35 Name string 36 Binary string 37 CommonArg VtctlProcess 38 ServiceMap string 39 BackupStorageImplementation string 40 FileBackupStorageRoot string 41 LogDir string 42 Port int 43 GrpcPort int 44 VerifyURL string 45 Directory string 46 47 proc *exec.Cmd 48 exit chan error 49 } 50 51 // Setup starts vtctld process with required arguements 52 func (vtctld *VtctldProcess) Setup(cell string, extraArgs ...string) (err error) { 53 _ = createDirectory(vtctld.LogDir, 0700) 54 _ = createDirectory(path.Join(vtctld.Directory, "backups"), 0700) 55 vtctld.proc = exec.Command( 56 vtctld.Binary, 57 "--topo_implementation", vtctld.CommonArg.TopoImplementation, 58 "--topo_global_server_address", vtctld.CommonArg.TopoGlobalAddress, 59 "--topo_global_root", vtctld.CommonArg.TopoGlobalRoot, 60 "--cell", cell, 61 "--service_map", vtctld.ServiceMap, 62 "--backup_storage_implementation", vtctld.BackupStorageImplementation, 63 "--file_backup_storage_root", vtctld.FileBackupStorageRoot, 64 "--log_dir", vtctld.LogDir, 65 "--port", fmt.Sprintf("%d", vtctld.Port), 66 "--grpc_port", fmt.Sprintf("%d", vtctld.GrpcPort), 67 ) 68 if *isCoverage { 69 vtctld.proc.Args = append(vtctld.proc.Args, "--test.coverprofile="+getCoveragePath("vtctld.out")) 70 } 71 vtctld.proc.Args = append(vtctld.proc.Args, extraArgs...) 72 73 errFile, _ := os.Create(path.Join(vtctld.LogDir, "vtctld-stderr.txt")) 74 vtctld.proc.Stderr = errFile 75 76 vtctld.proc.Env = append(vtctld.proc.Env, os.Environ()...) 77 78 log.Infof("Starting vtctld with command: %v", strings.Join(vtctld.proc.Args, " ")) 79 80 err = vtctld.proc.Start() 81 if err != nil { 82 return 83 } 84 85 vtctld.exit = make(chan error) 86 go func() { 87 vtctld.exit <- vtctld.proc.Wait() 88 close(vtctld.exit) 89 }() 90 91 timeout := time.Now().Add(60 * time.Second) 92 for time.Now().Before(timeout) { 93 if vtctld.IsHealthy() { 94 return nil 95 } 96 select { 97 case err := <-vtctld.exit: 98 return fmt.Errorf("process '%s' exited prematurely (err: %s)", vtctld.Name, err) 99 default: 100 time.Sleep(300 * time.Millisecond) 101 } 102 } 103 104 return fmt.Errorf("process '%s' timed out after 60s (err: %s)", vtctld.Name, <-vtctld.exit) 105 } 106 107 func createDirectory(dirName string, mode os.FileMode) error { 108 if _, err := os.Stat(dirName); os.IsNotExist(err) { 109 return os.Mkdir(dirName, mode) 110 } 111 return nil 112 } 113 114 // IsHealthy function checks if vtctld process is up and running 115 func (vtctld *VtctldProcess) IsHealthy() bool { 116 resp, err := http.Get(vtctld.VerifyURL) 117 if err != nil { 118 return false 119 } 120 defer resp.Body.Close() 121 return resp.StatusCode == 200 122 } 123 124 // TearDown shutdowns the running vtctld service 125 func (vtctld *VtctldProcess) TearDown() error { 126 if vtctld.proc == nil || vtctld.exit == nil { 127 return nil 128 } 129 130 // Attempt graceful shutdown with SIGTERM first 131 vtctld.proc.Process.Signal(syscall.SIGTERM) 132 133 select { 134 case err := <-vtctld.exit: 135 vtctld.proc = nil 136 return err 137 138 case <-time.After(10 * time.Second): 139 vtctld.proc.Process.Kill() 140 err := <-vtctld.exit 141 vtctld.proc = nil 142 return err 143 } 144 } 145 146 // VtctldProcessInstance returns a VtctlProcess handle for vtctl process 147 // configured with the given Config. 148 // The process must be manually started by calling setup() 149 func VtctldProcessInstance(httpPort int, grpcPort int, topoPort int, hostname string, tmpDirectory string) *VtctldProcess { 150 vtctl := VtctlProcessInstance(topoPort, hostname) 151 vtctld := &VtctldProcess{ 152 Name: "vtctld", 153 Binary: "vtctld", 154 CommonArg: *vtctl, 155 ServiceMap: "grpc-vtctl,grpc-vtctld", 156 BackupStorageImplementation: "file", 157 FileBackupStorageRoot: path.Join(os.Getenv("VTDATAROOT"), "/backups"), 158 LogDir: tmpDirectory, 159 Port: httpPort, 160 GrpcPort: grpcPort, 161 Directory: os.Getenv("VTDATAROOT"), 162 } 163 vtctld.VerifyURL = fmt.Sprintf("http://%s:%d/debug/vars", hostname, vtctld.Port) 164 return vtctld 165 }