gitlab.com/jfprevost/gitlab-runner-notlscheck@v11.11.4+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 KubernetesPullPolicy string
   123  
   124  // Get returns one of the predefined values in kubernetes notation or returns an error if the value can't match the predefined
   125  func (p KubernetesPullPolicy) Get() (KubernetesPullPolicy, error) {
   126  	switch {
   127  	case p == "":
   128  		return "", nil
   129  	case p == PullPolicyAlways:
   130  		return "Always", nil
   131  	case p == PullPolicyNever:
   132  		return "Never", nil
   133  	case p == PullPolicyIfNotPresent:
   134  		return "IfNotPresent", nil
   135  	}
   136  	return "", fmt.Errorf("unsupported kubernetes-pull-policy: %v", p)
   137  }
   138  
   139  type KubernetesConfig struct {
   140  	Host                           string               `toml:"host" json:"host" long:"host" env:"KUBERNETES_HOST" description:"Optional Kubernetes master host URL (auto-discovery attempted if not specified)"`
   141  	CertFile                       string               `toml:"cert_file,omitempty" json:"cert_file" long:"cert-file" env:"KUBERNETES_CERT_FILE" description:"Optional Kubernetes master auth certificate"`
   142  	KeyFile                        string               `toml:"key_file,omitempty" json:"key_file" long:"key-file" env:"KUBERNETES_KEY_FILE" description:"Optional Kubernetes master auth private key"`
   143  	CAFile                         string               `toml:"ca_file,omitempty" json:"ca_file" long:"ca-file" env:"KUBERNETES_CA_FILE" description:"Optional Kubernetes master auth ca certificate"`
   144  	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."`
   145  	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."`
   146  	Image                          string               `toml:"image" json:"image" long:"image" env:"KUBERNETES_IMAGE" description:"Default docker image to use for builds when none is specified"`
   147  	Namespace                      string               `toml:"namespace" json:"namespace" long:"namespace" env:"KUBERNETES_NAMESPACE" description:"Namespace to run Kubernetes jobs in"`
   148  	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"`
   149  	Privileged                     bool                 `toml:"privileged,omitzero" json:"privileged" long:"privileged" env:"KUBERNETES_PRIVILEGED" description:"Run all containers with the privileged flag enabled"`
   150  	CPULimit                       string               `toml:"cpu_limit,omitempty" json:"cpu_limit" long:"cpu-limit" env:"KUBERNETES_CPU_LIMIT" description:"The CPU allocation given to build containers"`
   151  	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"`
   152  	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"`
   153  	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"`
   154  	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"`
   155  	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"`
   156  	CPURequest                     string               `toml:"cpu_request,omitempty" json:"cpu_request" long:"cpu-request" env:"KUBERNETES_CPU_REQUEST" description:"The CPU allocation requested for build containers"`
   157  	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"`
   158  	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"`
   159  	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"`
   160  	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"`
   161  	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"`
   162  	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"`
   163  	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."`
   164  	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."`
   165  	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"`
   166  	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"`
   167  	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."`
   168  	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"`
   169  	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)"`
   170  	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."`
   171  	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"`
   172  	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"`
   173  	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_ANNOTATIONS_* variables"`
   174  	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"`
   175  	Volumes                        KubernetesVolumes    `toml:"volumes"`
   176  }
   177  
   178  type KubernetesVolumes struct {
   179  	HostPaths  []KubernetesHostPath  `toml:"host_path" description:"The host paths which will be mounted"`
   180  	PVCs       []KubernetesPVC       `toml:"pvc" description:"The persistent volume claims that will be mounted"`
   181  	ConfigMaps []KubernetesConfigMap `toml:"config_map" description:"The config maps which will be mounted as volumes"`
   182  	Secrets    []KubernetesSecret    `toml:"secret" description:"The secret maps which will be mounted"`
   183  	EmptyDirs  []KubernetesEmptyDir  `toml:"empty_dir" description:"The empty dirs which will be mounted"`
   184  }
   185  
   186  type KubernetesConfigMap struct {
   187  	Name      string            `toml:"name" json:"name" description:"The name of the volume and ConfigMap to use"`
   188  	MountPath string            `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   189  	ReadOnly  bool              `toml:"read_only,omitempty" description:"If this volume should be mounted read only"`
   190  	Items     map[string]string `toml:"items,omitempty" description:"Key-to-path mapping for keys from the config map that should be used."`
   191  }
   192  
   193  type KubernetesHostPath struct {
   194  	Name      string `toml:"name" json:"name" description:"The name of the volume"`
   195  	MountPath string `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   196  	ReadOnly  bool   `toml:"read_only,omitempty" description:"If this volume should be mounted read only"`
   197  	HostPath  string `toml:"host_path,omitempty" description:"Path from the host that should be mounted as a volume"`
   198  }
   199  
   200  type KubernetesPVC struct {
   201  	Name      string `toml:"name" json:"name" description:"The name of the volume and PVC to use"`
   202  	MountPath string `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   203  	ReadOnly  bool   `toml:"read_only,omitempty" description:"If this volume should be mounted read only"`
   204  }
   205  
   206  type KubernetesSecret struct {
   207  	Name      string            `toml:"name" json:"name" description:"The name of the volume and Secret to use"`
   208  	MountPath string            `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   209  	ReadOnly  bool              `toml:"read_only,omitempty" description:"If this volume should be mounted read only"`
   210  	Items     map[string]string `toml:"items,omitempty" description:"Key-to-path mapping for keys from the secret that should be used."`
   211  }
   212  
   213  type KubernetesEmptyDir struct {
   214  	Name      string `toml:"name" json:"name" description:"The name of the volume and EmptyDir to use"`
   215  	MountPath string `toml:"mount_path" description:"Path where volume should be mounted inside of container"`
   216  	Medium    string `toml:"medium,omitempty" description:"Set to 'Memory' to have a tmpfs"`
   217  }
   218  
   219  type RunnerCredentials struct {
   220  	URL         string `toml:"url" json:"url" short:"u" long:"url" env:"CI_SERVER_URL" required:"true" description:"Runner URL"`
   221  	Token       string `toml:"token" json:"token" short:"t" long:"token" env:"CI_SERVER_TOKEN" required:"true" description:"Runner token"`
   222  	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"`
   223  	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"`
   224  	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"`
   225  }
   226  
   227  type CacheGCSCredentials struct {
   228  	AccessID   string `toml:"AccessID,omitempty" long:"access-id" env:"CACHE_GCS_ACCESS_ID" description:"ID of GCP Service Account used to access the storage"`
   229  	PrivateKey string `toml:"PrivateKey,omitempty" long:"private-key" env:"CACHE_GCS_PRIVATE_KEY" description:"Private key used to sign GCS requests"`
   230  }
   231  
   232  type CacheGCSConfig struct {
   233  	CacheGCSCredentials
   234  	CredentialsFile string `toml:"CredentialsFile,omitempty" long:"credentials-file" env:"GOOGLE_APPLICATION_CREDENTIALS" description:"File with GCP credentials, containing AccessID and PrivateKey"`
   235  	BucketName      string `toml:"BucketName,omitempty" long:"bucket-name" env:"CACHE_GCS_BUCKET_NAME" description:"Name of the bucket where cache will be stored"`
   236  }
   237  
   238  type CacheS3Config struct {
   239  	ServerAddress  string `toml:"ServerAddress,omitempty" long:"server-address" env:"CACHE_S3_SERVER_ADDRESS" description:"A host:port to the used S3-compatible server"`
   240  	AccessKey      string `toml:"AccessKey,omitempty" long:"access-key" env:"CACHE_S3_ACCESS_KEY" description:"S3 Access Key"`
   241  	SecretKey      string `toml:"SecretKey,omitempty" long:"secret-key" env:"CACHE_S3_SECRET_KEY" description:"S3 Secret Key"`
   242  	BucketName     string `toml:"BucketName,omitempty" long:"bucket-name" env:"CACHE_S3_BUCKET_NAME" description:"Name of the bucket where cache will be stored"`
   243  	BucketLocation string `toml:"BucketLocation,omitempty" long:"bucket-location" env:"CACHE_S3_BUCKET_LOCATION" description:"Name of S3 region"`
   244  	Insecure       bool   `toml:"Insecure,omitempty" long:"insecure" env:"CACHE_S3_INSECURE" description:"Use insecure mode (without https)"`
   245  }
   246  
   247  type CacheConfig struct {
   248  	Type   string `toml:"Type,omitempty" long:"type" env:"CACHE_TYPE" description:"Select caching method"`
   249  	Path   string `toml:"Path,omitempty" long:"path" env:"CACHE_PATH" description:"Name of the path to prepend to the cache URL"`
   250  	Shared bool   `toml:"Shared,omitempty" long:"shared" env:"CACHE_SHARED" description:"Enable cache sharing between runners."`
   251  
   252  	S3  *CacheS3Config  `toml:"s3,omitempty" json:"s3" namespace:"s3"`
   253  	GCS *CacheGCSConfig `toml:"gcs,omitempty" json:"gcs" namespace:"gcs"`
   254  
   255  	// TODO: Remove in 12.0
   256  	S3CachePath    string `toml:"-" long:"s3-cache-path" env:"S3_CACHE_PATH" description:"Name of the path to prepend to the cache URL. DEPRECATED"` // DEPRECATED
   257  	CacheShared    bool   `toml:"-" long:"cache-shared" description:"Enable cache sharing between runners. DEPRECATED"`                              // DEPRECATED
   258  	ServerAddress  string `toml:"ServerAddress,omitempty" description:"A host:port to the used S3-compatible server DEPRECATED"`                     // DEPRECATED
   259  	AccessKey      string `toml:"AccessKey,omitempty" description:"S3 Access Key DEPRECATED"`                                                        // DEPRECATED
   260  	SecretKey      string `toml:"SecretKey,omitempty" description:"S3 Secret Key DEPRECATED"`                                                        // DEPRECATED
   261  	BucketName     string `toml:"BucketName,omitempty" description:"Name of the bucket where cache will be stored DEPRECATED"`                       // DEPRECATED
   262  	BucketLocation string `toml:"BucketLocation,omitempty" description:"Name of S3 region DEPRECATED"`                                               // DEPRECATED
   263  	Insecure       bool   `toml:"Insecure,omitempty" description:"Use insecure mode (without https) DEPRECATED"`                                     // DEPRECATED
   264  }
   265  
   266  type RunnerSettings struct {
   267  	Executor  string `toml:"executor" json:"executor" long:"executor" env:"RUNNER_EXECUTOR" required:"true" description:"Select executor, eg. shell, docker, etc."`
   268  	BuildsDir string `toml:"builds_dir,omitempty" json:"builds_dir" long:"builds-dir" env:"RUNNER_BUILDS_DIR" description:"Directory where builds are stored"`
   269  	CacheDir  string `toml:"cache_dir,omitempty" json:"cache_dir" long:"cache-dir" env:"RUNNER_CACHE_DIR" description:"Directory where build cache is stored"`
   270  	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"`
   271  
   272  	Environment     []string `toml:"environment,omitempty" json:"environment" long:"env" env:"RUNNER_ENV" description:"Custom environment variables injected to build environment"`
   273  	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"`
   274  	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"`
   275  	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"`
   276  
   277  	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"`
   278  
   279  	Shell string `toml:"shell,omitempty" json:"shell" long:"shell" env:"RUNNER_SHELL" description:"Select bash, cmd or powershell"`
   280  
   281  	CustomBuildDir *CustomBuildDir   `toml:"custom_build_dir,omitempty" json:"custom_build_dir" group:"custom build dir configuration" namespace:"custom_build_dir"`
   282  	SSH            *ssh.Config       `toml:"ssh,omitempty" json:"ssh" group:"ssh executor" namespace:"ssh"`
   283  	Docker         *DockerConfig     `toml:"docker,omitempty" json:"docker" group:"docker executor" namespace:"docker"`
   284  	Parallels      *ParallelsConfig  `toml:"parallels,omitempty" json:"parallels" group:"parallels executor" namespace:"parallels"`
   285  	VirtualBox     *VirtualBoxConfig `toml:"virtualbox,omitempty" json:"virtualbox" group:"virtualbox executor" namespace:"virtualbox"`
   286  	Cache          *CacheConfig      `toml:"cache,omitempty" json:"cache" group:"cache configuration" namespace:"cache"`
   287  	Machine        *DockerMachine    `toml:"machine,omitempty" json:"machine" group:"docker machine provider" namespace:"machine"`
   288  	Kubernetes     *KubernetesConfig `toml:"kubernetes,omitempty" json:"kubernetes" group:"kubernetes executor" namespace:"kubernetes"`
   289  }
   290  
   291  type RunnerConfig struct {
   292  	Name               string `toml:"name" json:"name" short:"name" long:"description" env:"RUNNER_NAME" description:"Runner name"`
   293  	Limit              int    `toml:"limit,omitzero" json:"limit" long:"limit" env:"RUNNER_LIMIT" description:"Maximum number of builds processed by this runner"`
   294  	OutputLimit        int    `toml:"output_limit,omitzero" long:"output-limit" env:"RUNNER_OUTPUT_LIMIT" description:"Maximum build trace size in kilobytes"`
   295  	RequestConcurrency int    `toml:"request_concurrency,omitzero" long:"request-concurrency" env:"RUNNER_REQUEST_CONCURRENCY" description:"Maximum concurrency for job requests"`
   296  
   297  	RunnerCredentials
   298  	RunnerSettings
   299  }
   300  
   301  type SessionServer struct {
   302  	ListenAddress    string `toml:"listen_address,omitempty" json:"listen_address" description:"Address that the runner will communicate directly with"`
   303  	AdvertiseAddress string `toml:"advertise_address,omitempty" json:"advertise_address" description:"Address the runner will expose to the world to connect to the session server"`
   304  	SessionTimeout   int    `toml:"session_timeout,omitempty" json:"session_timeout" description:"How long a terminal session can be active after a build completes, in seconds"`
   305  }
   306  
   307  type Config struct {
   308  	ListenAddress string        `toml:"listen_address,omitempty" json:"listen_address"`
   309  	SessionServer SessionServer `toml:"session_server,omitempty" json:"session_server"`
   310  
   311  	// TODO: Remove in 12.0
   312  	MetricsServerAddress string `toml:"metrics_server,omitempty" json:"metrics_server"` // DEPRECATED
   313  
   314  	Concurrent    int             `toml:"concurrent" json:"concurrent"`
   315  	CheckInterval int             `toml:"check_interval" json:"check_interval" description:"Define active checking interval of jobs"`
   316  	LogLevel      *string         `toml:"log_level" json:"log_level" description:"Define log level (one of: panic, fatal, error, warning, info, debug)"`
   317  	LogFormat     *string         `toml:"log_format" json:"log_format" description:"Define log format (one of: runner, text, json)"`
   318  	User          string          `toml:"user,omitempty" json:"user"`
   319  	Runners       []*RunnerConfig `toml:"runners" json:"runners"`
   320  	SentryDSN     *string         `toml:"sentry_dsn"`
   321  	ModTime       time.Time       `toml:"-"`
   322  	Loaded        bool            `toml:"-"`
   323  }
   324  
   325  type CustomBuildDir struct {
   326  	Enabled bool `toml:"enabled,omitempty" json:"enabled" long:"enabled" env:"CUSTOM_BUILD_DIR_ENABLED" description:"Enable job specific build directories"`
   327  }
   328  
   329  func getDeprecatedStringSetting(setting string, tomlField string, envVariable string, tomlReplacement string, envReplacement string) string {
   330  	if setting != "" {
   331  		logrus.Warningf("%s setting is deprecated and will be removed in GitLab Runner 12.0. Please use %s instead", tomlField, tomlReplacement)
   332  		return setting
   333  	}
   334  
   335  	value := os.Getenv(envVariable)
   336  	if value != "" {
   337  		logrus.Warningf("%s environment variables is deprecated and will be removed in GitLab Runner 12.0. Please use %s instead", envVariable, envReplacement)
   338  	}
   339  
   340  	return value
   341  }
   342  
   343  func getDeprecatedBoolSetting(setting bool, tomlField string, envVariable string, tomlReplacement string, envReplacement string) bool {
   344  	if setting {
   345  		logrus.Warningf("%s setting is deprecated and will be removed in GitLab Runner 12.0. Please use %s instead", tomlField, tomlReplacement)
   346  		return setting
   347  	}
   348  
   349  	value, _ := strconv.ParseBool(os.Getenv(envVariable))
   350  	if value {
   351  		logrus.Warningf("%s environment variables is deprecated and will be removed in GitLab Runner 12.0. Please use %s instead", envVariable, envReplacement)
   352  	}
   353  
   354  	return value
   355  }
   356  
   357  func (c *CacheS3Config) ShouldUseIAMCredentials() bool {
   358  	return c.ServerAddress == "" || c.AccessKey == "" || c.SecretKey == ""
   359  }
   360  
   361  func (c *CacheConfig) GetPath() string {
   362  	if c.Path != "" {
   363  		return c.Path
   364  	}
   365  
   366  	// TODO: Remove in 12.0
   367  	if c.S3CachePath != "" {
   368  		logrus.Warning("'--cache-s3-cache-path' command line option and `$S3_CACHE_PATH` environment variables are deprecated and will be removed in GitLab Runner 12.0. Please use '--cache-path' or '$CACHE_PATH' instead")
   369  	}
   370  
   371  	return c.S3CachePath
   372  }
   373  
   374  func (c *CacheConfig) GetShared() bool {
   375  	if c.Shared {
   376  		return c.Shared
   377  	}
   378  
   379  	// TODO: Remove in 12.0
   380  	if c.CacheShared {
   381  		logrus.Warning("'--cache-cache-shared' command line is deprecated and will be removed in GitLab Runner 12.0. Please use '--cache-shared' instead")
   382  	}
   383  
   384  	return c.CacheShared
   385  }
   386  
   387  // DEPRECATED
   388  // TODO: Remove in 12.0
   389  func (c *CacheConfig) GetServerAddress() string {
   390  	return getDeprecatedStringSetting(
   391  		c.ServerAddress,
   392  		"[runners.cache] ServerAddress",
   393  		"S3_SERVER_ADDRESS",
   394  		"[runners.cache.s3] ServerAddress",
   395  		"CACHE_S3_SERVER_ADDRESS")
   396  }
   397  
   398  // DEPRECATED
   399  // TODO: Remove in 12.0
   400  func (c *CacheConfig) GetAccessKey() string {
   401  	return getDeprecatedStringSetting(
   402  		c.AccessKey,
   403  		"[runners.cache] AccessKey",
   404  		"S3_ACCESS_KEY",
   405  		"[runners.cache.s3] AccessKey",
   406  		"CACHE_S3_ACCESS_KEY")
   407  }
   408  
   409  // DEPRECATED
   410  // TODO: Remove in 12.0
   411  func (c *CacheConfig) GetSecretKey() string {
   412  	return getDeprecatedStringSetting(
   413  		c.SecretKey,
   414  		"[runners.cache] SecretKey",
   415  		"S3_SECRET_KEY",
   416  		"[runners.cache.s3] SecretKey",
   417  		"CACHE_S3_SECRET_KEY")
   418  }
   419  
   420  // DEPRECATED
   421  // TODO: Remove in 12.0
   422  func (c *CacheConfig) GetBucketName() string {
   423  	return getDeprecatedStringSetting(
   424  		c.BucketName,
   425  		"[runners.cache] BucketName",
   426  		"S3_BUCKET_NAME",
   427  		"[runners.cache.s3] BucketName",
   428  		"CACHE_S3_BUCKET_NAME")
   429  }
   430  
   431  // DEPRECATED
   432  // TODO: Remove in 12.0
   433  func (c *CacheConfig) GetBucketLocation() string {
   434  	return getDeprecatedStringSetting(
   435  		c.BucketLocation,
   436  		"[runners.cache] BucketLocation",
   437  		"S3_BUCKET_LOCATION",
   438  		"[runners.cache.s3] BucketLocation",
   439  		"CACHE_S3_BUCKET_LOCATION")
   440  }
   441  
   442  // DEPRECATED
   443  // TODO: Remove in 12.0
   444  func (c *CacheConfig) GetInsecure() bool {
   445  	return getDeprecatedBoolSetting(
   446  		c.Insecure,
   447  		"[runners.cache] Insecure",
   448  		"S3_CACHE_INSECURE",
   449  		"[runners.cache.s3] Insecure",
   450  		"CACHE_S3_INSECURE")
   451  }
   452  
   453  func (c *SessionServer) GetSessionTimeout() time.Duration {
   454  	if c.SessionTimeout > 0 {
   455  		return time.Duration(c.SessionTimeout) * time.Second
   456  	}
   457  
   458  	return DefaultSessionTimeout
   459  }
   460  
   461  func (c *DockerConfig) GetNanoCPUs() (int64, error) {
   462  	if c.CPUS == "" {
   463  		return 0, nil
   464  	}
   465  
   466  	cpu, ok := new(big.Rat).SetString(c.CPUS)
   467  	if !ok {
   468  		return 0, fmt.Errorf("failed to parse %v as a rational number", c.CPUS)
   469  	}
   470  
   471  	nano, _ := cpu.Mul(cpu, big.NewRat(1e9, 1)).Float64()
   472  
   473  	return int64(nano), nil
   474  }
   475  
   476  func (c *DockerConfig) getMemoryBytes(size string, fieldName string) int64 {
   477  	if size == "" {
   478  		return 0
   479  	}
   480  
   481  	bytes, err := units.RAMInBytes(size)
   482  	if err != nil {
   483  		logrus.Fatalf("Error parsing docker %s: %s", fieldName, err)
   484  	}
   485  
   486  	return bytes
   487  }
   488  
   489  func (c *DockerConfig) GetMemory() int64 {
   490  	return c.getMemoryBytes(c.Memory, "memory")
   491  }
   492  
   493  func (c *DockerConfig) GetMemorySwap() int64 {
   494  	return c.getMemoryBytes(c.MemorySwap, "memory_swap")
   495  }
   496  
   497  func (c *DockerConfig) GetMemoryReservation() int64 {
   498  	return c.getMemoryBytes(c.MemoryReservation, "memory_reservation")
   499  }
   500  
   501  func (c *DockerConfig) GetOomKillDisable() *bool {
   502  	return &c.OomKillDisable
   503  }
   504  
   505  func (c *KubernetesConfig) GetPollAttempts() int {
   506  	if c.PollTimeout <= 0 {
   507  		c.PollTimeout = KubernetesPollTimeout
   508  	}
   509  
   510  	return c.PollTimeout / c.GetPollInterval()
   511  }
   512  
   513  func (c *KubernetesConfig) GetPollInterval() int {
   514  	if c.PollInterval <= 0 {
   515  		c.PollInterval = KubernetesPollInterval
   516  	}
   517  
   518  	return c.PollInterval
   519  }
   520  
   521  func (c *KubernetesConfig) GetNodeTolerations() []api.Toleration {
   522  	var tolerations []api.Toleration
   523  
   524  	for toleration, effect := range c.NodeTolerations {
   525  		newToleration := api.Toleration{
   526  			Effect: api.TaintEffect(effect),
   527  		}
   528  
   529  		if strings.Contains(toleration, "=") {
   530  			parts := strings.Split(toleration, "=")
   531  			newToleration.Key = parts[0]
   532  			if len(parts) > 1 {
   533  				newToleration.Value = parts[1]
   534  			}
   535  			newToleration.Operator = api.TolerationOpEqual
   536  		} else {
   537  			newToleration.Key = toleration
   538  			newToleration.Operator = api.TolerationOpExists
   539  		}
   540  
   541  		tolerations = append(tolerations, newToleration)
   542  	}
   543  
   544  	return tolerations
   545  }
   546  
   547  func (c *DockerMachine) GetIdleCount() int {
   548  	if c.isOffPeak() {
   549  		return c.OffPeakIdleCount
   550  	}
   551  
   552  	return c.IdleCount
   553  }
   554  
   555  func (c *DockerMachine) GetIdleTime() int {
   556  	if c.isOffPeak() {
   557  		return c.OffPeakIdleTime
   558  	}
   559  
   560  	return c.IdleTime
   561  }
   562  
   563  func (c *DockerMachine) isOffPeak() bool {
   564  	if c.offPeakTimePeriods == nil {
   565  		c.CompileOffPeakPeriods()
   566  	}
   567  
   568  	return c.offPeakTimePeriods != nil && c.offPeakTimePeriods.InPeriod()
   569  }
   570  
   571  func (c *DockerMachine) CompileOffPeakPeriods() (err error) {
   572  	c.offPeakTimePeriods, err = timeperiod.TimePeriods(c.OffPeakPeriods, c.OffPeakTimezone)
   573  	if err != nil {
   574  		err = errors.New(fmt.Sprint("Invalid OffPeakPeriods value: ", err))
   575  	}
   576  
   577  	return
   578  }
   579  
   580  func (c *RunnerCredentials) GetURL() string {
   581  	return c.URL
   582  }
   583  
   584  func (c *RunnerCredentials) GetTLSCAFile() string {
   585  	return c.TLSCAFile
   586  }
   587  
   588  func (c *RunnerCredentials) GetTLSCertFile() string {
   589  	return c.TLSCertFile
   590  }
   591  
   592  func (c *RunnerCredentials) GetTLSKeyFile() string {
   593  	return c.TLSKeyFile
   594  }
   595  
   596  func (c *RunnerCredentials) GetToken() string {
   597  	return c.Token
   598  }
   599  
   600  func (c *RunnerCredentials) ShortDescription() string {
   601  	return helpers.ShortenToken(c.Token)
   602  }
   603  
   604  func (c *RunnerCredentials) UniqueID() string {
   605  	return c.URL + c.Token
   606  }
   607  
   608  func (c *RunnerCredentials) Log() *logrus.Entry {
   609  	if c.ShortDescription() != "" {
   610  		return logrus.WithField("runner", c.ShortDescription())
   611  	}
   612  	return logrus.WithFields(logrus.Fields{})
   613  }
   614  
   615  func (c *RunnerCredentials) SameAs(other *RunnerCredentials) bool {
   616  	return c.URL == other.URL && c.Token == other.Token
   617  }
   618  
   619  func (c *RunnerConfig) String() string {
   620  	return fmt.Sprintf("%v url=%v token=%v executor=%v", c.Name, c.URL, c.Token, c.Executor)
   621  }
   622  
   623  func (c *RunnerConfig) GetRequestConcurrency() int {
   624  	if c.RequestConcurrency <= 0 {
   625  		return 1
   626  	}
   627  	return c.RequestConcurrency
   628  }
   629  
   630  func (c *RunnerConfig) GetVariables() JobVariables {
   631  	var variables JobVariables
   632  
   633  	for _, environment := range c.Environment {
   634  		if variable, err := ParseVariable(environment); err == nil {
   635  			variable.Internal = true
   636  			variables = append(variables, variable)
   637  		}
   638  	}
   639  
   640  	return variables
   641  }
   642  
   643  // DeepCopy attempts to make a deep clone of the object
   644  func (c *RunnerConfig) DeepCopy() (*RunnerConfig, error) {
   645  	var r RunnerConfig
   646  
   647  	bytes, err := json.Marshal(c)
   648  	if err != nil {
   649  		return nil, fmt.Errorf("serialization of runner config failed: %v", err)
   650  	}
   651  
   652  	err = json.Unmarshal(bytes, &r)
   653  	if err != nil {
   654  		return nil, fmt.Errorf("deserialization of runner config failed: %v", err)
   655  	}
   656  
   657  	return &r, err
   658  }
   659  
   660  func NewConfig() *Config {
   661  	return &Config{
   662  		Concurrent: 1,
   663  		SessionServer: SessionServer{
   664  			SessionTimeout: int(DefaultSessionTimeout.Seconds()),
   665  		},
   666  	}
   667  }
   668  
   669  func (c *Config) StatConfig(configFile string) error {
   670  	_, err := os.Stat(configFile)
   671  	if err != nil {
   672  		return err
   673  	}
   674  	return nil
   675  }
   676  
   677  func (c *Config) LoadConfig(configFile string) error {
   678  	info, err := os.Stat(configFile)
   679  
   680  	// permission denied is soft error
   681  	if os.IsNotExist(err) {
   682  		return nil
   683  	} else if err != nil {
   684  		return err
   685  	}
   686  
   687  	if _, err = toml.DecodeFile(configFile, c); err != nil {
   688  		return err
   689  	}
   690  
   691  	for _, runner := range c.Runners {
   692  		if runner.Machine == nil {
   693  			continue
   694  		}
   695  
   696  		err := runner.Machine.CompileOffPeakPeriods()
   697  		if err != nil {
   698  			return err
   699  		}
   700  	}
   701  
   702  	c.ModTime = info.ModTime()
   703  	c.Loaded = true
   704  	return nil
   705  }
   706  
   707  func (c *Config) SaveConfig(configFile string) error {
   708  	var newConfig bytes.Buffer
   709  	newBuffer := bufio.NewWriter(&newConfig)
   710  
   711  	if err := toml.NewEncoder(newBuffer).Encode(c); err != nil {
   712  		logrus.Fatalf("Error encoding TOML: %s", err)
   713  		return err
   714  	}
   715  
   716  	if err := newBuffer.Flush(); err != nil {
   717  		return err
   718  	}
   719  
   720  	// create directory to store configuration
   721  	os.MkdirAll(filepath.Dir(configFile), 0700)
   722  
   723  	// write config file
   724  	if err := ioutil.WriteFile(configFile, newConfig.Bytes(), 0600); err != nil {
   725  		return err
   726  	}
   727  
   728  	c.Loaded = true
   729  	return nil
   730  }
   731  
   732  func (c *Config) GetCheckInterval() time.Duration {
   733  	if c.CheckInterval > 0 {
   734  		return time.Duration(c.CheckInterval) * time.Second
   735  	}
   736  	return CheckInterval
   737  }
   738  
   739  func (c *Config) ListenOrServerMetricAddress() string {
   740  	if c.ListenAddress != "" {
   741  		return c.ListenAddress
   742  	}
   743  
   744  	// TODO: Remove in 12.0
   745  	if c.MetricsServerAddress != "" {
   746  		logrus.Warnln("'metrics_server' configuration entry is deprecated and will be removed in one of future releases; please use 'listen_address' instead")
   747  	}
   748  
   749  	return c.MetricsServerAddress
   750  }