github.com/informationsea/shellflow@v0.1.3/executer.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "os" 8 "os/exec" 9 "os/user" 10 "path" 11 "path/filepath" 12 "regexp" 13 "strconv" 14 "strings" 15 "time" 16 17 "github.com/google/uuid" 18 ) 19 20 type GeneratedScript struct { 21 JobRoot string 22 StdoutPath string 23 StderrPath string 24 RunScriptPath string 25 ScriptPath string 26 Skip bool 27 } 28 29 type TaskScripts struct { 30 workflowRoot string 31 jobName string 32 scripts map[int]*GeneratedScript 33 env *Environment 34 builder *ShellTaskBuilder 35 } 36 37 type Execute func(ge *TaskScripts) error 38 type FollowUp func(log *JobLog) error 39 40 type WorkflowMetaData struct { 41 Env map[string]string 42 Shellflow string 43 Args []string 44 WorkDir string 45 Date time.Time 46 User *user.User 47 Workflow string 48 WorkflowPath string 49 Tasks []*ShellTask 50 Parameters map[string]interface{} 51 ParameterFile string 52 } 53 54 var jobNameRegexp = regexp.MustCompile("(\\w+).*") 55 56 func GenerateTaskScripts(scriptPath string, paramPath string, env *Environment, builder *ShellTaskBuilder) (*TaskScripts, error) { 57 originalWorkDir, err := os.Getwd() 58 if err != nil { 59 return nil, err 60 } 61 err = os.Chdir(env.workDir) 62 if err != nil { 63 return nil, err 64 } 65 defer os.Chdir(originalWorkDir) 66 67 basename := path.Base(scriptPath) 68 uuidObj := uuid.New() 69 workflowDir := path.Join(env.workflowRoot, fmt.Sprintf("%s-%s-%s", time.Now().Format("20060102-150405.000"), basename, uuidObj)) 70 os.MkdirAll(workflowDir, 0755) 71 72 var shellflowPath string 73 _, err = Stat(os.Args[0]) 74 if os.IsNotExist(err) { 75 shellflowPath, err = exec.LookPath(os.Args[0]) 76 if err != nil { 77 return nil, fmt.Errorf("Cannot find shellflow binary absolute path: %s", err.Error()) 78 } 79 } else if err == nil { 80 shellflowPath = Abs(os.Args[0]) 81 } else { 82 return nil, fmt.Errorf("Cannot find shellflow binary absolute path: %s", err.Error()) 83 } 84 85 jobName := path.Base(scriptPath) 86 if paramPath != "" { 87 jobName += " " + path.Base(paramPath) 88 } 89 90 ret := TaskScripts{ 91 workflowRoot: workflowDir, 92 jobName: jobName, 93 scripts: make(map[int]*GeneratedScript), 94 env: env, 95 builder: builder, 96 } 97 98 { 99 // create input file list 100 fileList, err := os.OpenFile(path.Join(workflowDir, "input.json"), os.O_CREATE|os.O_WRONLY, 0644) 101 if err != nil { 102 return nil, err 103 } 104 defer fileList.Close() 105 106 encoder := json.NewEncoder(fileList) 107 encoder.SetIndent("", " ") 108 files, err := CreateFileLog(builder.MissingCreatorFiles.Array(), false, MaximumContentLogSize) 109 if err != nil { 110 return nil, err 111 } 112 err = encoder.Encode(files) 113 if err != nil { 114 return nil, err 115 } 116 117 // for _, x := range files { 118 // tmp, _ := json.MarshalIndent(x, "", " ") 119 // changed, _ := x.IsChanged() 120 // fmt.Printf("input files: %s %v\n", tmp, changed) 121 // } 122 } 123 124 pathEnv := os.ExpandEnv("${PATH}") 125 ldLibraryPathEnv := os.ExpandEnv("${LD_LIBRARY_PATH}") 126 127 { 128 // create runtime information 129 runtimeFile, err := os.OpenFile(path.Join(workflowDir, "runtime.json"), os.O_CREATE|os.O_WRONLY, 0644) 130 if err != nil { 131 return nil, err 132 } 133 defer runtimeFile.Close() 134 135 envMap := make(map[string]string) 136 envMap["PATH"] = pathEnv 137 envMap["LD_LIBRARY_PATH"] = ldLibraryPathEnv 138 139 wd, err := os.Getwd() 140 if err != nil { 141 return nil, err 142 } 143 144 user, err := user.Current() 145 if err != nil { 146 return nil, err 147 } 148 149 var absParamPath string 150 if paramPath != "" { 151 absParamPath = Abs(paramPath) 152 } 153 154 runtime := WorkflowMetaData{ 155 Env: envMap, 156 Shellflow: os.Args[0], 157 Args: os.Args, 158 WorkDir: wd, 159 Date: time.Now(), 160 User: user, 161 Workflow: builder.WorkflowContent, 162 WorkflowPath: Abs(scriptPath), 163 Tasks: builder.Tasks, 164 Parameters: env.parameters, 165 ParameterFile: absParamPath, 166 } 167 168 encoder := json.NewEncoder(runtimeFile) 169 encoder.SetIndent("", " ") 170 encoder.Encode(runtime) 171 } 172 173 { 174 // create job scripts 175 for _, v := range builder.Tasks { 176 jobDir := path.Join(workflowDir, fmt.Sprintf("job%03d", v.ID)) 177 err := os.MkdirAll(jobDir, 0755) 178 if err != nil { 179 return nil, err 180 } 181 182 absScriptPath := Abs(path.Join(jobDir, "script.sh")) 183 absRunScriptPath := Abs(path.Join(jobDir, "run.sh")) 184 absStdoutPath := Abs(path.Join(jobDir, "script.stdout")) 185 absStderrPath := Abs(path.Join(jobDir, "script.stderr")) 186 absResultPath := Abs(path.Join(jobDir, "rc")) 187 absInputPath := Abs(path.Join(jobDir, "input.json")) 188 absOutputPath := Abs(path.Join(jobDir, "output.json")) 189 190 if v.ShouldSkip { 191 copyFiles := []string{"script.sh", "run.sh", "script.stdout", "script.stderr", "rc", "input.json", "output.json"} 192 for _, x := range copyFiles { 193 srcFile, err := os.Open(path.Join(v.ReuseLog.JobLogRoot, x)) 194 if err != nil { 195 return nil, err 196 } 197 defer srcFile.Close() 198 dstFile, err := os.OpenFile(path.Join(jobDir, x), os.O_CREATE|os.O_WRONLY, 0644) 199 if err != nil { 200 return nil, err 201 } 202 defer dstFile.Close() 203 _, err = io.Copy(dstFile, srcFile) 204 if err != nil { 205 return nil, err 206 } 207 } 208 rel, err := filepath.Rel(Abs(jobDir), Abs(v.ReuseLog.JobLogRoot)) 209 if err != nil { 210 return nil, err 211 } 212 err = os.Symlink(rel, path.Join(jobDir, "original")) 213 if err != nil { 214 return nil, err 215 } 216 } else { 217 218 scriptFile, err := os.OpenFile(path.Join(jobDir, "script.sh"), os.O_CREATE|os.O_WRONLY, 0755) 219 if err != nil { 220 return nil, err 221 } 222 defer scriptFile.Close() 223 _, err = scriptFile.WriteString(v.ShellScript) 224 if err != nil { 225 return nil, err 226 } 227 228 var absDependentFilesBuilder strings.Builder 229 for _, v := range v.DependentFiles.Array() { 230 //str := Abs(v) 231 absDependentFilesBuilder.WriteString(strconv.Quote(v)) 232 absDependentFilesBuilder.WriteString(" ") 233 } 234 absDependentFiles := absDependentFilesBuilder.String() 235 236 var absCreatingFilesBuilder strings.Builder 237 for _, v := range v.CreatingFiles.Array() { 238 //str := Abs(v) 239 absCreatingFilesBuilder.WriteString(strconv.Quote(v)) 240 absCreatingFilesBuilder.WriteString(" ") 241 } 242 absCreatingFiles := absCreatingFilesBuilder.String() 243 244 runFile, err := os.OpenFile(path.Join(jobDir, "run.sh"), os.O_CREATE|os.O_WRONLY, 0755) 245 if err != nil { 246 return nil, err 247 } 248 defer runFile.Close() 249 fmt.Fprintf(runFile, `#/bin/bash 250 #set -x 251 cd %s 252 `, env.workDir) 253 254 if !v.CommandConfiguration.DontInheirtPath { 255 fmt.Fprintf(runFile, `#/bin/bash 256 export PATH="%s" 257 export LD_LIBRARY_PATH="%s" 258 `, pathEnv, ldLibraryPathEnv) 259 } 260 261 skipSha := "" 262 if env.skipSha { 263 skipSha = " -skipSha " 264 } 265 266 fmt.Fprintf(runFile, "%s filelog %s -output %s %s || exit 1\n", shellflowPath, skipSha, absInputPath, absDependentFiles) 267 268 fmt.Fprintf(runFile, `/bin/bash -o pipefail -e "%s" > %s 2> %s 269 EXIT_CODE=$? 270 `, absScriptPath, absStdoutPath, absStderrPath) 271 272 skipSha = "" 273 if env.skipSha { 274 skipSha = " -skipSha " 275 } 276 fmt.Fprintf(runFile, "%s filelog %s -output %s %s || exit 1\n", shellflowPath, skipSha, absOutputPath, absCreatingFiles) 277 fmt.Fprintf(runFile, "echo $EXIT_CODE > \"%s\"\n", absResultPath) 278 fmt.Fprintf(runFile, "exit $EXIT_CODE\n") 279 280 } 281 282 ret.scripts[v.ID] = &GeneratedScript{ 283 JobRoot: jobDir, 284 StdoutPath: absStdoutPath, 285 StderrPath: absStderrPath, 286 ScriptPath: absScriptPath, 287 RunScriptPath: absRunScriptPath, 288 Skip: v.ShouldSkip, 289 } 290 } 291 } 292 293 // print workflow directory 294 { 295 cwd, err := os.Getwd() 296 if err != nil { 297 return &ret, nil 298 } 299 r, err := filepath.Rel(cwd, workflowDir) 300 if err != nil { 301 return &ret, nil 302 } 303 fmt.Printf("Workflow Log: %s\n", r) 304 } 305 306 return &ret, nil 307 } 308 309 func Abs(path string) string { 310 abs, err := filepath.Abs(path) 311 if err != nil { 312 panic(err) 313 } 314 return abs 315 }