feat: [CDE-461]: gitspaces expose the container methods (#2974)

* feat: [CDE-461]: gitspaces expose the container methods
* feat: [CDE-461]: gitspaces expose the container methods
* feat: [CDE-461]: gitspaces expose the container methods
* feat: [CDE-461]: gitspaces expose the container methods
* feat: [CDE-461]: gitspaces expose the container methods
* feat: [CDE-461]: gitspaces expose the container methods
This commit is contained in:
Ansuman Satapathy 2024-11-11 18:09:38 +00:00 committed by Harness
parent c635effd2f
commit 4d4917eddc
3 changed files with 128 additions and 104 deletions

View File

@ -22,10 +22,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/harness/gitness/app/gitspace/orchestrator/devcontainer"
"github.com/harness/gitness/app/gitspace/orchestrator/ide"
orchestratorTypes "github.com/harness/gitness/app/gitspace/orchestrator/types" orchestratorTypes "github.com/harness/gitness/app/gitspace/orchestrator/types"
"github.com/harness/gitness/app/gitspace/scm"
"github.com/harness/gitness/types" "github.com/harness/gitness/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
@ -42,6 +39,14 @@ const (
catchAllIP = "0.0.0.0" catchAllIP = "0.0.0.0"
) )
var containerStateMapping = map[string]State{
"running": ContainerStateRunning,
"exited": ContainerStateStopped,
"dead": ContainerStateDead,
"created": ContainerStateCreated,
"paused": ContainerStatePaused,
}
// Helper function to log messages and handle error wrapping. // Helper function to log messages and handle error wrapping.
func logStreamWrapError(gitspaceLogger orchestratorTypes.GitspaceLogger, msg string, err error) error { func logStreamWrapError(gitspaceLogger orchestratorTypes.GitspaceLogger, msg string, err error) error {
gitspaceLogger.Error(msg, err) gitspaceLogger.Error(msg, err)
@ -49,7 +54,7 @@ func logStreamWrapError(gitspaceLogger orchestratorTypes.GitspaceLogger, msg str
} }
// Generalized Docker Container Management. // Generalized Docker Container Management.
func (e *EmbeddedDockerOrchestrator) manageContainer( func ManageContainer(
ctx context.Context, ctx context.Context,
action Action, action Action,
containerName string, containerName string,
@ -83,7 +88,7 @@ func (e *EmbeddedDockerOrchestrator) manageContainer(
return nil return nil
} }
func (e *EmbeddedDockerOrchestrator) containerState( func FetchContainerState(
ctx context.Context, ctx context.Context,
containerName string, containerName string,
dockerClient *client.Client, dockerClient *client.Client,
@ -99,12 +104,22 @@ func (e *EmbeddedDockerOrchestrator) containerState(
if len(containers) == 0 { if len(containers) == 0 {
return ContainerStateRemoved, nil return ContainerStateRemoved, nil
} }
containerState := ContainerStateUnknown
for _, value := range containers {
name, _ := strings.CutPrefix(value.Names[0], "/")
if name == containerName {
if state, ok := containerStateMapping[value.State]; ok {
containerState = state
}
break
}
}
return State(containers[0].State), nil return containerState, nil
} }
// Create a new Docker container. // Create a new Docker container.
func (e *EmbeddedDockerOrchestrator) createContainer( func CreateContainer(
ctx context.Context, ctx context.Context,
dockerClient *client.Client, dockerClient *client.Client,
imageName string, imageName string,
@ -174,7 +189,7 @@ func prepareHostConfig(volumeName, homeDir string, portBindings nat.PortMap) *co
return hostConfig return hostConfig
} }
func (e *EmbeddedDockerOrchestrator) getContainerInfo( func GetContainerInfo(
ctx context.Context, ctx context.Context,
containerName string, containerName string,
dockerClient *client.Client, dockerClient *client.Client,
@ -201,7 +216,7 @@ func (e *EmbeddedDockerOrchestrator) getContainerInfo(
return inspectResp.ID, usedPorts, nil return inspectResp.ID, usedPorts, nil
} }
func (e *EmbeddedDockerOrchestrator) pullImage( func PullImage(
ctx context.Context, ctx context.Context,
imageName string, imageName string,
dockerClient *client.Client, dockerClient *client.Client,
@ -230,101 +245,15 @@ func (e *EmbeddedDockerOrchestrator) pullImage(
return nil return nil
} }
func (e *EmbeddedDockerOrchestrator) runGitspaceSetupSteps(
ctx context.Context,
gitspaceConfig types.GitspaceConfig,
dockerClient *client.Client,
ideService ide.IDE,
infrastructure types.Infrastructure,
resolvedRepoDetails scm.ResolvedDetails,
defaultBaseImage string,
gitspaceLogger orchestratorTypes.GitspaceLogger,
) error {
homeDir := GetUserHomeDir(gitspaceConfig.GitspaceUser.Identifier)
containerName := GetGitspaceContainerName(gitspaceConfig)
devcontainerConfig := resolvedRepoDetails.DevcontainerConfig
imageName := devcontainerConfig.Image
if imageName == "" {
imageName = defaultBaseImage
}
// Pull the required image
if err := e.pullImage(ctx, imageName, dockerClient, gitspaceLogger); err != nil {
return err
}
portMappings := infrastructure.GitspacePortMappings
forwardPorts := ExtractForwardPorts(devcontainerConfig)
if len(forwardPorts) > 0 {
for _, port := range forwardPorts {
portMappings[port] = &types.PortMapping{
PublishedPort: port,
ForwardedPort: port,
}
}
gitspaceLogger.Info(fmt.Sprintf("Forwarding ports : %v", forwardPorts))
}
storage := infrastructure.Storage
environment := ExtractEnv(devcontainerConfig)
if len(environment) > 0 {
gitspaceLogger.Info(fmt.Sprintf("Setting Environment : %v", environment))
}
// Create the container
err := e.createContainer(
ctx,
dockerClient,
imageName,
containerName,
gitspaceLogger,
storage,
homeDir,
portMappings,
environment,
)
if err != nil {
return err
}
// Start the container
if err := e.manageContainer(ctx, ContainerActionStart, containerName, dockerClient, gitspaceLogger); err != nil {
return err
}
// Setup and run commands
exec := &devcontainer.Exec{
ContainerName: containerName,
DockerClient: dockerClient,
HomeDir: homeDir,
UserIdentifier: gitspaceConfig.GitspaceUser.Identifier,
AccessKey: *gitspaceConfig.GitspaceInstance.AccessKey,
AccessType: gitspaceConfig.GitspaceInstance.AccessType,
}
if err := e.setupGitspaceAndIDE(
ctx,
exec,
gitspaceLogger,
ideService,
gitspaceConfig,
resolvedRepoDetails,
defaultBaseImage,
); err != nil {
return err
}
return nil
}
// getContainerResponse retrieves container information and prepares the start response. // getContainerResponse retrieves container information and prepares the start response.
func (e *EmbeddedDockerOrchestrator) getContainerResponse( func GetContainerResponse(
ctx context.Context, ctx context.Context,
dockerClient *client.Client, dockerClient *client.Client,
containerName string, containerName string,
portMappings map[int]*types.PortMapping, portMappings map[int]*types.PortMapping,
codeRepoDir string, codeRepoDir string,
) (*StartResponse, error) { ) (*StartResponse, error) {
id, ports, err := e.getContainerInfo(ctx, containerName, dockerClient, portMappings) id, ports, err := GetContainerInfo(ctx, containerName, dockerClient, portMappings)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -156,6 +156,9 @@ func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
ideService); err != nil { ideService); err != nil {
return nil, err return nil, err
} }
case ContainerStatePaused, ContainerStateCreated, ContainerStateUnknown, ContainerStateDead:
// TODO handle the following states
return nil, fmt.Errorf("gitspace %s is in a unhandled state: %s", containerName, state)
default: default:
return nil, fmt.Errorf("gitspace %s is in a bad state: %s", containerName, state) return nil, fmt.Errorf("gitspace %s is in a bad state: %s", containerName, state)
@ -165,7 +168,7 @@ func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
codeRepoDir := filepath.Join(homeDir, resolvedRepoDetails.RepoName) codeRepoDir := filepath.Join(homeDir, resolvedRepoDetails.RepoName)
// Step 5: Retrieve container information and return response // Step 5: Retrieve container information and return response
return e.getContainerResponse(ctx, dockerClient, containerName, infra.GitspacePortMappings, codeRepoDir) return GetContainerResponse(ctx, dockerClient, containerName, infra.GitspacePortMappings, codeRepoDir)
} }
// startStoppedGitspace starts the Gitspace container if it was stopped. // startStoppedGitspace starts the Gitspace container if it was stopped.
@ -185,7 +188,7 @@ func (e *EmbeddedDockerOrchestrator) startStoppedGitspace(
} }
defer e.flushLogStream(logStreamInstance, gitspaceConfig.ID) defer e.flushLogStream(logStreamInstance, gitspaceConfig.ID)
startErr := e.manageContainer(ctx, ContainerActionStart, containerName, dockerClient, logStreamInstance) startErr := ManageContainer(ctx, ContainerActionStart, containerName, dockerClient, logStreamInstance)
if startErr != nil { if startErr != nil {
return startErr return startErr
} }
@ -260,7 +263,9 @@ func (e *EmbeddedDockerOrchestrator) StopGitspace(
if err := e.stopRunningGitspace(ctx, gitspaceConfig, containerName, dockerClient); err != nil { if err := e.stopRunningGitspace(ctx, gitspaceConfig, containerName, dockerClient); err != nil {
return err return err
} }
case ContainerStatePaused, ContainerStateCreated, ContainerStateUnknown, ContainerStateDead:
// TODO handle the following states
return fmt.Errorf("gitspace %s is in a unhandled state: %s", containerName, state)
default: default:
return fmt.Errorf("gitspace %s is in a bad state: %s", containerName, state) return fmt.Errorf("gitspace %s is in a bad state: %s", containerName, state)
} }
@ -284,7 +289,7 @@ func (e *EmbeddedDockerOrchestrator) stopRunningGitspace(
defer e.flushLogStream(logStreamInstance, gitspaceConfig.ID) defer e.flushLogStream(logStreamInstance, gitspaceConfig.ID)
// Step 5: Stop the container // Step 5: Stop the container
return e.manageContainer(ctx, ContainerActionStop, containerName, dockerClient, logStreamInstance) return ManageContainer(ctx, ContainerActionStop, containerName, dockerClient, logStreamInstance)
} }
// Status is NOOP for EmbeddedDockerOrchestrator as the docker host is verified by the infra provisioner. // Status is NOOP for EmbeddedDockerOrchestrator as the docker host is verified by the infra provisioner.
@ -331,7 +336,7 @@ func (e *EmbeddedDockerOrchestrator) StopAndRemoveGitspace(
// Step 5: Stop the container if it's not already stopped // Step 5: Stop the container if it's not already stopped
if state != ContainerStateStopped { if state != ContainerStateStopped {
logger.Debug().Msg("stopping gitspace") logger.Debug().Msg("stopping gitspace")
if err := e.manageContainer( if err := ManageContainer(
ctx, ContainerActionStop, containerName, dockerClient, logStreamInstance); err != nil { ctx, ContainerActionStop, containerName, dockerClient, logStreamInstance); err != nil {
return fmt.Errorf("failed to stop gitspace %s: %w", containerName, err) return fmt.Errorf("failed to stop gitspace %s: %w", containerName, err)
} }
@ -340,7 +345,7 @@ func (e *EmbeddedDockerOrchestrator) StopAndRemoveGitspace(
// Step 6: Remove the container // Step 6: Remove the container
logger.Debug().Msg("removing gitspace") logger.Debug().Msg("removing gitspace")
if err := e.manageContainer( if err := ManageContainer(
ctx, ContainerActionRemove, containerName, dockerClient, logStreamInstance); err != nil { ctx, ContainerActionRemove, containerName, dockerClient, logStreamInstance); err != nil {
return fmt.Errorf("failed to remove gitspace %s: %w", containerName, err) return fmt.Errorf("failed to remove gitspace %s: %w", containerName, err)
} }
@ -364,6 +369,92 @@ func (e *EmbeddedDockerOrchestrator) getAccessKey(gitspaceConfig types.GitspaceC
return "", fmt.Errorf("no access key is configured: %s", gitspaceConfig.Identifier) return "", fmt.Errorf("no access key is configured: %s", gitspaceConfig.Identifier)
} }
func (e *EmbeddedDockerOrchestrator) runGitspaceSetupSteps(
ctx context.Context,
gitspaceConfig types.GitspaceConfig,
dockerClient *client.Client,
ideService ide.IDE,
infrastructure types.Infrastructure,
resolvedRepoDetails scm.ResolvedDetails,
defaultBaseImage string,
gitspaceLogger orchestratorTypes.GitspaceLogger,
) error {
homeDir := GetUserHomeDir(gitspaceConfig.GitspaceUser.Identifier)
containerName := GetGitspaceContainerName(gitspaceConfig)
devcontainerConfig := resolvedRepoDetails.DevcontainerConfig
imageName := devcontainerConfig.Image
if imageName == "" {
imageName = defaultBaseImage
}
// Pull the required image
if err := PullImage(ctx, imageName, dockerClient, gitspaceLogger); err != nil {
return err
}
portMappings := infrastructure.GitspacePortMappings
forwardPorts := ExtractForwardPorts(devcontainerConfig)
if len(forwardPorts) > 0 {
for _, port := range forwardPorts {
portMappings[port] = &types.PortMapping{
PublishedPort: port,
ForwardedPort: port,
}
}
gitspaceLogger.Info(fmt.Sprintf("Forwarding ports : %v", forwardPorts))
}
storage := infrastructure.Storage
environment := ExtractEnv(devcontainerConfig)
if len(environment) > 0 {
gitspaceLogger.Info(fmt.Sprintf("Setting Environment : %v", environment))
}
// Create the container
err := CreateContainer(
ctx,
dockerClient,
imageName,
containerName,
gitspaceLogger,
storage,
homeDir,
portMappings,
environment,
)
if err != nil {
return err
}
// Start the container
if err := ManageContainer(ctx, ContainerActionStart, containerName, dockerClient, gitspaceLogger); err != nil {
return err
}
// Setup and run commands
exec := &devcontainer.Exec{
ContainerName: containerName,
DockerClient: dockerClient,
HomeDir: homeDir,
UserIdentifier: gitspaceConfig.GitspaceUser.Identifier,
AccessKey: *gitspaceConfig.GitspaceInstance.AccessKey,
AccessType: gitspaceConfig.GitspaceInstance.AccessType,
}
if err := e.setupGitspaceAndIDE(
ctx,
exec,
gitspaceLogger,
ideService,
gitspaceConfig,
resolvedRepoDetails,
defaultBaseImage,
); err != nil {
return err
}
return nil
}
// getDockerClient creates and returns a new Docker client using the factory. // getDockerClient creates and returns a new Docker client using the factory.
func (e *EmbeddedDockerOrchestrator) getDockerClient( func (e *EmbeddedDockerOrchestrator) getDockerClient(
ctx context.Context, ctx context.Context,
@ -390,7 +481,7 @@ func (e *EmbeddedDockerOrchestrator) checkContainerState(
containerName string, containerName string,
) (State, error) { ) (State, error) {
log.Debug().Msg("checking current state of gitspace") log.Debug().Msg("checking current state of gitspace")
state, err := e.containerState(ctx, containerName, dockerClient) state, err := FetchContainerState(ctx, containerName, dockerClient)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -33,7 +33,11 @@ type State string
const ( const (
ContainerStateRunning = State("running") ContainerStateRunning = State("running")
ContainerStateRemoved = State("removed") ContainerStateRemoved = State("removed")
ContainerStateDead = State("dead")
ContainerStateStopped = State("exited") ContainerStateStopped = State("exited")
ContainerStatePaused = State("paused")
ContainerStateUnknown = State("unknown")
ContainerStateCreated = State("created")
) )
type Action string type Action string