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 }