mirror of
https://github.com/harness/drone.git
synced 2025-05-04 14:43:15 +08:00
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
This commit is contained in:
parent
9232029c74
commit
b6034a25aa
@ -58,12 +58,29 @@ func (c *Controller) Action(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to authorize: %w", err)
|
return nil, fmt.Errorf("failed to authorize: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gitspaceConfig, err := c.gitspaceConfigStore.FindByIdentifier(ctx, space.ID, in.Identifier)
|
gitspaceConfig, err := c.gitspaceConfigStore.FindByIdentifier(ctx, space.ID, in.Identifier)
|
||||||
gitspaceConfig.SpacePath = space.Path
|
gitspaceConfig.SpacePath = space.Path
|
||||||
gitspaceConfig.SpaceID = space.ID
|
gitspaceConfig.SpaceID = space.ID
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to find gitspace config: %w", err)
|
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.
|
// All the actions should be idempotent.
|
||||||
switch in.Action {
|
switch in.Action {
|
||||||
case enum.GitspaceActionTypeStart:
|
case enum.GitspaceActionTypeStart:
|
||||||
|
@ -37,6 +37,7 @@ type Controller struct {
|
|||||||
tx dbtx.Transactor
|
tx dbtx.Transactor
|
||||||
statefulLogger *logutil.StatefulLogger
|
statefulLogger *logutil.StatefulLogger
|
||||||
scm scm.SCM
|
scm scm.SCM
|
||||||
|
repoStore store.RepoStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewController(
|
func NewController(
|
||||||
@ -51,6 +52,7 @@ func NewController(
|
|||||||
gitspaceEventStore store.GitspaceEventStore,
|
gitspaceEventStore store.GitspaceEventStore,
|
||||||
statefulLogger *logutil.StatefulLogger,
|
statefulLogger *logutil.StatefulLogger,
|
||||||
scm scm.SCM,
|
scm scm.SCM,
|
||||||
|
repoStore store.RepoStore,
|
||||||
) *Controller {
|
) *Controller {
|
||||||
return &Controller{
|
return &Controller{
|
||||||
tx: tx,
|
tx: tx,
|
||||||
@ -64,5 +66,6 @@ func NewController(
|
|||||||
gitspaceEventStore: gitspaceEventStore,
|
gitspaceEventStore: gitspaceEventStore,
|
||||||
statefulLogger: statefulLogger,
|
statefulLogger: statefulLogger,
|
||||||
scm: scm,
|
scm: scm,
|
||||||
|
repoStore: repoStore,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ type CreateInput struct {
|
|||||||
ResourceIdentifier string `json:"resource_identifier"`
|
ResourceIdentifier string `json:"resource_identifier"`
|
||||||
ResourceSpaceRef string `json:"resource_space_ref"`
|
ResourceSpaceRef string `json:"resource_space_ref"`
|
||||||
CodeRepoURL string `json:"code_repo_url"`
|
CodeRepoURL string `json:"code_repo_url"`
|
||||||
|
CodeRepoType enum.GitspaceCodeRepoType `json:"code_repo_type"`
|
||||||
Branch string `json:"branch"`
|
Branch string `json:"branch"`
|
||||||
DevcontainerPath *string `json:"devcontainer_path"`
|
DevcontainerPath *string `json:"devcontainer_path"`
|
||||||
Metadata map[string]string `json:"metadata"`
|
Metadata map[string]string `json:"metadata"`
|
||||||
@ -75,6 +76,21 @@ func (c *Controller) Create(
|
|||||||
enum.PermissionGitspaceEdit); err != nil {
|
enum.PermissionGitspaceEdit); err != nil {
|
||||||
return nil, err
|
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)
|
suffixUID, err := gonanoid.Generate(allowedUIDAlphabet, 6)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not generate UID for gitspace config : %q %w", in.Identifier, err)
|
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()
|
now := time.Now().UnixMilli()
|
||||||
var gitspaceConfig *types.GitspaceConfig
|
var gitspaceConfig *types.GitspaceConfig
|
||||||
resourceIdentifier := in.ResourceIdentifier
|
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 == "" {
|
if in.ResourceSpaceRef == "" {
|
||||||
in.ResourceSpaceRef = in.SpaceRef
|
in.ResourceSpaceRef = in.SpaceRef
|
||||||
}
|
}
|
||||||
@ -121,7 +137,7 @@ func (c *Controller) Create(
|
|||||||
IDE: in.IDE,
|
IDE: in.IDE,
|
||||||
InfraProviderResourceID: infraProviderResource.ID,
|
InfraProviderResourceID: infraProviderResource.ID,
|
||||||
InfraProviderResourceIdentifier: infraProviderResource.Identifier,
|
InfraProviderResourceIdentifier: infraProviderResource.Identifier,
|
||||||
CodeRepoType: enum.CodeRepoTypeUnknown,
|
CodeRepoType: in.CodeRepoType,
|
||||||
State: enum.GitspaceStateUninitialized,
|
State: enum.GitspaceStateUninitialized,
|
||||||
CodeRepoURL: in.CodeRepoURL,
|
CodeRepoURL: in.CodeRepoURL,
|
||||||
Branch: in.Branch,
|
Branch: in.Branch,
|
||||||
|
@ -73,29 +73,29 @@ func (c *Controller) Events(
|
|||||||
func eventsMessageMapping() map[enum.GitspaceEventType]string {
|
func eventsMessageMapping() map[enum.GitspaceEventType]string {
|
||||||
var gitspaceConfigsMap = make(map[enum.GitspaceEventType]string)
|
var gitspaceConfigsMap = make(map[enum.GitspaceEventType]string)
|
||||||
|
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStart] = "Starting Gitspace..."
|
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStart] = "Starting gitspace..."
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStartCompleted] = "Started Gitspace"
|
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStartCompleted] = "Started gitspace"
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStartFailed] = "Starting Gitspace Failed"
|
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStartFailed] = "Starting gitspace failed"
|
||||||
|
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStop] = "Stopping Gitspace"
|
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStop] = "Stopping gitspace"
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStopCompleted] = "Stopped Gitspace"
|
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStopCompleted] = "Stopped gitspace"
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStopFailed] = "Stopping Gitspace Failed"
|
gitspaceConfigsMap[enum.GitspaceEventTypeGitspaceActionStopFailed] = "Stopping gitspace failed"
|
||||||
|
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeFetchDevcontainerStart] = "Fetching devcontainer config..."
|
gitspaceConfigsMap[enum.GitspaceEventTypeFetchDevcontainerStart] = "Fetching devcontainer config..."
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeFetchDevcontainerCompleted] = "Fetched devcontainer config"
|
gitspaceConfigsMap[enum.GitspaceEventTypeFetchDevcontainerCompleted] = "Fetched devcontainer config"
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeFetchDevcontainerFailed] = "Fetching devcontainer config failed"
|
gitspaceConfigsMap[enum.GitspaceEventTypeFetchDevcontainerFailed] = "Fetching devcontainer config failed"
|
||||||
|
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningStart] = "Provisioning Infrastructure..."
|
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningStart] = "Provisioning infrastructure..."
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningCompleted] = "Provisioning Infrastructure Completed"
|
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningCompleted] = "Provisioning infrastructure completed"
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningFailed] = "Provisioning Infrastructure Failed"
|
gitspaceConfigsMap[enum.GitspaceEventTypeInfraProvisioningFailed] = "Provisioning infrastructure failed"
|
||||||
|
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopStart] = "Stopping Infrastructure..."
|
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopStart] = "Stopping infrastructure..."
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopCompleted] = "Stopping Infrastructure Completed"
|
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopCompleted] = "Stopping infrastructure completed"
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopFailed] = "Stopping Infrastructure Failed"
|
gitspaceConfigsMap[enum.GitspaceEventTypeInfraStopFailed] = "Stopping infrastructure failed"
|
||||||
|
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningStart] = "Deprovisioning Infrastructure..."
|
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningStart] = "Deprovisioning infrastructure..."
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningCompleted] = "Deprovisioning Infrastructure Completed"
|
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningCompleted] = "Deprovisioning infrastructure completed"
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningFailed] = "Deprovisioning Infrastructure Failed"
|
gitspaceConfigsMap[enum.GitspaceEventTypeInfraDeprovisioningFailed] = "Deprovisioning infrastructure failed"
|
||||||
|
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeAgentConnectStart] = "Connecting to the gitspace agent..."
|
gitspaceConfigsMap[enum.GitspaceEventTypeAgentConnectStart] = "Connecting to the gitspace agent..."
|
||||||
gitspaceConfigsMap[enum.GitspaceEventTypeAgentConnectCompleted] = "Connected to the gitspace agent"
|
gitspaceConfigsMap[enum.GitspaceEventTypeAgentConnectCompleted] = "Connected to the gitspace agent"
|
||||||
|
@ -44,6 +44,7 @@ func ProvideController(
|
|||||||
eventStore store.GitspaceEventStore,
|
eventStore store.GitspaceEventStore,
|
||||||
statefulLogger *logutil.StatefulLogger,
|
statefulLogger *logutil.StatefulLogger,
|
||||||
scm scm.SCM,
|
scm scm.SCM,
|
||||||
|
repoStore store.RepoStore,
|
||||||
) *Controller {
|
) *Controller {
|
||||||
return NewController(
|
return NewController(
|
||||||
tx,
|
tx,
|
||||||
@ -57,5 +58,6 @@ func ProvideController(
|
|||||||
eventStore,
|
eventStore,
|
||||||
statefulLogger,
|
statefulLogger,
|
||||||
scm,
|
scm,
|
||||||
|
repoStore,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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.
|
// Bootstrap is an abstraction of a function that bootstraps a system.
|
||||||
type Bootstrap func(context.Context) error
|
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 {
|
if err := PipelineService(ctx, config, serviceCtrl); err != nil {
|
||||||
return fmt.Errorf("failed to setup pipeline service: %w", err)
|
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 {
|
if err := AdminUser(ctx, config, userCtrl); err != nil {
|
||||||
return fmt.Errorf("failed to setup admin user: %w", err)
|
return fmt.Errorf("failed to setup admin user: %w", err)
|
||||||
@ -196,6 +210,36 @@ func PipelineService(
|
|||||||
return nil
|
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(
|
func createServicePrincipal(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
serviceCtrl *service.Controller,
|
serviceCtrl *service.Controller,
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/harness/gitness/app/gitspace/orchestrator/ide"
|
"github.com/harness/gitness/app/gitspace/orchestrator/ide"
|
||||||
|
"github.com/harness/gitness/app/gitspace/scm"
|
||||||
"github.com/harness/gitness/infraprovider"
|
"github.com/harness/gitness/infraprovider"
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
)
|
)
|
||||||
@ -29,9 +30,8 @@ type Orchestrator interface {
|
|||||||
CreateAndStartGitspace(
|
CreateAndStartGitspace(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
gitspaceConfig *types.GitspaceConfig,
|
gitspaceConfig *types.GitspaceConfig,
|
||||||
devcontainerConfig *types.DevcontainerConfig,
|
|
||||||
infra *infraprovider.Infrastructure,
|
infra *infraprovider.Infrastructure,
|
||||||
repoName string,
|
resolvedDetails *scm.ResolvedDetails,
|
||||||
defaultBaseImage string,
|
defaultBaseImage string,
|
||||||
ideService ide.IDE,
|
ideService ide.IDE,
|
||||||
) (*StartResponse, error)
|
) (*StartResponse, error)
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/harness/gitness/app/gitspace/orchestrator/devcontainer"
|
"github.com/harness/gitness/app/gitspace/orchestrator/devcontainer"
|
||||||
"github.com/harness/gitness/app/gitspace/orchestrator/ide"
|
"github.com/harness/gitness/app/gitspace/orchestrator/ide"
|
||||||
"github.com/harness/gitness/app/gitspace/orchestrator/template"
|
"github.com/harness/gitness/app/gitspace/orchestrator/template"
|
||||||
|
"github.com/harness/gitness/app/gitspace/scm"
|
||||||
"github.com/harness/gitness/infraprovider"
|
"github.com/harness/gitness/infraprovider"
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
@ -46,6 +47,9 @@ const (
|
|||||||
containerStateRemoved = "removed"
|
containerStateRemoved = "removed"
|
||||||
containerStateStopped = "exited"
|
containerStateStopped = "exited"
|
||||||
templateCloneGit = "clone_git.sh"
|
templateCloneGit = "clone_git.sh"
|
||||||
|
templateAuthenticateGit = "authenticate_git.sh"
|
||||||
|
templateManageUser = "manage_user.sh"
|
||||||
|
harnessUser = "harness"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EmbeddedDockerOrchestrator struct {
|
type EmbeddedDockerOrchestrator struct {
|
||||||
@ -70,9 +74,8 @@ func NewEmbeddedDockerOrchestrator(
|
|||||||
func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
|
func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
gitspaceConfig *types.GitspaceConfig,
|
gitspaceConfig *types.GitspaceConfig,
|
||||||
devcontainerConfig *types.DevcontainerConfig,
|
|
||||||
infra *infraprovider.Infrastructure,
|
infra *infraprovider.Infrastructure,
|
||||||
repoName string,
|
resolvedRepoDetails *scm.ResolvedDetails,
|
||||||
defaultBaseImage string,
|
defaultBaseImage string,
|
||||||
ideService ide.IDE,
|
ideService ide.IDE,
|
||||||
) (*StartResponse, error) {
|
) (*StartResponse, error) {
|
||||||
@ -122,12 +125,18 @@ func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
|
|||||||
return nil, startErr
|
return nil, startErr
|
||||||
}
|
}
|
||||||
|
|
||||||
devcontainer := &devcontainer.Devcontainer{
|
devcontainer := &devcontainer.Exec{
|
||||||
ContainerName: containerName,
|
ContainerName: containerName,
|
||||||
WorkingDir: e.getWorkingDir(repoName),
|
WorkingDir: e.getWorkingDir(resolvedRepoDetails.RepoName),
|
||||||
DockerClient: dockerClient,
|
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)
|
err = e.runIDE(ctx, devcontainer, ideService, logStreamInstance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -154,13 +163,13 @@ func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace(
|
|||||||
startErr := e.startGitspace(
|
startErr := e.startGitspace(
|
||||||
ctx,
|
ctx,
|
||||||
gitspaceConfig,
|
gitspaceConfig,
|
||||||
devcontainerConfig,
|
|
||||||
containerName,
|
containerName,
|
||||||
dockerClient,
|
dockerClient,
|
||||||
ideService,
|
ideService,
|
||||||
logStreamInstance,
|
logStreamInstance,
|
||||||
infra.Storage,
|
infra.Storage,
|
||||||
e.getWorkingDir(repoName),
|
e.getWorkingDir(resolvedRepoDetails.RepoName),
|
||||||
|
resolvedRepoDetails,
|
||||||
infra.PortMappings,
|
infra.PortMappings,
|
||||||
defaultBaseImage,
|
defaultBaseImage,
|
||||||
)
|
)
|
||||||
@ -194,17 +203,17 @@ func (e *EmbeddedDockerOrchestrator) getWorkingDir(repoName string) string {
|
|||||||
func (e *EmbeddedDockerOrchestrator) startGitspace(
|
func (e *EmbeddedDockerOrchestrator) startGitspace(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
gitspaceConfig *types.GitspaceConfig,
|
gitspaceConfig *types.GitspaceConfig,
|
||||||
devcontainerConfig *types.DevcontainerConfig,
|
|
||||||
containerName string,
|
containerName string,
|
||||||
dockerClient *client.Client,
|
dockerClient *client.Client,
|
||||||
ideService ide.IDE,
|
ideService ide.IDE,
|
||||||
logStreamInstance *logutil.LogStreamInstance,
|
logStreamInstance *logutil.LogStreamInstance,
|
||||||
volumeName string,
|
volumeName string,
|
||||||
workingDirectory string,
|
workingDirectory string,
|
||||||
|
resolvedRepoDetails *scm.ResolvedDetails,
|
||||||
portMappings map[int]*infraprovider.PortMapping,
|
portMappings map[int]*infraprovider.PortMapping,
|
||||||
defaultBaseImage string,
|
defaultBaseImage string,
|
||||||
) error {
|
) error {
|
||||||
var imageName = devcontainerConfig.Image
|
var imageName = resolvedRepoDetails.DevcontainerConfig.Image
|
||||||
if imageName == "" {
|
if imageName == "" {
|
||||||
imageName = defaultBaseImage
|
imageName = defaultBaseImage
|
||||||
}
|
}
|
||||||
@ -233,12 +242,17 @@ func (e *EmbeddedDockerOrchestrator) startGitspace(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var devcontainer = &devcontainer.Devcontainer{
|
var devcontainer = &devcontainer.Exec{
|
||||||
ContainerName: containerName,
|
ContainerName: containerName,
|
||||||
DockerClient: dockerClient,
|
DockerClient: dockerClient,
|
||||||
WorkingDir: workingDirectory,
|
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)
|
err = e.setupIDE(ctx, gitspaceConfig.GitspaceInstance, devcontainer, ideService, logStreamInstance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -249,12 +263,12 @@ func (e *EmbeddedDockerOrchestrator) startGitspace(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = e.cloneCode(ctx, gitspaceConfig, devcontainer, defaultBaseImage, logStreamInstance)
|
err = e.cloneCode(ctx, devcontainer, defaultBaseImage, logStreamInstance, resolvedRepoDetails)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = e.executePostCreateCommand(ctx, devcontainerConfig, devcontainer, logStreamInstance)
|
err = e.executePostCreateCommand(ctx, resolvedRepoDetails.DevcontainerConfig, devcontainer, logStreamInstance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -266,7 +280,7 @@ func (e *EmbeddedDockerOrchestrator) startGitspace(
|
|||||||
|
|
||||||
func (e *EmbeddedDockerOrchestrator) runIDE(
|
func (e *EmbeddedDockerOrchestrator) runIDE(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
devcontainer *devcontainer.Devcontainer,
|
devcontainer *devcontainer.Exec,
|
||||||
ideService ide.IDE,
|
ideService ide.IDE,
|
||||||
logStreamInstance *logutil.LogStreamInstance,
|
logStreamInstance *logutil.LogStreamInstance,
|
||||||
) error {
|
) error {
|
||||||
@ -304,7 +318,7 @@ func (e *EmbeddedDockerOrchestrator) runIDE(
|
|||||||
func (e *EmbeddedDockerOrchestrator) setupIDE(
|
func (e *EmbeddedDockerOrchestrator) setupIDE(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
gitspaceInstance *types.GitspaceInstance,
|
gitspaceInstance *types.GitspaceInstance,
|
||||||
devcontainer *devcontainer.Devcontainer,
|
devcontainer *devcontainer.Exec,
|
||||||
ideService ide.IDE,
|
ideService ide.IDE,
|
||||||
logStreamInstance *logutil.LogStreamInstance,
|
logStreamInstance *logutil.LogStreamInstance,
|
||||||
) error {
|
) error {
|
||||||
@ -366,30 +380,103 @@ func (e *EmbeddedDockerOrchestrator) getContainerInfo(
|
|||||||
return inspectResp.ID, usedPorts, nil
|
return inspectResp.ID, usedPorts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EmbeddedDockerOrchestrator) cloneCode(
|
func (e *EmbeddedDockerOrchestrator) authenticateGit(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
gitspaceConfig *types.GitspaceConfig,
|
devcontainer *devcontainer.Exec,
|
||||||
devcontainer *devcontainer.Devcontainer,
|
resolvedRepoDetails *scm.ResolvedDetails,
|
||||||
defaultBaseImage string,
|
|
||||||
logStreamInstance *logutil.LogStreamInstance,
|
|
||||||
) error {
|
) error {
|
||||||
gitCloneScript, err := template.GenerateScriptFromTemplate(
|
data := &template.AuthenticateGitPayload{
|
||||||
templateCloneGit, &template.CloneGitPayload{
|
Password: resolvedRepoDetails.Credentials.Password,
|
||||||
RepoURL: gitspaceConfig.CodeRepoURL,
|
}
|
||||||
Image: defaultBaseImage,
|
gitAuthenticateScript, err := template.GenerateScriptFromTemplate(
|
||||||
Branch: gitspaceConfig.Branch,
|
templateAuthenticateGit, data)
|
||||||
})
|
|
||||||
if err != nil {
|
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(
|
loggingErr := logStreamInstance.Write(
|
||||||
"Cloning git repo inside container: " + gitspaceConfig.CodeRepoURL + " branch: " + gitspaceConfig.Branch)
|
"creating user inside container: " + data.Username)
|
||||||
if loggingErr != nil {
|
if loggingErr != nil {
|
||||||
return fmt.Errorf("logging error: %w", loggingErr)
|
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 {
|
if err != nil {
|
||||||
loggingErr = logStreamInstance.Write("Error while cloning git repo inside container: " + err.Error())
|
loggingErr = logStreamInstance.Write("Error while cloning git repo inside container: " + err.Error())
|
||||||
|
|
||||||
@ -418,7 +505,7 @@ func (e *EmbeddedDockerOrchestrator) cloneCode(
|
|||||||
func (e *EmbeddedDockerOrchestrator) executePostCreateCommand(
|
func (e *EmbeddedDockerOrchestrator) executePostCreateCommand(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
devcontainerConfig *types.DevcontainerConfig,
|
devcontainerConfig *types.DevcontainerConfig,
|
||||||
devcontainer *devcontainer.Devcontainer,
|
devcontainer *devcontainer.Exec,
|
||||||
logStreamInstance *logutil.LogStreamInstance,
|
logStreamInstance *logutil.LogStreamInstance,
|
||||||
) error {
|
) error {
|
||||||
if devcontainerConfig.PostCreateCommand == "" {
|
if devcontainerConfig.PostCreateCommand == "" {
|
||||||
@ -435,7 +522,7 @@ func (e *EmbeddedDockerOrchestrator) executePostCreateCommand(
|
|||||||
return fmt.Errorf("logging error: %w", loggingErr)
|
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 {
|
if err != nil {
|
||||||
loggingErr = logStreamInstance.Write("Error while executing postCreate command")
|
loggingErr = logStreamInstance.Write("Error while executing postCreate command")
|
||||||
|
|
||||||
|
@ -23,32 +23,32 @@ import (
|
|||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Devcontainer struct {
|
type Exec struct {
|
||||||
ContainerName string
|
ContainerName string
|
||||||
WorkingDir string
|
WorkingDir string
|
||||||
DockerClient *client.Client
|
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}
|
cmd := []string{"/bin/sh", "-c", command}
|
||||||
|
|
||||||
execConfig := dockerTypes.ExecConfig{
|
execConfig := dockerTypes.ExecConfig{
|
||||||
User: "root",
|
User: userName,
|
||||||
AttachStdout: true,
|
AttachStdout: true,
|
||||||
AttachStderr: true,
|
AttachStderr: true,
|
||||||
Cmd: cmd,
|
Cmd: cmd,
|
||||||
Detach: detach,
|
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 {
|
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" {
|
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 {
|
if execResponse.Conn != nil {
|
||||||
|
@ -27,12 +27,12 @@ type IDE interface {
|
|||||||
// copying settings and configurations.
|
// copying settings and configurations.
|
||||||
Setup(
|
Setup(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
devcontainer *devcontainer.Devcontainer,
|
devcontainer *devcontainer.Exec,
|
||||||
gitspaceInstance *types.GitspaceInstance,
|
gitspaceInstance *types.GitspaceInstance,
|
||||||
) ([]byte, error)
|
) ([]byte, error)
|
||||||
|
|
||||||
// Run runs the IDE and supporting services.
|
// 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 provides the port which will be used by this IDE.
|
||||||
Port() int
|
Port() int
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Installing VSCode Web"
|
||||||
|
|
||||||
|
curl -fsSL https://code-server.dev/install.sh | sh
|
@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo "Running VSCode Web"
|
|
||||||
|
|
||||||
code-server
|
|
@ -43,7 +43,7 @@ func NewVsCodeService() *VSCode {
|
|||||||
// Setup installs the SSH server inside the container.
|
// Setup installs the SSH server inside the container.
|
||||||
func (v *VSCode) Setup(
|
func (v *VSCode) Setup(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
devcontainer *devcontainer.Devcontainer,
|
devcontainer *devcontainer.Exec,
|
||||||
gitspaceInstance *types.GitspaceInstance,
|
gitspaceInstance *types.GitspaceInstance,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
sshServerScript, err := template.GenerateScriptFromTemplate(
|
sshServerScript, err := template.GenerateScriptFromTemplate(
|
||||||
@ -59,7 +59,7 @@ func (v *VSCode) Setup(
|
|||||||
|
|
||||||
output := "Installing ssh-server inside container\n"
|
output := "Installing ssh-server inside container\n"
|
||||||
|
|
||||||
_, err = devcontainer.ExecuteCommand(ctx, sshServerScript, false)
|
_, err = devcontainer.ExecuteCommand(ctx, sshServerScript, false, rootUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to setup SSH serverr: %w", err)
|
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.
|
// 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 = ""
|
var output = ""
|
||||||
|
|
||||||
execOutput, err := devcontainer.ExecuteCommand(ctx, runSSHScript, false)
|
execOutput, err := devcontainer.ExecuteCommand(ctx, runSSHScript, false, rootUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to run SSH serverr: %w", err)
|
return nil, fmt.Errorf("failed to run SSH serverr: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,8 @@ import (
|
|||||||
|
|
||||||
var _ IDE = (*VSCodeWeb)(nil)
|
var _ IDE = (*VSCodeWeb)(nil)
|
||||||
|
|
||||||
//go:embed script/run_vscode_web.sh
|
//go:embed script/install_vscode_web.sh
|
||||||
var runScript string
|
var installScript string
|
||||||
|
|
||||||
//go:embed script/find_vscode_web_path.sh
|
//go:embed script/find_vscode_web_path.sh
|
||||||
var findPathScript string
|
var findPathScript string
|
||||||
@ -44,9 +44,11 @@ var findPathScript string
|
|||||||
//go:embed media/vscodeweb/*
|
//go:embed media/vscodeweb/*
|
||||||
var mediaFiles embed.FS
|
var mediaFiles embed.FS
|
||||||
|
|
||||||
const templateInstallVSCodeWeb = "install_vscode_web.sh"
|
const templateRunVSCodeWeb = "run_vscode_web.sh"
|
||||||
const startMarker = "START_MARKER"
|
const startMarker = "START_MARKER"
|
||||||
const endMarker = "END_MARKER"
|
const endMarker = "END_MARKER"
|
||||||
|
const rootUser = "root"
|
||||||
|
const harnessUser = "harness"
|
||||||
|
|
||||||
type VSCodeWebConfig struct {
|
type VSCodeWebConfig struct {
|
||||||
Port int
|
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.
|
// Setup runs the installScript which downloads the required version of the code-server binary.
|
||||||
func (v *VSCodeWeb) Setup(
|
func (v *VSCodeWeb) Setup(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
devcontainer *devcontainer.Devcontainer,
|
devcontainer *devcontainer.Exec,
|
||||||
_ *types.GitspaceInstance,
|
_ *types.GitspaceInstance,
|
||||||
) ([]byte, error) {
|
) ([]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"
|
output := "Installing VSCode Web inside container.\n"
|
||||||
|
|
||||||
_, err = devcontainer.ExecuteCommand(ctx, installScript, false)
|
_, err := devcontainer.ExecuteCommand(ctx, installScript, false, rootUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to install VSCode Web: %w", err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to find VSCode Web install path: %w", err)
|
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.
|
// 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
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to run VSCode Web: %w", err)
|
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(
|
func (v *VSCodeWeb) copyMediaToContainer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
devcontainer *devcontainer.Devcontainer,
|
devcontainer *devcontainer.Exec,
|
||||||
path string,
|
path string,
|
||||||
) error {
|
) error {
|
||||||
// Create a buffer to hold the tar data
|
// Create a buffer to hold the tar data
|
||||||
|
@ -83,16 +83,17 @@ func (o orchestrator) StartGitspace(
|
|||||||
|
|
||||||
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerStart)
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerStart)
|
||||||
|
|
||||||
repoName, devcontainerConfig, err := o.scm.RepoNameAndDevcontainerConfig(ctx, gitspaceConfig)
|
scmResolvedDetails, err := o.scm.Resolve(ctx, gitspaceConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerFailed)
|
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 {
|
if devcontainerConfig == nil {
|
||||||
log.Warn().Err(err).Msg("devcontainer config is nil, using empty config")
|
log.Warn().Err(err).Msg("devcontainer config is nil, using empty config")
|
||||||
devcontainerConfig = &types.DevcontainerConfig{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerCompleted)
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerCompleted)
|
||||||
@ -137,7 +138,7 @@ func (o orchestrator) StartGitspace(
|
|||||||
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationStart)
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationStart)
|
||||||
|
|
||||||
startResponse, err := o.containerOrchestrator.CreateAndStartGitspace(
|
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 {
|
if err != nil {
|
||||||
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationFailed)
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationFailed)
|
||||||
|
|
||||||
|
@ -37,9 +37,14 @@ type CloneGitPayload struct {
|
|||||||
RepoURL string
|
RepoURL string
|
||||||
Image string
|
Image string
|
||||||
Branch string
|
Branch string
|
||||||
|
AuthenticateGitPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstallVSCodeWebPayload struct {
|
type AuthenticateGitPayload struct {
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunVSCodeWebPayload struct {
|
||||||
Port string
|
Port string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
@ -3,10 +3,20 @@
|
|||||||
repo_url={{ .RepoURL }}
|
repo_url={{ .RepoURL }}
|
||||||
image={{ .Image }}
|
image={{ .Image }}
|
||||||
branch={{ .Branch }}
|
branch={{ .Branch }}
|
||||||
|
password={{ .Password }}
|
||||||
|
|
||||||
# Extract the repository name from the URL
|
# Extract the repository name from the URL
|
||||||
repo_name=$(basename -s .git "$repo_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
|
# Check if Git is installed
|
||||||
if ! command -v git >/dev/null 2>&1; then
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
echo "Git is not installed. Installing Git..."
|
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..."
|
echo "Git is not installed. Exiting..."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
git config --global --add safe.directory /$repo_name
|
||||||
# Clone the repository inside the working directory if it doesn't exist
|
# Clone the repository inside the working directory if it doesn't exist
|
||||||
if [ ! -d ".git" ]; then
|
if [ ! -d ".git" ]; then
|
||||||
echo "Cloning the repository..."
|
echo "Cloning the repository..."
|
||||||
git clone "$repo_url" --branch "$branch" .
|
git clone "$repo_url" --branch "$branch" /$repo_name
|
||||||
else
|
else
|
||||||
echo "Repository already exists. Skipping clone."
|
echo "Repository already exists. Skipping clone."
|
||||||
fi
|
fi
|
||||||
|
rm $HOME/.git-askpass
|
||||||
# Check if .devcontainer/devcontainer.json exists
|
# Check if .devcontainer/devcontainer.json exists
|
||||||
if [ ! -f ".devcontainer/devcontainer.json" ]; then
|
if [ ! -f ".devcontainer/devcontainer.json" ]; then
|
||||||
echo "Creating .devcontainer directory and devcontainer.json..."
|
echo "Creating .devcontainer directory and devcontainer.json..."
|
||||||
mkdir -p ".devcontainer"
|
mkdir -p /$repo_name/.devcontainer
|
||||||
cat <<EOL > ".devcontainer/devcontainer.json"
|
cat <<EOL > /$repo_name/.devcontainer/devcontainer.json
|
||||||
{
|
{
|
||||||
"image": "$image"
|
"image": "$image"
|
||||||
}
|
}
|
||||||
|
24
app/gitspace/orchestrator/template/templates/manage_user.sh
Normal file
24
app/gitspace/orchestrator/template/templates/manage_user.sh
Normal 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."
|
@ -1,17 +1,17 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
echo "Installing VSCode Web"
|
echo "Running VSCode Web"
|
||||||
|
|
||||||
curl -fsSL https://code-server.dev/install.sh | sh
|
|
||||||
|
|
||||||
port={{ .Port }}
|
port={{ .Port }}
|
||||||
|
|
||||||
# Ensure the configuration directory exists
|
# 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
|
# 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
|
bind-addr: 0.0.0.0:$port
|
||||||
auth: none
|
auth: none
|
||||||
cert: false
|
cert: false
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
code-server --disable-workspace-trust
|
@ -13,21 +13,6 @@ username={{ .Username }}
|
|||||||
password={{ .Password }}
|
password={{ .Password }}
|
||||||
workingDir={{ .WorkingDirectory }}
|
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
|
# Configure SSH to allow this user
|
||||||
config_file='/etc/ssh/sshd_config'
|
config_file='/etc/ssh/sshd_config'
|
||||||
grep -q "^AllowUsers" $config_file
|
grep -q "^AllowUsers" $config_file
|
||||||
@ -45,7 +30,4 @@ if ! grep -q "^PasswordAuthentication yes" $config_file; then
|
|||||||
echo "PasswordAuthentication yes" >> $config_file
|
echo "PasswordAuthentication yes" >> $config_file
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Changing ownership of everything inside user home to the newly created user
|
|
||||||
chown -R $username .
|
|
||||||
|
|
||||||
mkdir /var/run/sshd
|
mkdir /var/run/sshd
|
@ -26,9 +26,18 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"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/git/command"
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@ -38,23 +47,49 @@ var (
|
|||||||
ErrNoDefaultBranch = errors.New("no default branch")
|
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 {
|
type SCM interface {
|
||||||
// RepoNameAndDevcontainerConfig fetches repository name & devcontainer config file from the given repo and branch.
|
// RepoNameAndDevcontainerConfig fetches repository name & devcontainer config file from the given repo and branch.
|
||||||
RepoNameAndDevcontainerConfig(
|
Resolve(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
gitspaceConfig *types.GitspaceConfig,
|
gitspaceConfig *types.GitspaceConfig,
|
||||||
) (string, *types.DevcontainerConfig, error)
|
) (*ResolvedDetails, error)
|
||||||
|
|
||||||
// CheckValidCodeRepo checks if the current URL is a valid and accessible code repo,
|
// CheckValidCodeRepo checks if the current URL is a valid and accessible code repo,
|
||||||
// input can be connector info, user token etc.
|
// input can be connector info, user token etc.
|
||||||
CheckValidCodeRepo(ctx context.Context, request CodeRepositoryRequest) (*CodeRepositoryResponse, error)
|
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)
|
err := validateURL(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid URL, %w", err)
|
return nil, fmt.Errorf("invalid URL, %w", err)
|
||||||
@ -78,27 +113,24 @@ func (s scm) CheckValidCodeRepo(ctx context.Context, request CodeRepositoryReque
|
|||||||
return codeRepositoryResponse, nil
|
return codeRepositoryResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSCM() SCM {
|
func (s genericSCM) Resolve(
|
||||||
return &scm{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s scm) RepoNameAndDevcontainerConfig(
|
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
gitspaceConfig *types.GitspaceConfig,
|
gitspaceConfig *types.GitspaceConfig,
|
||||||
) (string, *types.DevcontainerConfig, error) {
|
) (*ResolvedDetails, error) {
|
||||||
|
resolvedDetails := &ResolvedDetails{Branch: gitspaceConfig.Branch, CloneURL: gitspaceConfig.CodeRepoURL}
|
||||||
repoURL, err := url.Parse(gitspaceConfig.CodeRepoURL)
|
repoURL, err := url.Parse(gitspaceConfig.CodeRepoURL)
|
||||||
if err != nil {
|
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")
|
repoName := strings.TrimSuffix(path.Base(repoURL.Path), ".git")
|
||||||
|
resolvedDetails.RepoName = repoName
|
||||||
gitWorkingDirectory := "/tmp/git/"
|
gitWorkingDirectory := "/tmp/git/"
|
||||||
|
|
||||||
cloneDir := gitWorkingDirectory + uuid.New().String()
|
cloneDir := gitWorkingDirectory + uuid.New().String()
|
||||||
|
|
||||||
err = os.MkdirAll(cloneDir, os.ModePerm)
|
err = os.MkdirAll(cloneDir, os.ModePerm)
|
||||||
if err != nil {
|
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() {
|
defer func() {
|
||||||
@ -109,11 +141,90 @@ func (s scm) RepoNameAndDevcontainerConfig(
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
filePath := ".devcontainer/devcontainer.json"
|
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 {
|
if err != nil {
|
||||||
return "", nil, fmt.Errorf("invalid branch or url: %w", err)
|
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("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...")
|
log.Info().Msg("Cloning the repository...")
|
||||||
cmd := command.New("clone",
|
cmd := command.New("clone",
|
||||||
command.WithFlag("--branch", gitspaceConfig.Branch),
|
command.WithFlag("--branch", gitspaceConfig.Branch),
|
||||||
@ -122,12 +233,8 @@ func (s scm) RepoNameAndDevcontainerConfig(
|
|||||||
command.WithArg(gitspaceConfig.CodeRepoURL),
|
command.WithArg(gitspaceConfig.CodeRepoURL),
|
||||||
command.WithArg(cloneDir),
|
command.WithArg(cloneDir),
|
||||||
)
|
)
|
||||||
err = cmd.Run(
|
if err := cmd.Run(ctx, command.WithDir(cloneDir)); err != nil {
|
||||||
ctx,
|
return nil, fmt.Errorf("failed to clone repository %s: %w", gitspaceConfig.CodeRepoURL, err)
|
||||||
command.WithDir(cloneDir),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, fmt.Errorf("failed to clone repository %s: %w", gitspaceConfig.CodeRepoURL, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var lsTreeOutput bytes.Buffer
|
var lsTreeOutput bytes.Buffer
|
||||||
@ -135,19 +242,14 @@ func (s scm) RepoNameAndDevcontainerConfig(
|
|||||||
command.WithArg("HEAD"),
|
command.WithArg("HEAD"),
|
||||||
command.WithArg(filePath),
|
command.WithArg(filePath),
|
||||||
)
|
)
|
||||||
err = lsTreeCmd.Run(
|
|
||||||
ctx,
|
if err := lsTreeCmd.Run(ctx, command.WithDir(cloneDir), command.WithStdout(&lsTreeOutput)); err != nil {
|
||||||
command.WithDir(cloneDir),
|
return nil, fmt.Errorf("failed to list files in repository %s: %w", cloneDir, err)
|
||||||
command.WithStdout(&lsTreeOutput),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, fmt.Errorf("failed to list files in repository %s: %w", cloneDir, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if lsTreeOutput.Len() == 0 {
|
if lsTreeOutput.Len() == 0 {
|
||||||
log.Info().Msg("File not found, returning empty devcontainerConfig")
|
log.Info().Msg("File not found, returning empty devcontainerConfig")
|
||||||
emptyConfig := &types.DevcontainerConfig{}
|
return nil, nil
|
||||||
return repoName, emptyConfig, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fields := strings.Fields(lsTreeOutput.String())
|
fields := strings.Fields(lsTreeOutput.String())
|
||||||
@ -155,25 +257,16 @@ func (s scm) RepoNameAndDevcontainerConfig(
|
|||||||
|
|
||||||
var catFileOutput bytes.Buffer
|
var catFileOutput bytes.Buffer
|
||||||
catFileCmd := command.New("cat-file", command.WithFlag("-p"), command.WithArg(blobSHA))
|
catFileCmd := command.New("cat-file", command.WithFlag("-p"), command.WithArg(blobSHA))
|
||||||
err = catFileCmd.Run(
|
err := catFileCmd.Run(
|
||||||
ctx,
|
ctx,
|
||||||
command.WithDir(cloneDir),
|
command.WithDir(cloneDir),
|
||||||
command.WithStderr(io.Discard),
|
command.WithStderr(io.Discard),
|
||||||
command.WithStdout(&catFileOutput),
|
command.WithStdout(&catFileOutput),
|
||||||
)
|
)
|
||||||
if err != nil {
|
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)
|
||||||
}
|
}
|
||||||
|
return catFileOutput.Bytes(), nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeComments(input []byte) []byte {
|
func removeComments(input []byte) []byte {
|
||||||
@ -209,7 +302,49 @@ func validateURL(request CodeRepositoryRequest) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateArgs(_ *types.GitspaceConfig) error {
|
func findUserFromUID(ctx context.Context,
|
||||||
// TODO Validate the args
|
principalStore store.PrincipalStore, userUID string,
|
||||||
return nil
|
) (*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
33
app/gitspace/scm/types.go
Normal 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
|
||||||
|
}
|
||||||
|
)
|
@ -14,13 +14,24 @@
|
|||||||
|
|
||||||
package scm
|
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.
|
// WireSet provides a wire set for this package.
|
||||||
var WireSet = wire.NewSet(
|
var WireSet = wire.NewSet(
|
||||||
ProvideSCM,
|
ProvideSCM,
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProvideSCM() SCM {
|
func ProvideSCM(repoStore store.RepoStore,
|
||||||
return NewSCM()
|
rpcClient git.Interface,
|
||||||
|
tokenStore store.TokenStore,
|
||||||
|
principalStore store.PrincipalStore,
|
||||||
|
urlProvider urlprovider.Provider,
|
||||||
|
) SCM {
|
||||||
|
return NewSCM(repoStore, rpcClient, tokenStore, principalStore, urlProvider)
|
||||||
}
|
}
|
||||||
|
@ -343,7 +343,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
scmSCM := scm.ProvideSCM()
|
scmSCM := scm.ProvideSCM(repoStore, gitInterface, tokenStore, principalStore, provider)
|
||||||
infraProvisioner := infrastructure.ProvideInfraProvisionerService(infraProviderConfigStore, infraProviderResourceStore, factory)
|
infraProvisioner := infrastructure.ProvideInfraProvisionerService(infraProviderConfigStore, infraProviderResourceStore, factory)
|
||||||
statefulLogger := logutil.ProvideStatefulLogger(logStream)
|
statefulLogger := logutil.ProvideStatefulLogger(logStream)
|
||||||
containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, statefulLogger)
|
containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, statefulLogger)
|
||||||
@ -353,7 +353,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||||||
vsCodeWeb := ide.ProvideVSCodeWebService(vsCodeWebConfig)
|
vsCodeWeb := ide.ProvideVSCodeWebService(vsCodeWebConfig)
|
||||||
orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter3, orchestratorConfig, vsCode, vsCodeWeb)
|
orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter3, orchestratorConfig, vsCode, vsCodeWeb)
|
||||||
gitspaceEventStore := database.ProvideGitspaceEventStore(db)
|
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)
|
migrateController := migrate.ProvideController(authorizer, principalStore)
|
||||||
openapiService := openapi.ProvideOpenAPIService()
|
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)
|
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)
|
||||||
|
@ -253,6 +253,14 @@ type Config struct {
|
|||||||
DisplayName string `envconfig:"GITNESS_PRINCIPAL_PIPELINE_DISPLAY_NAME" default:"Gitness Pipeline"`
|
DisplayName string `envconfig:"GITNESS_PRINCIPAL_PIPELINE_DISPLAY_NAME" default:"Gitness Pipeline"`
|
||||||
Email string `envconfig:"GITNESS_PRINCIPAL_PIPELINE_EMAIL" default:"pipeline@gitness.io"`
|
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.
|
// 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.
|
// NOTE: The admin user is only auto-created in case a password and an email is provided.
|
||||||
Admin struct {
|
Admin struct {
|
||||||
|
@ -19,12 +19,14 @@ type GitspaceCodeRepoType string
|
|||||||
func (GitspaceCodeRepoType) Enum() []interface{} { return toInterfaceSlice(codeRepoTypes) }
|
func (GitspaceCodeRepoType) Enum() []interface{} { return toInterfaceSlice(codeRepoTypes) }
|
||||||
|
|
||||||
var codeRepoTypes = []GitspaceCodeRepoType{
|
var codeRepoTypes = []GitspaceCodeRepoType{
|
||||||
CodeRepoTypeGithub, CodeRepoTypeGitlab, CodeRepoTypeHarnessCode, CodeRepoTypeBitbucket, CodeRepoTypeUnknown,
|
CodeRepoTypeGithub, CodeRepoTypeGitlab, CodeRepoTypeHarnessCode,
|
||||||
|
CodeRepoTypeBitbucket, CodeRepoTypeUnknown, CodeRepoTypeGitness,
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CodeRepoTypeGithub GitspaceCodeRepoType = "github"
|
CodeRepoTypeGithub GitspaceCodeRepoType = "github"
|
||||||
CodeRepoTypeGitlab GitspaceCodeRepoType = "gitlab"
|
CodeRepoTypeGitlab GitspaceCodeRepoType = "gitlab"
|
||||||
|
CodeRepoTypeGitness GitspaceCodeRepoType = "gitness"
|
||||||
CodeRepoTypeHarnessCode GitspaceCodeRepoType = "harness_code"
|
CodeRepoTypeHarnessCode GitspaceCodeRepoType = "harness_code"
|
||||||
CodeRepoTypeBitbucket GitspaceCodeRepoType = "bitbucket"
|
CodeRepoTypeBitbucket GitspaceCodeRepoType = "bitbucket"
|
||||||
CodeRepoTypeUnknown GitspaceCodeRepoType = "unknown"
|
CodeRepoTypeUnknown GitspaceCodeRepoType = "unknown"
|
||||||
|
@ -27,7 +27,7 @@ type GitspaceConfig struct {
|
|||||||
InfraProviderResourceID int64 `json:"-"`
|
InfraProviderResourceID int64 `json:"-"`
|
||||||
InfraProviderResourceIdentifier string `json:"resource_identifier"`
|
InfraProviderResourceIdentifier string `json:"resource_identifier"`
|
||||||
CodeRepoURL string `json:"code_repo_url"`
|
CodeRepoURL string `json:"code_repo_url"`
|
||||||
CodeRepoType enum.GitspaceCodeRepoType `json:"-"`
|
CodeRepoType enum.GitspaceCodeRepoType `json:"code_repo_type"`
|
||||||
Branch string `json:"branch"`
|
Branch string `json:"branch"`
|
||||||
DevcontainerPath *string `json:"devcontainer_path,omitempty"`
|
DevcontainerPath *string `json:"devcontainer_path,omitempty"`
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
|
Loading…
Reference in New Issue
Block a user