diff --git a/Dockerfile b/Dockerfile index 381d73749..7b84ec057 100644 --- a/Dockerfile +++ b/Dockerfile @@ -82,7 +82,6 @@ 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_ROOT /data ENV GITNESS_DOCKER_HOST unix:///var/run/docker.sock COPY --from=builder /app/gitness /app/gitness diff --git a/app/gitspace/infrastructure/provisioner_impl.go b/app/gitspace/infrastructure/provisioner_impl.go index 71b31f2e7..77f6cb143 100644 --- a/app/gitspace/infrastructure/provisioner_impl.go +++ b/app/gitspace/infrastructure/provisioner_impl.go @@ -93,7 +93,7 @@ func (i infraProvisioner) Provision( // TODO: Update the infraProvisioned record } - return &provisionedInfra, nil + return provisionedInfra, nil } func (i infraProvisioner) Stop( @@ -133,11 +133,11 @@ func (i infraProvisioner) Stop( return nil, fmt.Errorf("invalid provisioning params %+v: %w", infraProviderResource.Metadata, err) } - var provisionedInfra infraprovider.Infrastructure + var provisionedInfra *infraprovider.Infrastructure if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive // TODO: Fetch and check existing infraProvisioned record } else { - provisionedInfra = infraprovider.Infrastructure{ + provisionedInfra = &infraprovider.Infrastructure{ ResourceKey: gitspaceConfig.Identifier, ProviderType: infraProviderEntity.Type, Parameters: allParams, @@ -153,7 +153,7 @@ func (i infraProvisioner) Stop( // TODO: Update existing infraProvisioned record } - return &stoppedInfra, err + return stoppedInfra, err } func (i infraProvisioner) Deprovision( @@ -193,18 +193,16 @@ func (i infraProvisioner) Deprovision( return nil, fmt.Errorf("invalid provisioning params %+v: %w", infraProviderResource.Metadata, err) } - var provisionedInfra infraprovider.Infrastructure + var provisionedInfra *infraprovider.Infrastructure if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive // TODO: Fetch and check existing infraProvisioned record } else { - provisionedInfra = infraprovider.Infrastructure{ - ResourceKey: gitspaceConfig.Identifier, - SpacePath: gitspaceConfig.SpacePath, - ProviderType: infraProviderEntity.Type, - Parameters: allParams, + provisionedInfra, err = infraProvider.Find(ctx, gitspaceConfig.SpacePath, gitspaceConfig.Identifier, allParams) + if err != nil { + return nil, fmt.Errorf("unable to find provisioned infra for gitspace %s: %w", + gitspaceConfig.Identifier, err) } } - destroyedInfra, err := infraProvider.Deprovision(ctx, provisionedInfra) if err != nil { return nil, fmt.Errorf("unable to stop provisioned infra %+v: %w", provisionedInfra, err) @@ -214,7 +212,7 @@ func (i infraProvisioner) Deprovision( // TODO: Update existing infraProvisioned record } - return &destroyedInfra, err + return destroyedInfra, err } func (i infraProvisioner) Find( diff --git a/app/gitspace/orchestrator/container/embedded_docker.go b/app/gitspace/orchestrator/container/embedded_docker.go index 148506a5a..8bf5e967f 100644 --- a/app/gitspace/orchestrator/container/embedded_docker.go +++ b/app/gitspace/orchestrator/container/embedded_docker.go @@ -18,7 +18,6 @@ import ( "context" "fmt" "io" - "path/filepath" "strings" "github.com/harness/gitness/app/gitspace/logutil" @@ -46,7 +45,6 @@ const ( containerStateRemoved = "removed" templateCloneGit = "clone_git.sh" templateSetupSSHServer = "setup_ssh_server.sh" - gitspacesDir = "gitspaces" ) type Config struct { @@ -159,6 +157,7 @@ func (e *EmbeddedDockerOrchestrator) StartGitspace( dockerClient, ideService, logStreamInstance, + infra.Storage, ) if startErr != nil { return nil, fmt.Errorf("failed to start gitspace %s: %w", containerName, startErr) @@ -194,6 +193,7 @@ func (e *EmbeddedDockerOrchestrator) startGitspace( dockerClient *client.Client, ideService IDE, logStreamInstance *logutil.LogStreamInstance, + volumeName string, ) error { var imageName = devcontainerConfig.Image if imageName == "" { @@ -205,7 +205,15 @@ func (e *EmbeddedDockerOrchestrator) startGitspace( return err } - err = e.createContainer(ctx, gitspaceConfig, dockerClient, imageName, containerName, ideService, logStreamInstance) + err = e.createContainer( + ctx, + dockerClient, + imageName, + containerName, + ideService, + logStreamInstance, + volumeName, + ) if err != nil { return err } @@ -411,12 +419,12 @@ func (e *EmbeddedDockerOrchestrator) executePostCreateCommand( func (e *EmbeddedDockerOrchestrator) createContainer( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, dockerClient *client.Client, imageName string, containerName string, ideService IDE, logStreamInstance *logutil.LogStreamInstance, + volumeName string, ) error { portUsedByIDE := ideService.PortAndProtocol() @@ -442,14 +450,6 @@ func (e *EmbeddedDockerOrchestrator) createContainer( commands := make(strslice.StrSlice, 0) commands = append(commands, "infinity") - mountSource := - filepath.Join( - e.config.RootSource, - gitspacesDir, - gitspaceConfig.SpacePath, - gitspaceConfig.Identifier, - ) - loggingErr := logStreamInstance.Write("Creating container: " + containerName) if loggingErr != nil { return fmt.Errorf("logging error: %w", loggingErr) @@ -464,8 +464,8 @@ func (e *EmbeddedDockerOrchestrator) createContainer( PortBindings: portBindings, Mounts: []mount.Mount{ { - Type: mount.TypeBind, - Source: mountSource, + Type: mount.TypeVolume, + Source: volumeName, Target: e.config.WorkingDirectory, }, }, diff --git a/cli/operations/server/config.go b/cli/operations/server/config.go index 3827dad98..efcf958f6 100644 --- a/cli/operations/server/config.go +++ b/cli/operations/server/config.go @@ -405,18 +405,10 @@ func ProvideIDEVSCodeWebConfig(config *types.Config) *container.VSCodeWebConfig } // ProvideGitspaceContainerOrchestratorConfig loads the Gitspace container orchestrator config from the main config. -func ProvideGitspaceContainerOrchestratorConfig( - config *types.Config, - dockerProviderConfig *infraprovider.DockerProviderConfig, -) *container.Config { - if config.Gitspace.RootSource == "" { - config.Gitspace.RootSource = dockerProviderConfig.MountSourceBasePath - } - +func ProvideGitspaceContainerOrchestratorConfig(config *types.Config) *container.Config { return &container.Config{ DefaultBaseImage: config.Gitspace.DefaultBaseImage, WorkingDirectory: config.Gitspace.WorkingDirectory, - RootSource: config.Gitspace.RootSource, } } @@ -428,21 +420,3 @@ 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 -} diff --git a/cmd/gitness/wire.go b/cmd/gitness/wire.go index 8999c66dc..ae575b32c 100644 --- a/cmd/gitness/wire.go +++ b/cmd/gitness/wire.go @@ -225,7 +225,6 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e cliserver.ProvideGitspaceContainerOrchestratorConfig, cliserver.ProvideGitspaceEventConfig, logutil.WireSet, - cliserver.ProvideDockerProviderConfig, ) return &cliserver.System{}, nil } diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index 43939a77b..83b71f531 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -327,11 +327,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro return nil, err } dockerClientFactory := infraprovider.ProvideDockerClientFactory(dockerConfig) - dockerProviderConfig, err := server.ProvideDockerProviderConfig(config) - if err != nil { - return nil, err - } - dockerProvider := infraprovider.ProvideDockerProvider(dockerConfig, dockerClientFactory, dockerProviderConfig) + dockerProvider := infraprovider.ProvideDockerProvider(dockerConfig, dockerClientFactory) factory := infraprovider.ProvideFactory(dockerProvider) providerService := infraprovider2.ProvideInfraProvider(infraProviderResourceStore, infraProviderConfigStore, factory, spaceStore, transactor) infraproviderController := infraprovider3.ProvideController(authorizer, spaceStore, providerService) @@ -344,7 +340,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro vsCode := container.ProvideVSCodeService() vsCodeWebConfig := server.ProvideIDEVSCodeWebConfig(config) vsCodeWeb := container.ProvideVSCodeWebService(vsCodeWebConfig) - containerConfig := server.ProvideGitspaceContainerOrchestratorConfig(config, dockerProviderConfig) + containerConfig := server.ProvideGitspaceContainerOrchestratorConfig(config) statefulLogger := logutil.ProvideStatefulLogger(logStream) containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, vsCode, vsCodeWeb, containerConfig, statefulLogger) orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter3) diff --git a/infraprovider/docker_provider.go b/infraprovider/docker_provider.go index c8d9bf2f8..9d94eecea 100644 --- a/infraprovider/docker_provider.go +++ b/infraprovider/docker_provider.go @@ -18,56 +18,47 @@ import ( "context" "fmt" "io" - "os" - "path/filepath" + "strings" "github.com/harness/gitness/infraprovider/enum" + "github.com/docker/docker/api/types/volume" + "github.com/docker/docker/client" "github.com/rs/zerolog/log" ) var _ InfraProvider = (*DockerProvider)(nil) -const gitspacesDir = "gitspaces" - -type DockerProviderConfig struct { - MountSourceBasePath string -} - type DockerProvider struct { - config *DockerConfig - dockerClientFactory *DockerClientFactory - dockerProviderConfig *DockerProviderConfig + config *DockerConfig + dockerClientFactory *DockerClientFactory } func NewDockerProvider( config *DockerConfig, dockerClientFactory *DockerClientFactory, - dockerProviderConfig *DockerProviderConfig, ) *DockerProvider { return &DockerProvider{ - config: config, - dockerClientFactory: dockerClientFactory, - dockerProviderConfig: dockerProviderConfig, + config: config, + dockerClientFactory: dockerClientFactory, } } -// Provision assumes a docker engine is already running on the Gitness host machine and re-uses that as infra. +// 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. 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) { +) (*Infrastructure, error) { dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &Infrastructure{ ProviderType: enum.InfraProviderTypeDocker, Parameters: params, }) if err != nil { - return Infrastructure{}, fmt.Errorf("error getting docker client from docker client factory: %w", err) + return nil, fmt.Errorf("error getting docker client from docker client factory: %w", err) } - defer func() { closingErr := dockerClient.Close() if closingErr != nil { @@ -75,82 +66,78 @@ func (d DockerProvider) Provision( } }() - info, err := dockerClient.Info(ctx) + infrastructure, err := d.dockerHostInfo(ctx, dockerClient) if err != nil { - return Infrastructure{}, fmt.Errorf("unable to connect to docker engine: %w", err) + return nil, err } - - err = d.createMountSourceDirectory(spacePath, resourceKey) + volumeName, err := d.createNamedVolume(ctx, spacePath, resourceKey, dockerClient) + infrastructure.Storage = volumeName if err != nil { - return Infrastructure{}, err + return nil, err } + return infrastructure, nil +} - return Infrastructure{ - Identifier: info.ID, +// Find fetches the infrastructure with the current state, the method has no side effects on the infra. +func (d DockerProvider) Find( + ctx context.Context, + spacePath string, + resourceKey string, + params []Parameter, +) (*Infrastructure, error) { + dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &Infrastructure{ ProviderType: enum.InfraProviderTypeDocker, - Status: enum.InfraStatusProvisioned, - Host: d.config.DockerMachineHostName, - }, nil -} - -func (d DockerProvider) createMountSourceDirectory(spacePath string, resourceKey string) error { - mountSourcePath := d.getMountSourceDirectoryPath(spacePath, resourceKey) - - err := os.MkdirAll(mountSourcePath, os.ModePerm) + Parameters: params, + }) if err != nil { - return fmt.Errorf( - "could not create bind mount source path %s: %w", mountSourcePath, err) + return nil, fmt.Errorf("error getting docker client from docker client factory: %w", err) } - - return nil -} - -func (d DockerProvider) deleteMountSourceDirectory(spacePath string, resourceKey string) error { - mountSourcePath := d.getMountSourceDirectoryPath(spacePath, resourceKey) - - err := os.RemoveAll(mountSourcePath) + defer func() { + closingErr := dockerClient.Close() + if closingErr != nil { + log.Ctx(ctx).Warn().Err(closingErr).Msg("failed to close docker client") + } + }() + infrastructure, err := d.dockerHostInfo(ctx, dockerClient) if err != nil { - return fmt.Errorf( - "could not delete bind mount source path %s: %w", mountSourcePath, err) + return nil, 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") + name := volumeName(spacePath, resourceKey) + volumeInspect, err := dockerClient.VolumeInspect(ctx, name) + if err != nil { + return nil, fmt.Errorf("couldn't find the volume for %s : %w", name, err) + } + infrastructure.Storage = volumeInspect.Name + return infrastructure, nil } // Stop is NOOP as this provider uses already running docker engine. It does not stop the docker engine. -func (d DockerProvider) Stop(_ context.Context, infra Infrastructure) (Infrastructure, error) { +func (d DockerProvider) Stop(_ context.Context, infra *Infrastructure) (*Infrastructure, error) { return infra, nil } // 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) +func (d DockerProvider) Deprovision(ctx context.Context, infra *Infrastructure) (*Infrastructure, error) { + dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &Infrastructure{ + ProviderType: enum.InfraProviderTypeDocker, + Parameters: infra.Parameters, + }) if err != nil { - return Infrastructure{}, err + return nil, fmt.Errorf("error getting docker client from docker client factory: %w", err) + } + defer func() { + closingErr := dockerClient.Close() + if closingErr != nil { + log.Ctx(ctx).Warn().Err(closingErr).Msg("failed to close docker client") + } + }() + err = dockerClient.VolumeRemove(ctx, infra.Storage, true) + if err != nil { + return nil, fmt.Errorf("couldn't delete volume for %s : %w", infra.Storage, err) } - return infra, nil } -func (d DockerProvider) Status(_ context.Context, _ Infrastructure) (enum.InfraStatus, error) { - // TODO implement me - panic("implement me") -} - // AvailableParams returns empty slice as no params are defined. func (d DockerProvider) AvailableParams() []ParameterSchema { return []ParameterSchema{} @@ -171,7 +158,45 @@ func (d DockerProvider) ProvisioningType() enum.InfraProvisioningType { return enum.InfraProvisioningTypeExisting } -func (d DockerProvider) Exec(_ context.Context, _ Infrastructure, _ []string) (io.Reader, io.Reader, error) { +func (d DockerProvider) Exec(_ context.Context, _ *Infrastructure, _ []string) (io.Reader, io.Reader, error) { // TODO implement me panic("implement me") } + +func (d DockerProvider) dockerHostInfo(ctx context.Context, dockerClient *client.Client) (*Infrastructure, error) { + info, err := dockerClient.Info(ctx) + if err != nil { + return nil, fmt.Errorf("unable to connect to docker engine: %w", err) + } + return &Infrastructure{ + Identifier: info.ID, + ProviderType: enum.InfraProviderTypeDocker, + Status: enum.InfraStatusProvisioned, + Host: d.config.DockerMachineHostName, + }, nil +} + +func (d DockerProvider) createNamedVolume( + ctx context.Context, + spacePath string, + resourceKey string, + dockerClient *client.Client, +) (string, error) { + name := volumeName(spacePath, resourceKey) + dockerVolume, err := dockerClient.VolumeCreate(ctx, volume.VolumeCreateBody{ + Name: name, + Driver: "local", + Labels: nil, + DriverOpts: nil}) + if err != nil { + return "", fmt.Errorf( + "could not create name volume %s: %w", name, err) + } + log.Info().Msgf("created volume %s", dockerVolume.Name) + return dockerVolume.Name, nil +} + +func volumeName(spacePath string, resourceKey string) string { + name := "gitspace-" + strings.ReplaceAll(spacePath, "/", "-") + "-" + resourceKey + return name +} diff --git a/infraprovider/infra_provider.go b/infraprovider/infra_provider.go index ea7c2ee94..529d8d579 100644 --- a/infraprovider/infra_provider.go +++ b/infraprovider/infra_provider.go @@ -23,15 +23,13 @@ import ( type InfraProvider interface { // Provision provisions infrastructure against a resourceKey with the provided parameters. - Provision(ctx context.Context, spacePath string, 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) + Find(ctx context.Context, spacePath string, 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) + Stop(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) + Deprovision(ctx context.Context, infra *Infrastructure) (*Infrastructure, error) // AvailableParams provides a schema to define the infrastructure. AvailableParams() []ParameterSchema // ValidateParams validates the supplied params before defining the infrastructure resource . @@ -41,5 +39,5 @@ type InfraProvider interface { // ProvisioningType specifies whether the provider will provision new infra resources or it will reuse existing. ProvisioningType() enum.InfraProvisioningType // Exec executes a shell command in the infrastructure. - Exec(ctx context.Context, infra Infrastructure, cmd []string) (io.Reader, io.Reader, error) + Exec(ctx context.Context, infra *Infrastructure, cmd []string) (io.Reader, io.Reader, error) } diff --git a/infraprovider/types.go b/infraprovider/types.go index 44718ea84..e5545b6e3 100644 --- a/infraprovider/types.go +++ b/infraprovider/types.go @@ -39,4 +39,5 @@ type Infrastructure struct { Status enum.InfraStatus Host string Port int + Storage string } diff --git a/infraprovider/wire.go b/infraprovider/wire.go index 3ac200574..fd3cd1b7a 100644 --- a/infraprovider/wire.go +++ b/infraprovider/wire.go @@ -28,9 +28,8 @@ var WireSet = wire.NewSet( func ProvideDockerProvider( config *DockerConfig, dockerClientFactory *DockerClientFactory, - dockerProviderConfig *DockerProviderConfig, ) *DockerProvider { - return NewDockerProvider(config, dockerClientFactory, dockerProviderConfig) + return NewDockerProvider(config, dockerClientFactory) } func ProvideFactory(dockerProvider *DockerProvider) Factory { diff --git a/types/config.go b/types/config.go index 04d666ffa..dd7125abc 100644 --- a/types/config.go +++ b/types/config.go @@ -406,15 +406,6 @@ type Config struct { Enable bool `envconfig:"GITNESS_GITSPACE_ENABLE" default:"false"` - // Root is the source for bind mount in the Gitspace container. - // Sub-directories will be created from this eg /gitspace/space1/space2/config1 - // If left blank, it will be set to $HOME/.gitness - 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"` MaxRetries int `envconfig:"GITNESS_GITSPACE_EVENTS_MAX_RETRIES" default:"3"`