github.com/informationsea/shellflow@v0.1.3/execute_local_single.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "os/exec" 8 "path" 9 "syscall" 10 ) 11 12 var localRunPidFile = "local-run-pid.txt" 13 14 type executationError struct { 15 message string 16 exitCode int 17 jobRoot string 18 shellTask *ShellTask 19 } 20 21 func (s *executationError) Error() string { 22 return s.message 23 } 24 25 func IsExecutionError(err error) bool { 26 _, ok := err.(*executationError) 27 return ok 28 } 29 30 func FollowUpLocalSingle(jobLogRoot string) (bool, error) { 31 rc, err := os.Open(path.Join(jobLogRoot, "rc")) 32 if err == nil { 33 defer rc.Close() 34 return false, nil 35 } else if os.IsNotExist(err) { 36 pidFile, err := os.Open(path.Join(jobLogRoot, localRunPidFile)) 37 if err == nil { 38 var pid int 39 i, err := fmt.Fscanf(pidFile, "%d", &pid) 40 if err != nil { 41 return false, err 42 } 43 if i != 1 { 44 return false, fmt.Errorf("Cannot read pid: %d", i) 45 } 46 process, err := os.FindProcess(pid) 47 if err != nil { 48 return false, err 49 } 50 err = process.Signal(syscall.Signal(0)) 51 if err != nil { 52 rc, err = os.OpenFile(path.Join(jobLogRoot, "rc"), os.O_CREATE|os.O_WRONLY, 0644) 53 if err != nil { 54 return false, err 55 } 56 defer rc.Close() 57 _, err = fmt.Fprintf(rc, "1000") 58 if err != nil { 59 return false, err 60 } 61 } 62 return true, nil 63 } else if os.IsNotExist(err) { 64 return false, nil 65 } else { 66 return false, err 67 } 68 } else { 69 return false, err 70 } 71 } 72 73 // ExecuteLocalSingle runs tasks in local machine and single thread 74 func ExecuteLocalSingle(ge *TaskScripts) error { 75 originalWorkDir, err := os.Getwd() 76 if err != nil { 77 return err 78 } 79 err = os.Chdir(ge.env.workDir) 80 if err != nil { 81 return err 82 } 83 defer os.Chdir(originalWorkDir) 84 85 var finalErr error 86 87 for _, v := range ge.builder.Tasks { 88 if finalErr != nil { 89 scriptInfo := ge.scripts[v.ID] 90 rc, err := os.OpenFile(path.Join(scriptInfo.JobRoot, "rc"), os.O_CREATE|os.O_WRONLY, 0644) 91 if err != nil { 92 return err 93 } 94 defer rc.Close() 95 fmt.Fprintf(rc, "2000") 96 continue 97 } 98 99 if v.ShouldSkip { 100 fmt.Printf("skipping: %s\n", v.ShellScript) 101 continue 102 } 103 104 err := ExecuteLocalSingleOneTask(ge, v) 105 if err != nil { 106 finalErr = err 107 } 108 } 109 110 return finalErr 111 } 112 113 func ExecuteLocalSingleOneTask(ge *TaskScripts, v *ShellTask) error { 114 scriptInfo := ge.scripts[v.ID] 115 args := []string{scriptInfo.RunScriptPath} 116 117 fmt.Printf("%s\n", v.ShellScript) 118 119 cmd := exec.Command("/bin/bash", args...) 120 121 stdout, err := os.OpenFile(path.Join(scriptInfo.JobRoot, "run.stdout"), os.O_CREATE|os.O_WRONLY, 0644) 122 if err != nil { 123 return err 124 } 125 defer stdout.Close() 126 127 stderr, err := os.OpenFile(path.Join(scriptInfo.JobRoot, "run.stderr"), os.O_CREATE|os.O_WRONLY, 0644) 128 if err != nil { 129 return err 130 } 131 defer stderr.Close() 132 133 pid, err := os.OpenFile(path.Join(scriptInfo.JobRoot, localRunPidFile), os.O_CREATE|os.O_WRONLY, 0644) 134 if err != nil { 135 return err 136 } 137 defer pid.Close() 138 139 stdoutPipe, err := cmd.StdoutPipe() 140 if err != nil { 141 return err 142 } 143 144 stderrPipe, err := cmd.StderrPipe() 145 if err != nil { 146 return err 147 } 148 149 err = cmd.Start() 150 if err != nil { 151 return err 152 } 153 154 if _, err = fmt.Fprintf(pid, "%d", cmd.Process.Pid); err != nil { 155 return err 156 } 157 158 if _, err = io.Copy(stdout, stdoutPipe); err != nil { 159 return err 160 } 161 162 if _, err = io.Copy(stderr, stderrPipe); err != nil { 163 return err 164 } 165 166 if err = cmd.Wait(); err != nil { 167 exitCode := 1000 168 status, ok := cmd.ProcessState.Sys().(syscall.WaitStatus) 169 if ok { 170 exitCode = status.ExitStatus() 171 } 172 173 return &executationError{ 174 message: fmt.Sprintf("exit status %d: %s", exitCode, scriptInfo.RunScriptPath), 175 exitCode: exitCode, 176 jobRoot: scriptInfo.JobRoot, 177 shellTask: v, 178 } 179 } 180 181 return nil 182 }