feat: [CDE-195]: read devcontainer.json directly for gitness (#2294)

* feat: [CDE-195]: Run VS Code Web as non-root user.
* feat: [CDE-195]: read devcontainer.json directly for gitness
pull/3545/head
Ansuman Satapathy 2024-07-26 12:44:36 +00:00 committed by Harness
parent 9232029c74
commit b6034a25aa
28 changed files with 588 additions and 191 deletions

View File

@ -58,12 +58,29 @@ func (c *Controller) Action(
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
gitspaceConfig, err := c.gitspaceConfigStore.FindByIdentifier(ctx, space.ID, in.Identifier)
gitspaceConfig.SpacePath = space.Path
gitspaceConfig.SpaceID = space.ID
if err != nil {
return nil, fmt.Errorf("failed to find gitspace config: %w", err)
}
// check if it's an internal repo
if gitspaceConfig.CodeRepoType == enum.CodeRepoTypeGitness && gitspaceConfig.CodeRepoURL != "" {
repo, err := c.repoStore.FindByRef(ctx, gitspaceConfig.CodeRepoURL)
if err != nil {
return nil, fmt.Errorf("couldn't fetch repo for the user: %w", err)
}
if err = apiauth.CheckRepo(
ctx,
c.authorizer,
session,
repo,
enum.PermissionRepoView); err != nil {
return nil, err
}
}
// All the actions should be idempotent.
switch in.Action {
case enum.GitspaceActionTypeStart:

View File

@ -37,6 +37,7 @@ type Controller struct {
tx dbtx.Transactor
statefulLogger *logutil.StatefulLogger
scm scm.SCM
repoStore store.RepoStore
}
func NewController(
@ -51,6 +52,7 @@ func NewController(
gitspaceEventStore store.GitspaceEventStore,
statefulLogger *logutil.StatefulLogger,
scm scm.SCM,
repoStore store.RepoStore,
) *Controller {
return &Controller{
tx: tx,
@ -64,5 +66,6 @@ func NewController(
gitspaceEventStore: gitspaceEventStore,
statefulLogger: statefulLogger,
scm: scm,
repoStore: repoStore,
}
}

View File

@ -44,16 +44,17 @@ var (
// CreateInput is the input used for create operations.
type CreateInput struct {
Identifier string `json:"identifier"`
Name string `json:"name"`
SpaceRef string `json:"space_ref"` // Ref of the parent space
IDE enum.IDEType `json:"ide"`
ResourceIdentifier string `json:"resource_identifier"`
ResourceSpaceRef string `json:"resource_space_ref"`
CodeRepoURL string `json:"code_repo_url"`
Branch string `json:"branch"`
DevcontainerPath *string `json:"devcontainer_path"`
Metadata map[string]string `json:"metadata"`
Identifier string `json:"identifier"`
Name string `json:"name"`
SpaceRef string `json:"space_ref"` // Ref of the parent space
IDE enum.IDEType `json:"ide"`
ResourceIdentifier string `json:"resource_identifier"`
ResourceSpaceRef string `json:"resource_space_ref"`
CodeRepoURL string `json:"code_repo_url"`
CodeRepoType enum.GitspaceCodeRepoType `json:"code_repo_type"`
Branch string `json:"branch"`
DevcontainerPath *string `json:"devcontainer_path"`
Metadata map[string]string `json:"metadata"`
}
// Create creates a new gitspace.
@ -75,6 +76,21 @@ func (c *Controller) Create(
enum.PermissionGitspaceEdit); err != nil {
return nil, err
}
// check if it's an internal repo
if in.CodeRepoType == enum.CodeRepoTypeGitness && in.CodeRepoURL != "" {
repo, err := c.repoStore.FindByRef(ctx, in.CodeRepoURL)
if err != nil {
return nil, fmt.Errorf("couldn't fetch repo for the user: %w", err)
}
if err = apiauth.CheckRepo(
ctx,
c.authorizer,
session,
repo,
enum.PermissionRepoView); err != nil {
return nil, err
}
}
suffixUID, err := gonanoid.Generate(allowedUIDAlphabet, 6)
if err != nil {
return nil, fmt.Errorf("could not generate UID for gitspace config : %q %w", in.Identifier, err)
@ -86,7 +102,7 @@ func (c *Controller) Create(
now := time.Now().UnixMilli()
var gitspaceConfig *types.GitspaceConfig
resourceIdentifier := in.ResourceIdentifier
// assume resource to be in same space if its not explicitly specified.
// assume resource to be in same space if it's not explicitly specified.
if in.ResourceSpaceRef == "" {
in.ResourceSpaceRef = in.SpaceRef
}
@ -121,7 +137,7 @@ func (c *Controller) Create(
IDE: in.IDE,
InfraProviderResourceID: infraProviderResource.ID,
InfraProviderResourceIdentifier: infraProviderResource.Identifier,
CodeRepoType: enum.CodeRepoTypeUnknown,
CodeRepoType: in.CodeRepoType,
State: enum.GitspaceStateUninitialized,
CodeRepoURL: in.CodeRepoURL,
Branch: in.Branch,

View File

@ -73,29 +73,29 @@ func (c *Controller) Events(
func eventsMessageMapping() map[enum.GitspaceEventType]string {
var gitspaceConfigsMap = make(map[enum.GitspaceEventType]string)
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStart] = "Starting Gitspace..."
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStartCompleted] = "Started Gitspace"
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStartFailed] = "Starting Gitspace Failed"
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStart] = "Starting gitspace..."
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStartCompleted] = "Started gitspace"
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStartFailed] = "Starting gitspace failed"
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStop] = "Stopping Gitspace"
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStopCompleted] = "Stopped Gitspace"
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStopFailed] = "Stopping Gitspace Failed"
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStop] = "Stopping gitspace"
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStopCompleted] = "Stopped gitspace"
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStopFailed] = "Stopping gitspace failed"
gitspaceConfigsMap[enum.GitspaceEventTypeFetchDevcontainerStart] = "Fetching devcontainer config..."
gitspaceConfigsMap[enum.GitspaceEventTypeFetchDevcontainerCompleted] = "Fetched devcontainer config"
gitspaceConfigsMap[enum.GitspaceEventTypeFetchDevcontainerFailed] = "Fetching devcontainer config failed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningStart] = "Provisioning Infrastructure..."
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningCompleted] = "Provisioning Infrastructure Completed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningFailed] = "Provisioning Infrastructure Failed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningStart] = "Provisioning infrastructure..."
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningCompleted] = "Provisioning infrastructure completed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningFailed] = "Provisioning infrastructure failed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopStart] = "Stopping Infrastructure..."
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopCompleted] = "Stopping Infrastructure Completed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopFailed] = "Stopping Infrastructure Failed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopStart] = "Stopping infrastructure..."
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopCompleted] = "Stopping infrastructure completed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopFailed] = "Stopping infrastructure failed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningStart] = "Deprovisioning Infrastructure..."
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningCompleted] = "Deprovisioning Infrastructure Completed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningFailed] = "Deprovisioning Infrastructure Failed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningStart] = "Deprovisioning infrastructure..."
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningCompleted] = "Deprovisioning infrastructure completed"
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningFailed] = "Deprovisioning infrastructure failed"
gitspaceConfigsMap[enum.GitspaceEventTypeAgentConnectStart] = "Connecting to the gitspace agent..."
gitspaceConfigsMap[enum.GitspaceEventTypeAgentConnectCompleted] = "Connected to the gitspace agent"

