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  }