github.com/pingcap/chaos@v0.0.0-20190710112158-c86faf4b3719/pkg/util/util.go (about)

     1  package util
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/url"
     7  	"path"
     8  	"path/filepath"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/pingcap/chaos/pkg/util/ssh"
    13  )
    14  
    15  // IsFileExist runs on node and returns true if the file exists.
    16  func IsFileExist(ctx context.Context, node string, name string) bool {
    17  	err := ssh.Exec(ctx, node, "stat", name)
    18  	return err == nil
    19  }
    20  
    21  // IsProcessExist runs on node and returns true if the porcess still exists.
    22  func IsProcessExist(ctx context.Context, node string, pid int) bool {
    23  	err := ssh.Exec(ctx, node, "kill", fmt.Sprintf("-s 0 %d", pid))
    24  	return err == nil
    25  }
    26  
    27  // Wget runs on node, downloads a string URL to the dest directory and returns the file path.
    28  // SKips if the file already exists.
    29  func Wget(ctx context.Context, node string, rawURL string, dest string) (string, error) {
    30  	u, err := url.Parse(rawURL)
    31  	if err != nil {
    32  		return "", err
    33  	}
    34  
    35  	if len(dest) == 0 {
    36  		dest = "."
    37  	}
    38  
    39  	fileName := path.Base(u.Path)
    40  	filePath := path.Join(dest, fileName)
    41  
    42  	Mkdir(ctx, node, dest)
    43  	err = ssh.Exec(ctx, node, "wget", "--tries", "20", "--waitretry", "60",
    44  		"--retry-connrefused", "--dns-timeout", "60", "--connect-timeout", "60",
    45  		"--read-timeout", "60", "--no-clobber", "--no-verbose", "--directory-prefix", dest, rawURL)
    46  	return filePath, err
    47  }
    48  
    49  // InstallArchive runs on node, downloads the URL and extracts the archive to the dest diretory.
    50  // Supports zip, and tarball.
    51  func InstallArchive(ctx context.Context, node string, rawURL string, dest string) error {
    52  	err := ssh.Exec(ctx, node, "mkdir", "-p", "/tmp/chaos")
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	tmpDir := fmt.Sprintf("/tmp/chaos/archive_%d", time.Now().UnixNano())
    58  	Mkdir(ctx, node, tmpDir)
    59  	defer RemoveDir(ctx, node, tmpDir)
    60  
    61  	var name string
    62  	if strings.HasPrefix(rawURL, "file://") {
    63  		name = rawURL[len("file://"):]
    64  	} else {
    65  		if name, err = Wget(ctx, node, rawURL, "/tmp/chaos"); err != nil {
    66  			return err
    67  		}
    68  	}
    69  
    70  	if strings.HasSuffix(name, ".zip") {
    71  		err = ssh.Exec(ctx, node, "unzip", "-d", tmpDir, name)
    72  	} else if strings.HasSuffix(name, ".tar.gz") {
    73  		err = ssh.Exec(ctx, node, "tar", "-xzf", name, "-C", tmpDir)
    74  	} else {
    75  		err = ssh.Exec(ctx, node, "tar", "-xf", name, "-C", tmpDir)
    76  	}
    77  
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	if dest, err = filepath.Abs(dest); err != nil {
    83  		return err
    84  	}
    85  
    86  	RemoveDir(ctx, node, dest)
    87  	Mkdir(ctx, node, path.Dir(dest))
    88  
    89  	var files []string
    90  	if files, err = ReadDir(ctx, node, tmpDir); err != nil {
    91  		return err
    92  	} else if len(files) == 1 && IsDir(ctx, node, path.Join(tmpDir, files[0])) {
    93  		return ssh.Exec(ctx, node, "mv", path.Join(tmpDir, files[0]), dest)
    94  	}
    95  
    96  	return ssh.Exec(ctx, node, "mv", tmpDir, dest)
    97  }
    98  
    99  // ReadDir runs on node and lists the files of dir.
   100  func ReadDir(ctx context.Context, node string, dir string) ([]string, error) {
   101  	output, err := ssh.CombinedOutput(ctx, node, "ls", dir)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	seps := strings.Split(string(output), "\n")
   107  	v := make([]string, 0, len(seps))
   108  	for _, sep := range seps {
   109  		sep = strings.TrimSpace(sep)
   110  		if len(sep) > 0 {
   111  			v = append(v, sep)
   112  		}
   113  	}
   114  
   115  	return v, nil
   116  }
   117  
   118  // IsDir runs on node and checks path is directory or not.
   119  func IsDir(ctx context.Context, node string, path string) bool {
   120  	err := ssh.Exec(ctx, node, "test", "-d", path)
   121  	return err == nil
   122  }
   123  
   124  // Mkdir runs on node and makes a directory
   125  func Mkdir(ctx context.Context, node string, dir string) error {
   126  	return ssh.Exec(ctx, node, "mkdir", "-p", dir)
   127  }
   128  
   129  // RemoveDir runs on node and removes the diretory
   130  func RemoveDir(ctx context.Context, node string, dir string) error {
   131  	return ssh.Exec(ctx, node, "rm", "-rf", dir)
   132  }
   133  
   134  // WriteFile runs on node and writes data to file
   135  func WriteFile(ctx context.Context, node string, file string, data string) error {
   136  	return ssh.Exec(ctx, node, "echo", "-e", data, ">", file)
   137  }
   138  
   139  // DaemonOptions is the options to start a command in daemon mode.
   140  type DaemonOptions struct {
   141  	ChDir   string
   142  	PidFile string
   143  	NoClose bool
   144  }
   145  
   146  // NewDaemonOptions returns a default daemon options.
   147  func NewDaemonOptions(chDir string, pidFile string) DaemonOptions {
   148  	return DaemonOptions{
   149  		ChDir:   chDir,
   150  		PidFile: pidFile,
   151  		NoClose: false,
   152  	}
   153  }
   154  
   155  // StartDaemon runs on node and starts a daemon process with options
   156  func StartDaemon(ctx context.Context, node string, opts DaemonOptions, cmd string, cmdArgs ...string) error {
   157  	var args []string
   158  	args = append(args, "--start")
   159  	args = append(args, "--background")
   160  	if opts.NoClose {
   161  		args = append(args, "--no-close")
   162  	}
   163  	args = append(args, "--make-pidfile")
   164  
   165  	processName := path.Base(cmd)
   166  	args = append(args, "--name", processName)
   167  
   168  	args = append(args, "--pidfile", opts.PidFile)
   169  	args = append(args, "--chdir", opts.ChDir)
   170  	args = append(args, "--oknodo", "--startas", cmd)
   171  	args = append(args, "--")
   172  	args = append(args, cmdArgs...)
   173  
   174  	return ssh.Exec(ctx, node, "start-stop-daemon", args...)
   175  }
   176  
   177  func parsePID(ctx context.Context, node string, pidFile string) string {
   178  	data, err := ssh.CombinedOutput(ctx, node, "cat", pidFile)
   179  	if err != nil {
   180  		return ""
   181  	}
   182  
   183  	return strings.TrimSpace(string(data))
   184  }
   185  
   186  // StopDaemon runs on node and stops the daemon process.
   187  func StopDaemon(ctx context.Context, node string, cmd string, pidFile string) error {
   188  	return stopDaemon(ctx, node, cmd, pidFile, "TERM")
   189  }
   190  
   191  // KillDaemon runs on node and kills the daemon process.
   192  func KillDaemon(ctx context.Context, node string, cmd string, pidFile string) error {
   193  	return stopDaemon(ctx, node, cmd, pidFile, "KILL")
   194  }
   195  
   196  func stopDaemon(ctx context.Context, node string, cmd string, pidFile string, sig string) error {
   197  	name := path.Base(cmd)
   198  
   199  	return ssh.Exec(ctx, node, "start-stop-daemon", "--stop", "--remove-pidfile",
   200  		"--pidfile", pidFile, "--oknodo", "--name", name, "--signal", sig)
   201  }
   202  
   203  // IsDaemonRunning runs on node and returns whether the daemon is still running or not.
   204  func IsDaemonRunning(ctx context.Context, node string, cmd string, pidFile string) bool {
   205  	name := path.Base(cmd)
   206  
   207  	err := ssh.Exec(ctx, node, "start-stop-daemon", "--status", "--pidfile", pidFile, "--name", name)
   208  
   209  	return err == nil
   210  }