github.com/secure-build/gitlab-runner@v12.5.0+incompatible/executors/custom/testdata/test_executor/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strconv" 11 12 "gitlab.com/gitlab-org/gitlab-runner/executors/custom/api" 13 ) 14 15 const ( 16 isBuildError = "CUSTOM_ENV_IS_BUILD_ERROR" 17 isSystemError = "CUSTOM_ENV_IS_SYSTEM_ERROR" 18 isUnknownError = "CUSTOM_ENV_IS_UNKNOWN_ERROR" 19 isRunOnCustomDir = "CUSTOM_ENV_IS_RUN_ON_CUSTOM_DIR" 20 ) 21 22 const ( 23 stageConfig = "config" 24 stagePrepare = "prepare" 25 stageRun = "run" 26 stageCleanup = "cleanup" 27 ) 28 29 func setBuildFailure(msg string, args ...interface{}) { 30 fmt.Println("setting build failure") 31 setFailure(api.BuildFailureExitCodeVariable, msg, args...) 32 } 33 34 func setSystemFailure(msg string, args ...interface{}) { 35 fmt.Println("setting system failure") 36 setFailure(api.SystemFailureExitCodeVariable, msg, args...) 37 } 38 39 func setFailure(failureType string, msg string, args ...interface{}) { 40 fmt.Println() 41 fmt.Printf(msg, args...) 42 fmt.Println() 43 44 exitCode := os.Getenv(failureType) 45 46 code, err := strconv.Atoi(exitCode) 47 if err != nil { 48 panic(fmt.Sprintf("Error while parsing the variable: %v", err)) 49 } 50 51 fmt.Printf("Exitting with code %d\n", code) 52 53 os.Exit(code) 54 } 55 56 type stageFunc func(shell string, args []string) 57 58 func main() { 59 defer func() { 60 r := recover() 61 if r == nil { 62 return 63 } 64 65 setSystemFailure("Executor panicked with: %v", r) 66 }() 67 68 shell := os.Args[1] 69 stage := os.Args[2] 70 71 var args []string 72 if len(os.Args) > 3 { 73 args = os.Args[3:] 74 } 75 76 stages := map[string]stageFunc{ 77 stageConfig: config, 78 stagePrepare: prepare, 79 stageRun: run, 80 stageCleanup: cleanup, 81 } 82 83 stageFn, ok := stages[stage] 84 if !ok { 85 setSystemFailure("Unknown stage %q", stage) 86 } 87 88 _, _ = fmt.Fprintf(os.Stderr, "Custom Executor binary - %q stage\n", stage) 89 _, _ = fmt.Fprintf(os.Stderr, "Mocking execution of: %v\n", args) 90 _, _ = fmt.Fprintln(os.Stderr) 91 92 stageFn(shell, args) 93 } 94 95 func config(shell string, args []string) { 96 customDir := os.Getenv(isRunOnCustomDir) 97 if customDir == "" { 98 return 99 } 100 101 concurrentID := os.Getenv("CUSTOM_ENV_CI_CONCURRENT_PROJECT_ID") 102 projectSlug := os.Getenv("CUSTOM_ENV_CI_PROJECT_PATH_SLUG") 103 104 dir := filepath.Join(customDir, concurrentID, projectSlug) 105 106 type output struct { 107 BuildsDir string `json:"builds_dir"` 108 } 109 110 jsonOutput, err := json.Marshal(output{BuildsDir: dir}) 111 if err != nil { 112 panic(fmt.Errorf("error while creating JSON output: %v", err)) 113 } 114 115 fmt.Print(string(jsonOutput)) 116 } 117 118 func prepare(shell string, args []string) { 119 fmt.Println("PREPARE doesn't accept any arguments. It just does its job") 120 fmt.Println() 121 } 122 123 func run(shell string, args []string) { 124 fmt.Println("RUN accepts two arguments: the path to the script to execute and the stage of the job") 125 fmt.Println() 126 127 mockError() 128 129 if len(args) < 1 { 130 setSystemFailure("Missing script for the run stage") 131 } 132 133 output := bytes.NewBuffer(nil) 134 135 cmd := createCommand(shell, args[0], args[1]) 136 cmd.Stdout = output 137 cmd.Stderr = output 138 139 fmt.Printf("Executing: %#v\n\n", cmd) 140 141 err := cmd.Run() 142 if err != nil { 143 setBuildFailure("Job script exited with: %v", err) 144 } 145 146 fmt.Printf(">>>>>>>>>>\n%s\n<<<<<<<<<<\n\n", output.String()) 147 } 148 149 func mockError() { 150 if len(os.Getenv(isBuildError)) > 0 { 151 // It's a build error. For example: user used an invalid 152 // command in his script which ends with an error thrown 153 // from the underlying shell. 154 155 setBuildFailure("mocked build failure") 156 } 157 158 if len(os.Getenv(isSystemError)) > 0 { 159 // It's a system error. For example: the Custom Executor 160 // script implements a libvirt executor and before executing 161 // the job it needs to prepare the VM. But the preparation 162 // failed. 163 164 setSystemFailure("mocked system failure") 165 } 166 167 if len(os.Getenv(isUnknownError)) > 0 { 168 // This situation should not happen. Custom Executor script 169 // should define the type of failure and return either "build 170 // failure" or "system failure", using the error code values 171 // provided by dedicated variables. 172 173 fmt.Println("mocked system failure") 174 os.Exit(255) 175 } 176 } 177 178 func createCommand(shell string, script string, stage string) *exec.Cmd { 179 shellConfigs := map[string]struct { 180 command string 181 args []string 182 }{ 183 "bash": { 184 command: "bash", 185 args: []string{}, 186 }, 187 "powershell": { 188 command: "powershell", 189 args: []string{"-noprofile", "-noninteractive", "-executionpolicy", "Bypass", "-command"}, 190 }, 191 "cmd": { 192 command: "cmd", 193 args: []string{"/C"}, 194 }, 195 } 196 197 shellConfig, ok := shellConfigs[shell] 198 if !ok { 199 panic(fmt.Sprintf("Unknown shell %q", shell)) 200 } 201 202 args := append(shellConfig.args, script) 203 204 return exec.Command(shellConfig.command, args...) 205 } 206 207 func cleanup(shell string, args []string) { 208 fmt.Println("CLEANUP doesn't accept any arguments. It just does its job") 209 fmt.Println() 210 }