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  }