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"
"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"
"github.com/harness/gitness/app/gitspace/scm"
"github.com/harness/gitness/types"
"github.com/docker/docker/api/types/container"
@ -42,6 +39,14 @@ const (
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.
func logStreamWrapError(gitspaceLogger orchestratorTypes.GitspaceLogger, msg string, err error) error {
gitspaceLogger.Error(msg, err)
@ -49,7 +54,7 @@ func logStreamWrapError(gitspaceLogger orchestratorTypes.GitspaceLogger, msg str
}
// Generalized Docker Container Management.
func (e *EmbeddedDockerOrchestrator) manageContainer(
func ManageContainer(
ctx context.Context,
action Action,
containerName string,
@ -83,7 +88,7 @@ func (e *EmbeddedDockerOrchestrator) manageContainer(
return nil
}
func (e *EmbeddedDockerOrchestrator) containerState(
func FetchContainerState(
ctx context.Context,
containerName string,
dockerClient *client.Client,
@ -99,12 +104,22 @@ func (e *EmbeddedDockerOrchestrator) containerState(
if len(containers) == 0 {
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.
func (e *EmbeddedDockerOrchestrator) createContainer(
func CreateContainer(
ctx context.Context,
dockerClient *client.Client,
imageName string,
@ -174,7 +189,7 @@ func prepareHostConfig(volumeName, homeDir string, portBindings nat.PortMap) *co
return hostConfig
}
func (e *EmbeddedDockerOrchestrator) getContainerInfo(
func GetContainerInfo(
ctx context.Context,
containerName string,
dockerClient *client.Client,
@ -201,7 +216,7 @@ func (e *EmbeddedDockerOrchestrator) getContainerInfo(
return inspectResp.ID, usedPorts, nil
}
func (e *EmbeddedDockerOrchestrator) pullImage(
func PullImage(
ctx context.Context,
imageName string,
dockerClient *client.Client,
@ -230,101 +245,15 @@ func (e *EmbeddedDockerOrchestrator) pullImage(
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.
func (e *EmbeddedDockerOrchestrator) getContainerResponse(
func GetContainerResponse(
ctx context.Context,
dockerClient *client.Client,
containerName string,
portMappings map[int]*types.PortMapping,
codeRepoDir string,
) (*StartResponse, error) {
id, ports, err := e.getContainerInfo(ctx, containerName, dockerClient, portMappings)
id, ports, err := GetContainerInfo(ctx, containerName, dockerClient, portMappings)
if err != nil {
return nil, err
}

View File

@ -156,6 +156,9 @@ func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
ideService); err != nil {
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:
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)
// 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.
@ -185,7 +188,7 @@ func (e *EmbeddedDockerOrchestrator) startStoppedGitspace(
}
defer e.flushLogStream(logStreamInstance, gitspaceConfig.ID)
startErr := e.manageContainer(ctx, ContainerActionStart, containerName, dockerClient, logStreamInstance)
startErr := ManageContainer(ctx, ContainerActionStart, containerName, dockerClient, logStreamInstance)
if startErr != nil {
return startErr
}
@ -260,7 +263,9 @@ func (e *EmbeddedDockerOrchestrator) StopGitspace(
if err := e.stopRunningGitspace(ctx, gitspaceConfig, containerName, dockerClient); err != nil {
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:
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)
// 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.
@ -331,7 +336,7 @@ func (e *EmbeddedDockerOrchestrator) StopAndRemoveGitspace(
// Step 5: Stop the container if it's not already stopped
if state != ContainerStateStopped {
logger.Debug().Msg("stopping gitspace")
if err := e.manageContainer(
if err := ManageContainer(
ctx, ContainerActionStop, containerName, dockerClient, logStreamInstance); err != nil {
return fmt.Errorf("failed to stop gitspace %s: %w", containerName, err)
}
@ -340,7 +345,7 @@ func (e *EmbeddedDockerOrchestrator) StopAndRemoveGitspace(
// Step 6: Remove the container
logger.Debug().Msg("removing gitspace")
if err := e.manageContainer(
if err := ManageContainer(
ctx, ContainerActionRemove, containerName, dockerClient, logStreamInstance); err != nil {
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)
}
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.
func (e *EmbeddedDockerOrchestrator) getDockerClient(
ctx context.Context,
@ -390,7 +481,7 @@ func (e *EmbeddedDockerOrchestrator) checkContainerState(
containerName string,
) (State, error) {
log.Debug().Msg("checking current state of gitspace")
state, err := e.containerState(ctx, containerName, dockerClient)
state, err := FetchContainerState(ctx, containerName, dockerClient)
if err != nil {
return "", err
}

View File

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