feat: [CDE-163]: Moving dir creation and deletion to infra provider. Refactoring gitspace env variables. Renaming unprovisioning to deprovisioning. (#2231)

* feat: [CDE-163]: Moving dir creation and deletion to infra provider. Refactoring gitspace env variables. Renaming unprovisioning to deprovisioning.
unified-ui
Dhruv Dhruv 2024-07-16 07:59:02 +00:00 committed by Harness
parent 1711ee098a
commit 6e516183fd
14 changed files with 150 additions and 101 deletions

View File

@ -82,7 +82,7 @@ ENV GITNESS_DATABASE_DATASOURCE /data/database.sqlite
ENV GITNESS_METRIC_ENABLED=true
ENV GITNESS_METRIC_ENDPOINT=https://stats.drone.ci/api/v1/gitness
ENV GITNESS_TOKEN_COOKIE_NAME=token
ENV GITNESS_GITSPACE_DEFAULT_BIND_MOUNT_SOURCE_BASE_PATH /data
ENV GITNESS_GITSPACE_ROOT /data
ENV GITNESS_DOCKER_HOST unix:///var/run/docker.sock
COPY --from=builder /app/gitness /app/gitness

View File

@ -50,6 +50,7 @@ func (c *Controller) Delete(
}
instance, _ := c.gitspaceInstanceStore.FindLatestByGitspaceConfigID(ctx, gitspaceConfig.ID, gitspaceConfig.SpaceID)
gitspaceConfig.GitspaceInstance = instance
gitspaceConfig.SpacePath = space.Path
if instance != nil {
if stopErr := c.stopRunningGitspace(ctx, gitspaceConfig); stopErr != nil {
return stopErr
@ -64,7 +65,8 @@ func (c *Controller) Delete(
func (c *Controller) stopRunningGitspace(
ctx context.Context,
config *types.GitspaceConfig) error {
config *types.GitspaceConfig,
) error {
if instanceUpdated, err := c.orchestrator.DeleteGitspace(ctx, config); err != nil {
return err
} else if err = c.gitspaceInstanceStore.Update(ctx, instanceUpdated); err != nil {

View File

@ -39,8 +39,8 @@ type InfraProvisioner interface {
gitspaceConfig *types.GitspaceConfig,
) (*infraprovider.Infrastructure, error)
// Unprovision unprovisions all the resources created for the gitspace.
Unprovision(
// Deprovision removes all the resources created for the gitspace.
Deprovision(
ctx context.Context,
infraProviderResource *types.InfraProviderResource,
gitspaceConfig *types.GitspaceConfig,

View File

@ -80,7 +80,7 @@ func (i infraProvisioner) Provision(
return nil, fmt.Errorf("invalid provisioning params %v: %w", infraProviderResource.Metadata, err)
}
provisionedInfra, err := infraProvider.Provision(ctx, gitspaceConfig.Identifier, allParams)
provisionedInfra, err := infraProvider.Provision(ctx, gitspaceConfig.SpacePath, gitspaceConfig.Identifier, allParams)
if err != nil {
return nil, fmt.Errorf(
"unable to provision infrastructure for gitspaceConfigIdentifier %v: %w",
@ -156,7 +156,7 @@ func (i infraProvisioner) Stop(
return &stoppedInfra, err
}
func (i infraProvisioner) Unprovision(
func (i infraProvisioner) Deprovision(
ctx context.Context,
infraProviderResource *types.InfraProviderResource,
gitspaceConfig *types.GitspaceConfig,
@ -199,12 +199,13 @@ func (i infraProvisioner) Unprovision(
} else {
provisionedInfra = infraprovider.Infrastructure{
ResourceKey: gitspaceConfig.Identifier,
SpacePath: gitspaceConfig.SpacePath,
ProviderType: infraProviderEntity.Type,
Parameters: allParams,
}
}
destroyedInfra, err := infraProvider.Destroy(ctx, provisionedInfra)
destroyedInfra, err := infraProvider.Deprovision(ctx, provisionedInfra)
if err != nil {
return nil, fmt.Errorf("unable to stop provisioned infra %+v: %w", provisionedInfra, err)
}

View File

@ -18,7 +18,6 @@ import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
@ -51,10 +50,9 @@ const (
)
type Config struct {
DefaultBaseImage string
DefaultBindMountTargetPath string
DefaultBindMountSourceBasePath string
DefaultBindMountSourceBasePathAbsolute string
DefaultBaseImage string
WorkingDirectory string
RootSource string
}
type EmbeddedDockerOrchestrator struct {
@ -174,7 +172,7 @@ func (e *EmbeddedDockerOrchestrator) StartGitspace(
usedPorts = ports
// TODO: Add gitspace status reporting.
log.Debug().Msgf("started gitspace: %s", gitspaceConfig.Identifier)
log.Debug().Msg("started gitspace")
default:
return nil, fmt.Errorf("gitspace %s is in a bad state: %s", containerName, state)
@ -183,7 +181,7 @@ func (e *EmbeddedDockerOrchestrator) StartGitspace(
return &StartResponse{
ContainerID: containerID,
ContainerName: containerName,
WorkingDirectory: strings.TrimPrefix(e.config.DefaultBindMountTargetPath, "/"),
WorkingDirectory: strings.TrimPrefix(e.config.WorkingDirectory, "/"),
PortsUsed: usedPorts,
}, nil
}
@ -215,7 +213,7 @@ func (e *EmbeddedDockerOrchestrator) startGitspace(
var devcontainer = &Devcontainer{
ContainerName: containerName,
DockerClient: dockerClient,
WorkingDir: e.config.DefaultBindMountTargetPath,
WorkingDir: e.config.WorkingDirectory,
}
err = e.executePostCreateCommand(ctx, devcontainerConfig, devcontainer, logStreamInstance)
@ -444,48 +442,15 @@ func (e *EmbeddedDockerOrchestrator) createContainer(
commands := make(strslice.StrSlice, 0)
commands = append(commands, "infinity")
bindMountSourcePath :=
mountSource :=
filepath.Join(
e.config.DefaultBindMountSourceBasePath,
e.config.RootSource,
gitspacesDir,
gitspaceConfig.SpacePath,
gitspaceConfig.Identifier,
)
absoluteBindMountSourcePath :=
filepath.Join(
e.config.DefaultBindMountSourceBasePathAbsolute,
gitspacesDir,
gitspaceConfig.SpacePath,
gitspaceConfig.Identifier,
)
loggingErr := logStreamInstance.Write(
"Creating bind mount source directory: " + bindMountSourcePath + " (" + absoluteBindMountSourcePath + ")")
if loggingErr != nil {
return fmt.Errorf("logging error: %w", loggingErr)
}
err := os.MkdirAll(bindMountSourcePath, os.ModePerm)
if err != nil {
loggingErr = logStreamInstance.Write("Error while creating bind mount source directory: " + err.Error())
err = fmt.Errorf(
"could not create bind mount source path %s: %w", bindMountSourcePath, err)
if loggingErr != nil {
err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr)
}
return err
}
loggingErr = logStreamInstance.Write("Successfully created bind mount source directory")
if loggingErr != nil {
return fmt.Errorf("logging error: %w", loggingErr)
}
loggingErr = logStreamInstance.Write("Creating container: " + containerName)
loggingErr := logStreamInstance.Write("Creating container: " + containerName)
if loggingErr != nil {
return fmt.Errorf("logging error: %w", loggingErr)
}
@ -500,8 +465,8 @@ func (e *EmbeddedDockerOrchestrator) createContainer(
Mounts: []mount.Mount{
{
Type: mount.TypeBind,
Source: absoluteBindMountSourcePath,
Target: e.config.DefaultBindMountTargetPath,
Source: mountSource,
Target: e.config.WorkingDirectory,
},
},
}, nil, containerName)

View File

@ -262,7 +262,7 @@ func (o orchestrator) DeleteGitspace(
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraUnprovisioningStart)
_, err = o.infraProvisioner.Unprovision(ctx, infraProviderResource, gitspaceConfig)
_, err = o.infraProvisioner.Deprovision(ctx, infraProviderResource, gitspaceConfig)
if err != nil {
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraUnprovisioningFailed)

View File

@ -405,28 +405,19 @@ func ProvideIDEVSCodeWebConfig(config *types.Config) *container.VSCodeWebConfig
}
// ProvideGitspaceContainerOrchestratorConfig loads the Gitspace container orchestrator config from the main config.
func ProvideGitspaceContainerOrchestratorConfig(config *types.Config) (*container.Config, error) {
if config.Gitspace.DefaultBindMountSourceBasePath == "" {
var homedir string
homedir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("unable to determine home directory: %w", err)
}
config.Gitspace.DefaultBindMountSourceBasePath = filepath.Join(homedir, gitnessHomeDir)
}
if config.Gitspace.DefaultBindMountSourceBasePathAbsolute == "" {
config.Gitspace.DefaultBindMountSourceBasePathAbsolute = config.Gitspace.DefaultBindMountSourceBasePath
func ProvideGitspaceContainerOrchestratorConfig(
config *types.Config,
dockerProviderConfig *infraprovider.DockerProviderConfig,
) *container.Config {
if config.Gitspace.RootSource == "" {
config.Gitspace.RootSource = dockerProviderConfig.MountSourceBasePath
}
return &container.Config{
DefaultBaseImage: config.Gitspace.DefaultBaseImage,
DefaultBindMountTargetPath: config.Gitspace.DefaultBindMountTargetPath,
DefaultBindMountSourceBasePath: config.Gitspace.DefaultBindMountSourceBasePath,
DefaultBindMountSourceBasePathAbsolute: config.Gitspace.DefaultBindMountSourceBasePathAbsolute,
}, nil
DefaultBaseImage: config.Gitspace.DefaultBaseImage,
WorkingDirectory: config.Gitspace.WorkingDirectory,
RootSource: config.Gitspace.RootSource,
}
}
// ProvideGitspaceEventConfig loads the gitspace event service config from the main config.
@ -437,3 +428,21 @@ func ProvideGitspaceEventConfig(config *types.Config) gitspaceevent.Config {
MaxRetries: config.Gitspace.Events.MaxRetries,
}
}
// ProvideDockerProviderConfig loads the Docker provider config from the main config.
func ProvideDockerProviderConfig(config *types.Config) (*infraprovider.DockerProviderConfig, error) {
if config.Gitspace.Root == "" {
var homedir string
homedir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("unable to determine home directory: %w", err)
}
config.Gitspace.Root = filepath.Join(homedir, gitnessHomeDir)
}
return &infraprovider.DockerProviderConfig{
MountSourceBasePath: config.Gitspace.Root,
}, nil
}

View File

@ -225,6 +225,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
cliserver.ProvideGitspaceContainerOrchestratorConfig,
cliserver.ProvideGitspaceEventConfig,
logutil.WireSet,
cliserver.ProvideDockerProviderConfig,
)
return &cliserver.System{}, nil
}

View File

@ -327,7 +327,11 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
return nil, err
}
dockerClientFactory := infraprovider.ProvideDockerClientFactory(dockerConfig)
dockerProvider := infraprovider.ProvideDockerProvider(dockerConfig, dockerClientFactory)
dockerProviderConfig, err := server.ProvideDockerProviderConfig(config)
if err != nil {
return nil, err
}
dockerProvider := infraprovider.ProvideDockerProvider(dockerConfig, dockerClientFactory, dockerProviderConfig)
factory := infraprovider.ProvideFactory(dockerProvider)
providerService := infraprovider2.ProvideInfraProvider(infraProviderResourceStore, infraProviderConfigStore, factory, spaceStore, transactor)
infraproviderController := infraprovider3.ProvideController(authorizer, spaceStore, providerService)
@ -340,10 +344,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
vsCode := container.ProvideVSCodeService()
vsCodeWebConfig := server.ProvideIDEVSCodeWebConfig(config)
vsCodeWeb := container.ProvideVSCodeWebService(vsCodeWebConfig)
containerConfig, err := server.ProvideGitspaceContainerOrchestratorConfig(config)
if err != nil {
return nil, err
}
containerConfig := server.ProvideGitspaceContainerOrchestratorConfig(config, dockerProviderConfig)
statefulLogger := logutil.ProvideStatefulLogger(logStream)
containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, vsCode, vsCodeWeb, containerConfig, statefulLogger)
orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter3)

View File

@ -18,6 +18,8 @@ import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"github.com/harness/gitness/infraprovider/enum"
@ -26,21 +28,38 @@ import (
var _ InfraProvider = (*DockerProvider)(nil)
type DockerProvider struct {
config *DockerConfig
dockerClientFactory *DockerClientFactory
const gitspacesDir = "gitspaces"
type DockerProviderConfig struct {
MountSourceBasePath string
}
func NewDockerProvider(config *DockerConfig, dockerClientFactory *DockerClientFactory) *DockerProvider {
type DockerProvider struct {
config *DockerConfig
dockerClientFactory *DockerClientFactory
dockerProviderConfig *DockerProviderConfig
}
func NewDockerProvider(
config *DockerConfig,
dockerClientFactory *DockerClientFactory,
dockerProviderConfig *DockerProviderConfig,
) *DockerProvider {
return &DockerProvider{
config: config,
dockerClientFactory: dockerClientFactory,
config: config,
dockerClientFactory: dockerClientFactory,
dockerProviderConfig: dockerProviderConfig,
}
}
// Provision assumes a docker engine is already running on the Gitness host machine and re-uses that as infra.
// It does not start docker engine.
func (d DockerProvider) Provision(ctx context.Context, _ string, params []Parameter) (Infrastructure, error) {
// It does not start docker engine. It creates a directory in the host machine using the given resource key.
func (d DockerProvider) Provision(
ctx context.Context,
spacePath string,
resourceKey string,
params []Parameter,
) (Infrastructure, error) {
dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &Infrastructure{
ProviderType: enum.InfraProviderTypeDocker,
Parameters: params,
@ -60,6 +79,12 @@ func (d DockerProvider) Provision(ctx context.Context, _ string, params []Parame
if err != nil {
return Infrastructure{}, fmt.Errorf("unable to connect to docker engine: %w", err)
}
err = d.createMountSourceDirectory(spacePath, resourceKey)
if err != nil {
return Infrastructure{}, err
}
return Infrastructure{
Identifier: info.ID,
ProviderType: enum.InfraProviderTypeDocker,
@ -68,6 +93,39 @@ func (d DockerProvider) Provision(ctx context.Context, _ string, params []Parame
}, nil
}
func (d DockerProvider) createMountSourceDirectory(spacePath string, resourceKey string) error {
mountSourcePath := d.getMountSourceDirectoryPath(spacePath, resourceKey)
err := os.MkdirAll(mountSourcePath, os.ModePerm)
if err != nil {
return fmt.Errorf(
"could not create bind mount source path %s: %w", mountSourcePath, err)
}
return nil
}
func (d DockerProvider) deleteMountSourceDirectory(spacePath string, resourceKey string) error {
mountSourcePath := d.getMountSourceDirectoryPath(spacePath, resourceKey)
err := os.RemoveAll(mountSourcePath)
if err != nil {
return fmt.Errorf(
"could not delete bind mount source path %s: %w", mountSourcePath, err)
}
return nil
}
func (d DockerProvider) getMountSourceDirectoryPath(spacePath string, resourceKey string) string {
return filepath.Join(
d.dockerProviderConfig.MountSourceBasePath,
gitspacesDir,
spacePath,
resourceKey,
)
}
func (d DockerProvider) Find(_ context.Context, _ string, _ []Parameter) (Infrastructure, error) {
// TODO implement me
panic("implement me")
@ -78,8 +136,13 @@ func (d DockerProvider) Stop(_ context.Context, infra Infrastructure) (Infrastru
return infra, nil
}
// Destroy is NOOP as this provider uses already running docker engine. It does not stop the docker engine.
func (d DockerProvider) Destroy(_ context.Context, infra Infrastructure) (Infrastructure, error) {
// Deprovision deletes the host machine directory created by Provision. It does not stop the docker engine.
func (d DockerProvider) Deprovision(_ context.Context, infra Infrastructure) (Infrastructure, error) {
err := d.deleteMountSourceDirectory(infra.SpacePath, infra.ResourceKey)
if err != nil {
return Infrastructure{}, err
}
return infra, nil
}

View File

@ -23,13 +23,13 @@ import (
type InfraProvider interface {
// Provision provisions infrastructure against a resourceKey with the provided parameters.
Provision(ctx context.Context, resourceKey string, parameters []Parameter) (Infrastructure, error)
Provision(ctx context.Context, spacePath string, resourceKey string, parameters []Parameter) (Infrastructure, error)
// Find finds infrastructure provisioned against a resourceKey.
Find(ctx context.Context, resourceKey string, parameters []Parameter) (Infrastructure, error)
// Stop frees up the resources allocated against a resourceKey, which can be freed.
Stop(ctx context.Context, infra Infrastructure) (Infrastructure, error)
// Destroy unprovisions all infrastructure provisioned againest the resourceKey.
Destroy(ctx context.Context, infra Infrastructure) (Infrastructure, error)
// Deprovision removes all infrastructure provisioned againest the resourceKey.
Deprovision(ctx context.Context, infra Infrastructure) (Infrastructure, error)
// Status checks the infrastructure status provisioned againest the resourceKey.
Status(ctx context.Context, infra Infrastructure) (enum.InfraStatus, error)
// AvailableParams provides a schema to define the infrastructure.

View File

@ -33,6 +33,7 @@ type Parameter struct {
type Infrastructure struct {
Identifier string
ResourceKey string
SpacePath string
ProviderType enum.InfraProviderType
Parameters []Parameter
Status enum.InfraStatus

View File

@ -25,8 +25,12 @@ var WireSet = wire.NewSet(
ProvideDockerClientFactory,
)
func ProvideDockerProvider(config *DockerConfig, dockerClientFactory *DockerClientFactory) *DockerProvider {
return NewDockerProvider(config, dockerClientFactory)
func ProvideDockerProvider(
config *DockerConfig,
dockerClientFactory *DockerClientFactory,
dockerProviderConfig *DockerProviderConfig,
) *DockerProvider {
return NewDockerProvider(config, dockerClientFactory, dockerProviderConfig)
}
func ProvideFactory(dockerProvider *DockerProvider) Factory {

View File

@ -400,18 +400,20 @@ type Config struct {
Gitspace struct {
// DefaultBaseImage is used to create the Gitspace when no devcontainer.json is absent or doesn't have image.
DefaultBaseImage string `envconfig:"GITNESS_GITSPACE_DEFAULT_BASE_IMAGE" default:"mcr.microsoft.com/devcontainers/base:dev-ubuntu-24.04"` //nolint:lll
// DefaultBindMountTargetPath is the target for bind mount in the Gitspace container.
DefaultBindMountTargetPath string `envconfig:"GITNESS_GITSPACE_DEFAULT_BIND_MOUNT_TARGET_PATH" default:"/gitspace"` //nolint:lll
// WorkingDirectory is the default working directory in the Gitspace container.
WorkingDirectory string `envconfig:"GITNESS_GITSPACE_WORKING_DIR" default:"/gitspace"`
Enable bool `envconfig:"GITNESS_GITSPACE_ENABLE" default:"false"`
// DefaultBindMountTargetPath is the source for bind mount in the Gitspace container.
// Sub-directories will be created from this eg <DefaultBindMountSourceBasePath>/gitspace/space1/space2/config1
// Root is the source for bind mount in the Gitspace container.
// Sub-directories will be created from this eg <Root>/gitspace/space1/space2/config1
// If left blank, it will be set to $HOME/.gitness
DefaultBindMountSourceBasePath string `envconfig:"GITNESS_GITSPACE_DEFAULT_BIND_MOUNT_SOURCE_BASE_PATH"`
// DefaultBindMountSourceBasePathAbsolute is the source path on which the DefaultBindMountSourceBasePath
// is mounted in Gitness container. If left blank, it will be equal to DefaultBindMountSourceBasePath.
DefaultBindMountSourceBasePathAbsolute string `envconfig:"GITNESS_GITSPACE_DEFAULT_BIND_MOUNT_SOURCE_BASE_PATH_ABSOLUTE"` //nolint:lll
Root string `envconfig:"GITNESS_GITSPACE_ROOT"`
// RootSource is the source path on which the Root is mounted in Gitness container.
// If left blank, it will be equal to Root.
RootSource string `envconfig:"GITNESS_GITSPACE_ROOT_SOURCE"`
Events struct {
Concurrency int `envconfig:"GITNESS_GITSPACE_EVENTS_CONCURRENCY" default:"4"`