From 7c358528ef917d09ef33af8388a3d5c038dc3add Mon Sep 17 00:00:00 2001 From: Dhruv Dhruv Date: Mon, 25 Nov 2024 09:56:30 +0000 Subject: [PATCH] feat: [CDE-475]: Adding support for runArgs. (#3031) * Rebasing * feat: [CDE-475]: Adding support for runArgs. * WIP * WIP --- .local.env | 2 +- .../container/devcontainer_config_utils.go | 167 ++++ ...tup.go => devcontainer_container_utils.go} | 155 +++- ...er_steps.go => devcontainer_step_utils.go} | 170 ---- .../container/devcontainer_utils.go | 40 - ...embedded_docker_container_orchestrator.go} | 185 ++++- .../orchestrator/container/runarg_utils.go | 541 +++++++++++++ app/gitspace/orchestrator/container/wire.go | 3 + app/gitspace/orchestrator/runarg/provider.go | 27 + app/gitspace/orchestrator/runarg/runArgs.yaml | 727 ++++++++++++++++++ .../orchestrator/runarg/static_provider.go | 61 ++ app/gitspace/orchestrator/runarg/wire.go | 27 + cmd/gitness/wire.go | 2 + cmd/gitness/wire_gen.go | 7 +- types/devcontainer_config.go | 1 + types/gitspace_run_arg.go | 103 +++ 16 files changed, 1992 insertions(+), 226 deletions(-) create mode 100644 app/gitspace/orchestrator/container/devcontainer_config_utils.go rename app/gitspace/orchestrator/container/{devcontainer_setup.go => devcontainer_container_utils.go} (65%) rename app/gitspace/orchestrator/container/{devcontainer_steps.go => devcontainer_step_utils.go} (56%) rename app/gitspace/orchestrator/container/{embedded_docker_provider.go => embedded_docker_container_orchestrator.go} (76%) create mode 100644 app/gitspace/orchestrator/container/runarg_utils.go create mode 100644 app/gitspace/orchestrator/runarg/provider.go create mode 100644 app/gitspace/orchestrator/runarg/runArgs.yaml create mode 100644 app/gitspace/orchestrator/runarg/static_provider.go create mode 100644 app/gitspace/orchestrator/runarg/wire.go create mode 100644 types/gitspace_run_arg.go diff --git a/.local.env b/.local.env index 88b04fa54..b6826e715 100644 --- a/.local.env +++ b/.local.env @@ -7,7 +7,7 @@ GITNESS_METRIC_ENABLED=false GITNESS_HTTP_HOST=localhost GITNESS_GITSPACE_ENABLE=true GITNESS_DEBUG=true -GITNESS_DOCKER_API_VERSION=1.41 +GITNESS_DOCKER_API_VERSION=1.45 GITNESS_SSH_ENABLE=true GITNESS_SSH_HOST=localhost GITNESS_SSH_PORT=2222 diff --git a/app/gitspace/orchestrator/container/devcontainer_config_utils.go b/app/gitspace/orchestrator/container/devcontainer_config_utils.go new file mode 100644 index 000000000..668d697d5 --- /dev/null +++ b/app/gitspace/orchestrator/container/devcontainer_config_utils.go @@ -0,0 +1,167 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package container + +import ( + "context" + "fmt" + "strings" + + "github.com/harness/gitness/app/gitspace/orchestrator/runarg" + "github.com/harness/gitness/types" + + "github.com/rs/zerolog/log" +) + +func ExtractRunArgs( + ctx context.Context, + spaceID int64, + runArgProvider runarg.Provider, + runArgsRaw []string, +) (map[types.RunArg]*types.RunArgValue, error) { + supportedRunArgsMap, err := runArgProvider.ProvideSupportedRunArgs(ctx, spaceID) + if err != nil { + return nil, err + } + + var runArgsMap = make(map[types.RunArg]*types.RunArgValue) + primaryLoopCounter := 0 + for primaryLoopCounter < len(runArgsRaw) { + currentArg := runArgsRaw[primaryLoopCounter] + if currentArg == "" || !isArg(currentArg) { + primaryLoopCounter++ + continue + } + + argParts := strings.SplitN(currentArg, "=", 2) + argKey := argParts[0] + + currentRunArgDefinition, isSupportedArg := supportedRunArgsMap[types.RunArg(argKey)] + if !isSupportedArg { + primaryLoopCounter++ + continue + } + updatedPrimaryLoopCounter, allowedValues, isAnyValueBlocked := getValues(runArgsRaw, argParts, + primaryLoopCounter, currentRunArgDefinition) + + primaryLoopCounter = updatedPrimaryLoopCounter + + if isAnyValueBlocked && len(allowedValues) == 0 { + continue + } + + currentRunArgValue := types.RunArgValue{ + Name: currentRunArgDefinition.Name, + Values: allowedValues, + } + + existingRunArgValue, isAlreadyPresent := runArgsMap[currentRunArgDefinition.Name] + if isAlreadyPresent && currentRunArgDefinition.AllowMultipleOccurences { + existingRunArgValue.Values = append(existingRunArgValue.Values, currentRunArgValue.Values...) + } else { + runArgsMap[currentRunArgDefinition.Name] = ¤tRunArgValue + } + } + + return runArgsMap, nil +} + +func getValues( + runArgs []string, + argParts []string, + primaryLoopCounter int, + currentRunArgDefinition types.RunArgDefinition, +) (int, []string, bool) { + values := make([]string, 0) + if len(argParts) > 1 { + values = append(values, argParts[1]) + primaryLoopCounter++ + } else { + var secondaryLoopCounter = primaryLoopCounter + 1 + for secondaryLoopCounter < len(runArgs) { + currentValue := runArgs[secondaryLoopCounter] + if isArg(currentValue) { + break + } + values = append(values, currentValue) + secondaryLoopCounter++ + } + primaryLoopCounter = secondaryLoopCounter + } + allowedValues, isAnyValueBlocked := filterAllowedValues(values, currentRunArgDefinition) + return primaryLoopCounter, allowedValues, isAnyValueBlocked +} + +func filterAllowedValues(values []string, currentRunArgDefinition types.RunArgDefinition) ([]string, bool) { + isAnyValueBlocked := false + allowedValues := make([]string, 0) + for _, v := range values { + switch { + case len(currentRunArgDefinition.AllowedValues) > 0: + if _, ok := currentRunArgDefinition.AllowedValues[v]; ok { + allowedValues = append(allowedValues, v) + } else { + isAnyValueBlocked = true + } + case len(currentRunArgDefinition.BlockedValues) > 0: + if _, ok := currentRunArgDefinition.BlockedValues[v]; !ok { + allowedValues = append(allowedValues, v) + } else { + isAnyValueBlocked = true + } + default: + allowedValues = append(allowedValues, v) + } + } + return allowedValues, isAnyValueBlocked +} + +func isArg(str string) bool { + return strings.HasPrefix(str, "--") || strings.HasPrefix(str, "-") +} + +func ExtractEnv(devcontainerConfig types.DevcontainerConfig, runArgsMap map[types.RunArg]*types.RunArgValue) []string { + var env []string + for key, value := range devcontainerConfig.ContainerEnv { + env = append(env, fmt.Sprintf("%s=%s", key, value)) + } + envFromRunArgs := getEnv(runArgsMap) + env = append(env, envFromRunArgs...) + return env +} + +func ExtractForwardPorts(devcontainerConfig types.DevcontainerConfig) []int { + var ports []int + for _, strPort := range devcontainerConfig.ForwardPorts { + portAsInt, err := strPort.Int64() // Using Atoi to convert string to int + if err != nil { + log.Warn().Msgf("Error converting port string '%s' to int: %v", strPort, err) + continue // Skip the invalid port + } + ports = append(ports, int(portAsInt)) + } + return ports +} + +func ExtractLifecycleCommands(actionType PostAction, devcontainerConfig types.DevcontainerConfig) []string { + switch actionType { + case PostCreateAction: + return devcontainerConfig.PostCreateCommand.ToCommandArray() + case PostStartAction: + return devcontainerConfig.PostStartCommand.ToCommandArray() + default: + return []string{} // Return empty string if actionType is not recognized + } +} diff --git a/app/gitspace/orchestrator/container/devcontainer_setup.go b/app/gitspace/orchestrator/container/devcontainer_container_utils.go similarity index 65% rename from app/gitspace/orchestrator/container/devcontainer_setup.go rename to app/gitspace/orchestrator/container/devcontainer_container_utils.go index a1e1fff56..01dfe8d42 100644 --- a/app/gitspace/orchestrator/container/devcontainer_setup.go +++ b/app/gitspace/orchestrator/container/devcontainer_container_utils.go @@ -24,6 +24,7 @@ import ( "strconv" "strings" + "github.com/harness/gitness/app/gitspace/orchestrator/runarg" gitspaceTypes "github.com/harness/gitness/app/gitspace/types" "github.com/harness/gitness/types" @@ -31,6 +32,7 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/api/types/strslice" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/rs/zerolog/log" @@ -131,22 +133,50 @@ func CreateContainer( mountType mount.Type, portMappings map[int]*types.PortMapping, env []string, + runArgsMap map[types.RunArg]*types.RunArgValue, ) error { exposedPorts, portBindings := applyPortMappings(portMappings) gitspaceLogger.Info("Creating container: " + containerName) - hostConfig := prepareHostConfig(bindMountSource, bindMountTarget, mountType, portBindings) + hostConfig, err := prepareHostConfig(bindMountSource, bindMountTarget, mountType, portBindings, runArgsMap) + if err != nil { + return err + } + healthCheckConfig, err := getHealthCheckConfig(runArgsMap) + if err != nil { + return err + } + stopTimeout, err := getStopTimeout(runArgsMap) + if err != nil { + return err + } + + entrypoint := getEntrypoint(runArgsMap) + var cmd strslice.StrSlice + if len(entrypoint) == 0 { + entrypoint = []string{"/bin/sh"} + cmd = []string{"-c", "trap 'exit 0' 15; sleep infinity & wait $!"} + } // Create the container containerConfig := &container.Config{ + Hostname: getHostname(runArgsMap), + Domainname: getDomainname(runArgsMap), Image: imageName, Env: env, - Entrypoint: []string{"/bin/sh"}, - Cmd: []string{"-c", "trap 'exit 0' 15; sleep infinity & wait $!"}, + Entrypoint: entrypoint, + Cmd: cmd, ExposedPorts: exposedPorts, + Labels: getLabels(runArgsMap), + Healthcheck: healthCheckConfig, + MacAddress: getMACAddress(runArgsMap), + StopSignal: getStopSignal(runArgsMap), + StopTimeout: stopTimeout, + User: getUser(runArgsMap), } - _, err := dockerClient.ContainerCreate(ctx, containerConfig, hostConfig, nil, nil, containerName) + + _, err = dockerClient.ContainerCreate(ctx, containerConfig, hostConfig, nil, nil, containerName) if err != nil { return logStreamWrapError(gitspaceLogger, "Error while creating container", err) } @@ -178,7 +208,33 @@ func prepareHostConfig( bindMountTarget string, mountType mount.Type, portBindings nat.PortMap, -) *container.HostConfig { + runArgsMap map[types.RunArg]*types.RunArgValue, +) (*container.HostConfig, error) { + hostResources, err := getHostResources(runArgsMap) + if err != nil { + return nil, err + } + + extraHosts := getExtraHosts(runArgsMap) + if goruntime.GOOS == "linux" { + extraHosts = append(extraHosts, "host.docker.internal:host-gateway") + } + + restartPolicy, err := getRestartPolicy(runArgsMap) + if err != nil { + return nil, err + } + + oomScoreAdj, err := getOomScoreAdj(runArgsMap) + if err != nil { + return nil, err + } + + shmSize, err := getSHMSize(runArgsMap) + if err != nil { + return nil, err + } + hostConfig := &container.HostConfig{ PortBindings: portBindings, Mounts: []mount.Mount{ @@ -188,14 +244,33 @@ func prepareHostConfig( Target: bindMountTarget, }, }, + Resources: hostResources, + Annotations: getAnnotations(runArgsMap), + ExtraHosts: extraHosts, + NetworkMode: getNetworkMode(runArgsMap), + RestartPolicy: restartPolicy, + AutoRemove: getAutoRemove(runArgsMap), + CapDrop: getCapDrop(runArgsMap), + CgroupnsMode: getCgroupNSMode(runArgsMap), + DNS: getDNS(runArgsMap), + DNSOptions: getDNSOptions(runArgsMap), + DNSSearch: getDNSSearch(runArgsMap), + IpcMode: getIPCMode(runArgsMap), + Isolation: getIsolation(runArgsMap), + Init: getInit(runArgsMap), + Links: getLinks(runArgsMap), + OomScoreAdj: oomScoreAdj, + PidMode: getPIDMode(runArgsMap), + Runtime: getRuntime(runArgsMap), + SecurityOpt: getSecurityOpt(runArgsMap), + StorageOpt: getStorageOpt(runArgsMap), + ShmSize: shmSize, + Sysctls: getSysctls(runArgsMap), } - if goruntime.GOOS == "linux" { - hostConfig.ExtraHosts = []string{"host.docker.internal:host-gateway"} - } - - return hostConfig + return hostConfig, nil } + func GetContainerInfo( ctx context.Context, containerName string, @@ -227,10 +302,46 @@ func PullImage( ctx context.Context, imageName string, dockerClient *client.Client, + runArgsMap map[types.RunArg]*types.RunArgValue, gitspaceLogger gitspaceTypes.GitspaceLogger, ) error { + imagePullRunArg := getImagePullPolicy(runArgsMap) + gitspaceLogger.Info("Image pull policy is: " + imagePullRunArg) + if imagePullRunArg == "never" { + return nil + } + if imagePullRunArg == "missing" { + gitspaceLogger.Info("Checking if image " + imageName + " is present locally") + + filterArgs := filters.NewArgs() + filterArgs.Add("reference", imageName) + + images, err := dockerClient.ImageList(ctx, image.ListOptions{Filters: filterArgs}) + if err != nil { + gitspaceLogger.Error("Error listing images locally", err) + return err + } + + if len(images) > 0 { + gitspaceLogger.Info("Image " + imageName + " is present locally") + return nil + } + gitspaceLogger.Info("Image " + imageName + " is not present locally") + } + gitspaceLogger.Info("Pulling image: " + imageName) - pullResponse, err := dockerClient.ImagePull(ctx, imageName, image.PullOptions{}) + + pullResponse, err := dockerClient.ImagePull(ctx, imageName, image.PullOptions{Platform: getPlatform(runArgsMap)}) + defer func() { + if pullResponse == nil { + return + } + closingErr := pullResponse.Close() + if closingErr != nil { + log.Warn().Err(closingErr).Msg("failed to close image pull response") + } + }() + if err != nil { return logStreamWrapError(gitspaceLogger, "Error while pulling image", err) } @@ -271,6 +382,28 @@ func PullImage( return nil } +func ExtractRunArgsWithLogging( + ctx context.Context, + spaceID int64, + runArgProvider runarg.Provider, + runArgsRaw []string, + gitspaceLogger gitspaceTypes.GitspaceLogger, +) (map[types.RunArg]*types.RunArgValue, error) { + gitspaceLogger.Info("Extracting runsArgs") + runArgsMap, err := ExtractRunArgs(ctx, spaceID, runArgProvider, runArgsRaw) + if err != nil { + return nil, logStreamWrapError(gitspaceLogger, "Error while extracting runArgs", err) + } + if len(runArgsMap) > 0 { + st := "" + for key, value := range runArgsMap { + st = fmt.Sprintf("%s%s: %s\n", st, key, value) + } + gitspaceLogger.Info(fmt.Sprintf("Extracted following runArgs\n%v", st)) + } + return runArgsMap, nil +} + // getContainerResponse retrieves container information and prepares the start response. func GetContainerResponse( ctx context.Context, diff --git a/app/gitspace/orchestrator/container/devcontainer_steps.go b/app/gitspace/orchestrator/container/devcontainer_step_utils.go similarity index 56% rename from app/gitspace/orchestrator/container/devcontainer_steps.go rename to app/gitspace/orchestrator/container/devcontainer_step_utils.go index 1642ee781..479a920ed 100644 --- a/app/gitspace/orchestrator/container/devcontainer_steps.go +++ b/app/gitspace/orchestrator/container/devcontainer_step_utils.go @@ -17,7 +17,6 @@ package container import ( "context" "fmt" - "path/filepath" "github.com/harness/gitness/app/gitspace/orchestrator/common" "github.com/harness/gitness/app/gitspace/orchestrator/devcontainer" @@ -26,178 +25,9 @@ import ( "github.com/harness/gitness/app/gitspace/orchestrator/user" "github.com/harness/gitness/app/gitspace/scm" gitspaceTypes "github.com/harness/gitness/app/gitspace/types" - "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" ) -// buildSetupSteps constructs the steps to be executed in the setup process. -func (e *EmbeddedDockerOrchestrator) buildSetupSteps( - _ context.Context, - ideService ide.IDE, - gitspaceConfig types.GitspaceConfig, - resolvedRepoDetails scm.ResolvedDetails, - defaultBaseImage string, - environment []string, - devcontainerConfig types.DevcontainerConfig, - codeRepoDir string, -) []gitspaceTypes.Step { - return []gitspaceTypes.Step{ - { - Name: "Validate Supported OS", - Execute: ValidateSupportedOS, - StopOnFailure: true, - }, - { - Name: "Manage User", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - return ManageUser(ctx, exec, e.userService, gitspaceLogger) - }, - StopOnFailure: true, - }, - { - Name: "Set environment", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - return SetEnv(ctx, exec, gitspaceLogger, environment) - }, - StopOnFailure: true, - }, - { - Name: "Install Tools", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - return InstallTools(ctx, exec, gitspaceLogger, gitspaceConfig.IDE) - }, - StopOnFailure: true, - }, - { - Name: "Setup IDE", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - return SetupIDE(ctx, exec, ideService, gitspaceLogger) - }, - StopOnFailure: true, - }, - { - Name: "Run IDE", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - return RunIDE(ctx, exec, ideService, gitspaceLogger) - }, - StopOnFailure: true, - }, - { - Name: "Install Git", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - return InstallGit(ctx, exec, e.gitService, gitspaceLogger) - }, - StopOnFailure: true, - }, - { - Name: "Setup Git Credentials", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - if resolvedRepoDetails.ResolvedCredentials.Credentials != nil { - return SetupGitCredentials(ctx, exec, resolvedRepoDetails, e.gitService, gitspaceLogger) - } - return nil - }, - StopOnFailure: true, - }, - { - Name: "Clone Code", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - return CloneCode(ctx, exec, defaultBaseImage, resolvedRepoDetails, e.gitService, gitspaceLogger) - }, - StopOnFailure: true, - }, - // Post-create and Post-start steps - { - Name: "Execute PostCreate Command", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - command := ExtractLifecycleCommands(PostCreateAction, devcontainerConfig) - return ExecuteCommands(ctx, exec, codeRepoDir, gitspaceLogger, command, PostCreateAction) - }, - StopOnFailure: false, - }, - { - Name: "Execute PostStart Command", - Execute: func( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ) error { - command := ExtractLifecycleCommands(PostStartAction, devcontainerConfig) - return ExecuteCommands(ctx, exec, codeRepoDir, gitspaceLogger, command, PostStartAction) - }, - StopOnFailure: false, - }, - } -} - -// setupGitspaceAndIDE initializes Gitspace and IDE by registering and executing the setup steps. -func (e *EmbeddedDockerOrchestrator) setupGitspaceAndIDE( - ctx context.Context, - exec *devcontainer.Exec, - gitspaceLogger gitspaceTypes.GitspaceLogger, - ideService ide.IDE, - gitspaceConfig types.GitspaceConfig, - resolvedRepoDetails scm.ResolvedDetails, - defaultBaseImage string, - environment []string, -) error { - homeDir := GetUserHomeDir(gitspaceConfig.GitspaceUser.Identifier) - devcontainerConfig := resolvedRepoDetails.DevcontainerConfig - codeRepoDir := filepath.Join(homeDir, resolvedRepoDetails.RepoName) - - steps := e.buildSetupSteps( - ctx, - ideService, - gitspaceConfig, - resolvedRepoDetails, - defaultBaseImage, - environment, - devcontainerConfig, - codeRepoDir) - - // Execute the registered steps - if err := e.ExecuteSteps(ctx, exec, gitspaceLogger, steps); err != nil { - return err - } - return nil -} - func InstallTools( ctx context.Context, exec *devcontainer.Exec, diff --git a/app/gitspace/orchestrator/container/devcontainer_utils.go b/app/gitspace/orchestrator/container/devcontainer_utils.go index c32df6223..ed8b56013 100644 --- a/app/gitspace/orchestrator/container/devcontainer_utils.go +++ b/app/gitspace/orchestrator/container/devcontainer_utils.go @@ -13,43 +13,3 @@ // limitations under the License. package container - -import ( - "fmt" - - "github.com/harness/gitness/types" - - "github.com/rs/zerolog/log" -) - -func ExtractEnv(devcontainerConfig types.DevcontainerConfig) []string { - var env []string - for key, value := range devcontainerConfig.ContainerEnv { - env = append(env, fmt.Sprintf("%s=%s", key, value)) - } - return env -} - -func ExtractForwardPorts(devcontainerConfig types.DevcontainerConfig) []int { - var ports []int - for _, strPort := range devcontainerConfig.ForwardPorts { - portAsInt, err := strPort.Int64() // Using Atoi to convert string to int - if err != nil { - log.Warn().Msgf("Error converting port string '%s' to int: %v", strPort, err) - continue // Skip the invalid port - } - ports = append(ports, int(portAsInt)) - } - return ports -} - -func ExtractLifecycleCommands(actionType PostAction, devcontainerConfig types.DevcontainerConfig) []string { - switch actionType { - case PostCreateAction: - return devcontainerConfig.PostCreateCommand.ToCommandArray() - case PostStartAction: - return devcontainerConfig.PostStartCommand.ToCommandArray() - default: - return []string{} // Return empty string if actionType is not recognized - } -} diff --git a/app/gitspace/orchestrator/container/embedded_docker_provider.go b/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go similarity index 76% rename from app/gitspace/orchestrator/container/embedded_docker_provider.go rename to app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go index 9fd68d158..58a742237 100644 --- a/app/gitspace/orchestrator/container/embedded_docker_provider.go +++ b/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go @@ -23,6 +23,7 @@ import ( "github.com/harness/gitness/app/gitspace/orchestrator/devcontainer" "github.com/harness/gitness/app/gitspace/orchestrator/git" "github.com/harness/gitness/app/gitspace/orchestrator/ide" + "github.com/harness/gitness/app/gitspace/orchestrator/runarg" "github.com/harness/gitness/app/gitspace/orchestrator/user" "github.com/harness/gitness/app/gitspace/scm" gitspaceTypes "github.com/harness/gitness/app/gitspace/types" @@ -45,6 +46,7 @@ type EmbeddedDockerOrchestrator struct { statefulLogger *logutil.StatefulLogger gitService git.Service userService user.Service + runArgProvider runarg.Provider } // ExecuteSteps executes all registered steps in sequence, respecting stopOnFailure flag. @@ -73,12 +75,14 @@ func NewEmbeddedDockerOrchestrator( statefulLogger *logutil.StatefulLogger, gitService git.Service, userService user.Service, + runArgProvider runarg.Provider, ) Orchestrator { return &EmbeddedDockerOrchestrator{ dockerClientFactory: dockerClientFactory, statefulLogger: statefulLogger, gitService: gitService, userService: userService, + runArgProvider: runArgProvider, } } @@ -375,8 +379,14 @@ func (e *EmbeddedDockerOrchestrator) runGitspaceSetupSteps( imageName = defaultBaseImage } + runArgsMap, err := ExtractRunArgsWithLogging(ctx, gitspaceConfig.SpaceID, e.runArgProvider, + devcontainerConfig.RunArgs, gitspaceLogger) + if err != nil { + return err + } + // Pull the required image - if err := PullImage(ctx, imageName, dockerClient, gitspaceLogger); err != nil { + if err := PullImage(ctx, imageName, dockerClient, runArgsMap, gitspaceLogger); err != nil { return err } portMappings := infrastructure.GitspacePortMappings @@ -392,12 +402,12 @@ func (e *EmbeddedDockerOrchestrator) runGitspaceSetupSteps( } storage := infrastructure.Storage - environment := ExtractEnv(devcontainerConfig) + environment := ExtractEnv(devcontainerConfig, runArgsMap) if len(environment) > 0 { gitspaceLogger.Info(fmt.Sprintf("Setting Environment : %v", environment)) } // Create the container - err := CreateContainer( + err = CreateContainer( ctx, dockerClient, imageName, @@ -408,6 +418,7 @@ func (e *EmbeddedDockerOrchestrator) runGitspaceSetupSteps( mount.TypeVolume, portMappings, environment, + runArgsMap, ) if err != nil { return err @@ -444,6 +455,174 @@ func (e *EmbeddedDockerOrchestrator) runGitspaceSetupSteps( return nil } +// buildSetupSteps constructs the steps to be executed in the setup process. +func (e *EmbeddedDockerOrchestrator) buildSetupSteps( + _ context.Context, + ideService ide.IDE, + gitspaceConfig types.GitspaceConfig, + resolvedRepoDetails scm.ResolvedDetails, + defaultBaseImage string, + environment []string, + devcontainerConfig types.DevcontainerConfig, + codeRepoDir string, +) []gitspaceTypes.Step { + return []gitspaceTypes.Step{ + { + Name: "Validate Supported OS", + Execute: ValidateSupportedOS, + StopOnFailure: true, + }, + { + Name: "Manage User", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + return ManageUser(ctx, exec, e.userService, gitspaceLogger) + }, + StopOnFailure: true, + }, + { + Name: "Set environment", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + return SetEnv(ctx, exec, gitspaceLogger, environment) + }, + StopOnFailure: true, + }, + { + Name: "Install Tools", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + return InstallTools(ctx, exec, gitspaceLogger, gitspaceConfig.IDE) + }, + StopOnFailure: true, + }, + { + Name: "Setup IDE", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + return SetupIDE(ctx, exec, ideService, gitspaceLogger) + }, + StopOnFailure: true, + }, + { + Name: "Run IDE", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + return RunIDE(ctx, exec, ideService, gitspaceLogger) + }, + StopOnFailure: true, + }, + { + Name: "Install Git", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + return InstallGit(ctx, exec, e.gitService, gitspaceLogger) + }, + StopOnFailure: true, + }, + { + Name: "Setup Git Credentials", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + if resolvedRepoDetails.ResolvedCredentials.Credentials != nil { + return SetupGitCredentials(ctx, exec, resolvedRepoDetails, e.gitService, gitspaceLogger) + } + return nil + }, + StopOnFailure: true, + }, + { + Name: "Clone Code", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + return CloneCode(ctx, exec, defaultBaseImage, resolvedRepoDetails, e.gitService, gitspaceLogger) + }, + StopOnFailure: true, + }, + // Post-create and Post-start steps + { + Name: "Execute PostCreate Command", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + command := ExtractLifecycleCommands(PostCreateAction, devcontainerConfig) + return ExecuteCommands(ctx, exec, codeRepoDir, gitspaceLogger, command, PostCreateAction) + }, + StopOnFailure: false, + }, + { + Name: "Execute PostStart Command", + Execute: func( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ) error { + command := ExtractLifecycleCommands(PostStartAction, devcontainerConfig) + return ExecuteCommands(ctx, exec, codeRepoDir, gitspaceLogger, command, PostStartAction) + }, + StopOnFailure: false, + }, + } +} + +// setupGitspaceAndIDE initializes Gitspace and IDE by registering and executing the setup steps. +func (e *EmbeddedDockerOrchestrator) setupGitspaceAndIDE( + ctx context.Context, + exec *devcontainer.Exec, + gitspaceLogger gitspaceTypes.GitspaceLogger, + ideService ide.IDE, + gitspaceConfig types.GitspaceConfig, + resolvedRepoDetails scm.ResolvedDetails, + defaultBaseImage string, + environment []string, +) error { + homeDir := GetUserHomeDir(gitspaceConfig.GitspaceUser.Identifier) + devcontainerConfig := resolvedRepoDetails.DevcontainerConfig + codeRepoDir := filepath.Join(homeDir, resolvedRepoDetails.RepoName) + + steps := e.buildSetupSteps( + ctx, + ideService, + gitspaceConfig, + resolvedRepoDetails, + defaultBaseImage, + environment, + devcontainerConfig, + codeRepoDir) + + // Execute the registered steps + if err := e.ExecuteSteps(ctx, exec, gitspaceLogger, steps); err != nil { + return err + } + return nil +} + // getDockerClient creates and returns a new Docker client using the factory. func (e *EmbeddedDockerOrchestrator) getDockerClient( ctx context.Context, diff --git a/app/gitspace/orchestrator/container/runarg_utils.go b/app/gitspace/orchestrator/container/runarg_utils.go new file mode 100644 index 000000000..65e1369ea --- /dev/null +++ b/app/gitspace/orchestrator/container/runarg_utils.go @@ -0,0 +1,541 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package container + +import ( + "fmt" + "math/big" + "strconv" + "strings" + "time" + + "github.com/harness/gitness/types" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/strslice" + "github.com/docker/go-units" + "github.com/rs/zerolog/log" +) + +func getHostResources(runArgsMap map[types.RunArg]*types.RunArgValue) (container.Resources, error) { // nolint: gocognit + var resources = container.Resources{} + blkioWeight, err := getArgValueUint16(runArgsMap, types.RunArgBlkioWeight) + if err != nil { + return resources, err + } + resources.BlkioWeight = blkioWeight + + cpuShares, err := getArgValueInt64(runArgsMap, types.RunArgCPUShares) + if err != nil { + return resources, err + } + resources.CPUShares = cpuShares + + memory, err := getArgValueMemoryBytes(runArgsMap, types.RunArgMemory) + if err != nil { + return resources, err + } + resources.Memory = memory + + cpus, err := getCPUs(runArgsMap, types.RunArgCpus) + if err != nil { + return resources, err + } + resources.NanoCPUs = cpus + + resources.CgroupParent = getArgValueString(runArgsMap, types.RunArgCgroupParent) + + cpuPeriod, err := getArgValueInt64(runArgsMap, types.RunArgCPUPeriod) + if err != nil { + return resources, err + } + resources.CPUPeriod = cpuPeriod + + cpuQuota, err := getArgValueInt64(runArgsMap, types.RunArgCPUQuota) + if err != nil { + return resources, err + } + resources.CPUQuota = cpuQuota + + cpuRTPeriod, err := getArgValueInt64(runArgsMap, types.RunArgCPURtPeriod) + if err != nil { + return resources, err + } + resources.CPURealtimePeriod = cpuRTPeriod + + cpuRTRuntime, err := getArgValueInt64(runArgsMap, types.RunArgCPURtRuntime) + if err != nil { + return resources, err + } + resources.CPURealtimeRuntime = cpuRTRuntime + + resources.CpusetCpus = getArgValueString(runArgsMap, types.RunArgCpusetCpus) + + resources.CpusetMems = getArgValueString(runArgsMap, types.RunArgCpusetMems) + + cpuCount, err := getArgValueInt64(runArgsMap, types.RunArgCPUCount) + if err != nil { + return resources, err + } + resources.CPUCount = cpuCount + + cpuPercent, err := getArgValueInt64(runArgsMap, types.RunArgCPUPercent) + if err != nil { + return resources, err + } + resources.CPUPercent = cpuPercent + + kernelMemory, err := getArgValueMemoryBytes(runArgsMap, types.RunArgKernelMemory) + if err != nil { + return resources, err + } + resources.KernelMemory = kernelMemory + + memoryReservation, err := getArgValueMemoryBytes(runArgsMap, types.RunArgMemoryReservation) + if err != nil { + return resources, err + } + resources.MemoryReservation = memoryReservation + + memorySwappiness, err := getArgValueInt64Ptr(runArgsMap, types.RunArgMemorySwappiness) + if err != nil { + return resources, err + } + resources.MemorySwappiness = memorySwappiness + + memorySwap, err := getArgValueMemorySwapBytes(runArgsMap, types.RunArgMemorySwap) + if err != nil { + return resources, err + } + resources.MemorySwap = memorySwap + + resources.OomKillDisable = getArgValueBoolPtr(runArgsMap, types.RunArgOomKillDisable) + + pidsLimit, err := getArgValueInt64Ptr(runArgsMap, types.RunArgPidsLimit) + if err != nil { + return resources, err + } + resources.PidsLimit = pidsLimit + + ioMaxiops, err := getArgValueUint64(runArgsMap, types.RunArgIoMaxiops) + if err != nil { + return resources, err + } + resources.IOMaximumIOps = ioMaxiops + + ioMaxbandwidth, err := getArgValueMemoryBytes(runArgsMap, types.RunArgIoMaxbandwidth) + if err != nil { + return resources, err + } + resources.IOMaximumBandwidth = uint64(ioMaxbandwidth) + + if arg, ok := runArgsMap[types.RunArgUlimit]; ok { + ulimits := []*container.Ulimit{} + for _, v := range arg.Values { + ulimit, err := units.ParseUlimit(v) + if err != nil { + return resources, err + } + ulimits = append(ulimits, ulimit) + } + resources.Ulimits = ulimits + } + return resources, nil +} + +func getNetworkMode(runArgsMap map[types.RunArg]*types.RunArgValue) container.NetworkMode { + return container.NetworkMode(getArgValueString(runArgsMap, types.RunArgNetwork)) +} + +func getCapDrop(runArgsMap map[types.RunArg]*types.RunArgValue) strslice.StrSlice { + return getArgValueStringSlice(runArgsMap, types.RunArgCapDrop) +} + +func getSHMSize(runArgsMap map[types.RunArg]*types.RunArgValue) (int64, error) { + return getArgValueMemoryBytes(runArgsMap, types.RunArgShmSize) +} + +func getArgValueMemorySwapBytes(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (int64, error) { + value := getArgValueString(runArgsMap, argName) + if value == "" { + return 0, nil + } + if value == "-1" { + return -1, nil + } + memorySwapBytes, err := units.RAMInBytes(value) + if err != nil { + return 0, err + } + return memorySwapBytes, nil +} + +func getSysctls(runArgsMap map[types.RunArg]*types.RunArgValue) map[string]string { + values := getArgValueStringSlice(runArgsMap, types.RunArgSysctl) + var opt = map[string]string{} + for _, value := range values { + parts := strings.SplitN(value, "=", 2) + if len(parts) != 2 { + parts = append(parts, "") + } + opt[parts[0]] = parts[1] + } + return opt +} + +func getDNS(runArgsMap map[types.RunArg]*types.RunArgValue) []string { + return getArgValueStringSlice(runArgsMap, types.RunArgDNS) +} + +func getDNSOptions(runArgsMap map[types.RunArg]*types.RunArgValue) []string { + return getArgValueStringSlice(runArgsMap, types.RunArgDNSOption) +} + +func getDNSSearch(runArgsMap map[types.RunArg]*types.RunArgValue) []string { + return getArgValueStringSlice(runArgsMap, types.RunArgDNSSearch) +} + +func getCgroupNSMode(runArgsMap map[types.RunArg]*types.RunArgValue) container.CgroupnsMode { + value := getArgValueString(runArgsMap, types.RunArgCgroupns) + return container.CgroupnsMode(value) +} + +func getIPCMode(runArgsMap map[types.RunArg]*types.RunArgValue) container.IpcMode { + return container.IpcMode(getArgValueString(runArgsMap, types.RunArgIpc)) +} + +func getIsolation(runArgsMap map[types.RunArg]*types.RunArgValue) container.Isolation { + return container.Isolation(getArgValueString(runArgsMap, types.RunArgIsolation)) +} + +func getRuntime(runArgsMap map[types.RunArg]*types.RunArgValue) string { + return getArgValueString(runArgsMap, types.RunArgRuntime) +} + +func getPlatform(runArgsMap map[types.RunArg]*types.RunArgValue) string { + return getArgValueString(runArgsMap, types.RunArgPlatform) +} + +func getPIDMode(runArgsMap map[types.RunArg]*types.RunArgValue) container.PidMode { + return container.PidMode(getArgValueString(runArgsMap, types.RunArgPid)) +} + +func getSecurityOpt(runArgsMap map[types.RunArg]*types.RunArgValue) []string { + return getArgValueStringSlice(runArgsMap, types.RunArgSecurityOpt) +} + +func getStorageOpt(runArgsMap map[types.RunArg]*types.RunArgValue) map[string]string { + values := getArgValueStringSlice(runArgsMap, types.RunArgStorageOpt) + var opt = map[string]string{} + for _, value := range values { + parts := strings.SplitN(value, "=", 2) + if len(parts) != 2 { + parts = append(parts, "") + } + opt[parts[0]] = parts[1] + } + return opt +} + +func getAutoRemove(runArgsMap map[types.RunArg]*types.RunArgValue) bool { + return getArgValueBool(runArgsMap, types.RunArgRm) +} + +func getInit(runArgsMap map[types.RunArg]*types.RunArgValue) *bool { + return getArgValueBoolPtr(runArgsMap, types.RunArgInit) +} + +func getEnv(runArgsMap map[types.RunArg]*types.RunArgValue) []string { + return getArgValueStringSlice(runArgsMap, types.RunArgEnv) +} + +func getLinks(runArgsMap map[types.RunArg]*types.RunArgValue) []string { + return getArgValueStringSlice(runArgsMap, types.RunArgLink) +} + +func getOomScoreAdj(runArgsMap map[types.RunArg]*types.RunArgValue) (int, error) { + return getArgValueInt(runArgsMap, types.RunArgOomScoreAdj) +} + +func getRestartPolicy(runArgsMap map[types.RunArg]*types.RunArgValue) (container.RestartPolicy, error) { + value := getArgValueString(runArgsMap, types.RunArgRestart) + if len(value) == 0 { + return container.RestartPolicy{}, nil + } + parts := strings.SplitN(value, ":", 2) + maxCount := 0 + if container.RestartPolicyMode(parts[0]) == container.RestartPolicyOnFailure && len(parts) == 2 { + count, err := strconv.Atoi(parts[1]) + if err != nil { + return container.RestartPolicy{}, err + } + maxCount = count + } + return container.RestartPolicy{ + Name: container.RestartPolicyMode(parts[0]), + MaximumRetryCount: maxCount, + }, nil +} + +func getExtraHosts(runArgsMap map[types.RunArg]*types.RunArgValue) []string { + return getArgValueStringSlice(runArgsMap, types.RunArgAddHost) +} + +func getHostname(runArgsMap map[types.RunArg]*types.RunArgValue) string { + return getArgValueString(runArgsMap, types.RunArgHostname) +} + +func getDomainname(runArgsMap map[types.RunArg]*types.RunArgValue) string { + return getArgValueString(runArgsMap, types.RunArgDomainname) +} + +func getMACAddress(runArgsMap map[types.RunArg]*types.RunArgValue) string { + return getArgValueString(runArgsMap, types.RunArgMacAddress) +} + +func getStopSignal(runArgsMap map[types.RunArg]*types.RunArgValue) string { + return getArgValueString(runArgsMap, types.RunArgStopSignal) +} + +func getStopTimeout(runArgsMap map[types.RunArg]*types.RunArgValue) (*int, error) { + return getArgValueIntPtr(runArgsMap, types.RunArgStopTimeout) +} + +func getImagePullPolicy(runArgsMap map[types.RunArg]*types.RunArgValue) string { + policy := getArgValueString(runArgsMap, types.RunArgPull) + if policy == "" { + policy = "missing" + } + return policy +} + +func getUser(runArgsMap map[types.RunArg]*types.RunArgValue) string { + return getArgValueString(runArgsMap, types.RunArgUser) +} + +func getEntrypoint(runArgsMap map[types.RunArg]*types.RunArgValue) []string { + return getArgValueStringSlice(runArgsMap, types.RunArgEntrypoint) +} + +func getHealthCheckConfig(runArgsMap map[types.RunArg]*types.RunArgValue) (*container.HealthConfig, error) { + var healthConfig = &container.HealthConfig{} + + retries, err := getArgValueInt(runArgsMap, types.RunArgHealthRetries) + if err != nil { + return healthConfig, err + } + healthConfig.Retries = retries + + interval, err := getArgValueDuration(runArgsMap, types.RunArgHealthInterval) + if err != nil { + return healthConfig, err + } + healthConfig.Interval = interval + + timeout, err := getArgValueDuration(runArgsMap, types.RunArgHealthTimeout) + if err != nil { + return healthConfig, err + } + healthConfig.Timeout = timeout + + startPeriod, err := getArgValueDuration(runArgsMap, types.RunArgHealthStartPeriod) + if err != nil { + return healthConfig, err + } + healthConfig.StartPeriod = startPeriod + + startInterval, err := getArgValueDuration(runArgsMap, types.RunArgHealthStartInterval) + if err != nil { + return healthConfig, err + } + healthConfig.StartInterval = startInterval + + if _, ok := runArgsMap[types.RunArgNoHealthcheck]; ok { + healthConfig.Test = []string{"NONE"} + } else if arg, healthCmdOK := runArgsMap[types.RunArgHealthCmd]; healthCmdOK { + healthConfig.Test = arg.Values + } + + return healthConfig, nil +} + +func getLabels(runArgsMap map[types.RunArg]*types.RunArgValue) map[string]string { + arg, ok := runArgsMap[types.RunArgLabel] + labelsMap := make(map[string]string) + if ok { + labels := arg.Values + for _, v := range labels { + parts := strings.SplitN(v, "=", 2) + if len(parts) < 2 { + labelsMap[parts[0]] = "" + } else { + labelsMap[parts[0]] = parts[1] + } + } + } + return labelsMap +} + +func getAnnotations(runArgsMap map[types.RunArg]*types.RunArgValue) map[string]string { + arg, ok := runArgsMap[types.RunArgAnnotation] + annotationsMap := make(map[string]string) + if ok { + annotations := arg.Values + for _, v := range annotations { + annotationParts := strings.SplitN(v, "=", 2) + if len(annotationParts) != 2 { + log.Warn().Msgf("invalid annotation: %s", v) + } else { + annotationsMap[annotationParts[0]] = annotationParts[1] + } + } + } + return annotationsMap +} + +func getArgValueMemoryBytes(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (int64, error) { + value := getArgValueString(runArgsMap, argName) + if value == "" { + return 0, nil + } + memoryBytes, err := units.RAMInBytes(value) + if err != nil { + return 0, err + } + return memoryBytes, nil +} + +func getArgValueString(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) string { + if arg, ok := runArgsMap[argName]; ok { + return arg.Values[0] + } + return "" +} + +func getArgValueStringSlice(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) []string { + if arg, ok := runArgsMap[argName]; ok { + return arg.Values + } + return []string{} +} + +func getArgValueInt(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (int, error) { + value, err := getArgValueIntPtr(runArgsMap, argName) + if err != nil { + return 0, err + } + if value == nil { + return 0, nil + } + return *value, nil +} + +func getArgValueIntPtr(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (*int, error) { + if arg, ok := runArgsMap[argName]; ok { + value, err := strconv.Atoi(arg.Values[0]) + if err != nil { + return nil, err + } + return &value, nil + } + return nil, nil // nolint: nilnil +} + +func getArgValueUint16(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (uint16, error) { + if arg, ok := runArgsMap[argName]; ok { + value, err := strconv.ParseUint(arg.Values[0], 10, 16) + if err != nil { + return 0, err + } + return uint16(value), nil + } + return 0, nil +} + +func getArgValueUint64(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (uint64, error) { + if arg, ok := runArgsMap[argName]; ok { + value, err := strconv.ParseUint(arg.Values[0], 10, 64) + if err != nil { + return 0, err + } + return value, nil + } + return 0, nil +} + +func getCPUs(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (int64, error) { + if arg, ok := runArgsMap[argName]; ok { + value := arg.Values[0] + cpu, ok1 := new(big.Rat).SetString(value) // nolint: gosec + if !ok1 { + return 0, fmt.Errorf("failed to parse %v as a rational number", value) + } + nano := cpu.Mul(cpu, big.NewRat(1e9, 1)) + if !nano.IsInt() { + return 0, fmt.Errorf("value is too precise") + } + return nano.Num().Int64(), nil + } + return 0, nil +} + +func getArgValueInt64(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (int64, error) { + value, err := getArgValueInt64Ptr(runArgsMap, argName) + if err != nil { + return 0, err + } + if value == nil { + return 0, nil + } + return *value, nil +} + +func getArgValueInt64Ptr(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (*int64, error) { + if arg, ok := runArgsMap[argName]; ok { + value, err := strconv.ParseInt(arg.Values[0], 10, 64) + if err != nil { + return nil, err + } + return &value, nil + } + return nil, nil // nolint: nilnil +} + +func getArgValueDuration(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) (time.Duration, error) { + defaultDur := time.Second * 0 + if arg, ok := runArgsMap[argName]; ok { + dur, err := time.ParseDuration(arg.Values[0]) + if err != nil { + return defaultDur, err + } + return dur, nil + } + return defaultDur, nil +} + +func getArgValueBool(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) bool { + value := getArgValueBoolPtr(runArgsMap, argName) + if value == nil { + return false + } + return *value +} +func getArgValueBoolPtr(runArgsMap map[types.RunArg]*types.RunArgValue, argName types.RunArg) *bool { + _, ok := runArgsMap[argName] + if ok { + return &ok + } + return nil +} diff --git a/app/gitspace/orchestrator/container/wire.go b/app/gitspace/orchestrator/container/wire.go index e51d082d7..9a5a2b576 100644 --- a/app/gitspace/orchestrator/container/wire.go +++ b/app/gitspace/orchestrator/container/wire.go @@ -17,6 +17,7 @@ package container import ( "github.com/harness/gitness/app/gitspace/logutil" "github.com/harness/gitness/app/gitspace/orchestrator/git" + "github.com/harness/gitness/app/gitspace/orchestrator/runarg" "github.com/harness/gitness/app/gitspace/orchestrator/user" "github.com/harness/gitness/infraprovider" @@ -32,11 +33,13 @@ func ProvideEmbeddedDockerOrchestrator( statefulLogger *logutil.StatefulLogger, gitService git.Service, userService user.Service, + runArgProvdier runarg.Provider, ) Orchestrator { return NewEmbeddedDockerOrchestrator( dockerClientFactory, statefulLogger, gitService, userService, + runArgProvdier, ) } diff --git a/app/gitspace/orchestrator/runarg/provider.go b/app/gitspace/orchestrator/runarg/provider.go new file mode 100644 index 000000000..51e527ca8 --- /dev/null +++ b/app/gitspace/orchestrator/runarg/provider.go @@ -0,0 +1,27 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runarg + +import ( + "context" + + "github.com/harness/gitness/types" +) + +type Provider interface { + // ProvideSupportedRunArgs returns a map of the run arg definitions for all the supported arg applicable for the + // given gitspace spaceID. + ProvideSupportedRunArgs(ctx context.Context, spaceID int64) (map[types.RunArg]types.RunArgDefinition, error) +} diff --git a/app/gitspace/orchestrator/runarg/runArgs.yaml b/app/gitspace/orchestrator/runarg/runArgs.yaml new file mode 100644 index 000000000..3c7aa0545 --- /dev/null +++ b/app/gitspace/orchestrator/runarg/runArgs.yaml @@ -0,0 +1,727 @@ +--- +- name: --add-host + short_hand: + supported: true + blocked_values: {} + allowed_values: {} + allow_multiple_occurrences: true + +- name: --annotation + short_hand: + supported: true + blocked_values: {} + allowed_values: {} + allow_multiple_occurrences: true + +- name: --attach + short_hand: -a + supported: false + blocked_values: {} + allowed_values: {} + allow_multiple_occurrences: true + +- name: --blkio-weight + short_hand: + supported: true + blocked_values: {} + allowed_values: {} + allow_multiple_occurrences: false + +- name: --blkio-weight-device + short_hand: + supported: false + blocked_values: {} + allowed_values: {} + allow_multiple_occurrences: true + +- name: --cap-add + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --cap-drop + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --cgroup-parent + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cgroupns + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cidfile + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --cpu-count + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cpu-percent + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cpu-period + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cpu-quota + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cpu-rt-period + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cpu-rt-runtime + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cpu-shares + short_hand: -c + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cpus + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cpuset-cpus + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --cpuset-mems + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --detach + short_hand: -d + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --detach-keys + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --device + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --device-cgroup-rule + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --device-read-bps + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --device-read-iops + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --device-write-bps + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --device-write-iops + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --disable-content-trust + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --dns + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --dns-option + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --dns-search + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --domainname + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --entrypoint + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --env + short_hand: -e + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --env-file + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --expose + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --gpus + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --group-add + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --health-cmd + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --health-interval + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --health-retries + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --health-start-interval + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --health-start-period + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --health-timeout + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --help + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --hostname + short_hand: -h + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --init + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --interactive + short_hand: -i + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --io-maxbandwidth + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --io-maxiops + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --ip + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --ip6 + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --ipc + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --isolation + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --kernel-memory + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --label + short_hand: -l + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --label-file + short_hand: + supported: false + blocked_values: {} + allowed_values: {} + allow_multiple_occurrences: true + +- name: --link + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --link-local-ip + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --log-driver + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --log-opt + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --mac-address + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --memory + short_hand: -m + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --memory-reservation + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --memory-swap + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --memory-swappiness + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --mount + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --name + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --network + short_hand: + supported: true + blocked_values: + host: true + none: true + allowed_values: { } + allow_multiple_occurrences: false + +- name: --network-alias + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --no-healthcheck + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --oom-kill-disable + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --oom-score-adj + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --pid + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --pids-limit + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --platform + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --privileged + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --publish + short_hand: -p + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --publish-all + short_hand: -P + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --pull + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --quiet + short_hand: -q + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --read-only + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --restart + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --rm + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + + +- name: --runtime + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --security-opt + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --shm-size + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --sig-proxy + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --stop-signal + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --stop-timeout + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + +- name: --storage-opt + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --sysctl + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --tmpfs + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --tty + short_hand: -t + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --ulimit + short_hand: + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + + +- name: --user + short_hand: -u + supported: true + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: false + + +- name: --userns + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + + +- name: --uts + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --volume + short_hand: -v + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --volume-driver + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --volumes-from + short_hand: + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true + +- name: --workdir + short_hand: -w + supported: false + blocked_values: { } + allowed_values: { } + allow_multiple_occurrences: true diff --git a/app/gitspace/orchestrator/runarg/static_provider.go b/app/gitspace/orchestrator/runarg/static_provider.go new file mode 100644 index 000000000..26b0b40da --- /dev/null +++ b/app/gitspace/orchestrator/runarg/static_provider.go @@ -0,0 +1,61 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runarg + +import ( + "context" + "fmt" + + "github.com/harness/gitness/types" + + "gopkg.in/yaml.v3" + + _ "embed" +) + +var _ Provider = (*StaticProvider)(nil) + +//go:embed runArgs.yaml +var supportedRunArgsRaw []byte + +type StaticProvider struct { + supportedRunArgsMap map[types.RunArg]types.RunArgDefinition +} + +func NewStaticProvider() (Provider, error) { + allRunArgs := make([]types.RunArgDefinition, 0) + err := yaml.Unmarshal(supportedRunArgsRaw, &allRunArgs) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal runArgs.yaml: %w", err) + } + argsMap := make(map[types.RunArg]types.RunArgDefinition) + for _, arg := range allRunArgs { + if arg.Supported { + argsMap[arg.Name] = arg + if arg.ShortHand != "" { + argsMap[arg.ShortHand] = arg + } + } + } + return &StaticProvider{supportedRunArgsMap: argsMap}, nil +} + +// ProvideSupportedRunArgs provides a static map of supported run args. +func (s *StaticProvider) ProvideSupportedRunArgs( + _ context.Context, + _ int64, +) (map[types.RunArg]types.RunArgDefinition, error) { + return s.supportedRunArgsMap, nil +} diff --git a/app/gitspace/orchestrator/runarg/wire.go b/app/gitspace/orchestrator/runarg/wire.go new file mode 100644 index 000000000..ff2360f98 --- /dev/null +++ b/app/gitspace/orchestrator/runarg/wire.go @@ -0,0 +1,27 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runarg + +import ( + "github.com/google/wire" +) + +var WireSet = wire.NewSet( + ProvideStaticProvider, +) + +func ProvideStaticProvider() (Provider, error) { + return NewStaticProvider() +} diff --git a/cmd/gitness/wire.go b/cmd/gitness/wire.go index 1619cceaa..f0edf78c7 100644 --- a/cmd/gitness/wire.go +++ b/cmd/gitness/wire.go @@ -66,6 +66,7 @@ import ( containerorchestrator "github.com/harness/gitness/app/gitspace/orchestrator/container" containerGit "github.com/harness/gitness/app/gitspace/orchestrator/git" "github.com/harness/gitness/app/gitspace/orchestrator/ide" + "github.com/harness/gitness/app/gitspace/orchestrator/runarg" containerUser "github.com/harness/gitness/app/gitspace/orchestrator/user" "github.com/harness/gitness/app/gitspace/platformconnector" "github.com/harness/gitness/app/gitspace/scm" @@ -281,6 +282,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e containerGit.WireSet, containerUser.WireSet, messagingservice.WireSet, + runarg.WireSet, ) return &cliserver.System{}, nil } diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index 0a7210105..8c89edfcd 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -55,6 +55,7 @@ import ( "github.com/harness/gitness/app/gitspace/orchestrator/container" git2 "github.com/harness/gitness/app/gitspace/orchestrator/git" "github.com/harness/gitness/app/gitspace/orchestrator/ide" + "github.com/harness/gitness/app/gitspace/orchestrator/runarg" user2 "github.com/harness/gitness/app/gitspace/orchestrator/user" "github.com/harness/gitness/app/gitspace/platformconnector" "github.com/harness/gitness/app/gitspace/scm" @@ -318,7 +319,11 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro statefulLogger := logutil.ProvideStatefulLogger(logStream) gitService := git2.ProvideGitServiceImpl() userService := user2.ProvideUserServiceImpl() - containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, statefulLogger, gitService, userService) + runargProvider, err := runarg.ProvideStaticProvider() + if err != nil { + return nil, err + } + containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, statefulLogger, gitService, userService, runargProvider) orchestratorConfig := server.ProvideGitspaceOrchestratorConfig(config) vsCodeConfig := server.ProvideIDEVSCodeConfig(config) vsCode := ide.ProvideVSCodeService(vsCodeConfig) diff --git a/types/devcontainer_config.go b/types/devcontainer_config.go index d94608072..25870e892 100644 --- a/types/devcontainer_config.go +++ b/types/devcontainer_config.go @@ -27,6 +27,7 @@ type DevcontainerConfig struct { ForwardPorts []json.Number `json:"forwardPorts"` //nolint:tagliatelle ContainerEnv map[string]string `json:"containerEnv"` //nolint:tagliatelle Customizations DevContainerConfigCustomizations `json:"customizations"` + RunArgs []string `json:"runArgs"` //nolint:tagliatelle } // LifecycleCommand supports multiple formats for lifecycle commands. diff --git a/types/gitspace_run_arg.go b/types/gitspace_run_arg.go new file mode 100644 index 000000000..5b059205e --- /dev/null +++ b/types/gitspace_run_arg.go @@ -0,0 +1,103 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "strings" +) + +type RunArg string + +const ( + RunArgAddHost = RunArg("--add-host") + RunArgAnnotation = RunArg("--annotation") + RunArgBlkioWeight = RunArg("--blkio-weight") + RunArgCapDrop = RunArg("--cap-drop") + RunArgCgroupParent = RunArg("--cgroup-parent") + RunArgCgroupns = RunArg("--cgroupns") + RunArgCPUCount = RunArg("--cpu-count") + RunArgCPUPercent = RunArg("--cpu-percent") + RunArgCPUPeriod = RunArg("--cpu-period") + RunArgCPUQuota = RunArg("--cpu-quota") + RunArgCPURtPeriod = RunArg("--cpu-rt-period") + RunArgCPURtRuntime = RunArg("--cpu-rt-runtime") + RunArgCPUShares = RunArg("--cpu-shares") + RunArgCpus = RunArg("--cpus") + RunArgCpusetCpus = RunArg("--cpuset-cpus") + RunArgCpusetMems = RunArg("--cpuset-mems") + RunArgDNS = RunArg("--dns") + RunArgDNSOption = RunArg("--dns-option") + RunArgDNSSearch = RunArg("--dns-search") + RunArgDomainname = RunArg("--domainname") + RunArgEntrypoint = RunArg("--entrypoint") + RunArgEnv = RunArg("--env") + RunArgHealthCmd = RunArg("--health-cmd") + RunArgHealthInterval = RunArg("--health-interval") + RunArgHealthRetries = RunArg("--health-retries") + RunArgHealthStartInterval = RunArg("--health-start-interval") + RunArgHealthStartPeriod = RunArg("--health-start-period") + RunArgHealthTimeout = RunArg("--health-timeout") + RunArgHostname = RunArg("--hostname") + RunArgInit = RunArg("--init") + RunArgIoMaxbandwidth = RunArg("--io-maxbandwidth") + RunArgIoMaxiops = RunArg("--io-maxiops") + RunArgIpc = RunArg("--ipc") + RunArgIsolation = RunArg("--isolation") + RunArgKernelMemory = RunArg("--kernel-memory") + RunArgLabel = RunArg("--label") + RunArgLink = RunArg("--link") + RunArgMacAddress = RunArg("--mac-address") + RunArgMemory = RunArg("--memory") + RunArgMemoryReservation = RunArg("--memory-reservation") + RunArgMemorySwap = RunArg("--memory-swap") + RunArgMemorySwappiness = RunArg("--memory-swappiness") + RunArgNetwork = RunArg("--network") + RunArgNoHealthcheck = RunArg("--no-healthcheck") + RunArgOomKillDisable = RunArg("--oom-kill-disable") + RunArgOomScoreAdj = RunArg("--oom-score-adj") + RunArgPid = RunArg("--pid") + RunArgPidsLimit = RunArg("--pids-limit") + RunArgPlatform = RunArg("--platform") + RunArgPull = RunArg("--pull") + RunArgRestart = RunArg("--restart") + RunArgRm = RunArg("--rm") + RunArgRuntime = RunArg("--runtime") + RunArgSecurityOpt = RunArg("--security-opt") + RunArgShmSize = RunArg("--shm-size") + RunArgStopSignal = RunArg("--stop-signal") + RunArgStopTimeout = RunArg("--stop-timeout") + RunArgStorageOpt = RunArg("--storage-opt") + RunArgSysctl = RunArg("--sysctl") + RunArgUlimit = RunArg("--ulimit") + RunArgUser = RunArg("--user") +) + +type RunArgDefinition struct { + Name RunArg `yaml:"name"` + ShortHand RunArg `yaml:"short_hand"` + Supported bool `yaml:"supported"` + BlockedValues map[string]bool `yaml:"blocked_values"` + AllowedValues map[string]bool `yaml:"allowed_values"` + AllowMultipleOccurences bool `yaml:"allow_multiple_occurrences"` +} + +type RunArgValue struct { + Name RunArg + Values []string +} + +func (c *RunArgValue) String() string { + return strings.Join(c.Values, ", ") +}