View File

@ -44,6 +44,7 @@ func ProvideController(
eventStore store.GitspaceEventStore,
statefulLogger *logutil.StatefulLogger,
scm scm.SCM,
repoStore store.RepoStore,
) *Controller {
return NewController(
tx,
@ -57,5 +58,6 @@ func ProvideController(
eventStore,
statefulLogger,
scm,
repoStore,
)
}

View File

@ -52,6 +52,17 @@ func NewPipelineServiceSession() *auth.Session {
}
}
// gitspaceServicePrincipal is the principal that is used during
// gitspace token injection for calling gitness APIs.
var gitspaceServicePrincipal *types.Principal
func NewGitspaceServiceSession() *auth.Session {
return &auth.Session{
Principal: *gitspaceServicePrincipal,
Metadata: &auth.EmptyMetadata{},
}
}
// Bootstrap is an abstraction of a function that bootstraps a system.
type Bootstrap func(context.Context) error
@ -65,6 +76,9 @@ func System(config *types.Config, userCtrl *user.Controller,
if err := PipelineService(ctx, config, serviceCtrl); err != nil {
return fmt.Errorf("failed to setup pipeline service: %w", err)
}
if err := GitspaceService(ctx, config, serviceCtrl); err != nil {
return fmt.Errorf("failed to setup gitspace service: %w", err)
}
if err := AdminUser(ctx, config, userCtrl); err != nil {
return fmt.Errorf("failed to setup admin user: %w", err)
@ -196,6 +210,36 @@ func PipelineService(
return nil
}
// GitspaceService sets up the gitspace service principal that is used during
// gitspace credential injection for calling gitness APIs.
func GitspaceService(
ctx context.Context,
config *types.Config,
serviceCtrl *service.Controller,
) error {
svc, err := serviceCtrl.FindNoAuth(ctx, config.Principal.Gitspace.UID)
if errors.Is(err, store.ErrResourceNotFound) {
svc, err = createServicePrincipal(
ctx,
serviceCtrl,
config.Principal.Gitspace.UID,
config.Principal.Gitspace.Email,
config.Principal.Gitspace.DisplayName,
false,
)
}
if err != nil {
return fmt.Errorf("failed to setup gitspace service: %w", err)
}
gitspaceServicePrincipal = svc.ToPrincipal()
log.Ctx(ctx).Info().Msgf("Completed setup of gitspace service '%s' (id: %d).", svc.UID, svc.ID)
return nil
}
func createServicePrincipal(
ctx context.Context,
serviceCtrl *service.Controller,

View File

@ -18,6 +18,7 @@ import (
"context"
"github.com/harness/gitness/app/gitspace/orchestrator/ide"
"github.com/harness/gitness/app/gitspace/scm"
"github.com/harness/gitness/infraprovider"
"github.com/harness/gitness/types"
)
@ -29,9 +30,8 @@ type Orchestrator interface {
CreateAndStartGitspace(
ctx context.Context,
gitspaceConfig *types.GitspaceConfig,
devcontainerConfig *types.DevcontainerConfig,
infra *infraprovider.Infrastructure,
repoName string,
resolvedDetails *scm.ResolvedDetails,
defaultBaseImage string,
ideService ide.IDE,
) (*StartResponse, error)

View File

@ -25,6 +25,7 @@ import (
"github.com/harness/gitness/app/gitspace/orchestrator/devcontainer"
"github.com/harness/gitness/app/gitspace/orchestrator/ide"
"github.com/harness/gitness/app/gitspace/orchestrator/template"
"github.com/harness/gitness/app/gitspace/scm"
"github.com/harness/gitness/infraprovider"
"github.com/harness/gitness/types"
@ -40,12 +41,15 @@ import (
var _ Orchestrator = (*EmbeddedDockerOrchestrator)(nil)
const (
loggingKey = "gitspace.container"
catchAllIP = "0.0.0.0"
containerStateRunning = "running"
containerStateRemoved = "removed"
containerStateStopped = "exited"
templateCloneGit = "clone_git.sh"
loggingKey = "gitspace.container"
catchAllIP = "0.0.0.0"
containerStateRunning = "running"
containerStateRemoved = "removed"
containerStateStopped = "exited"
templateCloneGit = "clone_git.sh"
templateAuthenticateGit = "authenticate_git.sh"
templateManageUser = "manage_user.sh"
harnessUser = "harness"
)
type EmbeddedDockerOrchestrator struct {
@ -70,9 +74,8 @@ func NewEmbeddedDockerOrchestrator(
func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
ctx context.Context,
gitspaceConfig *types.GitspaceConfig,
devcontainerConfig *types.DevcontainerConfig,
infra *infraprovider.Infrastructure,
repoName string,
resolvedRepoDetails *scm.ResolvedDetails,
defaultBaseImage string,
ideService ide.IDE,
) (*StartResponse, error) {
@ -122,12 +125,18 @@ func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
return nil, startErr
}
devcontainer := &devcontainer.Devcontainer{
devcontainer := &devcontainer.Exec{
ContainerName: containerName,
WorkingDir: e.getWorkingDir(repoName),
WorkingDir: e.getWorkingDir(resolvedRepoDetails.RepoName),
DockerClient: dockerClient,
}
if resolvedRepoDetails.Credentials != nil {
if err := e.authenticateGit(ctx, devcontainer, resolvedRepoDetails); err != nil {
return nil, err
}
}
err = e.runIDE(ctx, devcontainer, ideService, logStreamInstance)
if err != nil {
return nil, err
@ -154,13 +163,13 @@ func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
startErr := e.startGitspace(
ctx,
gitspaceConfig,
devcontainerConfig,
containerName,
dockerClient,
ideService,
logStreamInstance,
infra.Storage,
e.getWorkingDir(repoName),
e.getWorkingDir(resolvedRepoDetails.RepoName),
resolvedRepoDetails,
infra.PortMappings,
defaultBaseImage,
)
@ -194,17 +203,17 @@ func (e *EmbeddedDockerOrchestrator) getWorkingDir(repoName string) string {
func (e *EmbeddedDockerOrchestrator) startGitspace(
ctx context.Context,
gitspaceConfig *types.GitspaceConfig,
devcontainerConfig *types.DevcontainerConfig,
containerName string,
dockerClient *client.Client,
ideService ide.IDE,
logStreamInstance *logutil.LogStreamInstance,
volumeName string,
workingDirectory string,
resolvedRepoDetails *scm.ResolvedDetails,
portMappings map[int]*infraprovider.PortMapping,
defaultBaseImage string,
) error {
var imageName = devcontainerConfig.Image
var imageName = resolvedRepoDetails.DevcontainerConfig.Image
if imageName == "" {
imageName = defaultBaseImage
}
@ -233,12 +242,17 @@ func (e *EmbeddedDockerOrchestrator) startGitspace(
return err
}
var devcontainer = &devcontainer.Devcontainer{
var devcontainer = &devcontainer.Exec{
ContainerName: containerName,
DockerClient: dockerClient,
WorkingDir: workingDirectory,
}
err = e.manageUser(ctx, devcontainer, logStreamInstance, gitspaceConfig.GitspaceInstance.AccessKey)
if err != nil {
return err
}
err = e.setupIDE(ctx, gitspaceConfig.GitspaceInstance, devcontainer, ideService, logStreamInstance)
if err != nil {
return err
@ -249,12 +263,12 @@ func (e *EmbeddedDockerOrchestrator) startGitspace(
return err
}
err = e.cloneCode(ctx, gitspaceConfig, devcontainer, defaultBaseImage, logStreamInstance)
err = e.cloneCode(ctx, devcontainer, defaultBaseImage, logStreamInstance, resolvedRepoDetails)
if err != nil {
return err
}
err = e.executePostCreateCommand(ctx, devcontainerConfig, devcontainer, logStreamInstance)
err = e.executePostCreateCommand(ctx, resolvedRepoDetails.DevcontainerConfig, devcontainer, logStreamInstance)
if err != nil {
return err
}
@ -266,7 +280,7 @@ func (e *EmbeddedDockerOrchestrator) startGitspace(
func (e *EmbeddedDockerOrchestrator) runIDE(
ctx context.Context,
devcontainer *devcontainer.Devcontainer,
devcontainer *devcontainer.Exec,
ideService ide.IDE,
logStreamInstance *logutil.LogStreamInstance,
) error {
@ -304,7 +318,7 @@ func (e *EmbeddedDockerOrchestrator) runIDE(
func (e *EmbeddedDockerOrchestrator) setupIDE(
ctx context.Context,
gitspaceInstance *types.GitspaceInstance,
devcontainer *devcontainer.Devcontainer,
devcontainer *devcontainer.Exec,
ideService ide.IDE,
logStreamInstance *logutil.LogStreamInstance,
) error {
@ -366,30 +380,103 @@ func (e *EmbeddedDockerOrchestrator) getContainerInfo(
return inspectResp.ID, usedPorts, nil
}
func (e *EmbeddedDockerOrchestrator) cloneCode(
func (e *EmbeddedDockerOrchestrator) authenticateGit(
ctx context.Context,
gitspaceConfig *types.GitspaceConfig,
devcontainer *devcontainer.Devcontainer,
defaultBaseImage string,
logStreamInstance *logutil.LogStreamInstance,
devcontainer *devcontainer.Exec,
resolvedRepoDetails *scm.ResolvedDetails,
) error {
gitCloneScript, err := template.GenerateScriptFromTemplate(
templateCloneGit, &template.CloneGitPayload{
RepoURL: gitspaceConfig.CodeRepoURL,
Image: defaultBaseImage,
Branch: gitspaceConfig.Branch,
})
data := &template.AuthenticateGitPayload{
Password: resolvedRepoDetails.Credentials.Password,
}
gitAuthenticateScript, err := template.GenerateScriptFromTemplate(
templateAuthenticateGit, data)
if err != nil {
return fmt.Errorf("failed to generate scipt to clone git from template %s: %w", templateCloneGit, err)
return fmt.Errorf("failed to generate scipt to authenticate git from template %s: %w", templateAuthenticateGit, err)
}
_, err = devcontainer.ExecuteCommand(ctx, gitAuthenticateScript, false, harnessUser)
if err != nil {
err = fmt.Errorf("failed to authenticate git in container: %w", err)
return err
}
return nil
}
func (e *EmbeddedDockerOrchestrator) manageUser(
ctx context.Context,
devcontainer *devcontainer.Exec,
logStreamInstance *logutil.LogStreamInstance,
accessKey *string,
) error {
data := template.SetupSSHServerPayload{
Username: "harness",
Password: *accessKey,
WorkingDirectory: devcontainer.WorkingDir,
}
manageUserScript, err := template.GenerateScriptFromTemplate(
templateManageUser, data)
if err != nil {
return fmt.Errorf("failed to generate scipt to manage user from template %s: %w", templateManageUser, err)
}
loggingErr := logStreamInstance.Write(
"Cloning git repo inside container: " + gitspaceConfig.CodeRepoURL + " branch: " + gitspaceConfig.Branch)
"creating user inside container: " + data.Username)
if loggingErr != nil {
return fmt.Errorf("logging error: %w", loggingErr)
}
output, err := devcontainer.ExecuteCommand(ctx, gitCloneScript, false)
output, err := devcontainer.ExecuteCommand(ctx, manageUserScript, false, "root")
if err != nil {
loggingErr = logStreamInstance.Write("Error while creating user inside container : " + err.Error())
err = fmt.Errorf("failed to create user: %w", err)
if loggingErr != nil {
err = fmt.Errorf("original error: %w; logging error: %w", err, loggingErr)
}
return err
}
loggingErr = logStreamInstance.Write("Managing user output...\n" + string(output))
if loggingErr != nil {
return fmt.Errorf("logging error: %w", loggingErr)
}
loggingErr = logStreamInstance.Write("Successfully created user inside container")
if loggingErr != nil {
return fmt.Errorf("logging error: %w", loggingErr)
}
return nil
}
func (e *EmbeddedDockerOrchestrator) cloneCode(
ctx context.Context,
devcontainer *devcontainer.Exec,
defaultBaseImage string,
logStreamInstance *logutil.LogStreamInstance,
resolvedRepoDetails *scm.ResolvedDetails,
) error {
data := &template.CloneGitPayload{
RepoURL: resolvedRepoDetails.CloneURL,
Image: defaultBaseImage,
Branch: resolvedRepoDetails.Branch,
}
if resolvedRepoDetails.Credentials != nil {
data.Password = resolvedRepoDetails.Credentials.Password
}
gitCloneScript, err := template.GenerateScriptFromTemplate(
templateCloneGit, data)
if err != nil {
return fmt.Errorf("failed to generate scipt to clone git from template %s: %w", templateCloneGit, err)
}
loggingErr := logStreamInstance.Write(
"Cloning git repo inside container: " + resolvedRepoDetails.CloneURL + " branch: " + resolvedRepoDetails.Branch)
if loggingErr != nil {
return fmt.Errorf("logging error: %w", loggingErr)
}
output, err := devcontainer.ExecuteCommand(ctx, gitCloneScript, false, harnessUser)
if err != nil {
loggingErr = logStreamInstance.Write("Error while cloning git repo inside container: " + err.Error())
@ -418,7 +505,7 @@ func (e *EmbeddedDockerOrchestrator) cloneCode(
func (e *EmbeddedDockerOrchestrator) executePostCreateCommand(
ctx context.Context,
devcontainerConfig *types.DevcontainerConfig,
devcontainer *devcontainer.Devcontainer,
devcontainer *devcontainer.Exec,
logStreamInstance *logutil.LogStreamInstance,
) error {
if devcontainerConfig.PostCreateCommand == "" {
@ -435,7 +522,7 @@ func (e *EmbeddedDockerOrchestrator) executePostCreateCommand(
return fmt.Errorf("logging error: %w", loggingErr)
}
output, err := devcontainer.ExecuteCommand(ctx, devcontainerConfig.PostCreateCommand, false)
output, err := devcontainer.ExecuteCommand(ctx, devcontainerConfig.PostCreateCommand, false, harnessUser)
if err != nil {
loggingErr = logStreamInstance.Write("Error while executing postCreate command")

View File

@ -23,32 +23,32 @@ import (
"github.com/docker/docker/client"
)
type Devcontainer struct {
type Exec struct {
ContainerName string
WorkingDir string
DockerClient *client.Client
}
func (d *Devcontainer) ExecuteCommand(ctx context.Context, command string, detach bool) ([]byte, error) {
func (e *Exec) ExecuteCommand(ctx context.Context, command string, detach bool, userName string) ([]byte, error) {
cmd := []string{"/bin/sh", "-c", command}
execConfig := dockerTypes.ExecConfig{
User: "root",
User: userName,
AttachStdout: true,
AttachStderr: true,
Cmd: cmd,
Detach: detach,
WorkingDir: d.WorkingDir,
WorkingDir: e.WorkingDir,
}
execID, err := d.DockerClient.ContainerExecCreate(ctx, d.ContainerName, execConfig)
execID, err := e.DockerClient.ContainerExecCreate(ctx, e.ContainerName, execConfig)
if err != nil {
return nil, fmt.Errorf("failed to create docker exec for container %s: %w", d.ContainerName, err)
return nil, fmt.Errorf("failed to create docker exec for container %s: %w", e.ContainerName, err)
}
execResponse, err := d.DockerClient.ContainerExecAttach(ctx, execID.ID, dockerTypes.ExecStartCheck{Detach: detach})
execResponse, err := e.DockerClient.ContainerExecAttach(ctx, execID.ID, dockerTypes.ExecStartCheck{Detach: detach})
if err != nil && err.Error() != "unable to upgrade to tcp, received 200" {
return nil, fmt.Errorf("failed to start docker exec for container %s: %w", d.ContainerName, err)
return nil, fmt.Errorf("failed to start docker exec for container %s: %w", e.ContainerName, err)
}
if execResponse.Conn != nil {

View File

@ -27,12 +27,12 @@ type IDE interface {
// copying settings and configurations.
Setup(
ctx context.Context,
devcontainer *devcontainer.Devcontainer,
devcontainer *devcontainer.Exec,
gitspaceInstance *types.GitspaceInstance,
) ([]byte, error)
// Run runs the IDE and supporting services.
Run(ctx context.Context, devcontainer *devcontainer.Devcontainer) ([]byte, error)
Run(ctx context.Context, devcontainer *devcontainer.Exec) ([]byte, error)
// Port provides the port which will be used by this IDE.
Port() int

View File

@ -0,0 +1,5 @@
#!/bin/sh
echo "Installing VSCode Web"
curl -fsSL https://code-server.dev/install.sh | sh

View File

@ -1,5 +0,0 @@
#!/bin/sh
echo "Running VSCode Web"
code-server

View File

@ -43,7 +43,7 @@ func NewVsCodeService() *VSCode {
// Setup installs the SSH server inside the container.
func (v *VSCode) Setup(
ctx context.Context,
devcontainer *devcontainer.Devcontainer,
devcontainer *devcontainer.Exec,
gitspaceInstance *types.GitspaceInstance,
) ([]byte, error) {
sshServerScript, err := template.GenerateScriptFromTemplate(
@ -59,7 +59,7 @@ func (v *VSCode) Setup(
output := "Installing ssh-server inside container\n"
_, err = devcontainer.ExecuteCommand(ctx, sshServerScript, false)
_, err = devcontainer.ExecuteCommand(ctx, sshServerScript, false, rootUser)
if err != nil {
return nil, fmt.Errorf("failed to setup SSH serverr: %w", err)
}
@ -70,10 +70,10 @@ func (v *VSCode) Setup(
}
// Run runs the SSH server inside the container.
func (v *VSCode) Run(ctx context.Context, devcontainer *devcontainer.Devcontainer) ([]byte, error) {
func (v *VSCode) Run(ctx context.Context, devcontainer *devcontainer.Exec) ([]byte, error) {
var output = ""
execOutput, err := devcontainer.ExecuteCommand(ctx, runSSHScript, false)
execOutput, err := devcontainer.ExecuteCommand(ctx, runSSHScript, false, rootUser)
if err != nil {
return nil, fmt.Errorf("failed to run SSH serverr: %w", err)
}

View File

@ -35,8 +35,8 @@ import (
var _ IDE = (*VSCodeWeb)(nil)
//go:embed script/run_vscode_web.sh
var runScript string
//go:embed script/install_vscode_web.sh
var installScript string
//go:embed script/find_vscode_web_path.sh
var findPathScript string
@ -44,9 +44,11 @@ var findPathScript string
//go:embed media/vscodeweb/*
var mediaFiles embed.FS
const templateInstallVSCodeWeb = "install_vscode_web.sh"
const templateRunVSCodeWeb = "run_vscode_web.sh"
const startMarker = "START_MARKER"
const endMarker = "END_MARKER"
const rootUser = "root"
const harnessUser = "harness"
type VSCodeWebConfig struct {
Port int
@ -63,29 +65,17 @@ func NewVsCodeWebService(config *VSCodeWebConfig) *VSCodeWeb {
// Setup runs the installScript which downloads the required version of the code-server binary.
func (v *VSCodeWeb) Setup(
ctx context.Context,
devcontainer *devcontainer.Devcontainer,
devcontainer *devcontainer.Exec,
_ *types.GitspaceInstance,
) ([]byte, error) {
installScript, err := template.GenerateScriptFromTemplate(
templateInstallVSCodeWeb, &template.InstallVSCodeWebPayload{
Port: strconv.Itoa(v.config.Port),
})
if err != nil {
return nil, fmt.Errorf(
"failed to generate scipt to install VSCode Web from template %s: %w",
templateInstallVSCodeWeb,
err,
)
}
output := "Installing VSCode Web inside container.\n"
_, err = devcontainer.ExecuteCommand(ctx, installScript, false)
_, err := devcontainer.ExecuteCommand(ctx, installScript, false, rootUser)
if err != nil {
return nil, fmt.Errorf("failed to install VSCode Web: %w", err)
}
findOutput, err := devcontainer.ExecuteCommand(ctx, findPathScript, false)
findOutput, err := devcontainer.ExecuteCommand(ctx, findPathScript, false, rootUser)
if err != nil {
return nil, fmt.Errorf("failed to find VSCode Web install path: %w", err)
}
@ -109,10 +99,22 @@ func (v *VSCodeWeb) Setup(
}
// Run runs the code-server binary.
func (v *VSCodeWeb) Run(ctx context.Context, devcontainer *devcontainer.Devcontainer) ([]byte, error) {
func (v *VSCodeWeb) Run(ctx context.Context, devcontainer *devcontainer.Exec) ([]byte, error) {
var output []byte
_, err := devcontainer.ExecuteCommand(ctx, runScript, true)
runScript, err := template.GenerateScriptFromTemplate(
templateRunVSCodeWeb, &template.RunVSCodeWebPayload{
Port: strconv.Itoa(v.config.Port),
})
if err != nil {
return nil, fmt.Errorf(
"failed to generate scipt to run VSCode Web from template %s: %w",
templateRunVSCodeWeb,
err,
)
}
_, err = devcontainer.ExecuteCommand(ctx, runScript, true, harnessUser)
if err != nil {
return nil, fmt.Errorf("failed to run VSCode Web: %w", err)
}
@ -130,7 +132,7 @@ func (v *VSCodeWeb) Type() enum.IDEType {
func (v *VSCodeWeb) copyMediaToContainer(
ctx context.Context,
devcontainer *devcontainer.Devcontainer,
devcontainer *devcontainer.Exec,
path string,
) error {
// Create a buffer to hold the tar data

View File

@ -83,16 +83,17 @@ func (o orchestrator) StartGitspace(
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerStart)
repoName, devcontainerConfig, err := o.scm.RepoNameAndDevcontainerConfig(ctx, gitspaceConfig)
scmResolvedDetails, err := o.scm.Resolve(ctx, gitspaceConfig)
if err != nil {
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerFailed)
return fmt.Errorf("failed to fetch code repo details for gitspace config ID %d", gitspaceConfig.ID)
return fmt.Errorf("failed to fetch code repo details for gitspace config ID %w %d", err, gitspaceConfig.ID)
}
devcontainerConfig := scmResolvedDetails.DevcontainerConfig
repoName := scmResolvedDetails.RepoName
if devcontainerConfig == nil {
log.Warn().Err(err).Msg("devcontainer config is nil, using empty config")
devcontainerConfig = &types.DevcontainerConfig{}
}
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerCompleted)
@ -137,7 +138,7 @@ func (o orchestrator) StartGitspace(
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationStart)
startResponse, err := o.containerOrchestrator.CreateAndStartGitspace(
ctx, gitspaceConfig, devcontainerConfig, infra, repoName, o.config.DefaultBaseImage, ideSvc)
ctx, gitspaceConfig, infra, scmResolvedDetails, o.config.DefaultBaseImage, ideSvc)
if err != nil {
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationFailed)

View File

@ -37,9 +37,14 @@ type CloneGitPayload struct {
RepoURL string
Image string
Branch string
AuthenticateGitPayload
}
type InstallVSCodeWebPayload struct {
type AuthenticateGitPayload struct {
Password string
}
type RunVSCodeWebPayload struct {
Port string
}

View File

@ -0,0 +1,15 @@
#!/bin/sh
password={{ .Password }}
# Create or overwrite the config file with new settings
touch $HOME/.git-askpass
cat > $HOME/.git-askpass <<EOF
echo $password
EOF
chmod 700 $HOME/.git-askpass
git config --global credential.helper 'cache --timeout=2592000'
#run git operation to cache the credential in memory
export GIT_ASKPASS=$HOME/.git-askpass
git ls-remote
rm $HOME/.git-askpass

View File

@ -3,10 +3,20 @@
repo_url={{ .RepoURL }}
image={{ .Image }}
branch={{ .Branch }}
password={{ .Password }}
# Extract the repository name from the URL
repo_name=$(basename -s .git "$repo_url")
# Create or overwrite the config file with new settings
touch $HOME/.git-askpass
cat > $HOME/.git-askpass <<EOF
echo $password
EOF
chmod 700 $HOME/.git-askpass
export GIT_ASKPASS=$HOME/.git-askpass
git config --global credential.helper 'cache --timeout=2592000'
# Check if Git is installed
if ! command -v git >/dev/null 2>&1; then
echo "Git is not installed. Installing Git..."
@ -18,20 +28,20 @@ if ! command -v git >/dev/null 2>&1; then
echo "Git is not installed. Exiting..."
exit 1
fi
git config --global --add safe.directory /$repo_name
# Clone the repository inside the working directory if it doesn't exist
if [ ! -d ".git" ]; then
echo "Cloning the repository..."
git clone "$repo_url" --branch "$branch" .
git clone "$repo_url" --branch "$branch" /$repo_name
else
echo "Repository already exists. Skipping clone."
fi
rm $HOME/.git-askpass
# Check if .devcontainer/devcontainer.json exists
if [ ! -f ".devcontainer/devcontainer.json" ]; then
echo "Creating .devcontainer directory and devcontainer.json..."
mkdir -p ".devcontainer"
cat <<EOL > ".devcontainer/devcontainer.json"
mkdir -p /$repo_name/.devcontainer
cat <<EOL > /$repo_name/.devcontainer/devcontainer.json
{
"image": "$image"
}

View File

@ -0,0 +1,24 @@
#!/bin/sh
username={{ .Username }}
password={{ .Password }}
workingDir={{ .WorkingDirectory }}
# Check if the user already exists
if id "$username" >/dev/null 2>&1; then
echo "User $username already exists."
else
# Create a new user
adduser --disabled-password --gecos "" "$username"
if [ $? -ne 0 ]; then
echo "Failed to create user $username."
exit 1
fi
fi
# Set or update the user's password using chpasswd
echo "$username:$password" | chpasswd
# Changing ownership of everything inside user home to the newly created user
chown -R $username $workingDir
echo "Changing ownership of dir $workingDir to user $username."

View File

@ -1,17 +1,17 @@
#!/bin/sh
echo "Installing VSCode Web"
curl -fsSL https://code-server.dev/install.sh | sh
echo "Running VSCode Web"
port={{ .Port }}
# Ensure the configuration directory exists
mkdir -p /root/.config/code-server
mkdir -p $HOME/.config/code-server
# Create or overwrite the config file with new settings
cat > /root/.config/code-server/config.yaml <<EOF
cat > $HOME/.config/code-server/config.yaml <<EOF
bind-addr: 0.0.0.0:$port
auth: none
cert: false
EOF
EOF
code-server --disable-workspace-trust

View File

@ -13,21 +13,6 @@ username={{ .Username }}
password={{ .Password }}
workingDir={{ .WorkingDirectory }}
# Check if the user already exists
if id "$username" >/dev/null 2>&1; then
echo "User $username already exists."
else
# Create a new user
adduser --disabled-password --home "$workingDir" --gecos "" "$username"
if [ $? -ne 0 ]; then
echo "Failed to create user $username."
exit 1
fi
fi
# Set or update the user's password using chpasswd
echo "$username:$password" | chpasswd
# Configure SSH to allow this user
config_file='/etc/ssh/sshd_config'
grep -q "^AllowUsers" $config_file
@ -45,7 +30,4 @@ if ! grep -q "^PasswordAuthentication yes" $config_file; then
echo "PasswordAuthentication yes" >> $config_file
fi
# Changing ownership of everything inside user home to the newly created user
chown -R $username .
mkdir /var/run/sshd

View File

@ -26,9 +26,18 @@ import (
"path"
"regexp"
"strings"
"time"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/bootstrap"
"github.com/harness/gitness/app/jwt"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/token"
urlprovider "github.com/harness/gitness/app/url"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/command"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
@ -38,23 +47,49 @@ var (
ErrNoDefaultBranch = errors.New("no default branch")
)
var _ SCM = (*scm)(nil)
var gitspaceJWTLifetime = 720 * 24 * time.Hour
const defaultGitspacePATIdentifier = "Gitspace_Default"
var _ SCM = (*genericSCM)(nil)
type SCM interface {
// RepoNameAndDevcontainerConfig fetches repository name & devcontainer config file from the given repo and branch.
RepoNameAndDevcontainerConfig(
Resolve(
ctx context.Context,
gitspaceConfig *types.GitspaceConfig,
) (string, *types.DevcontainerConfig, error)
) (*ResolvedDetails, error)
// CheckValidCodeRepo checks if the current URL is a valid and accessible code repo,
// input can be connector info, user token etc.
CheckValidCodeRepo(ctx context.Context, request CodeRepositoryRequest) (*CodeRepositoryResponse, error)
}
type scm struct{}
type genericSCM struct {
git git.Interface
repoStore store.RepoStore
tokenStore store.TokenStore
principalStore store.PrincipalStore
urlProvider urlprovider.Provider
}
func (s scm) CheckValidCodeRepo(ctx context.Context, request CodeRepositoryRequest) (*CodeRepositoryResponse, error) {
func NewSCM(repoStore store.RepoStore, git git.Interface,
tokenStore store.TokenStore,
principalStore store.PrincipalStore,
urlProvider urlprovider.Provider) SCM {
return &genericSCM{
repoStore: repoStore,
git: git,
tokenStore: tokenStore,
principalStore: principalStore,
urlProvider: urlProvider,
}
}
func (s genericSCM) CheckValidCodeRepo(
ctx context.Context,
request CodeRepositoryRequest,
) (*CodeRepositoryResponse, error) {
err := validateURL(request)
if err != nil {
return nil, fmt.Errorf("invalid URL, %w", err)
@ -78,27 +113,24 @@ func (s scm) CheckValidCodeRepo(ctx context.Context, request CodeRepositoryReque
return codeRepositoryResponse, nil
}
func NewSCM() SCM {
return &scm{}
}
func (s scm) RepoNameAndDevcontainerConfig(
func (s genericSCM) Resolve(
ctx context.Context,
gitspaceConfig *types.GitspaceConfig,
) (string, *types.DevcontainerConfig, error) {
) (*ResolvedDetails, error) {
resolvedDetails := &ResolvedDetails{Branch: gitspaceConfig.Branch, CloneURL: gitspaceConfig.CodeRepoURL}
repoURL, err := url.Parse(gitspaceConfig.CodeRepoURL)
if err != nil {
return "", nil, fmt.Errorf("failed to parse repository URL %s: %w", gitspaceConfig.CodeRepoURL, err)
return nil, fmt.Errorf("failed to parse repository URL %s: %w", gitspaceConfig.CodeRepoURL, err)
}
repoName := strings.TrimSuffix(path.Base(repoURL.Path), ".git")
resolvedDetails.RepoName = repoName
gitWorkingDirectory := "/tmp/git/"
cloneDir := gitWorkingDirectory + uuid.New().String()
err = os.MkdirAll(cloneDir, os.ModePerm)
if err != nil {
return "", nil, fmt.Errorf("error creating directory %s: %w", cloneDir, err)
return nil, fmt.Errorf("error creating directory %s: %w", cloneDir, err)
}
defer func() {
@ -109,11 +141,90 @@ func (s scm) RepoNameAndDevcontainerConfig(
}()
filePath := ".devcontainer/devcontainer.json"
err = validateArgs(gitspaceConfig)
var catFileOutputBytes []byte
switch gitspaceConfig.CodeRepoType { //nolint:exhaustive
case enum.CodeRepoTypeGitness:
repo, err := s.repoStore.FindByRef(ctx, gitspaceConfig.CodeRepoURL)
// Backfill clone URL
repo.GitURL = s.urlProvider.GenerateContainerGITCloneURL(ctx, repo.Path)
if err != nil {
return nil, fmt.Errorf("failed to find repository: %w", err)
}
resolvedDetails.CloneURL = repo.GitURL
catFileOutputBytes, err = s.getDevContainerConfigInternal(ctx, gitspaceConfig, filePath, repo)
if err != nil {
return nil, fmt.Errorf("failed to read devcontainer file : %w", err)
}
netrc, err := s.gitnessCredentials(ctx, repo, gitspaceConfig.UserID)
if err != nil {
return nil, fmt.Errorf("failed to resolve repo credentials: %w", err)
}
resolvedDetails.Credentials = netrc
default:
catFileOutputBytes, err = s.getDevContainerConfigPublic(ctx, gitspaceConfig, cloneDir, filePath)
if err != nil {
return nil, err
}
}
if len(catFileOutputBytes) == 0 {
resolvedDetails.DevcontainerConfig = &types.DevcontainerConfig{}
return resolvedDetails, nil
}
sanitizedJSON := removeComments(catFileOutputBytes)
var config *types.DevcontainerConfig
err = json.Unmarshal(sanitizedJSON, &config)
if err != nil {
return "", nil, fmt.Errorf("invalid branch or url: %w", err)
return nil, fmt.Errorf("failed to parse devcontainer json: %w", err)
}
resolvedDetails.DevcontainerConfig = config
return resolvedDetails, nil
}
func (s genericSCM) getDevContainerConfigInternal(ctx context.Context,
gitspaceConfig *types.GitspaceConfig,
filePath string,
repo *types.Repository,
) ([]byte, error) {
// create read params once
readParams := git.CreateReadParams(repo)
treeNodeOutput, err := s.git.GetTreeNode(ctx, &git.GetTreeNodeParams{
ReadParams: readParams,
GitREF: gitspaceConfig.Branch,
Path: filePath,
IncludeLatestCommit: false,
})
if err != nil {
return nil, fmt.Errorf("failed to read tree node: %w", err)
}
// viewing Raw content is only supported for blob content
if treeNodeOutput.Node.Type != git.TreeNodeTypeBlob {
return nil, usererror.BadRequestf(
"Object in '%s' at '/%s' is of type '%s'. Only objects of type %s support raw viewing.",
gitspaceConfig.Branch, filePath, treeNodeOutput.Node.Type, git.TreeNodeTypeBlob)
}
blobReader, err := s.git.GetBlob(ctx, &git.GetBlobParams{
ReadParams: readParams,
SHA: treeNodeOutput.Node.SHA,
SizeLimit: 0, // no size limit, we stream whatever data there is
})
if err != nil {
return nil, fmt.Errorf("failed to read blob: %w", err)
}
catFileOutput, err := io.ReadAll(blobReader.Content)
if err != nil {
return nil, fmt.Errorf("failed to read blob content: %w", err)
}
return catFileOutput, nil
}
func (s genericSCM) getDevContainerConfigPublic(ctx context.Context,
gitspaceConfig *types.GitspaceConfig,
cloneDir string,
filePath string,
) ([]byte, error) {
log.Info().Msg("Cloning the repository...")
cmd := command.New("clone",
command.WithFlag("--branch", gitspaceConfig.Branch),
@ -122,12 +233,8 @@ func (s scm) RepoNameAndDevcontainerConfig(
command.WithArg(gitspaceConfig.CodeRepoURL),
command.WithArg(cloneDir),
)
err = cmd.Run(
ctx,
command.WithDir(cloneDir),
)
if err != nil {
return "", nil, fmt.Errorf("failed to clone repository %s: %w", gitspaceConfig.CodeRepoURL, err)
if err := cmd.Run(ctx, command.WithDir(cloneDir)); err != nil {
return nil, fmt.Errorf("failed to clone repository %s: %w", gitspaceConfig.CodeRepoURL, err)
}
var lsTreeOutput bytes.Buffer
@ -135,19 +242,14 @@ func (s scm) RepoNameAndDevcontainerConfig(
command.WithArg("HEAD"),
command.WithArg(filePath),
)
err = lsTreeCmd.Run(
ctx,
command.WithDir(cloneDir),
command.WithStdout(&lsTreeOutput),
)
if err != nil {
return "", nil, fmt.Errorf("failed to list files in repository %s: %w", cloneDir, err)
if err := lsTreeCmd.Run(ctx, command.WithDir(cloneDir), command.WithStdout(&lsTreeOutput)); err != nil {
return nil, fmt.Errorf("failed to list files in repository %s: %w", cloneDir, err)
}
if lsTreeOutput.Len() == 0 {
log.Info().Msg("File not found, returning empty devcontainerConfig")
emptyConfig := &types.DevcontainerConfig{}
return repoName, emptyConfig, nil
return nil, nil
}
fields := strings.Fields(lsTreeOutput.String())
@ -155,25 +257,16 @@ func (s scm) RepoNameAndDevcontainerConfig(
var catFileOutput bytes.Buffer
catFileCmd := command.New("cat-file", command.WithFlag("-p"), command.WithArg(blobSHA))
err = catFileCmd.Run(
err := catFileCmd.Run(
ctx,
command.WithDir(cloneDir),
command.WithStderr(io.Discard),
command.WithStdout(&catFileOutput),
)
if err != nil {
return "", nil, fmt.Errorf("failed to read devcontainer file from path %s: %w", filePath, err)
return nil, fmt.Errorf("failed to read devcontainer file from path %s: %w", filePath, err)
}
sanitizedJSON := removeComments(catFileOutput.Bytes())
var config types.DevcontainerConfig
err = json.Unmarshal(sanitizedJSON, &config)
if err != nil {
return "", nil, fmt.Errorf("failed to parse devcontainer json: %w", err)
}
return repoName, &config, nil
return catFileOutput.Bytes(), nil
}
func removeComments(input []byte) []byte {
@ -209,7 +302,49 @@ func validateURL(request CodeRepositoryRequest) error {
return nil
}
func validateArgs(_ *types.GitspaceConfig) error {
// TODO Validate the args
return nil
func findUserFromUID(ctx context.Context,
principalStore store.PrincipalStore, userUID string,
) (*types.User, error) {
return principalStore.FindUserByUID(ctx, userUID)
}
func (s genericSCM) gitnessCredentials(
ctx context.Context,
repo *types.Repository,
userUID string,
) (*Credentials, error) {
gitspacePrincipal := bootstrap.NewGitspaceServiceSession().Principal
user, err := findUserFromUID(ctx, s.principalStore, userUID)
if err != nil {
return nil, err
}
var jwtToken string
existingToken, _ := s.tokenStore.FindByIdentifier(ctx, user.ID, defaultGitspacePATIdentifier)
if existingToken != nil {
// create jwt token.
jwtToken, err = jwt.GenerateForToken(existingToken, user.ToPrincipal().Salt)
if err != nil {
return nil, fmt.Errorf("failed to create JWT token: %w", err)
}
} else {
_, jwtToken, err = token.CreatePAT(
ctx,
s.tokenStore,
&gitspacePrincipal,
user,
defaultGitspacePATIdentifier,
&gitspaceJWTLifetime)
}
if err != nil {
return nil, fmt.Errorf("failed to create JWT: %w", err)
}
cloneURL, err := url.Parse(repo.GitURL)
if err != nil {
return nil, fmt.Errorf("failed to parse clone url '%s': %w", cloneURL, err)
}
return &Credentials{
Password: jwtToken,
}, nil
}

33
app/gitspace/scm/types.go Normal file
View File

@ -0,0 +1,33 @@
// 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 scm
import "github.com/harness/gitness/types"
type (
ResolvedDetails struct {
RepoName string
DevcontainerConfig *types.DevcontainerConfig
Credentials *Credentials
Branch string
CloneURL string
}
// Credentials contains login and initialization information used
// by an automated login process.
Credentials struct {
Password string
}
)

View File

@ -14,13 +14,24 @@
package scm
import "github.com/google/wire"
import (
"github.com/harness/gitness/app/store"
urlprovider "github.com/harness/gitness/app/url"
"github.com/harness/gitness/git"
"github.com/google/wire"
)
// WireSet provides a wire set for this package.
var WireSet = wire.NewSet(
ProvideSCM,
)
func ProvideSCM() SCM {
return NewSCM()
func ProvideSCM(repoStore store.RepoStore,
rpcClient git.Interface,
tokenStore store.TokenStore,
principalStore store.PrincipalStore,
urlProvider urlprovider.Provider,
) SCM {
return NewSCM(repoStore, rpcClient, tokenStore, principalStore, urlProvider)
}

View File

@ -343,7 +343,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
if err != nil {
return nil, err
}
scmSCM := scm.ProvideSCM()
scmSCM := scm.ProvideSCM(repoStore, gitInterface, tokenStore, principalStore, provider)
infraProvisioner := infrastructure.ProvideInfraProvisionerService(infraProviderConfigStore, infraProviderResourceStore, factory)
statefulLogger := logutil.ProvideStatefulLogger(logStream)
containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, statefulLogger)
@ -353,7 +353,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
vsCodeWeb := ide.ProvideVSCodeWebService(vsCodeWebConfig)
orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter3, orchestratorConfig, vsCode, vsCodeWeb)
gitspaceEventStore := database.ProvideGitspaceEventStore(db)
gitspaceController := gitspace2.ProvideController(transactor, authorizer, infraproviderService, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter3, orchestratorOrchestrator, gitspaceEventStore, statefulLogger, scmSCM)
gitspaceController := gitspace2.ProvideController(transactor, authorizer, infraproviderService, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter3, orchestratorOrchestrator, gitspaceEventStore, statefulLogger, scmSCM, repoStore)
migrateController := migrate.ProvideController(authorizer, principalStore)
openapiService := openapi.ProvideOpenAPIService()
routerRouter := router.ProvideRouter(ctx, config, authenticator, repoController, reposettingsController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, gitInterface, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController, infraproviderController, gitspaceController, migrateController, provider, openapiService)

View File

@ -253,6 +253,14 @@ type Config struct {
DisplayName string `envconfig:"GITNESS_PRINCIPAL_PIPELINE_DISPLAY_NAME" default:"Gitness Pipeline"`
Email string `envconfig:"GITNESS_PRINCIPAL_PIPELINE_EMAIL" default:"pipeline@gitness.io"`
}
// Pipeline defines the principal information used to create the pipeline service.
Gitspace struct {
UID string `envconfig:"GITNESS_PRINCIPAL_GITSPACE_UID" default:"gitspace"`
DisplayName string `envconfig:"GITNESS_PRINCIPAL_GITSPACE_DISPLAY_NAME" default:"Gitness Gitspace"`
Email string `envconfig:"GITNESS_PRINCIPAL_GITSPACE_EMAIL" default:"gitspace@gitness.io"`
}
// Admin defines the principal information used to create the admin user.
// NOTE: The admin user is only auto-created in case a password and an email is provided.
Admin struct {

View File

@ -19,12 +19,14 @@ type GitspaceCodeRepoType string
func (GitspaceCodeRepoType) Enum() []interface{} { return toInterfaceSlice(codeRepoTypes) }
var codeRepoTypes = []GitspaceCodeRepoType{
CodeRepoTypeGithub, CodeRepoTypeGitlab, CodeRepoTypeHarnessCode, CodeRepoTypeBitbucket, CodeRepoTypeUnknown,
CodeRepoTypeGithub, CodeRepoTypeGitlab, CodeRepoTypeHarnessCode,
CodeRepoTypeBitbucket, CodeRepoTypeUnknown, CodeRepoTypeGitness,
}
const (
CodeRepoTypeGithub GitspaceCodeRepoType = "github"
CodeRepoTypeGitlab GitspaceCodeRepoType = "gitlab"
CodeRepoTypeGitness GitspaceCodeRepoType = "gitness"
CodeRepoTypeHarnessCode GitspaceCodeRepoType = "harness_code"
CodeRepoTypeBitbucket GitspaceCodeRepoType = "bitbucket"
CodeRepoTypeUnknown GitspaceCodeRepoType = "unknown"

View File

@ -27,7 +27,7 @@ type GitspaceConfig struct {
InfraProviderResourceID int64 `json:"-"`
InfraProviderResourceIdentifier string `json:"resource_identifier"`
CodeRepoURL string `json:"code_repo_url"`
CodeRepoType enum.GitspaceCodeRepoType `json:"-"`
CodeRepoType enum.GitspaceCodeRepoType `json:"code_repo_type"`
Branch string `json:"branch"`
DevcontainerPath *string `json:"devcontainer_path,omitempty"`
UserID string `json:"user_id"`