github.com/nilium/gitlab-runner@v12.5.0+incompatible/common/config.go (about)

     1  package common
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"math/big"
    11  	"os"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/BurntSushi/toml"
    18  	"github.com/docker/go-units"
    19  	"github.com/sirupsen/logrus"
    20  	api "k8s.io/api/core/v1"
    21  
    22  	"gitlab.com/gitlab-org/gitlab-runner/helpers"
    23  	docker_helpers "gitlab.com/gitlab-org/gitlab-runner/helpers/docker"
    24  	"gitlab.com/gitlab-org/gitlab-runner/helpers/ssh"
    25  	"gitlab.com/gitlab-org/gitlab-runner/helpers/timeperiod"
    26  )
    27  
    28  type DockerPullPolicy string
    29  type DockerSysCtls map[string]string
    30  
    31  const (
    32  	PullPolicyAlways       = "always"
    33  	PullPolicyNever        = "never"
    34  	PullPolicyIfNotPresent = "if-not-present"
    35  )
    36  
    37  // Get returns one of the predefined values or returns an error if the value can't match the predefined
    38  func (p DockerPullPolicy) Get() (DockerPullPolicy, error) {
    39  	// Default policy is always
    40  	if p == "" {
    41  		return PullPolicyAlways, nil
    42  	}
    43  
    44  	// Verify pull policy
    45  	if p != PullPolicyNever &&
    46  		p != PullPolicyIfNotPresent &&
    47  		p != PullPolicyAlways {
    48  		return "", fmt.Errorf("unsupported docker-pull-policy: %v", p)
    49  	}
    50  	return p, nil
    51  }
    52  
    53  type DockerConfig struct {
    54  	docker_helpers.DockerCredentials
    55  	Hostname                   string            `toml:"hostname,omitempty" json:"hostname" long:"hostname" env:"DOCKER_HOSTNAME" description:"Custom container hostname"`
    56  	Image                      string            `toml:"image" json:"image" long:"image" env:"DOCKER_IMAGE" description:"Docker image to be used"`
    57  	Runtime                    string            `toml:"runtime,omitempty" json:"runtime" long:"runtime" env:"DOCKER_RUNTIME" description:"Docker runtime to be used"`
    58  	Memory                     string            `toml:"memory,omitempty" json:"memory" long:"memory" env:"DOCKER_MEMORY" description:"Memory limit (format: <number>[<unit>]). Unit can be one of b, k, m, or g. Minimum is 4M."`
    59  	MemorySwap                 string            `toml:"memory_swap,omitempty" json:"memory_swap" long:"memory-swap" env:"DOCKER_MEMORY_SWAP" description:"Total memory limit (memory + swap, format: <number>[<unit>]). Unit can be one of b, k, m, or g."`
    60  	MemoryReservation          string            `toml:"memory_reservation,omitempty" json:"memory_reservation" long:"memory-reservation" env:"DOCKER_MEMORY_RESERVATION" description:"Memory soft limit (format: <number>[<unit>]). Unit can be one of b, k, m, or g."`
    61  	CPUSetCPUs                 string            `toml:"cpuset_cpus,omitempty" json:"cpuset_cpus" long:"cpuset-cpus" env:"DOCKER_CPUSET_CPUS" description:"String value containing the cgroups CpusetCpus to use"`
    62  	CPUS                       string            `toml:"cpus,omitempty" json:"cpus" long:"cpus" env:"DOCKER_CPUS" description:"Number of CPUs"`
    63  	DNS                        []string          `toml:"dns,omitempty" json:"dns" long:"dns" env:"DOCKER_DNS" description:"A list of DNS servers for the container to use"`
    64  	DNSSearch                  []string          `toml:"dns_search,omitempty" json:"dns_search" long:"dns-search" env:"DOCKER_DNS_SEARCH" description:"A list of DNS search domains"`
    65  	Privileged                 bool              `toml:"privileged,omitzero" json:"privileged" long:"privileged" env:"DOCKER_PRIVILEGED" description:"Give extended privileges to container"`
    66  	DisableEntrypointOverwrite bool              `toml:"disable_entrypoint_overwrite,omitzero" json:"disable_entrypoint_overwrite" long:"disable-entrypoint-overwrite" env:"DOCKER_DISABLE_ENTRYPOINT_OVERWRITE" description:"Disable the possibility for a container to overwrite the default image entrypoint"`
    67  	UsernsMode                 string            `toml:"userns_mode,omitempty" json:"userns_mode" long:"userns" env:"DOCKER_USERNS_MODE" description:"User namespace to use"`
    68  	CapAdd                     []string          `toml:"cap_add" json:"cap_add" long:"cap-add" env:"DOCKER_CAP_ADD" description:"Add Linux capabilities"`
    69  	CapDrop                    []string          `toml:"cap_drop" json:"cap_drop" long:"cap-drop" env:"DOCKER_CAP_DROP" description:"Drop Linux capabilities"`
    70  	OomKillDisable             bool              `toml:"oom_kill_disable,omitzero" json:"oom_kill_disable" long:"oom-kill-disable" env:"DOCKER_OOM_KILL_DISABLE" description:"Do not kill processes in a container if an out-of-memory (OOM) error occurs"`
    71  	SecurityOpt                []string          `toml:"security_opt" json:"security_opt" long:"security-opt" env:"DOCKER_SECURITY_OPT" description:"Security Options"`
    72  	Devices                    []string          `toml:"devices" json:"devices" long:"devices" env:"DOCKER_DEVICES" description:"Add a host device to the container"`
    73  	DisableCache               bool              `toml:"disable_cache,omitzero" json:"disable_cache" long:"disable-cache" env:"DOCKER_DISABLE_CACHE" description:"Disable all container caching"`
    74  	Volumes                    []string          `toml:"volumes,omitempty" json:"volumes" long:"volumes" env:"DOCKER_VOLUMES" description:"Bind-mount a volume and create it if it doesn't exist prior to mounting. Can be specified multiple times once per mountpoint, e.g. --docker-volumes 'test0:/test0' --docker-volumes 'test1:/test1'"`
    75  	VolumeDriver               string            `toml:"volume_driver,omitempty" json:"volume_driver" long:"volume-driver" env:"DOCKER_VOLUME_DRIVER" description:"Volume driver to be used"`
    76  	CacheDir                   string            `toml:"cache_dir,omitempty" json:"cache_dir" long:"cache-dir" env:"DOCKER_CACHE_DIR" description:"Directory where to store caches"`
    77  	ExtraHosts                 []string          `toml:"extra_hosts,omitempty" json:"extra_hosts" long:"extra-hosts" env:"DOCKER_EXTRA_HOSTS" description:"Add a custom host-to-IP mapping"`
    78  	VolumesFrom                []string          `toml:"volumes_from,omitempty" json:"volumes_from" long:"volumes-from" env:"DOCKER_VOLUMES_FROM" description:"A list of volumes to inherit from another container"`
    79  	NetworkMode                string            `toml:"network_mode,omitempty" json:"network_mode" long:"network-mode" env:"DOCKER_NETWORK_MODE" description:"Add container to a custom network"`
    80  	Links                      []string          `toml:"links,omitempty" json:"links" long:"links" env:"DOCKER_LINKS" description:"Add link to another container"`
    81  	Services                   []string          `toml:"services,omitempty" json:"services" long:"services" env:"DOCKER_SERVICES" description:"Add service that is started with container"`
    82  	WaitForServicesTimeout     int               `toml:"wait_for_services_timeout,omitzero" json:"wait_for_services_timeout" long:"wait-for-services-timeout" env:"DOCKER_WAIT_FOR_SERVICES_TIMEOUT" description:"How long to wait for service startup"`
    83  	AllowedImages              []string          `toml:"allowed_images,omitempty" json:"allowed_images" long:"allowed-images" env:"DOCKER_ALLOWED_IMAGES" description:"Whitelist allowed images"`
    84  	AllowedServices            []string          `toml:"allowed_services,omitempty" json:"allowed_services" long:"allowed-services" env:"DOCKER_ALLOWED_SERVICES" description:"Whitelist allowed services"`
    85  	PullPolicy                 DockerPullPolicy  `toml:"pull_policy,omitempty" json:"pull_policy" long:"pull-policy" env:"DOCKER_PULL_POLICY" description:"Image pull policy: never, if-not-present, always"`
    86  	ShmSize                    int64             `toml:"shm_size,omitempty" json:"shm_size" long:"shm-size" env:"DOCKER_SHM_SIZE" description:"Shared memory size for docker images (in bytes)"`
    87  	Tmpfs                      map[string]string `toml:"tmpfs,omitempty" json:"tmpfs" long:"tmpfs" env:"DOCKER_TMPFS" description:"A toml table/json object with the format key=values. When set this will mount the specified path in the key as a tmpfs volume in the main container, using the options specified as key. For the supported options, see the documentation for the unix 'mount' command"`
    88  	ServicesTmpfs              map[string]string `toml:"services_tmpfs,omitempty" json:"services_tmpfs" long:"services-tmpfs" env:"DOCKER_SERVICES_TMPFS" description:"A toml table/json object with the format key=values. When set this will mount the specified path in the key as a tmpfs volume in all the service containers, using the options specified as key. For the supported options, see the documentation for the unix 'mount' command"`
    89  	SysCtls                    DockerSysCtls     `toml:"sysctls,omitempty" json:"sysctls" long:"sysctls" env:"DOCKER_SYSCTLS" description:"Sysctl options, a toml table/json object of key=value. Value is expected to be a string."`
    90  	HelperImage                string            `toml:"helper_image,omitempty" json:"helper_image" long:"helper-image" env:"DOCKER_HELPER_IMAGE" description:"[ADVANCED] Override the default helper image used to clone repos and upload artifacts"`
    91  }
    92  
    93  type DockerMachine struct {
    94  	IdleCount      int      `long:"idle-nodes" env:"MACHINE_IDLE_COUNT" description:"Maximum idle machines"`
    95  	IdleTime       int      `toml:"IdleTime,omitzero" long:"idle-time" env:"MACHINE_IDLE_TIME" description:"Minimum time after node can be destroyed"`
    96  	MaxBuilds      int      `toml:"MaxBuilds,omitzero" long:"max-builds" env:"MACHINE_MAX_BUILDS" description:"Maximum number of builds processed by machine"`
    97  	MachineDriver  string   `long:"machine-driver" env:"MACHINE_DRIVER" description:"The driver to use when creating machine"`
    98  	MachineName    string   `long:"machine-name" env:"MACHINE_NAME" description:"The template for machine name (needs to include %s)"`
    99  	MachineOptions []string `long:"machine-options" env:"MACHINE_OPTIONS" description:"Additional machine creation options"`
   100  
   101  	OffPeakPeriods   []string `long:"off-peak-periods" env:"MACHINE_OFF_PEAK_PERIODS" description:"Time periods when the scheduler is in the OffPeak mode"`
   102  	OffPeakTimezone  string   `long:"off-peak-timezone" env:"MACHINE_OFF_PEAK_TIMEZONE" description:"Timezone for the OffPeak periods (defaults to Local)"`
   103  	OffPeakIdleCount int      `long:"off-peak-idle-count" env:"MACHINE_OFF_PEAK_IDLE_COUNT" description:"Maximum idle machines when the scheduler is in the OffPeak mode"`
   104  	OffPeakIdleTime  int      `long:"off-peak-idle-time" env:"MACHINE_OFF_PEAK_IDLE_TIME" description:"Minimum time after machine can be destroyed when the scheduler is in the OffPeak mode"`
   105  
   106  	offPeakTimePeriods *timeperiod.TimePeriod
   107  }
   108  
   109  type ParallelsConfig struct {
   110  	BaseName         string `toml:"base_name" json:"base_name" long:"base-name" env:"PARALLELS_BASE_NAME" description:"VM name to be used"`
   111  	TemplateName     string `toml:"template_name,omitempty" json:"template_name" long:"template-name" env:"PARALLELS_TEMPLATE_NAME" description:"VM template to be created"`
   112  	DisableSnapshots bool   `toml:"disable_snapshots,omitzero" json:"disable_snapshots" long:"disable-snapshots" env:"PARALLELS_DISABLE_SNAPSHOTS" description:"Disable snapshoting to speedup VM creation"`
   113  	TimeServer       string `toml:"time_server,omitempty" json:"time_server" long:"time-server" env:"PARALLELS_TIME_SERVER" description:"Timeserver to sync the guests time from. Defaults to time.apple.com"`
   114  }
   115  
   116  type VirtualBoxConfig struct {
   117  	BaseName         string `toml:"base_name" json:"base_name" long:"base-name" env:"VIRTUALBOX_BASE_NAME" description:"VM name to be used"`
   118  	BaseSnapshot     string `toml:"base_snapshot,omitempty" json:"base_snapshot" long:"base-snapshot" env:"VIRTUALBOX_BASE_SNAPSHOT" description:"Name or UUID of a specific VM snapshot to clone"`
   119  	DisableSnapshots bool   `toml:"disable_snapshots,omitzero" json:"disable_snapshots" long:"disable-snapshots" env:"VIRTUALBOX_DISABLE_SNAPSHOTS" description:"Disable snapshoting to speedup VM creation"`
   120  }
   121  
   122  type CustomConfig struct {
   123  	ConfigExec        string   `toml:"config_exec,omitempty" json:"config_exec" long:"config-exec" env:"CUSTOM_CONFIG_EXEC" description:"Executable that allows to inject configuration values to the executor"`
   124  	ConfigArgs        []string `toml:"config_args,omitempty" json:"config_args" long:"config-args" description:"Arguments for the config executable"`
   125  	ConfigExecTimeout *int     `toml:"config_exec_timeout,omitempty" json:"config_exec_timeout" long:"config-exec-timeout" env:"CUSTOM_CONFIG_EXEC_TIMEOUT" description:"Timeout for the config executable (in seconds)"`
   126  
   127  	PrepareExec        string   `toml:"prepare_exec,omitempty" json:"prepare_exec" long:"prepare-exec" env:"CUSTOM_PREPARE_EXEC" description:"Executable that prepares executor"`
   128  	PrepareArgs        []string `toml:"prepare_args,omitempty" json:"prepare_args" long:"prepare-args" description:"Arguments for the prepare executable"`
   129  	PrepareExecTimeout *int     `toml:"prepare_exec_timeout,omitempty" json:"prepare_exec_timeout" long:"prepare-exec-timeout" env:"CUSTOM_PREPARE_EXEC_TIMEOUT" description:"Timeout for the prepare executable (in seconds)"`
   130  
   131  	RunExec string   `toml:"run_exec" json:"run_exec" long:"run-exec" env:"CUSTOM_RUN_EXEC" description:"Executable that runs the job script in executor"`
   132  	RunArgs []string `toml:"run_args,omitempty" json:"run_args" long:"run-args" description:"Arguments for the run executable"`
   133  
   134  	CleanupExec        string   `toml:"cleanup_exec,omitempty" json:"cleanup_exec" long:"cleanup-exec" env:"CUSTOM_CLEANUP_EXEC" description:"Executable that cleanups after executor run"`
   135  	CleanupArgs        []string `toml:"cleanup_args,omitempty" json:"cleanup_args" long:"cleanup-args" description:"Arguments for the cleanup executable"`
   136  	CleanupExecTimeout *int     `toml:"cleanup_exec_timeout,omitempty" json:"cleanup_exec_timeout" long:"cleanup-exec-timeout" env:"CUSTOM_CLEANUP_EXEC_TIMEOUT" description:"Timeout for the cleanup executable (in seconds)"`
   137  
   138  	GracefulKillTimeout *int `toml:"graceful_kill_timeout,omitempty" json:"graceful_kill_timeout" long:"graceful-kill-timeout" env:"CUSTOM_GRACEFUL_KILL_TIMEOUT" description:"Graceful timeout for scripts execution after SIGTERM is sent to the process (in seconds). This limits the time given for scripts to perform the cleanup before exiting"`
   139  	ForceKillTimeout    *int `toml:"force_kill_timeout,omitempty" json:"force_kill_timeout" long:"force-kill-timeout" env:"CUSTOM_FORCE_KILL_TIMEOUT" description:"Force timeout for scripts execution (in seconds). Counted from the force kill call; if process will be not terminated, Runner will abandon process termination and log an error"`
   140  }
   141  
   142  type KubernetesPullPolicy string
   143  
   144  // Get returns one of the predefined values in kubernetes notation or returns an error if the value can't match the predefined
   145  func (p KubernetesPullPolicy) Get() (KubernetesPullPolicy, error) {
   146  	switch {
   147  	case p == "":
   148  		return "", nil
   149  	case p == PullPolicyAlways:
   150  		return "Always", nil
   151  	case p == PullPolicyNever:
   152  		return "Never", nil
   153  	case p == PullPolicyIfNotPresent:
   154  		return "IfNotPresent", nil
   155  	}
   156  	return "", fmt.Errorf("unsupported kubernetes-pull-policy: %v", p)
   157  }
   158  
   159  type KubernetesConfig struct {
   160  	Host                           string                       `toml:"host" json:"host" long:"host" env:"KUBERNETES_HOST" description:"Optional Kubernetes master host URL (auto-discovery attempted if not specified)"`
   161  	CertFile                       string                       `toml:"cert_file,omitempty" json:"cert_file" long:"cert-file" env:"KUBERNETES_CERT_FILE" description:"Optional Kubernetes master auth certificate"`
   162  	KeyFile                        string                       `toml:"key_file,omitempty" json:"key_file" long:"key-file" env:"KUBERNETES_KEY_FILE" description:"Optional Kubernetes master auth private key"`
   163  	CAFile                         string                       `toml:"ca_file,omitempty" json:"ca_file" long:"ca-file" env:"KUBERNETES_CA_FILE" description:"Optional Kubernetes master auth ca certificate"`
   164  	BearerTokenOverwriteAllowed    bool                         `toml:"bearer_token_overwrite_allowed" json:"bearer_token_overwrite_allowed" long:"bearer_token_overwrite_allowed" env:"KUBERNETES_BEARER_TOKEN_OVERWRITE_ALLOWED" description:"Bool to authorize builds to specify their own bearer token for creation."`
   165  	BearerToken                    string                       `toml:"bearer_token,omitempty" json:"bearer_token" long:"bearer_token" env:"KUBERNETES_BEARER_TOKEN" description:"Optional Kubernetes service account token used to start build pods."`
   166  	Image                          string                       `toml:"image" json:"image" long:"image" env:"KUBERNETES_IMAGE" description:"Default docker image to use for builds when none is specified"`
   167  	Namespace                      string                       `toml:"namespace" json:"namespace" long:"namespace" env:"KUBERNETES_NAMESPACE" description:"Namespace to run Kubernetes jobs in"`
   168  	NamespaceOverwriteAllowed      string                       `toml:"namespace_overwrite_allowed" json:"namespace_overwrite_allowed" long:"namespace_overwrite_allowed" env:"KUBERNETES_NAMESPACE_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_NAMESPACE_OVERWRITE' value"`
   169  	Privileged                     bool                         `toml:"privileged,omitzero" json:"privileged" long:"privileged" env:"KUBERNETES_PRIVILEGED" description:"Run all containers with the privileged flag enabled"`
   170  	CPULimit                       string                       `toml:"cpu_limit,omitempty" json:"cpu_limit" long:"cpu-limit" env:"KUBERNETES_CPU_LIMIT" description:"The CPU allocation given to build containers"`
   171  	MemoryLimit                    string                       `toml:"memory_limit,omitempty" json:"memory_limit" long:"memory-limit" env:"KUBERNETES_MEMORY_LIMIT" description:"The amount of memory allocated to build containers"`
   172  	ServiceCPULimit                string                       `toml:"service_cpu_limit,omitempty" json:"service_cpu_limit" long:"service-cpu-limit" env:"KUBERNETES_SERVICE_CPU_LIMIT" description:"The CPU allocation given to build service containers"`
   173  	ServiceMemoryLimit             string                       `toml:"service_memory_limit,omitempty" json:"service_memory_limit" long:"service-memory-limit" env:"KUBERNETES_SERVICE_MEMORY_LIMIT" description:"The amount of memory allocated to build service containers"`
   174  	HelperCPULimit                 string                       `toml:"helper_cpu_limit,omitempty" json:"helper_cpu_limit" long:"helper-cpu-limit" env:"KUBERNETES_HELPER_CPU_LIMIT" description:"The CPU allocation given to build helper containers"`
   175  	HelperMemoryLimit              string                       `toml:"helper_memory_limit,omitempty" json:"helper_memory_limit" long:"helper-memory-limit" env:"KUBERNETES_HELPER_MEMORY_LIMIT" description:"The amount of memory allocated to build helper containers"`
   176  	CPURequest                     string                       `toml:"cpu_request,omitempty" json:"cpu_request" long:"cpu-request" env:"KUBERNETES_CPU_REQUEST" description:"The CPU allocation requested for build containers"`
   177  	MemoryRequest                  string                       `toml:"memory_request,omitempty" json:"memory_request" long:"memory-request" env:"KUBERNETES_MEMORY_REQUEST" description:"The amount of memory requested from build containers"`
   178  	ServiceCPURequest              string                       `toml:"service_cpu_request,omitempty" json:"service_cpu_request" long:"service-cpu-request" env:"KUBERNETES_SERVICE_CPU_REQUEST" description:"The CPU allocation requested for build service containers"`
   179  	ServiceMemoryRequest           string                       `toml:"service_memory_request,omitempty" json:"service_memory_request" long:"service-memory-request" env:"KUBERNETES_SERVICE_MEMORY_REQUEST" description:"The amount of memory requested for build service containers"`
   180  	HelperCPURequest               string                       `toml:"helper_cpu_request,omitempty" json:"helper_cpu_request" long:"helper-cpu-request" env:"KUBERNETES_HELPER_CPU_REQUEST" description:"The CPU allocation requested for build helper containers"`
   181  	HelperMemoryRequest            string                       `toml:"helper_memory_request,omitempty" json:"helper_memory_request" long:"helper-memory-request" env:"KUBERNETES_HELPER_MEMORY_REQUEST" description:"The amount of memory requested for build helper containers"`
   182  	PullPolicy                     KubernetesPullPolicy         `toml:"pull_policy,omitempty" json:"pull_policy" long:"pull-policy" env:"KUBERNETES_PULL_POLICY" description:"Policy for if/when to pull a container image (never, if-not-present, always). The cluster default will be used if not set"`
   183  	NodeSelector                   map[string]string            `toml:"node_selector,omitempty" json:"node_selector" long:"node-selector" env:"KUBERNETES_NODE_SELECTOR" description:"A toml table/json object of key=value. Value is expected to be a string. When set this will create pods on k8s nodes that match all the key=value pairs."`
   184  	NodeTolerations                map[string]string            `toml:"node_tolerations,omitempty" json:"node_tolerations" long:"node-tolerations" env:"KUBERNETES_NODE_TOLERATIONS" description:"A toml table/json object of key=value:effect. Value and effect are expected to be strings. When set, pods will tolerate the given taints. Only one toleration is supported through environment variable configuration."`
   185  	ImagePullSecrets               []string                     `toml:"image_pull_secrets,omitempty" json:"image_pull_secrets" long:"image-pull-secrets" env:"KUBERNETES_IMAGE_PULL_SECRETS" description:"A list of image pull secrets that are used for pulling docker image"`
   186  	HelperImage                    string                       `toml:"helper_image,omitempty" json:"helper_image" long:"helper-image" env:"KUBERNETES_HELPER_IMAGE" description:"[ADVANCED] Override the default helper image used to clone repos and upload artifacts"`
   187  	TerminationGracePeriodSeconds  int64                        `toml:"terminationGracePeriodSeconds,omitzero" json:"terminationGracePeriodSeconds" long:"terminationGracePeriodSeconds" env:"KUBERNETES_TERMINATIONGRACEPERIODSECONDS" description:"Duration after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal."`
   188  	PollInterval                   int                          `toml:"poll_interval,omitzero" json:"poll_interval" long:"poll-interval" env:"KUBERNETES_POLL_INTERVAL" description:"How frequently, in seconds, the runner will poll the Kubernetes pod it has just created to check its status"`
   189  	PollTimeout                    int                          `toml:"poll_timeout,omitzero" json:"poll_timeout" long:"poll-timeout" env:"KUBERNETES_POLL_TIMEOUT" description:"The total amount of time, in seconds, that needs to pass before the runner will timeout attempting to connect to the pod it has just created (useful for queueing more builds that the cluster can handle at a time)"`
   190  	PodLabels                      map[string]string            `toml:"pod_labels,omitempty" json:"pod_labels" long:"pod-labels" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given pod labels. Environment variables will be substituted for values here."`
   191  	ServiceAccount                 string                       `toml:"service_account,omitempty" json:"service_account" long:"service-account" env:"KUBERNETES_SERVICE_ACCOUNT" description:"Executor pods will use this Service Account to talk to kubernetes API"`
   192  	ServiceAccountOverwriteAllowed string                       `toml:"service_account_overwrite_allowed" json:"service_account_overwrite_allowed" long:"service_account_overwrite_allowed" env:"KUBERNETES_SERVICE_ACCOUNT_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_SERVICE_ACCOUNT' value"`
   193  	PodAnnotations                 map[string]string            `toml:"pod_annotations,omitempty" json:"pod_annotations" long:"pod-annotations" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given annotations. Can be overwritten in build with KUBERNETES_POD_ANNOTATION_* variables"`
   194  	PodAnnotationsOverwriteAllowed string                       `toml:"pod_annotations_overwrite_allowed" json:"pod_annotations_overwrite_allowed" long:"pod_annotations_overwrite_allowed" env:"KUBERNETES_POD_ANNOTATIONS_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_POD_ANNOTATIONS_*' values"`
   195  	PodSecurityContext             KubernetesPodSecurityContext `toml:"pod_security_context,omitempty" namespace:"pod-security-context" description:"A security context attached to each build pod"`
   196  	Volumes                        KubernetesVolumes            `toml:"volumes"`
   197  	Services                       []Service                    `toml:"services,omitempty" json:"services" long:"services" description:"Add service that is started with container"`
   198  }
   199  
   200  type KubernetesVolumes struct {
   201  	HostPaths  []KubernetesHostPath  `toml:"host_path" description:"The host paths which will be mounted"`
   202  	PVCs       []KubernetesPVC       `toml:"pvc" description:"The persistent volume claims that will be mounted"`
   203  	ConfigMaps []KubernetesConfigMap `toml:"config_map" description:"The config maps which will be mounted as volumes"`
   204  	Secrets    []KubernetesSecret    `toml:"secret" description:"The secret maps which will be mounted"`
   205  	EmptyDirs  []KubernetesEmptyDir  `toml:"empty_dir" description:"The empty dirs which will be mounted"`
   206  }
   207  
   208  type KubernetesConfigMap struct {
   209  	Name      string            `toml:"name" json:"name" description:"The name of the volume and ConfigMap to use"`
   210  	MountPath string            `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   211  	ReadOnly  bool              `toml:"read_only,omitempty" description:"If this volume should be mounted read only"`
   212  	Items     map[string]string `toml:"items,omitempty" description:"Key-to-path mapping for keys from the config map that should be used."`
   213  }
   214  
   215  type KubernetesHostPath struct {
   216  	Name      string `toml:"name" json:"name" description:"The name of the volume"`
   217  	MountPath string `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   218  	ReadOnly  bool   `toml:"read_only,omitempty" description:"If this volume should be mounted read only"`
   219  	HostPath  string `toml:"host_path,omitempty" description:"Path from the host that should be mounted as a volume"`
   220  }
   221  
   222  type KubernetesPVC struct {
   223  	Name      string `toml:"name" json:"name" description:"The name of the volume and PVC to use"`
   224  	MountPath string `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   225  	ReadOnly  bool   `toml:"read_only,omitempty" description:"If this volume should be mounted read only"`
   226  }
   227  
   228  type KubernetesSecret struct {
   229  	Name      string            `toml:"name" json:"name" description:"The name of the volume and Secret to use"`
   230  	MountPath string            `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   231  	ReadOnly  bool              `toml:"read_only,omitempty" description:"If this volume should be mounted read only"`
   232  	Items     map[string]string `toml:"items,omitempty" description:"Key-to-path mapping for keys from the secret that should be used."`
   233  }
   234  
   235  type KubernetesEmptyDir struct {
   236  	Name      string `toml:"name" json:"name" description:"The name of the volume and EmptyDir to use"`
   237  	MountPath string `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   238  	Medium    string `toml:"medium,omitempty" description:"Set to 'Memory' to have a tmpfs"`
   239  }
   240  
   241  type KubernetesPodSecurityContext struct {
   242  	FSGroup            *int64  `toml:"fs_group,omitempty" long:"fs-group" env:"KUBERNETES_POD_SECURITY_CONTEXT_FS_GROUP" description:"A special supplemental group that applies to all containers in a pod"`
   243  	RunAsGroup         *int64  `toml:"run_as_group,omitempty" long:"run-as-group" env:"KUBERNETES_POD_SECURITY_CONTEXT_RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"`
   244  	RunAsNonRoot       *bool   `toml:"run_as_non_root,omitempty" long:"run-as-non-root" env:"KUBERNETES_POD_SECURITY_CONTEXT_RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"`
   245  	RunAsUser          *int64  `toml:"run_as_user,omitempty" long:"run-as-user" env:"KUBERNETES_POD_SECURITY_CONTEXT_RUN_AS_USER" description:"The UID to run the entrypoint of the container process"`
   246  	SupplementalGroups []int64 `toml:"supplemental_groups,omitempty" long:"supplemental-groups" description:"A list of groups applied to the first process run in each container, in addition to the container's primary GID"`
   247  }
   248  
   249  type Service struct {
   250  	Name string `toml:"name" long:"name" description:"The image path for the service"`
   251  }
   252  
   253  type RunnerCredentials struct {
   254  	URL         string `toml:"url" json:"url" short:"u" long:"url" env:"CI_SERVER_URL" required:"true" description:"Runner URL"`
   255  	Token       string `toml:"token" json:"token" short:"t" long:"token" env:"CI_SERVER_TOKEN" required:"true" description:"Runner token"`
   256  	TLSCAFile   string `toml:"tls-ca-file,omitempty" json:"tls-ca-file" long:"tls-ca-file" env:"CI_SERVER_TLS_CA_FILE" description:"File containing the certificates to verify the peer when using HTTPS"`
   257  	TLSCertFile string `toml:"tls-cert-file,omitempty" json:"tls-cert-file" long:"tls-cert-file" env:"CI_SERVER_TLS_CERT_FILE" description:"File containing certificate for TLS client auth when using HTTPS"`
   258  	TLSKeyFile  string `toml:"tls-key-file,omitempty" json:"tls-key-file" long:"tls-key-file" env:"CI_SERVER_TLS_KEY_FILE" description:"File containing private key for TLS client auth when using HTTPS"`
   259  }
   260  
   261  type CacheGCSCredentials struct {
   262  	AccessID   string `toml:"AccessID,omitempty" long:"access-id" env:"CACHE_GCS_ACCESS_ID" description:"ID of GCP Service Account used to access the storage"`
   263  	PrivateKey string `toml:"PrivateKey,omitempty" long:"private-key" env:"CACHE_GCS_PRIVATE_KEY" description:"Private key used to sign GCS requests"`
   264  }
   265  
   266  type CacheGCSConfig struct {
   267  	CacheGCSCredentials
   268  	CredentialsFile string `toml:"CredentialsFile,omitempty" long:"credentials-file" env:"GOOGLE_APPLICATION_CREDENTIALS" description:"File with GCP credentials, containing AccessID and PrivateKey"`
   269  	BucketName      string `toml:"BucketName,omitempty" long:"bucket-name" env:"CACHE_GCS_BUCKET_NAME" description:"Name of the bucket where cache will be stored"`
   270  }
   271  
   272  type CacheS3Config struct {
   273  	ServerAddress  string `toml:"ServerAddress,omitempty" long:"server-address" env:"CACHE_S3_SERVER_ADDRESS" description:"A host:port to the used S3-compatible server"`
   274  	AccessKey      string `toml:"AccessKey,omitempty" long:"access-key" env:"CACHE_S3_ACCESS_KEY" description:"S3 Access Key"`
   275  	SecretKey      string `toml:"SecretKey,omitempty" long:"secret-key" env:"CACHE_S3_SECRET_KEY" description:"S3 Secret Key"`
   276  	BucketName     string `toml:"BucketName,omitempty" long:"bucket-name" env:"CACHE_S3_BUCKET_NAME" description:"Name of the bucket where cache will be stored"`
   277  	BucketLocation string `toml:"BucketLocation,omitempty" long:"bucket-location" env:"CACHE_S3_BUCKET_LOCATION" description:"Name of S3 region"`
   278  	Insecure       bool   `toml:"Insecure,omitempty" long:"insecure" env:"CACHE_S3_INSECURE" description:"Use insecure mode (without https)"`
   279  }
   280  
   281  type CacheConfig struct {
   282  	Type   string `toml:"Type,omitempty" long:"type" env:"CACHE_TYPE" description:"Select caching method"`
   283  	Path   string `toml:"Path,omitempty" long:"path" env:"CACHE_PATH" description:"Name of the path to prepend to the cache URL"`
   284  	Shared bool   `toml:"Shared,omitempty" long:"shared" env:"CACHE_SHARED" description:"Enable cache sharing between runners."`
   285  
   286  	S3  *CacheS3Config  `toml:"s3,omitempty" json:"s3" namespace:"s3"`
   287  	GCS *CacheGCSConfig `toml:"gcs,omitempty" json:"gcs" namespace:"gcs"`
   288  }
   289  
   290  type RunnerSettings struct {
   291  	Executor  string `toml:"executor" json:"executor" long:"executor" env:"RUNNER_EXECUTOR" required:"true" description:"Select executor, eg. shell, docker, etc."`
   292  	BuildsDir string `toml:"builds_dir,omitempty" json:"builds_dir" long:"builds-dir" env:"RUNNER_BUILDS_DIR" description:"Directory where builds are stored"`
   293  	CacheDir  string `toml:"cache_dir,omitempty" json:"cache_dir" long:"cache-dir" env:"RUNNER_CACHE_DIR" description:"Directory where build cache is stored"`
   294  	CloneURL  string `toml:"clone_url,omitempty" json:"clone_url" long:"clone-url" env:"CLONE_URL" description:"Overwrite the default URL used to clone or fetch the git ref"`
   295  
   296  	Environment     []string `toml:"environment,omitempty" json:"environment" long:"env" env:"RUNNER_ENV" description:"Custom environment variables injected to build environment"`
   297  	PreCloneScript  string   `toml:"pre_clone_script,omitempty" json:"pre_clone_script" long:"pre-clone-script" env:"RUNNER_PRE_CLONE_SCRIPT" description:"Runner-specific command script executed before code is pulled"`
   298  	PreBuildScript  string   `toml:"pre_build_script,omitempty" json:"pre_build_script" long:"pre-build-script" env:"RUNNER_PRE_BUILD_SCRIPT" description:"Runner-specific command script executed after code is pulled, just before build executes"`
   299  	PostBuildScript string   `toml:"post_build_script,omitempty" json:"post_build_script" long:"post-build-script" env:"RUNNER_POST_BUILD_SCRIPT" description:"Runner-specific command script executed after code is pulled and just after build executes"`
   300  
   301  	DebugTraceDisabled bool `toml:"debug_trace_disabled,omitempty" json:"debug_trace_disabled" long:"debug-trace-disabled" env:"RUNNER_DEBUG_TRACE_DISABLED" description:"When set to true Runner will disable the possibility of using the CI_DEBUG_TRACE feature"`
   302  
   303  	Shell string `toml:"shell,omitempty" json:"shell" long:"shell" env:"RUNNER_SHELL" description:"Select bash, cmd or powershell"`
   304  
   305  	CustomBuildDir *CustomBuildDir   `toml:"custom_build_dir,omitempty" json:"custom_build_dir" group:"custom build dir configuration" namespace:"custom_build_dir"`
   306  	SSH            *ssh.Config       `toml:"ssh,omitempty" json:"ssh" group:"ssh executor" namespace:"ssh"`
   307  	Docker         *DockerConfig     `toml:"docker,omitempty" json:"docker" group:"docker executor" namespace:"docker"`
   308  	Parallels      *ParallelsConfig  `toml:"parallels,omitempty" json:"parallels" group:"parallels executor" namespace:"parallels"`
   309  	VirtualBox     *VirtualBoxConfig `toml:"virtualbox,omitempty" json:"virtualbox" group:"virtualbox executor" namespace:"virtualbox"`
   310  	Cache          *CacheConfig      `toml:"cache,omitempty" json:"cache" group:"cache configuration" namespace:"cache"`
   311  	Machine        *DockerMachine    `toml:"machine,omitempty" json:"machine" group:"docker machine provider" namespace:"machine"`
   312  	Kubernetes     *KubernetesConfig `toml:"kubernetes,omitempty" json:"kubernetes" group:"kubernetes executor" namespace:"kubernetes"`
   313  	Custom         *CustomConfig     `toml:"custom,omitempty" json:"custom" group:"custom executor" namespace:"custom"`
   314  }
   315  
   316  type RunnerConfig struct {
   317  	Name               string `toml:"name" json:"name" short:"name" long:"description" env:"RUNNER_NAME" description:"Runner name"`
   318  	Limit              int    `toml:"limit,omitzero" json:"limit" long:"limit" env:"RUNNER_LIMIT" description:"Maximum number of builds processed by this runner"`
   319  	OutputLimit        int    `toml:"output_limit,omitzero" long:"output-limit" env:"RUNNER_OUTPUT_LIMIT" description:"Maximum build trace size in kilobytes"`
   320  	RequestConcurrency int    `toml:"request_concurrency,omitzero" long:"request-concurrency" env:"RUNNER_REQUEST_CONCURRENCY" description:"Maximum concurrency for job requests"`
   321  
   322  	RunnerCredentials
   323  	RunnerSettings
   324  }
   325  
   326  type SessionServer struct {
   327  	ListenAddress    string `toml:"listen_address,omitempty" json:"listen_address" description:"Address that the runner will communicate directly with"`
   328  	AdvertiseAddress string `toml:"advertise_address,omitempty" json:"advertise_address" description:"Address the runner will expose to the world to connect to the session server"`
   329  	SessionTimeout   int    `toml:"session_timeout,omitempty" json:"session_timeout" description:"How long a terminal session can be active after a build completes, in seconds"`
   330  }
   331  
   332  type Config struct {
   333  	ListenAddress string        `toml:"listen_address,omitempty" json:"listen_address"`
   334  	SessionServer SessionServer `toml:"session_server,omitempty" json:"session_server"`
   335  
   336  	Concurrent    int             `toml:"concurrent" json:"concurrent"`
   337  	CheckInterval int             `toml:"check_interval" json:"check_interval" description:"Define active checking interval of jobs"`
   338  	LogLevel      *string         `toml:"log_level" json:"log_level" description:"Define log level (one of: panic, fatal, error, warning, info, debug)"`
   339  	LogFormat     *string         `toml:"log_format" json:"log_format" description:"Define log format (one of: runner, text, json)"`
   340  	User          string          `toml:"user,omitempty" json:"user"`
   341  	Runners       []*RunnerConfig `toml:"runners" json:"runners"`
   342  	SentryDSN     *string         `toml:"sentry_dsn"`
   343  	ModTime       time.Time       `toml:"-"`
   344  	Loaded        bool            `toml:"-"`
   345  }
   346  
   347  type CustomBuildDir struct {
   348  	Enabled bool `toml:"enabled,omitempty" json:"enabled" long:"enabled" env:"CUSTOM_BUILD_DIR_ENABLED" description:"Enable job specific build directories"`
   349  }
   350  
   351  func getDeprecatedStringSetting(setting string, tomlField string, envVariable string, tomlReplacement string, envReplacement string) string {
   352  	if setting != "" {
   353  		logrus.Warningf("%s setting is deprecated and will be removed in GitLab Runner 12.0. Please use %s instead", tomlField, tomlReplacement)
   354  		return setting
   355  	}
   356  
   357  	value := os.Getenv(envVariable)
   358  	if value != "" {
   359  		logrus.Warningf("%s environment variables is deprecated and will be removed in GitLab Runner 12.0. Please use %s instead", envVariable, envReplacement)
   360  	}
   361  
   362  	return value
   363  }
   364  
   365  func getDeprecatedBoolSetting(setting bool, tomlField string, envVariable string, tomlReplacement string, envReplacement string) bool {
   366  	if setting {
   367  		logrus.Warningf("%s setting is deprecated and will be removed in GitLab Runner 12.0. Please use %s instead", tomlField, tomlReplacement)
   368  		return setting
   369  	}
   370  
   371  	value, _ := strconv.ParseBool(os.Getenv(envVariable))
   372  	if value {
   373  		logrus.Warningf("%s environment variables is deprecated and will be removed in GitLab Runner 12.0. Please use %s instead", envVariable, envReplacement)
   374  	}
   375  
   376  	return value
   377  }
   378  
   379  func (c *CacheS3Config) ShouldUseIAMCredentials() bool {
   380  	return c.ServerAddress == "" || c.AccessKey == "" || c.SecretKey == ""
   381  }
   382  
   383  func (c *CacheConfig) GetPath() string {
   384  	return c.Path
   385  }
   386  
   387  func (c *CacheConfig) GetShared() bool {
   388  	return c.Shared
   389  }
   390  
   391  func (c *SessionServer) GetSessionTimeout() time.Duration {
   392  	if c.SessionTimeout > 0 {
   393  		return time.Duration(c.SessionTimeout) * time.Second
   394  	}
   395  
   396  	return DefaultSessionTimeout
   397  }
   398  
   399  func (c *DockerConfig) GetNanoCPUs() (int64, error) {
   400  	if c.CPUS == "" {
   401  		return 0, nil
   402  	}
   403  
   404  	cpu, ok := new(big.Rat).SetString(c.CPUS)
   405  	if !ok {
   406  		return 0, fmt.Errorf("failed to parse %v as a rational number", c.CPUS)
   407  	}
   408  
   409  	nano, _ := cpu.Mul(cpu, big.NewRat(1e9, 1)).Float64()
   410  
   411  	return int64(nano), nil
   412  }
   413  
   414  func (c *DockerConfig) getMemoryBytes(size string, fieldName string) int64 {
   415  	if size == "" {
   416  		return 0
   417  	}
   418  
   419  	bytes, err := units.RAMInBytes(size)
   420  	if err != nil {
   421  		logrus.Fatalf("Error parsing docker %s: %s", fieldName, err)
   422  	}
   423  
   424  	return bytes
   425  }
   426  
   427  func (c *DockerConfig) GetMemory() int64 {
   428  	return c.getMemoryBytes(c.Memory, "memory")
   429  }
   430  
   431  func (c *DockerConfig) GetMemorySwap() int64 {
   432  	return c.getMemoryBytes(c.MemorySwap, "memory_swap")
   433  }
   434  
   435  func (c *DockerConfig) GetMemoryReservation() int64 {
   436  	return c.getMemoryBytes(c.MemoryReservation, "memory_reservation")
   437  }
   438  
   439  func (c *DockerConfig) GetOomKillDisable() *bool {
   440  	return &c.OomKillDisable
   441  }
   442  
   443  func (c *KubernetesConfig) GetPollAttempts() int {
   444  	if c.PollTimeout <= 0 {
   445  		c.PollTimeout = KubernetesPollTimeout
   446  	}
   447  
   448  	return c.PollTimeout / c.GetPollInterval()
   449  }
   450  
   451  func (c *KubernetesConfig) GetPollInterval() int {
   452  	if c.PollInterval <= 0 {
   453  		c.PollInterval = KubernetesPollInterval
   454  	}
   455  
   456  	return c.PollInterval
   457  }
   458  
   459  func (c *KubernetesConfig) GetNodeTolerations() []api.Toleration {
   460  	var tolerations []api.Toleration
   461  
   462  	for toleration, effect := range c.NodeTolerations {
   463  		newToleration := api.Toleration{
   464  			Effect: api.TaintEffect(effect),
   465  		}
   466  
   467  		if strings.Contains(toleration, "=") {
   468  			parts := strings.Split(toleration, "=")
   469  			newToleration.Key = parts[0]
   470  			if len(parts) > 1 {
   471  				newToleration.Value = parts[1]
   472  			}
   473  			newToleration.Operator = api.TolerationOpEqual
   474  		} else {
   475  			newToleration.Key = toleration
   476  			newToleration.Operator = api.TolerationOpExists
   477  		}
   478  
   479  		tolerations = append(tolerations, newToleration)
   480  	}
   481  
   482  	return tolerations
   483  }
   484  
   485  func (c *KubernetesConfig) GetPodSecurityContext() *api.PodSecurityContext {
   486  	podSecurityContext := c.PodSecurityContext
   487  
   488  	if podSecurityContext.FSGroup == nil &&
   489  		podSecurityContext.RunAsGroup == nil &&
   490  		podSecurityContext.RunAsNonRoot == nil &&
   491  		podSecurityContext.RunAsUser == nil &&
   492  		len(podSecurityContext.SupplementalGroups) == 0 {
   493  		return nil
   494  	}
   495  
   496  	return &api.PodSecurityContext{
   497  		FSGroup:            podSecurityContext.FSGroup,
   498  		RunAsGroup:         podSecurityContext.RunAsGroup,
   499  		RunAsNonRoot:       podSecurityContext.RunAsNonRoot,
   500  		RunAsUser:          podSecurityContext.RunAsUser,
   501  		SupplementalGroups: podSecurityContext.SupplementalGroups,
   502  	}
   503  }
   504  
   505  func (c *DockerMachine) GetIdleCount() int {
   506  	if c.isOffPeak() {
   507  		return c.OffPeakIdleCount
   508  	}
   509  
   510  	return c.IdleCount
   511  }
   512  
   513  func (c *DockerMachine) GetIdleTime() int {
   514  	if c.isOffPeak() {
   515  		return c.OffPeakIdleTime
   516  	}
   517  
   518  	return c.IdleTime
   519  }
   520  
   521  func (c *DockerMachine) isOffPeak() bool {
   522  	if c.offPeakTimePeriods == nil {
   523  		c.CompileOffPeakPeriods()
   524  	}
   525  
   526  	return c.offPeakTimePeriods != nil && c.offPeakTimePeriods.InPeriod()
   527  }
   528  
   529  func (c *DockerMachine) CompileOffPeakPeriods() (err error) {
   530  	c.offPeakTimePeriods, err = timeperiod.TimePeriods(c.OffPeakPeriods, c.OffPeakTimezone)
   531  	if err != nil {
   532  		err = errors.New(fmt.Sprint("Invalid OffPeakPeriods value: ", err))
   533  	}
   534  
   535  	return
   536  }
   537  
   538  func (c *RunnerCredentials) GetURL() string {
   539  	return c.URL
   540  }
   541  
   542  func (c *RunnerCredentials) GetTLSCAFile() string {
   543  	return c.TLSCAFile
   544  }
   545  
   546  func (c *RunnerCredentials) GetTLSCertFile() string {
   547  	return c.TLSCertFile
   548  }
   549  
   550  func (c *RunnerCredentials) GetTLSKeyFile() string {
   551  	return c.TLSKeyFile
   552  }
   553  
   554  func (c *RunnerCredentials) GetToken() string {
   555  	return c.Token
   556  }
   557  
   558  func (c *RunnerCredentials) ShortDescription() string {
   559  	return helpers.ShortenToken(c.Token)
   560  }
   561  
   562  func (c *RunnerCredentials) UniqueID() string {
   563  	return c.URL + c.Token
   564  }
   565  
   566  func (c *RunnerCredentials) Log() *logrus.Entry {
   567  	if c.ShortDescription() != "" {
   568  		return logrus.WithField("runner", c.ShortDescription())
   569  	}
   570  	return logrus.WithFields(logrus.Fields{})
   571  }
   572  
   573  func (c *RunnerCredentials) SameAs(other *RunnerCredentials) bool {
   574  	return c.URL == other.URL && c.Token == other.Token
   575  }
   576  
   577  func (c *RunnerConfig) String() string {
   578  	return fmt.Sprintf("%v url=%v token=%v executor=%v", c.Name, c.URL, c.Token, c.Executor)
   579  }
   580  
   581  func (c *RunnerConfig) GetRequestConcurrency() int {
   582  	if c.RequestConcurrency <= 0 {
   583  		return 1
   584  	}
   585  	return c.RequestConcurrency
   586  }
   587  
   588  func (c *RunnerConfig) GetVariables() JobVariables {
   589  	variables := JobVariables{
   590  		{Key: "CI_RUNNER_SHORT_TOKEN", Value: c.ShortDescription(), Public: true, Internal: true, File: false},
   591  	}
   592  
   593  	for _, environment := range c.Environment {
   594  		if variable, err := ParseVariable(environment); err == nil {
   595  			variable.Internal = true
   596  			variables = append(variables, variable)
   597  		}
   598  	}
   599  
   600  	return variables
   601  }
   602  
   603  // DeepCopy attempts to make a deep clone of the object
   604  func (c *RunnerConfig) DeepCopy() (*RunnerConfig, error) {
   605  	var r RunnerConfig
   606  
   607  	bytes, err := json.Marshal(c)
   608  	if err != nil {
   609  		return nil, fmt.Errorf("serialization of runner config failed: %v", err)
   610  	}
   611  
   612  	err = json.Unmarshal(bytes, &r)
   613  	if err != nil {
   614  		return nil, fmt.Errorf("deserialization of runner config failed: %v", err)
   615  	}
   616  
   617  	return &r, err
   618  }
   619  
   620  func NewConfig() *Config {
   621  	return &Config{
   622  		Concurrent: 1,
   623  		SessionServer: SessionServer{
   624  			SessionTimeout: int(DefaultSessionTimeout.Seconds()),
   625  		},
   626  	}
   627  }
   628  
   629  func (c *Config) StatConfig(configFile string) error {
   630  	_, err := os.Stat(configFile)
   631  	if err != nil {
   632  		return err
   633  	}
   634  	return nil
   635  }
   636  
   637  func (c *Config) LoadConfig(configFile string) error {
   638  	info, err := os.Stat(configFile)
   639  
   640  	// permission denied is soft error
   641  	if os.IsNotExist(err) {
   642  		return nil
   643  	} else if err != nil {
   644  		return err
   645  	}
   646  
   647  	if _, err = toml.DecodeFile(configFile, c); err != nil {
   648  		return err
   649  	}
   650  
   651  	for _, runner := range c.Runners {
   652  		if runner.Machine == nil {
   653  			continue
   654  		}
   655  
   656  		err := runner.Machine.CompileOffPeakPeriods()
   657  		if err != nil {
   658  			return err
   659  		}
   660  	}
   661  
   662  	c.ModTime = info.ModTime()
   663  	c.Loaded = true
   664  	return nil
   665  }
   666  
   667  func (c *Config) SaveConfig(configFile string) error {
   668  	var newConfig bytes.Buffer
   669  	newBuffer := bufio.NewWriter(&newConfig)
   670  
   671  	if err := toml.NewEncoder(newBuffer).Encode(c); err != nil {
   672  		logrus.Fatalf("Error encoding TOML: %s", err)
   673  		return err
   674  	}
   675  
   676  	if err := newBuffer.Flush(); err != nil {
   677  		return err
   678  	}
   679  
   680  	// create directory to store configuration
   681  	err := os.MkdirAll(filepath.Dir(configFile), 0700)
   682  	if err != nil {
   683  		return err
   684  	}
   685  
   686  	// write config file
   687  	if err := ioutil.WriteFile(configFile, newConfig.Bytes(), 0600); err != nil {
   688  		return err
   689  	}
   690  
   691  	c.Loaded = true
   692  	return nil
   693  }
   694  
   695  func (c *Config) GetCheckInterval() time.Duration {
   696  	if c.CheckInterval > 0 {
   697  		return time.Duration(c.CheckInterval) * time.Second
   698  	}
   699  	return CheckInterval
   700  }