mirror of
https://github.com/harness/drone.git
synced 2025-05-04 18:30:59 +08:00

* feat: [CDE-140]: Fixing ProvideGitspaceContainerOrchestratorConfig. * feat: [CDE-140]: Fixing ProvideGitspaceContainerOrchestratorConfig. * feat: [CDE-140]: Addressing review comments. * feat: [CDE-140]: Reverting changes done for docker config default values. Adding default value of GITNESS_GITSPACE_DEFAULT_BIND_MOUNT_SOURCE_BASE_PATH and GITNESS_DOCKER_HOST to Dockerfile. Adding new column in gitspace_events table for storing event timestamp in nanoseconds and corresponding migrations. * Lingting. * feat: [CDE-140]: Adding docker machine host name config to identify the public host name on which the docker host is running. Updated the docker config to read from the default docker env variables if config values are not set. Updated the events timestamp to use nanoseconds. Fixed the working directory response and cleaned the logic of how the gitspace instance url is generated. Updated the scm service to parse repo name in a provider agnostic manner.
299 lines
10 KiB
Go
299 lines
10 KiB
Go
// 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 orchestrator
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
events "github.com/harness/gitness/app/events/gitspace"
|
|
"github.com/harness/gitness/app/gitspace/infrastructure"
|
|
"github.com/harness/gitness/app/gitspace/orchestrator/container"
|
|
"github.com/harness/gitness/app/gitspace/scm"
|
|
"github.com/harness/gitness/app/store"
|
|
"github.com/harness/gitness/types"
|
|
"github.com/harness/gitness/types/enum"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type orchestrator struct {
|
|
scm scm.SCM
|
|
infraProviderResourceStore store.InfraProviderResourceStore
|
|
infraProvisioner infrastructure.InfraProvisioner
|
|
containerOrchestrator container.Orchestrator
|
|
eventReporter *events.Reporter
|
|
}
|
|
|
|
var _ Orchestrator = (*orchestrator)(nil)
|
|
|
|
func NewOrchestrator(
|
|
scm scm.SCM,
|
|
infraProviderResourceStore store.InfraProviderResourceStore,
|
|
infraProvisioner infrastructure.InfraProvisioner,
|
|
containerOrchestrator container.Orchestrator,
|
|
eventReporter *events.Reporter,
|
|
) Orchestrator {
|
|
return orchestrator{
|
|
scm: scm,
|
|
infraProviderResourceStore: infraProviderResourceStore,
|
|
infraProvisioner: infraProvisioner,
|
|
containerOrchestrator: containerOrchestrator,
|
|
eventReporter: eventReporter,
|
|
}
|
|
}
|
|
|
|
func (o orchestrator) StartGitspace(
|
|
ctx context.Context,
|
|
gitspaceConfig *types.GitspaceConfig,
|
|
) (*types.GitspaceInstance, error) {
|
|
gitspaceInstance := gitspaceConfig.GitspaceInstance
|
|
gitspaceInstance.State = enum.GitspaceInstanceStateError
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerStart)
|
|
|
|
repoName, devcontainerConfig, err := o.scm.RepoNameAndDevcontainerConfig(ctx, gitspaceConfig)
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerFailed)
|
|
|
|
return gitspaceInstance,
|
|
fmt.Errorf("failed to fetch code repo details for gitspace config ID %d", gitspaceConfig.ID)
|
|
}
|
|
|
|
if devcontainerConfig == nil {
|
|
log.Warn().Err(err).Msg("devcontainer config is nil, using empty config")
|
|
devcontainerConfig = &types.DevcontainerConfig{}
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerCompleted)
|
|
|
|
infraProviderResource, err := o.infraProviderResourceStore.Find(ctx, gitspaceConfig.InfraProviderResourceID)
|
|
if err != nil {
|
|
return gitspaceInstance, fmt.Errorf("cannot get the infraprovider resource for ID %d: %w",
|
|
gitspaceConfig.InfraProviderResourceID, err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraProvisioningStart)
|
|
|
|
infra, err := o.infraProvisioner.Provision(ctx, infraProviderResource, gitspaceConfig)
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraProvisioningFailed)
|
|
|
|
return gitspaceInstance, fmt.Errorf(
|
|
"cannot provision infrastructure for ID %d: %w", gitspaceConfig.InfraProviderResourceID, err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraProvisioningCompleted)
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectStart)
|
|
|
|
err = o.containerOrchestrator.Status(ctx, infra)
|
|
gitspaceInstance.State = enum.GitspaceInstanceStateError
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectFailed)
|
|
|
|
return gitspaceInstance, fmt.Errorf("couldn't call the agent health API: %w", err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectCompleted)
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationStart)
|
|
|
|
startResponse, err := o.containerOrchestrator.StartGitspace(ctx, gitspaceConfig, devcontainerConfig, infra)
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationFailed)
|
|
|
|
return gitspaceInstance, fmt.Errorf("couldn't call the agent start API: %w", err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationCompleted)
|
|
|
|
port := startResponse.PortsUsed[gitspaceConfig.IDE]
|
|
|
|
var ideURL url.URL
|
|
|
|
if gitspaceConfig.IDE == enum.IDETypeVSCodeWeb {
|
|
ideURL = url.URL{
|
|
Scheme: "http",
|
|
Host: infra.Host + ":" + port,
|
|
RawQuery: filepath.Join("folder=", startResponse.WorkingDirectory, repoName),
|
|
}
|
|
} else if gitspaceConfig.IDE == enum.IDETypeVSCode {
|
|
// TODO: the following userID is hard coded and should be changed.
|
|
userID := "harness"
|
|
ideURL = url.URL{
|
|
Scheme: "vscode-remote",
|
|
Host: "", // Empty since we include the host and port in the path
|
|
Path: fmt.Sprintf(
|
|
"ssh-remote+%s@%s:%s",
|
|
userID,
|
|
infra.Host,
|
|
filepath.Join(port, startResponse.WorkingDirectory, repoName),
|
|
),
|
|
}
|
|
}
|
|
ideURLString := ideURL.String()
|
|
gitspaceInstance.URL = &ideURLString
|
|
|
|
gitspaceInstance.LastUsed = time.Now().UnixMilli()
|
|
gitspaceInstance.State = enum.GitspaceInstanceStateRunning
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStartCompleted)
|
|
|
|
return gitspaceInstance, nil
|
|
}
|
|
|
|
func (o orchestrator) StopGitspace(
|
|
ctx context.Context,
|
|
gitspaceConfig *types.GitspaceConfig,
|
|
) (*types.GitspaceInstance, error) {
|
|
gitspaceInstance := gitspaceConfig.GitspaceInstance
|
|
gitspaceInstance.State = enum.GitspaceInstanceStateError
|
|
|
|
infraProviderResource, err := o.infraProviderResourceStore.Find(ctx, gitspaceConfig.InfraProviderResourceID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"cannot get the infraProviderResource with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err)
|
|
}
|
|
|
|
infra, err := o.infraProvisioner.Find(ctx, infraProviderResource, gitspaceConfig)
|
|
if err != nil {
|
|
return gitspaceInstance, fmt.Errorf("cannot find the provisioned infra: %w", err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectStart)
|
|
|
|
err = o.containerOrchestrator.Status(ctx, infra)
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectFailed)
|
|
|
|
return gitspaceConfig.GitspaceInstance, fmt.Errorf("couldn't call the agent health API: %w", err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectCompleted)
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceDeletionStart)
|
|
|
|
err = o.containerOrchestrator.StopGitspace(ctx, gitspaceConfig, infra)
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceDeletionFailed)
|
|
|
|
return gitspaceInstance, fmt.Errorf("error stopping the Gitspace container: %w", err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceDeletionCompleted)
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraUnprovisioningStart)
|
|
|
|
_, err = o.infraProvisioner.Stop(ctx, infraProviderResource, gitspaceConfig)
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraUnprovisioningFailed)
|
|
|
|
return gitspaceInstance, fmt.Errorf(
|
|
"cannot stop provisioned infrastructure with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraUnprovisioningCompleted)
|
|
|
|
gitspaceInstance.State = enum.GitspaceInstanceStateDeleted
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStopCompleted)
|
|
|
|
return gitspaceInstance, err
|
|
}
|
|
|
|
func (o orchestrator) DeleteGitspace(
|
|
ctx context.Context,
|
|
gitspaceConfig *types.GitspaceConfig,
|
|
) (*types.GitspaceInstance, error) {
|
|
gitspaceInstance := gitspaceConfig.GitspaceInstance
|
|
currentState := gitspaceInstance.State
|
|
gitspaceInstance.State = enum.GitspaceInstanceStateError
|
|
|
|
infraProviderResource, err := o.infraProviderResourceStore.Find(ctx, gitspaceConfig.InfraProviderResourceID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"cannot get the infraProviderResource with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err)
|
|
}
|
|
|
|
if currentState == enum.GitspaceInstanceStateRunning ||
|
|
currentState == enum.GitspaceInstanceStateUnknown {
|
|
infra, err := o.infraProvisioner.Find(ctx, infraProviderResource, gitspaceConfig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot find the provisioned infra: %w", err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectStart)
|
|
|
|
err = o.containerOrchestrator.Status(ctx, infra)
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectFailed)
|
|
|
|
return gitspaceConfig.GitspaceInstance, fmt.Errorf("couldn't call the agent health API: %w", err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectCompleted)
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceDeletionStart)
|
|
|
|
err = o.containerOrchestrator.StopGitspace(ctx, gitspaceConfig, infra)
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceDeletionFailed)
|
|
|
|
return nil, fmt.Errorf("error stopping the Gitspace container: %w", err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceDeletionCompleted)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraUnprovisioningStart)
|
|
|
|
_, err = o.infraProvisioner.Unprovision(ctx, infraProviderResource, gitspaceConfig)
|
|
if err != nil {
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraUnprovisioningFailed)
|
|
|
|
return nil, fmt.Errorf(
|
|
"cannot stop provisioned infrastructure with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err)
|
|
}
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraUnprovisioningCompleted)
|
|
|
|
gitspaceInstance.State = enum.GitspaceInstanceStateDeleted
|
|
|
|
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStopCompleted)
|
|
|
|
return gitspaceInstance, nil
|
|
}
|
|
|
|
func (o orchestrator) emitGitspaceEvent(
|
|
ctx context.Context,
|
|
config *types.GitspaceConfig,
|
|
eventType enum.GitspaceEventType,
|
|
) {
|
|
o.eventReporter.EmitGitspaceEvent(
|
|
ctx,
|
|
events.GitspaceEvent,
|
|
&events.GitspaceEventPayload{
|
|
QueryKey: config.Identifier,
|
|
EntityID: config.GitspaceInstance.ID,
|
|
EntityType: enum.GitspaceEntityTypeGitspaceInstance,
|
|
EventType: eventType,
|
|
Timestamp: time.Now().UnixNano(),
|
|
})
|
|
}
|