feat: [CDE-475]: Adding support for runArgs. (#3031)

* Rebasing
* feat: [CDE-475]: Adding support for runArgs.
* WIP
* WIP
pull/3597/head
Dhruv Dhruv 2024-11-25 09:56:30 +00:00 committed by Harness
parent 1299db775e
commit 7c358528ef
16 changed files with 1992 additions and 226 deletions

View File

@ -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

View File

@ -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] = &currentRunArgValue
}
}
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
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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
}
}

View File

@ -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,

View File

@ -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
}

View File

@ -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,
)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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)

View File

@ -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.

103
types/gitspace_run_arg.go Normal file
View File

@ -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, ", ")
}