diff --git a/app/api/controller/gitspace/action.go b/app/api/controller/gitspace/action.go index 295ca3bb2..b4aacb509 100644 --- a/app/api/controller/gitspace/action.go +++ b/app/api/controller/gitspace/action.go @@ -34,7 +34,7 @@ import ( const defaultAccessKey = "Harness@123" const defaultMachineUser = "harness" -const gitspaceTimedOutInMintues = 10 +const gitspaceTimedOutInMintues = 5 type ActionInput struct { Action enum.GitspaceActionType `json:"action"` @@ -135,21 +135,28 @@ func (c *Controller) startGitspaceAction( func (c *Controller) asyncOperation( ctxWithTimedOut context.Context, - config *types.GitspaceConfig, + config types.GitspaceConfig, action enum.GitspaceActionType, + stateChannel chan enum.GitspaceInstanceStateType, errChannel chan error, ) { + defer close(stateChannel) + defer close(errChannel) + var orchestrateErr error + var instanceState enum.GitspaceInstanceStateType + switch action { case enum.GitspaceActionTypeStart: - orchestrateErr = c.orchestrator.StartGitspace(ctxWithTimedOut, config) + instanceState, orchestrateErr = c.orchestrator.TriggerStartGitspace(ctxWithTimedOut, config) case enum.GitspaceActionTypeStop: - orchestrateErr = c.orchestrator.StopGitspace(ctxWithTimedOut, config) + instanceState, orchestrateErr = c.orchestrator.TriggerStopGitspace(ctxWithTimedOut, config) } + if orchestrateErr != nil { - errChannel <- fmt.Errorf("failed to find start/stop gitspace : %s %w", config.Identifier, orchestrateErr) + errChannel <- fmt.Errorf("failed to start/stop gitspace: %s %w", config.Identifier, orchestrateErr) } - close(errChannel) + stateChannel <- instanceState } func (c *Controller) submitAsyncOps( @@ -157,21 +164,25 @@ func (c *Controller) submitAsyncOps( config *types.GitspaceConfig, action enum.GitspaceActionType, ) { + errChannel := make(chan error) + stateChannel := make(chan enum.GitspaceInstanceStateType) + submitCtx := context.WithoutCancel(ctx) ttlExecuteContext, cancel := context.WithTimeout(submitCtx, gitspaceTimedOutInMintues*time.Minute) - // submit an async task with a TTL - errChannel := make(chan error) - go c.asyncOperation(ttlExecuteContext, config, action, errChannel) - // wait execution completion for the specified time or mark it as an error + + go c.asyncOperation(ttlExecuteContext, *config, action, stateChannel, errChannel) + var err error + var instanceState enum.GitspaceInstanceStateType + go func() { - defer c.updateGitspaceInstance(submitCtx, config) select { case <-ttlExecuteContext.Done(): if ttlExecuteContext.Err() != nil { err = ttlExecuteContext.Err() } case err = <-errChannel: + case instanceState = <-stateChannel: } if err != nil { log.Err(err).Msgf("error during async execution for %s", config.GitspaceInstance.Identifier) @@ -182,6 +193,15 @@ func (c *Controller) submitAsyncOps( c.emitGitspaceConfigEvent(ttlExecuteContext, config, enum.GitspaceEventTypeGitspaceActionStopFailed) } } + + if instanceState == "" { + instanceState = enum.GitspaceInstanceStateError + } + + config.GitspaceInstance.State = instanceState + + c.updateGitspaceInstance(submitCtx, config.GitspaceInstance) + cancel() }() } @@ -286,11 +306,11 @@ func (c *Controller) emitGitspaceConfigEvent( func (c *Controller) updateGitspaceInstance( ctx context.Context, - config *types.GitspaceConfig, + instance *types.GitspaceInstance, ) { - err := c.gitspaceInstanceStore.Update(ctx, config.GitspaceInstance) + err := c.gitspaceInstanceStore.Update(ctx, instance) if err != nil { log.Err(err).Msgf( - "failed to update gitspace instance during exec %q", config.GitspaceInstance.Identifier) + "failed to update gitspace instance during exec %q", instance.Identifier) } } diff --git a/app/api/controller/gitspace/controller.go b/app/api/controller/gitspace/controller.go index b78b9f781..595758eb3 100644 --- a/app/api/controller/gitspace/controller.go +++ b/app/api/controller/gitspace/controller.go @@ -20,6 +20,7 @@ import ( "github.com/harness/gitness/app/gitspace/logutil" "github.com/harness/gitness/app/gitspace/orchestrator" "github.com/harness/gitness/app/gitspace/scm" + "github.com/harness/gitness/app/services/gitspace" "github.com/harness/gitness/app/services/infraprovider" "github.com/harness/gitness/app/store" "github.com/harness/gitness/store/database/dbtx" @@ -38,6 +39,7 @@ type Controller struct { statefulLogger *logutil.StatefulLogger scm scm.SCM repoStore store.RepoStore + gitspaceSvc *gitspace.Service } func NewController( @@ -53,6 +55,7 @@ func NewController( statefulLogger *logutil.StatefulLogger, scm scm.SCM, repoStore store.RepoStore, + gitspaceSvc *gitspace.Service, ) *Controller { return &Controller{ tx: tx, @@ -67,5 +70,6 @@ func NewController( statefulLogger: statefulLogger, scm: scm, repoStore: repoStore, + gitspaceSvc: gitspaceSvc, } } diff --git a/app/api/controller/gitspace/create.go b/app/api/controller/gitspace/create.go index 3de4982bd..70b16902c 100644 --- a/app/api/controller/gitspace/create.go +++ b/app/api/controller/gitspace/create.go @@ -24,7 +24,6 @@ import ( apiauth "github.com/harness/gitness/app/api/auth" "github.com/harness/gitness/app/api/usererror" "github.com/harness/gitness/app/auth" - infraproviderenum "github.com/harness/gitness/infraprovider/enum" "github.com/harness/gitness/types" "github.com/harness/gitness/types/check" "github.com/harness/gitness/types/enum" @@ -189,7 +188,7 @@ func (c *Controller) autoCreateDefaultResource(ctx context.Context, parentSpace infraProviderConfig := &types.InfraProviderConfig{ Identifier: defaultResourceIdentifier, Name: "default docker infrastructure", - Type: infraproviderenum.InfraProviderTypeDocker, + Type: enum.InfraProviderTypeDocker, SpaceID: parentSpace.ID, SpacePath: parentSpace.Path, Created: now, @@ -199,7 +198,7 @@ func (c *Controller) autoCreateDefaultResource(ctx context.Context, parentSpace Identifier: defaultResourceIdentifier, Name: "Standard Docker Resource", InfraProviderConfigIdentifier: infraProviderConfig.Identifier, - InfraProviderType: infraproviderenum.InfraProviderTypeDocker, + InfraProviderType: enum.InfraProviderTypeDocker, CPU: wrapString("any"), Memory: wrapString("any"), Disk: wrapString("any"), diff --git a/app/api/controller/gitspace/delete.go b/app/api/controller/gitspace/delete.go index ce2fc45cb..e96b0fe6e 100644 --- a/app/api/controller/gitspace/delete.go +++ b/app/api/controller/gitspace/delete.go @@ -51,26 +51,34 @@ func (c *Controller) Delete( instance, _ := c.gitspaceInstanceStore.FindLatestByGitspaceConfigID(ctx, gitspaceConfig.ID, gitspaceConfig.SpaceID) gitspaceConfig.GitspaceInstance = instance gitspaceConfig.SpacePath = space.Path - if instance != nil { - if stopErr := c.stopRunningGitspace(ctx, gitspaceConfig); stopErr != nil { + if instance == nil { + gitspaceConfig.IsDeleted = true + err = c.gitspaceConfigStore.Update(ctx, gitspaceConfig) + if err != nil { + return fmt.Errorf("failed to mark gitspace config as deleted: %w", err) + } + } else { + instanceState, stopErr := c.stopRunningGitspace(ctx, *gitspaceConfig) + if stopErr != nil { return stopErr } - } - gitspaceConfig.IsDeleted = true - if err = c.gitspaceConfigStore.Update(ctx, gitspaceConfig); err != nil { - return fmt.Errorf("failed to delete gitspace config with ID: %s %w", gitspaceConfig.Identifier, err) + + instance.State = instanceState + err = c.gitspaceInstanceStore.Update(ctx, instance) + if err != nil { + return fmt.Errorf("failed to update instance: %w", err) + } } return nil } func (c *Controller) stopRunningGitspace( ctx context.Context, - config *types.GitspaceConfig, -) error { - if instanceUpdated, err := c.orchestrator.DeleteGitspace(ctx, config); err != nil { - return err - } else if err = c.gitspaceInstanceStore.Update(ctx, instanceUpdated); err != nil { - return err + config types.GitspaceConfig, +) (enum.GitspaceInstanceStateType, error) { + instanceState, err := c.orchestrator.TriggerDeleteGitspace(ctx, config) + if err != nil { + return instanceState, err } - return nil + return instanceState, nil } diff --git a/app/api/controller/gitspace/find.go b/app/api/controller/gitspace/find.go index c4da99395..c46459bcf 100644 --- a/app/api/controller/gitspace/find.go +++ b/app/api/controller/gitspace/find.go @@ -20,7 +20,6 @@ import ( apiauth "github.com/harness/gitness/app/api/auth" "github.com/harness/gitness/app/auth" - "github.com/harness/gitness/store/database/dbtx" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" ) @@ -32,45 +31,18 @@ func (c *Controller) Find( identifier string, ) (*types.GitspaceConfig, error) { space, err := c.spaceStore.FindByRef(ctx, spaceRef) - const resourceNotFoundErr = "Failed to find gitspace: resource not found" if err != nil { return nil, fmt.Errorf("failed to find space: %w", err) } + err = apiauth.CheckGitspace(ctx, c.authorizer, session, space.Path, identifier, enum.PermissionGitspaceView) if err != nil { return nil, fmt.Errorf("failed to authorize: %w", err) } - var gitspaceConfig *types.GitspaceConfig - err = c.tx.WithTx(ctx, func(ctx context.Context) error { - gitspaceConfig, err = c.gitspaceConfigStore.FindByIdentifier(ctx, space.ID, identifier) - if err != nil { - return fmt.Errorf("failed to find gitspace config: %w", err) - } - infraProviderResource, err := c.infraProviderSvc.FindResource(ctx, gitspaceConfig.InfraProviderResourceID) - if err != nil { - return fmt.Errorf("failed to find infra provider resource for gitspace config: %w", err) - } - gitspaceConfig.SpacePath = space.Path - gitspaceConfig.InfraProviderResourceIdentifier = infraProviderResource.Identifier - instance, err := c.gitspaceInstanceStore.FindLatestByGitspaceConfigID(ctx, gitspaceConfig.ID, gitspaceConfig.SpaceID) - if err != nil && err.Error() != resourceNotFoundErr { // TODO fix this - return fmt.Errorf("failed to find gitspace instance for config ID : %s %w", gitspaceConfig.Identifier, err) - } - if instance != nil { - gitspaceConfig.GitspaceInstance = instance - instance.SpacePath = gitspaceConfig.SpacePath - gitspaceStateType, err := enum.GetGitspaceStateFromInstance(instance.State) - if err != nil { - return err - } - gitspaceConfig.State = gitspaceStateType - } else { - gitspaceConfig.State = enum.GitspaceStateUninitialized - } - return nil - }, dbtx.TxDefaultReadOnly) + + res, err := c.gitspaceSvc.Find(ctx, space.ID, space.Path, identifier) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to find gitspace: %w", err) } - return gitspaceConfig, nil + return res, nil } diff --git a/app/api/controller/gitspace/wire.go b/app/api/controller/gitspace/wire.go index ee0d2e5fa..3fe6954d3 100644 --- a/app/api/controller/gitspace/wire.go +++ b/app/api/controller/gitspace/wire.go @@ -20,6 +20,7 @@ import ( "github.com/harness/gitness/app/gitspace/logutil" "github.com/harness/gitness/app/gitspace/orchestrator" "github.com/harness/gitness/app/gitspace/scm" + "github.com/harness/gitness/app/services/gitspace" "github.com/harness/gitness/app/services/infraprovider" "github.com/harness/gitness/app/store" "github.com/harness/gitness/store/database/dbtx" @@ -45,6 +46,7 @@ func ProvideController( statefulLogger *logutil.StatefulLogger, scm scm.SCM, repoStore store.RepoStore, + gitspaceSvc *gitspace.Service, ) *Controller { return NewController( tx, @@ -59,5 +61,6 @@ func ProvideController( statefulLogger, scm, repoStore, + gitspaceSvc, ) } diff --git a/app/api/controller/infraprovider/create.go b/app/api/controller/infraprovider/create.go index f5a13b476..5dea07e44 100644 --- a/app/api/controller/infraprovider/create.go +++ b/app/api/controller/infraprovider/create.go @@ -22,34 +22,33 @@ import ( apiauth "github.com/harness/gitness/app/api/auth" "github.com/harness/gitness/app/auth" - infraproviderenum "github.com/harness/gitness/infraprovider/enum" "github.com/harness/gitness/types" "github.com/harness/gitness/types/check" "github.com/harness/gitness/types/enum" ) type CreateInput struct { - Identifier string `json:"identifier"` - SpaceRef string `json:"space_ref"` // Ref of the parent space - Name string `json:"name"` - Type infraproviderenum.InfraProviderType `json:"type"` - Metadata map[string]string `json:"metadata"` - Resources []ResourceInput `json:"resources"` + Identifier string `json:"identifier"` + SpaceRef string `json:"space_ref"` // Ref of the parent space + Name string `json:"name"` + Type enum.InfraProviderType `json:"type"` + Metadata map[string]string `json:"metadata"` + Resources []ResourceInput `json:"resources"` } type ResourceInput struct { - Identifier string `json:"identifier"` - Name string `json:"name"` - InfraProviderType infraproviderenum.InfraProviderType `json:"infra_provider_type"` - CPU *string `json:"cpu"` - Memory *string `json:"memory"` - Disk *string `json:"disk"` - Network *string `json:"network"` - Region []string `json:"region"` - Metadata map[string]string `json:"metadata"` - GatewayHost *string `json:"gateway_host"` - GatewayPort *string `json:"gateway_port"` - TemplateIdentifier *string `json:"template_identifier"` + Identifier string `json:"identifier"` + Name string `json:"name"` + InfraProviderType enum.InfraProviderType `json:"infra_provider_type"` + CPU *string `json:"cpu"` + Memory *string `json:"memory"` + Disk *string `json:"disk"` + Network *string `json:"network"` + Region []string `json:"region"` + Metadata map[string]string `json:"metadata"` + GatewayHost *string `json:"gateway_host"` + GatewayPort *string `json:"gateway_port"` + TemplateIdentifier *string `json:"template_identifier"` } // Create creates a new infraprovider config. diff --git a/infraprovider/enum/common.go b/app/events/gitspaceinfra/category.go similarity index 77% rename from infraprovider/enum/common.go rename to app/events/gitspaceinfra/category.go index 1d22031bd..19fad183e 100644 --- a/infraprovider/enum/common.go +++ b/app/events/gitspaceinfra/category.go @@ -12,12 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package enum +package events -func toInterfaceSlice[T interface{}](vals []T) []interface{} { - res := make([]interface{}, len(vals)) - for i := range vals { - res[i] = vals[i] - } - return res -} +const ( + // category defines the event category used for this package. + category = "gitspace_infra" +) diff --git a/app/events/gitspaceinfra/events.go b/app/events/gitspaceinfra/events.go new file mode 100644 index 000000000..eec97c90e --- /dev/null +++ b/app/events/gitspaceinfra/events.go @@ -0,0 +1,62 @@ +// 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 events + +import ( + "context" + + "github.com/harness/gitness/events" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" + + "github.com/rs/zerolog/log" +) + +const ( + // List all Gitspace Infra events below. + + GitspaceInfraEvent events.EventType = "gitspace_infra_event" +) + +type ( + GitspaceInfraEventPayload struct { + Infra *types.Infrastructure `json:"infra,omitempty"` + Type enum.InfraEvent `json:"type"` + } +) + +func (r *Reporter) EmitGitspaceInfraEvent( + ctx context.Context, + event events.EventType, + payload *GitspaceInfraEventPayload, +) { + if payload == nil { + return + } + eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, event, payload) + if err != nil { + log.Ctx(ctx).Err(err).Msgf("failed to send %v event", event) + return + } + + log.Ctx(ctx).Debug().Msgf("reported %v event with id '%s'", event, eventID) +} + +func (r *Reader) RegisterGitspaceInfraEvent( + fn events.HandlerFunc[*GitspaceInfraEventPayload], + opts ...events.HandlerOption, +) error { + return events.ReaderRegisterEvent(r.innerReader, GitspaceInfraEvent, fn, opts...) +} diff --git a/app/events/gitspaceinfra/reader.go b/app/events/gitspaceinfra/reader.go new file mode 100644 index 000000000..27b39a9c5 --- /dev/null +++ b/app/events/gitspaceinfra/reader.go @@ -0,0 +1,38 @@ +// 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 events + +import ( + "github.com/harness/gitness/events" +) + +func NewReaderFactory(eventsSystem *events.System) (*events.ReaderFactory[*Reader], error) { + readerFactoryFunc := func(innerReader *events.GenericReader) (*Reader, error) { + return &Reader{ + innerReader: innerReader, + }, nil + } + + return events.NewReaderFactory(eventsSystem, category, readerFactoryFunc) +} + +// Reader is the event reader for this package. +type Reader struct { + innerReader *events.GenericReader +} + +func (r *Reader) Configure(opts ...events.ReaderOption) { + r.innerReader.Configure(opts...) +} diff --git a/app/events/gitspaceinfra/reporter.go b/app/events/gitspaceinfra/reporter.go new file mode 100644 index 000000000..68fdecaad --- /dev/null +++ b/app/events/gitspaceinfra/reporter.go @@ -0,0 +1,37 @@ +// 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 events + +import ( + "errors" + + "github.com/harness/gitness/events" +) + +// Reporter is the event reporter for this package. +type Reporter struct { + innerReporter *events.GenericReporter +} + +func NewReporter(eventsSystem *events.System) (*Reporter, error) { + innerReporter, err := events.NewReporter(eventsSystem, category) + if err != nil { + return nil, errors.New("failed to create new GenericReporter from event system") + } + + return &Reporter{ + innerReporter: innerReporter, + }, nil +} diff --git a/app/events/gitspaceinfra/wire.go b/app/events/gitspaceinfra/wire.go new file mode 100644 index 000000000..215800d79 --- /dev/null +++ b/app/events/gitspaceinfra/wire.go @@ -0,0 +1,35 @@ +// 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 events + +import ( + "github.com/harness/gitness/events" + + "github.com/google/wire" +) + +// WireSet provides a wire set for this package. +var WireSet = wire.NewSet( + ProvideReaderFactory, + ProvideReporter, +) + +func ProvideReaderFactory(eventsSystem *events.System) (*events.ReaderFactory[*Reader], error) { + return NewReaderFactory(eventsSystem) +} + +func ProvideReporter(eventsSystem *events.System) (*Reporter, error) { + return NewReporter(eventsSystem) +} diff --git a/app/gitspace/infrastructure/provisioner.go b/app/gitspace/infrastructure/provisioner.go index 3c5cf31be..2c8a6386d 100644 --- a/app/gitspace/infrastructure/provisioner.go +++ b/app/gitspace/infrastructure/provisioner.go @@ -17,40 +17,64 @@ package infrastructure import ( "context" - "github.com/harness/gitness/infraprovider" "github.com/harness/gitness/types" ) // TODO Check if the interface can be discarded type InfraProvisioner interface { - // Provision provisions infra resources using the infraProviderResource with different infra providers and - // stores the details in the db depending on the provisioning type. - Provision( + // TriggerProvision triggers the provisionining of infra resources using the infraProviderResource with different + // infra providers. + TriggerProvision( ctx context.Context, infraProviderResource *types.InfraProviderResource, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, requiredPorts []int, - ) (*infraprovider.Infrastructure, error) + ) error - // Stop unprovisions those resources which can be stopped without losing the gitspace data. - Stop( + // ResumeProvision stores the provisioned infra details in the db depending on the provisioning type. + ResumeProvision( ctx context.Context, infraProviderResource *types.InfraProviderResource, - gitspaceConfig *types.GitspaceConfig, - ) (*infraprovider.Infrastructure, error) + gitspaceConfig types.GitspaceConfig, + requiredPorts []int, + provisionedInfra *types.Infrastructure, + ) (*types.Infrastructure, error) - // Deprovision removes all the resources created for the gitspace. - Deprovision( + // TriggerStop triggers deprovisioning of those resources which can be stopped without losing the Gitspace data. + TriggerStop( ctx context.Context, infraProviderResource *types.InfraProviderResource, - gitspaceConfig *types.GitspaceConfig, - ) (*infraprovider.Infrastructure, error) + gitspaceConfig types.GitspaceConfig, + ) error + + // ResumeStop stores the deprovisioned infra details in the db depending on the provisioning type. + ResumeStop( + ctx context.Context, + infraProviderResource *types.InfraProviderResource, + gitspaceConfig types.GitspaceConfig, + deprovisionedInfra *types.Infrastructure, + ) (*types.Infrastructure, error) + + // TriggerDeprovision triggers deprovisionign of all the resources created for the Gitspace. + TriggerDeprovision( + ctx context.Context, + infraProviderResource *types.InfraProviderResource, + gitspaceConfig types.GitspaceConfig, + ) error + + // ResumeDeprovision stores the deprovisioned infra details in the db depending on the provisioning type. + ResumeDeprovision( + ctx context.Context, + infraProviderResource *types.InfraProviderResource, + gitspaceConfig types.GitspaceConfig, + deprovisionedInfra *types.Infrastructure, + ) (*types.Infrastructure, error) // Find finds the provisioned infra resources for the gitspace instance. Find( ctx context.Context, infraProviderResource *types.InfraProviderResource, - gitspaceConfig *types.GitspaceConfig, - ) (*infraprovider.Infrastructure, error) + gitspaceConfig types.GitspaceConfig, + ) (*types.Infrastructure, error) } diff --git a/app/gitspace/infrastructure/provisioner_impl.go b/app/gitspace/infrastructure/provisioner_impl.go index 3f2f20a97..56bfd33bf 100644 --- a/app/gitspace/infrastructure/provisioner_impl.go +++ b/app/gitspace/infrastructure/provisioner_impl.go @@ -20,8 +20,8 @@ import ( "github.com/harness/gitness/app/store" "github.com/harness/gitness/infraprovider" - "github.com/harness/gitness/infraprovider/enum" "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" ) var _ InfraProvisioner = (*infraProvisioner)(nil) @@ -44,31 +44,31 @@ func NewInfraProvisionerService( } } -func (i infraProvisioner) Provision( +func (i infraProvisioner) TriggerProvision( ctx context.Context, infraProviderResource *types.InfraProviderResource, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, requiredPorts []int, -) (*infraprovider.Infrastructure, error) { +) error { infraProviderEntity, err := i.getConfigFromResource(ctx, infraProviderResource) if err != nil { - return nil, err + return err } - infraProvider, err := i.getInfraProvider(infraProviderEntity) + infraProvider, err := i.getInfraProvider(infraProviderEntity.Type) if err != nil { - return nil, err + return err } if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive,staticcheck // TODO: Check if any existing infra is provisioned, its status and create new infraProvisioned record } - var allParams []infraprovider.Parameter + var allParams []types.InfraProviderParameter templateParams, err := i.getTemplateParams(infraProvider, infraProviderResource) if err != nil { - return nil, err + return err } allParams = append(allParams, templateParams...) @@ -79,47 +79,44 @@ func (i infraProvisioner) Provision( err = infraProvider.ValidateParams(allParams) if err != nil { - return nil, fmt.Errorf("invalid provisioning params %v: %w", infraProviderResource.Metadata, err) + return fmt.Errorf("invalid provisioning params %v: %w", infraProviderResource.Metadata, err) } - provisionedInfra, err := infraProvider.Provision( + err = infraProvider.Provision( ctx, + gitspaceConfig.SpaceID, gitspaceConfig.SpacePath, gitspaceConfig.Identifier, requiredPorts, allParams, ) if err != nil { - return nil, fmt.Errorf( - "unable to provision infrastructure for gitspaceConfigIdentifier %v: %w", + return fmt.Errorf( + "unable to trigger provision infrastructure for gitspaceConfigIdentifier %v: %w", gitspaceConfig.Identifier, err, ) } - if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive,staticcheck - // TODO: Update the infraProvisioned record - } - - return provisionedInfra, nil + return nil } -func (i infraProvisioner) Stop( +func (i infraProvisioner) TriggerStop( ctx context.Context, infraProviderResource *types.InfraProviderResource, - gitspaceConfig *types.GitspaceConfig, -) (*infraprovider.Infrastructure, error) { + gitspaceConfig types.GitspaceConfig, +) error { infraProviderEntity, err := i.getConfigFromResource(ctx, infraProviderResource) if err != nil { - return nil, err + return err } - infraProvider, err := i.getInfraProvider(infraProviderEntity) + infraProvider, err := i.getInfraProvider(infraProviderEntity.Type) if err != nil { - return nil, err + return err } - var allParams []infraprovider.Parameter + var allParams []types.InfraProviderParameter if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive // TODO: Fetch and check existing infraProvisioned record @@ -127,7 +124,7 @@ func (i infraProvisioner) Stop( } else { templateParams, err2 := i.getTemplateParams(infraProvider, infraProviderResource) if err2 != nil { - return nil, err2 + return err2 } allParams = append(allParams, templateParams...) @@ -138,48 +135,46 @@ func (i infraProvisioner) Stop( err = infraProvider.ValidateParams(allParams) if err != nil { - return nil, fmt.Errorf("invalid provisioning params %+v: %w", infraProviderResource.Metadata, err) + return fmt.Errorf("invalid provisioning params %+v: %w", infraProviderResource.Metadata, err) } - var provisionedInfra *infraprovider.Infrastructure + var provisionedInfra *types.Infrastructure if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive // TODO: Fetch and check existing infraProvisioned record } else { - provisionedInfra = &infraprovider.Infrastructure{ + provisionedInfra = &types.Infrastructure{ + SpaceID: gitspaceConfig.SpaceID, + SpacePath: gitspaceConfig.SpacePath, ResourceKey: gitspaceConfig.Identifier, ProviderType: infraProviderEntity.Type, Parameters: allParams, } } - stoppedInfra, err := infraProvider.Stop(ctx, provisionedInfra) + err = infraProvider.Stop(ctx, provisionedInfra) if err != nil { - return nil, fmt.Errorf("unable to stop provisioned infra %+v: %w", provisionedInfra, err) + return fmt.Errorf("unable to trigger stop infra %+v: %w", provisionedInfra, err) } - if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive,staticcheck - // TODO: Update existing infraProvisioned record - } - - return stoppedInfra, err + return nil } -func (i infraProvisioner) Deprovision( +func (i infraProvisioner) TriggerDeprovision( ctx context.Context, infraProviderResource *types.InfraProviderResource, - gitspaceConfig *types.GitspaceConfig, -) (*infraprovider.Infrastructure, error) { + gitspaceConfig types.GitspaceConfig, +) error { infraProviderEntity, err := i.getConfigFromResource(ctx, infraProviderResource) if err != nil { - return nil, err + return err } - infraProvider, err := i.getInfraProvider(infraProviderEntity) + infraProvider, err := i.getInfraProvider(infraProviderEntity.Type) if err != nil { - return nil, err + return err } - var allParams []infraprovider.Parameter + var allParams []types.InfraProviderParameter if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive // TODO: Fetch and check existing infraProvisioned record @@ -187,7 +182,7 @@ func (i infraProvisioner) Deprovision( } else { templateParams, err2 := i.getTemplateParams(infraProvider, infraProviderResource) if err2 != nil { - return nil, err2 + return err2 } allParams = append(allParams, templateParams...) @@ -198,51 +193,48 @@ func (i infraProvisioner) Deprovision( err = infraProvider.ValidateParams(allParams) if err != nil { - return nil, fmt.Errorf("invalid provisioning params %+v: %w", infraProviderResource.Metadata, err) + return fmt.Errorf("invalid provisioning params %+v: %w", infraProviderResource.Metadata, err) } - var provisionedInfra *infraprovider.Infrastructure + var provisionedInfra *types.Infrastructure if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive // TODO: Fetch and check existing infraProvisioned record } else { - provisionedInfra, err = infraProvider.Find(ctx, gitspaceConfig.SpacePath, gitspaceConfig.Identifier, allParams) + provisionedInfra, err = infraProvider.Find( + ctx, gitspaceConfig.SpaceID, gitspaceConfig.SpacePath, gitspaceConfig.Identifier, allParams) if err != nil { - return nil, fmt.Errorf("unable to find provisioned infra for gitspace %s: %w", + return fmt.Errorf("unable to find provisioned infra for gitspace %s: %w", gitspaceConfig.Identifier, err) } } - destroyedInfra, err := infraProvider.Deprovision(ctx, provisionedInfra) + err = infraProvider.Deprovision(ctx, provisionedInfra) if err != nil { - return nil, fmt.Errorf("unable to stop provisioned infra %+v: %w", provisionedInfra, err) + return fmt.Errorf("unable to trigger deprovision infra %+v: %w", provisionedInfra, err) } - if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive,staticcheck - // TODO: Update existing infraProvisioned record - } - - return destroyedInfra, err + return err } func (i infraProvisioner) Find( ctx context.Context, infraProviderResource *types.InfraProviderResource, - _ *types.GitspaceConfig, -) (*infraprovider.Infrastructure, error) { + _ types.GitspaceConfig, +) (*types.Infrastructure, error) { infraProviderEntity, err := i.getConfigFromResource(ctx, infraProviderResource) if err != nil { return nil, err } - infraProvider, err := i.getInfraProvider(infraProviderEntity) + infraProvider, err := i.getInfraProvider(infraProviderEntity.Type) if err != nil { return nil, err } - var infra infraprovider.Infrastructure + var infra types.Infrastructure if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive // TODO: Fetch existing infraProvisioned record and map to &infraprovider.Infrastructure } else { - var allParams []infraprovider.Parameter + var allParams []types.InfraProviderParameter templateParams, err2 := i.getTemplateParams(infraProvider, infraProviderResource) if err2 != nil { @@ -254,7 +246,7 @@ func (i infraProvisioner) Find( allParams = append(allParams, params...) - infra = infraprovider.Infrastructure{ + infra = types.Infrastructure{ ProviderType: infraProviderEntity.Type, Parameters: allParams, Status: enum.InfraStatusProvisioned, @@ -277,11 +269,11 @@ func (i infraProvisioner) getConfigFromResource( } func (i infraProvisioner) getInfraProvider( - infraProviderEntity *types.InfraProviderConfig, + infraProviderType enum.InfraProviderType, ) (infraprovider.InfraProvider, error) { - infraProvider, err := i.providerFactory.GetInfraProvider(infraProviderEntity.Type) + infraProvider, err := i.providerFactory.GetInfraProvider(infraProviderType) if err != nil { - return nil, fmt.Errorf("unable to get infra provider of type %v: %w", infraProviderEntity.Type, err) + return nil, fmt.Errorf("unable to get infra provider of type %v: %w", infraProviderType, err) } return infraProvider, nil } @@ -289,7 +281,7 @@ func (i infraProvisioner) getInfraProvider( func (i infraProvisioner) getTemplateParams( infraProvider infraprovider.InfraProvider, _ *types.InfraProviderResource, -) ([]infraprovider.Parameter, error) { //nolint:unparam +) ([]types.InfraProviderParameter, error) { //nolint:unparam templateParams := infraProvider.TemplateParams() if len(templateParams) > 0 { //nolint:revive,staticcheck // TODO: Fetch templates and convert into []Parameters @@ -299,14 +291,14 @@ func (i infraProvisioner) getTemplateParams( func (i infraProvisioner) paramsFromResource( infraProviderResource *types.InfraProviderResource, -) []infraprovider.Parameter { - params := make([]infraprovider.Parameter, len(infraProviderResource.Metadata)) +) []types.InfraProviderParameter { + params := make([]types.InfraProviderParameter, len(infraProviderResource.Metadata)) counter := 0 for key, value := range infraProviderResource.Metadata { if key == "" || value == "" { continue } - params[counter] = infraprovider.Parameter{ + params[counter] = types.InfraProviderParameter{ Name: key, Value: value, } @@ -314,3 +306,58 @@ func (i infraProvisioner) paramsFromResource( } return params } + +func (i infraProvisioner) ResumeProvision( + _ context.Context, + _ *types.InfraProviderResource, + _ types.GitspaceConfig, + _ []int, + provisionedInfra *types.Infrastructure, +) (*types.Infrastructure, error) { + infraProvider, err := i.getInfraProvider(provisionedInfra.ProviderType) + if err != nil { + return nil, err + } + + if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive,staticcheck + // TODO: Update the infraProvisioned record + } + + return provisionedInfra, nil +} + +func (i infraProvisioner) ResumeStop( + _ context.Context, + _ *types.InfraProviderResource, + _ types.GitspaceConfig, + stoppedInfra *types.Infrastructure, +) (*types.Infrastructure, error) { + infraProvider, err := i.getInfraProvider(stoppedInfra.ProviderType) + if err != nil { + return nil, err + } + + if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive,staticcheck + // TODO: Update existing infraProvisioned record + } + + return stoppedInfra, err +} + +func (i infraProvisioner) ResumeDeprovision( + _ context.Context, + _ *types.InfraProviderResource, + _ types.GitspaceConfig, + deprovisionedInfra *types.Infrastructure, +) (*types.Infrastructure, error) { + infraProvider, err := i.getInfraProvider(deprovisionedInfra.ProviderType) + if err != nil { + return nil, err + } + + if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { //nolint:revive,staticcheck + // TODO: Update existing infraProvisioned record + } + + return deprovisionedInfra, err +} diff --git a/app/gitspace/orchestrator/container/container_orchestrator.go b/app/gitspace/orchestrator/container/container_orchestrator.go index f51ad9a19..c970fe2e7 100644 --- a/app/gitspace/orchestrator/container/container_orchestrator.go +++ b/app/gitspace/orchestrator/container/container_orchestrator.go @@ -19,7 +19,6 @@ import ( "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,19 +28,19 @@ type Orchestrator interface { // It returns the container ID, name and ports used. CreateAndStartGitspace( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, - infra *infraprovider.Infrastructure, + gitspaceConfig types.GitspaceConfig, + infra *types.Infrastructure, resolvedDetails *scm.ResolvedDetails, defaultBaseImage string, ideService ide.IDE, ) (*StartResponse, error) // StopGitspace stops the gitspace container. - StopGitspace(ctx context.Context, config *types.GitspaceConfig, infra *infraprovider.Infrastructure) error + StopGitspace(ctx context.Context, config types.GitspaceConfig, infra *types.Infrastructure) error // StopAndRemoveGitspace stops and removes the gitspace container. - StopAndRemoveGitspace(ctx context.Context, config *types.GitspaceConfig, infra *infraprovider.Infrastructure) error + StopAndRemoveGitspace(ctx context.Context, config types.GitspaceConfig, infra *types.Infrastructure) error // Status checks if the infra is reachable and ready to orchestrate containers. - Status(ctx context.Context, infra *infraprovider.Infrastructure) error + Status(ctx context.Context, infra *types.Infrastructure) error } diff --git a/app/gitspace/orchestrator/container/embedded_docker.go b/app/gitspace/orchestrator/container/embedded_docker.go index 30b52bc7b..27b622388 100644 --- a/app/gitspace/orchestrator/container/embedded_docker.go +++ b/app/gitspace/orchestrator/container/embedded_docker.go @@ -73,8 +73,8 @@ func NewEmbeddedDockerOrchestrator( // It returns an error if the container is not running, exited or removed. func (e *EmbeddedDockerOrchestrator) CreateAndStartGitspace( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, - infra *infraprovider.Infrastructure, + gitspaceConfig types.GitspaceConfig, + infra *types.Infrastructure, resolvedRepoDetails *scm.ResolvedDetails, defaultBaseImage string, ideService ide.IDE, @@ -202,7 +202,7 @@ func (e *EmbeddedDockerOrchestrator) getWorkingDir(repoName string) string { func (e *EmbeddedDockerOrchestrator) startGitspace( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, containerName string, dockerClient *client.Client, ideService ide.IDE, @@ -210,7 +210,7 @@ func (e *EmbeddedDockerOrchestrator) startGitspace( volumeName string, workingDirectory string, resolvedRepoDetails *scm.ResolvedDetails, - portMappings map[int]*infraprovider.PortMapping, + portMappings map[int]*types.PortMapping, defaultBaseImage string, ) error { var imageName = resolvedRepoDetails.DevcontainerConfig.Image @@ -357,7 +357,7 @@ func (e *EmbeddedDockerOrchestrator) getContainerInfo( ctx context.Context, containerName string, dockerClient *client.Client, - portMappings map[int]*infraprovider.PortMapping, + portMappings map[int]*types.PortMapping, ) (string, map[int]string, error) { inspectResp, err := dockerClient.ContainerInspect(ctx, containerName) if err != nil { @@ -592,7 +592,7 @@ func (e *EmbeddedDockerOrchestrator) createContainer( logStreamInstance *logutil.LogStreamInstance, volumeName string, workingDirectory string, - portMappings map[int]*infraprovider.PortMapping, + portMappings map[int]*types.PortMapping, ) error { exposedPorts := nat.PortSet{} portBindings := nat.PortMap{} @@ -707,8 +707,8 @@ func (e *EmbeddedDockerOrchestrator) pullImage( // StopGitspace stops a container. If it is removed, it returns an error. func (e EmbeddedDockerOrchestrator) StopGitspace( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, - infra *infraprovider.Infrastructure, + gitspaceConfig types.GitspaceConfig, + infra *types.Infrastructure, ) error { containerName := getGitspaceContainerName(gitspaceConfig) @@ -797,12 +797,12 @@ func (e EmbeddedDockerOrchestrator) stopContainer( return nil } -func getGitspaceContainerName(config *types.GitspaceConfig) string { +func getGitspaceContainerName(config types.GitspaceConfig) string { return "gitspace-" + config.UserID + "-" + config.Identifier } // Status is NOOP for EmbeddedDockerOrchestrator as the docker host is verified by the infra provisioner. -func (e *EmbeddedDockerOrchestrator) Status(_ context.Context, _ *infraprovider.Infrastructure) error { +func (e *EmbeddedDockerOrchestrator) Status(_ context.Context, _ *types.Infrastructure) error { return nil } @@ -830,8 +830,8 @@ func (e *EmbeddedDockerOrchestrator) containerState( // If the container is already removed, it returns. func (e *EmbeddedDockerOrchestrator) StopAndRemoveGitspace( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, - infra *infraprovider.Infrastructure, + gitspaceConfig types.GitspaceConfig, + infra *types.Infrastructure, ) error { containerName := getGitspaceContainerName(gitspaceConfig) diff --git a/app/gitspace/orchestrator/devcontainer/devcontainer.go b/app/gitspace/orchestrator/devcontainer/exec.go similarity index 100% rename from app/gitspace/orchestrator/devcontainer/devcontainer.go rename to app/gitspace/orchestrator/devcontainer/exec.go diff --git a/app/gitspace/orchestrator/orchestrator.go b/app/gitspace/orchestrator/orchestrator.go index 186104d6a..827806d08 100644 --- a/app/gitspace/orchestrator/orchestrator.go +++ b/app/gitspace/orchestrator/orchestrator.go @@ -18,19 +18,39 @@ import ( "context" "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" ) type Orchestrator interface { - // StartGitspace is responsible for all the operations necessary to create the Gitspace container. It fetches the - // devcontainer.json from the code repo, provisions infra using the infra provisioner and setting up the Gitspace - // through the container orchestrator. - StartGitspace(ctx context.Context, gitspaceConfig *types.GitspaceConfig) error + // TriggerStartGitspace fetches the infra resources configured for the gitspace and triggers the infra provisioning. + TriggerStartGitspace(ctx context.Context, gitspaceConfig types.GitspaceConfig) (enum.GitspaceInstanceStateType, error) - // StopGitspace is responsible for stopping a running Gitspace. It stops the Gitspace container and unprovisions + // ResumeStartGitspace saves the provisioned infra, resolves the code repo details & creates the Gitspace container. + ResumeStartGitspace( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + provisionedInfra *types.Infrastructure, + ) (types.GitspaceInstance, error) + + // TriggerStopGitspace stops the Gitspace container and triggers infra deprovisioning to deprovision // all the infra resources which are not required to restart the Gitspace. - StopGitspace(ctx context.Context, gitspaceConfig *types.GitspaceConfig) error + TriggerStopGitspace(ctx context.Context, gitspaceConfig types.GitspaceConfig) (enum.GitspaceInstanceStateType, error) - // DeleteGitspace is responsible for deleting a Gitspace. It stops the Gitspace container and unprovisions + // ResumeStopGitspace saves the deprovisioned infra details. + ResumeStopGitspace( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + stoppedInfra *types.Infrastructure, + ) (enum.GitspaceInstanceStateType, error) + + // TriggerDeleteGitspace removes the Gitspace container and triggers infra deprovisioning to deprovision // all the infra resources. - DeleteGitspace(ctx context.Context, gitspaceConfig *types.GitspaceConfig) (*types.GitspaceInstance, error) + TriggerDeleteGitspace(ctx context.Context, gitspaceConfig types.GitspaceConfig) (enum.GitspaceInstanceStateType, error) + + // ResumeDeleteGitspace saves the deprovisioned infra details. + ResumeDeleteGitspace( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + deprovisionedInfra *types.Infrastructure, + ) (enum.GitspaceInstanceStateType, error) } diff --git a/app/gitspace/orchestrator/orchestrator_impl.go b/app/gitspace/orchestrator/orchestrator_impl.go index 3fe8b8f9b..dbd78fc81 100644 --- a/app/gitspace/orchestrator/orchestrator_impl.go +++ b/app/gitspace/orchestrator/orchestrator_impl.go @@ -28,7 +28,6 @@ import ( "github.com/harness/gitness/app/gitspace/orchestrator/ide" "github.com/harness/gitness/app/gitspace/scm" "github.com/harness/gitness/app/store" - "github.com/harness/gitness/infraprovider" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" @@ -74,166 +73,80 @@ func NewOrchestrator( } } -func (o orchestrator) StartGitspace( +func (o orchestrator) TriggerStartGitspace( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, -) error { - gitspaceInstance := gitspaceConfig.GitspaceInstance - gitspaceInstance.State = enum.GitspaceInstanceStateError - - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerStart) - - scmResolvedDetails, err := o.scm.GetSCMRepoDetails(ctx, gitspaceConfig) - if err != nil { - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerFailed) - - 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") - } - - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerCompleted) - + gitspaceConfig types.GitspaceConfig, +) (enum.GitspaceInstanceStateType, error) { + instanceState := enum.GitspaceInstanceStateError infraProviderResource, err := o.infraProviderResourceStore.Find(ctx, gitspaceConfig.InfraProviderResourceID) if err != nil { - return fmt.Errorf("cannot get the infraprovider resource for ID %d: %w", + return instanceState, fmt.Errorf("cannot get the infraprovider resource for ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) } ideSvc, err := o.getIDEService(gitspaceConfig) if err != nil { - return err + return instanceState, err } o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraProvisioningStart) idePort := ideSvc.Port() - infra, err := o.infraProvisioner.Provision(ctx, infraProviderResource, gitspaceConfig, []int{idePort}) + err = o.infraProvisioner.TriggerProvision(ctx, infraProviderResource, gitspaceConfig, []int{idePort}) if err != nil { o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraProvisioningFailed) - return fmt.Errorf( - "cannot provision infrastructure for ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) + return instanceState, fmt.Errorf( + "cannot trigger provision infrastructure for ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) } - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraProvisioningCompleted) + instanceState = enum.GitspaceInstanceStateStarting - 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 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.CreateAndStartGitspace( - ctx, gitspaceConfig, infra, scmResolvedDetails, o.config.DefaultBaseImage, ideSvc) - if err != nil { - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationFailed) - - return fmt.Errorf("couldn't call the agent start API: %w", err) - } - - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationCompleted) - - var ideURL url.URL - - var forwardedPort string - - if infra.PortMappings[idePort].PublishedPort == 0 { - forwardedPort = startResponse.PublishedPorts[idePort] - } else { - forwardedPort = strconv.Itoa(infra.PortMappings[idePort].ForwardedPort) - } - - if gitspaceConfig.IDE == enum.IDETypeVSCodeWeb { - ideURL = url.URL{ - Scheme: "http", - Host: infra.Host + ":" + forwardedPort, - RawQuery: filepath.Join("folder=", 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(forwardedPort, repoName), - ), - } - } - ideURLString := ideURL.String() - gitspaceInstance.URL = &ideURLString - - gitspaceInstance.LastUsed = time.Now().UnixMilli() - gitspaceInstance.State = enum.GitspaceInstanceStateRunning - - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStartCompleted) - - return nil + return instanceState, nil } -func (o orchestrator) StopGitspace( +func (o orchestrator) TriggerStopGitspace( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, -) error { - gitspaceInstance := gitspaceConfig.GitspaceInstance - gitspaceInstance.State = enum.GitspaceInstanceStateError + gitspaceConfig types.GitspaceConfig, +) (enum.GitspaceInstanceStateType, error) { + instanceState := enum.GitspaceInstanceStateError infraProviderResource, err := o.infraProviderResourceStore.Find(ctx, gitspaceConfig.InfraProviderResourceID) if err != nil { - return fmt.Errorf( + return instanceState, 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 fmt.Errorf("cannot find the provisioned infra: %w", err) + return instanceState, fmt.Errorf("cannot find the provisioned infra: %w", err) } err = o.stopGitspaceContainer(ctx, gitspaceConfig, infra) if err != nil { - return err + return instanceState, err } o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraStopStart) - _, err = o.infraProvisioner.Stop(ctx, infraProviderResource, gitspaceConfig) + err = o.infraProvisioner.TriggerStop(ctx, infraProviderResource, gitspaceConfig) if err != nil { o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraStopFailed) - return fmt.Errorf( - "cannot stop provisioned infrastructure with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) + return instanceState, fmt.Errorf( + "cannot trigger stop infrastructure with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) } - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraStopCompleted) + instanceState = enum.GitspaceInstanceStateStopping - gitspaceInstance.State = enum.GitspaceInstanceStateDeleted - - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStopCompleted) - - return err + return instanceState, nil } func (o orchestrator) stopGitspaceContainer( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, - infra *infraprovider.Infrastructure, + gitspaceConfig types.GitspaceConfig, + infra *types.Infrastructure, ) error { o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectStart) @@ -261,8 +174,8 @@ func (o orchestrator) stopGitspaceContainer( func (o orchestrator) stopAndRemoveGitspaceContainer( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, - infra *infraprovider.Infrastructure, + gitspaceConfig types.GitspaceConfig, + infra *types.Infrastructure, ) error { o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectStart) @@ -288,51 +201,45 @@ func (o orchestrator) stopAndRemoveGitspaceContainer( return nil } -func (o orchestrator) DeleteGitspace( +func (o orchestrator) TriggerDeleteGitspace( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, -) (*types.GitspaceInstance, error) { - gitspaceInstance := gitspaceConfig.GitspaceInstance - gitspaceInstance.State = enum.GitspaceInstanceStateError + gitspaceConfig types.GitspaceConfig, +) (enum.GitspaceInstanceStateType, error) { + instanceState := enum.GitspaceInstanceStateError infraProviderResource, err := o.infraProviderResourceStore.Find(ctx, gitspaceConfig.InfraProviderResourceID) if err != nil { - return nil, fmt.Errorf( + return instanceState, 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 nil, fmt.Errorf("cannot find the provisioned infra: %w", err) + return instanceState, fmt.Errorf("cannot find the provisioned infra: %w", err) } err = o.stopAndRemoveGitspaceContainer(ctx, gitspaceConfig, infra) if err != nil { - return nil, err + return instanceState, err } o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraDeprovisioningStart) - _, err = o.infraProvisioner.Deprovision(ctx, infraProviderResource, gitspaceConfig) + err = o.infraProvisioner.TriggerDeprovision(ctx, infraProviderResource, gitspaceConfig) if err != nil { o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraDeprovisioningFailed) - return nil, fmt.Errorf( - "cannot deprovision infrastructure with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) + return instanceState, fmt.Errorf( + "cannot trigger deprovision infrastructure with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) } + instanceState = enum.GitspaceInstanceStateStopping - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraDeprovisioningCompleted) - - gitspaceInstance.State = enum.GitspaceInstanceStateDeleted - - o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStopCompleted) - - return gitspaceInstance, nil + return instanceState, nil } func (o orchestrator) emitGitspaceEvent( ctx context.Context, - config *types.GitspaceConfig, + config types.GitspaceConfig, eventType enum.GitspaceEventType, ) { o.eventReporter.EmitGitspaceEvent( @@ -347,7 +254,7 @@ func (o orchestrator) emitGitspaceEvent( }) } -func (o orchestrator) getIDEService(gitspaceConfig *types.GitspaceConfig) (ide.IDE, error) { +func (o orchestrator) getIDEService(gitspaceConfig types.GitspaceConfig) (ide.IDE, error) { var ideService ide.IDE switch gitspaceConfig.IDE { @@ -361,3 +268,180 @@ func (o orchestrator) getIDEService(gitspaceConfig *types.GitspaceConfig) (ide.I return ideService, nil } + +func (o orchestrator) ResumeStartGitspace( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + provisionedInfra *types.Infrastructure, +) (types.GitspaceInstance, error) { + gitspaceInstance := *gitspaceConfig.GitspaceInstance + gitspaceInstance.State = enum.GitspaceInstanceStateError + + 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) + } + + ideSvc, err := o.getIDEService(gitspaceConfig) + if err != nil { + return gitspaceInstance, err + } + + idePort := ideSvc.Port() + + provisionedInfra, err = o.infraProvisioner.ResumeProvision( + ctx, infraProviderResource, gitspaceConfig, []int{idePort}, provisionedInfra) + 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.GitspaceEventTypeFetchDevcontainerStart) + + scmResolvedDetails, err := o.scm.GetSCMRepoDetails(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 %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") + } + + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerCompleted) + + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentConnectStart) + + err = o.containerOrchestrator.Status(ctx, provisionedInfra) + 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.CreateAndStartGitspace( + ctx, gitspaceConfig, provisionedInfra, scmResolvedDetails, o.config.DefaultBaseImage, ideSvc) + 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) + + var ideURL url.URL + + var forwardedPort string + + if provisionedInfra.PortMappings[idePort].PublishedPort == 0 { + forwardedPort = startResponse.PublishedPorts[idePort] + } else { + forwardedPort = strconv.Itoa(provisionedInfra.PortMappings[idePort].ForwardedPort) + } + + host := provisionedInfra.Host + if provisionedInfra.ProxyHost != "" { + host = provisionedInfra.ProxyHost + } + + if gitspaceConfig.IDE == enum.IDETypeVSCodeWeb { + ideURL = url.URL{ + Scheme: "http", + Host: host + ":" + forwardedPort, + RawQuery: filepath.Join("folder=", 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, + host, + filepath.Join(forwardedPort, 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) ResumeStopGitspace( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + stoppedInfra *types.Infrastructure, +) (enum.GitspaceInstanceStateType, error) { + instanceState := enum.GitspaceInstanceStateError + + infraProviderResource, err := o.infraProviderResourceStore.Find(ctx, gitspaceConfig.InfraProviderResourceID) + if err != nil { + return instanceState, fmt.Errorf( + "cannot get the infraProviderResource with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) + } + + _, err = o.infraProvisioner.ResumeStop(ctx, infraProviderResource, gitspaceConfig, stoppedInfra) + if err != nil { + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraStopFailed) + + return instanceState, fmt.Errorf( + "cannot stop provisioned infrastructure with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) + } + + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraStopCompleted) + + instanceState = enum.GitspaceInstanceStateDeleted + + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStopCompleted) + + return instanceState, nil +} + +func (o orchestrator) ResumeDeleteGitspace( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + deprovisionedInfra *types.Infrastructure, +) (enum.GitspaceInstanceStateType, error) { + instanceState := enum.GitspaceInstanceStateError + + infraProviderResource, err := o.infraProviderResourceStore.Find(ctx, gitspaceConfig.InfraProviderResourceID) + if err != nil { + return instanceState, fmt.Errorf( + "cannot get the infraProviderResource with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) + } + + _, err = o.infraProvisioner.ResumeDeprovision(ctx, infraProviderResource, gitspaceConfig, deprovisionedInfra) + if err != nil { + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraDeprovisioningFailed) + return instanceState, fmt.Errorf( + "cannot deprovision infrastructure with ID %d: %w", gitspaceConfig.InfraProviderResourceID, err) + } + + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraDeprovisioningCompleted) + + instanceState = enum.GitspaceInstanceStateDeleted + + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStopCompleted) + return instanceState, nil +} diff --git a/app/gitspace/scm/gitness_scm.go b/app/gitspace/scm/gitness_scm.go index 8c7918ae2..9e1e1761b 100644 --- a/app/gitspace/scm/gitness_scm.go +++ b/app/gitspace/scm/gitness_scm.go @@ -62,7 +62,7 @@ func NewGitnessSCM(repoStore store.RepoStore, git git.Interface, func (s GitnessSCM) ResolveCredentials( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, ) (*ResolvedCredentials, error) { repoURL, err := url.Parse(gitspaceConfig.CodeRepoURL) if err != nil { @@ -116,7 +116,7 @@ func (s GitnessSCM) ResolveCredentials( } func (s GitnessSCM) GetFileContent(ctx context.Context, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, filePath string, ) ([]byte, error) { repo, err := s.repoStore.FindByRef(ctx, *gitspaceConfig.CodeRepoRef) diff --git a/app/gitspace/scm/public_scm.go b/app/gitspace/scm/public_scm.go index 3a9cf246c..73156a9ca 100644 --- a/app/gitspace/scm/public_scm.go +++ b/app/gitspace/scm/public_scm.go @@ -39,7 +39,7 @@ func NewGenericSCM() *GenericSCM { } func (s GenericSCM) GetFileContent(ctx context.Context, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, filePath string, ) ([]byte, error) { gitWorkingDirectory := "/tmp/git/" @@ -102,7 +102,7 @@ func (s GenericSCM) GetFileContent(ctx context.Context, func (s GenericSCM) ResolveCredentials( _ context.Context, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, ) (*ResolvedCredentials, error) { var resolvedCredentials = &ResolvedCredentials{ Branch: gitspaceConfig.Branch, diff --git a/app/gitspace/scm/scm.go b/app/gitspace/scm/scm.go index 66c94e9dd..51fc9bdff 100644 --- a/app/gitspace/scm/scm.go +++ b/app/gitspace/scm/scm.go @@ -41,7 +41,7 @@ type SCM interface { // GetSCMRepoDetails fetches repository name, credentials & devcontainer config file from the given repo and branch. GetSCMRepoDetails( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, ) (*ResolvedDetails, error) // CheckValidCodeRepo checks if the current URL is a valid and accessible code repo, @@ -86,7 +86,7 @@ func (s scm) CheckValidCodeRepo( func (s scm) GetSCMRepoDetails( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, ) (*ResolvedDetails, error) { filePath := devcontainerDefaultPath if gitspaceConfig.CodeRepoType == "" { diff --git a/app/gitspace/scm/scm_factory.go b/app/gitspace/scm/scm_factory.go index 786ac9054..e44ad7592 100644 --- a/app/gitspace/scm/scm_factory.go +++ b/app/gitspace/scm/scm_factory.go @@ -23,10 +23,10 @@ import ( ) type Provider interface { - ResolveCredentials(ctx context.Context, gitspaceConfig *types.GitspaceConfig) (*ResolvedCredentials, error) + ResolveCredentials(ctx context.Context, gitspaceConfig types.GitspaceConfig) (*ResolvedCredentials, error) GetFileContent( ctx context.Context, - gitspaceConfig *types.GitspaceConfig, + gitspaceConfig types.GitspaceConfig, filePath string, ) ([]byte, error) } diff --git a/app/services/gitspace/find.go b/app/services/gitspace/find.go new file mode 100644 index 000000000..fad8baeb2 --- /dev/null +++ b/app/services/gitspace/find.go @@ -0,0 +1,68 @@ +// 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 gitspace + +import ( + "context" + "fmt" + + "github.com/harness/gitness/store/database/dbtx" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +const resourceNotFoundErr = "Failed to find gitspace: resource not found" + +func (c *Service) Find( + ctx context.Context, + spaceID int64, + spacePath string, + identifier string, +) (*types.GitspaceConfig, error) { + var gitspaceConfigResult *types.GitspaceConfig + txErr := c.tx.WithTx(ctx, func(ctx context.Context) error { + gitspaceConfig, err := c.gitspaceConfigStore.FindByIdentifier(ctx, spaceID, identifier) + if err != nil { + return fmt.Errorf("failed to find gitspace config: %w", err) + } + infraProviderResource, err := c.infraProviderSvc.FindResource(ctx, gitspaceConfig.InfraProviderResourceID) + if err != nil { + return fmt.Errorf("failed to find infra provider resource for gitspace config: %w", err) + } + gitspaceConfig.SpacePath = spacePath + gitspaceConfig.InfraProviderResourceIdentifier = infraProviderResource.Identifier + instance, err := c.gitspaceInstanceStore.FindLatestByGitspaceConfigID(ctx, gitspaceConfig.ID, gitspaceConfig.SpaceID) + if err != nil && err.Error() != resourceNotFoundErr { // TODO fix this + return fmt.Errorf("failed to find gitspace instance for config ID : %s %w", gitspaceConfig.Identifier, err) + } + if instance != nil { + gitspaceConfig.GitspaceInstance = instance + instance.SpacePath = gitspaceConfig.SpacePath + gitspaceStateType, err := enum.GetGitspaceStateFromInstance(instance.State) + if err != nil { + return err + } + gitspaceConfig.State = gitspaceStateType + } else { + gitspaceConfig.State = enum.GitspaceStateUninitialized + } + gitspaceConfigResult = gitspaceConfig + return nil + }, dbtx.TxDefaultReadOnly) + if txErr != nil { + return nil, txErr + } + return gitspaceConfigResult, nil +} diff --git a/app/services/gitspace/gitspace.go b/app/services/gitspace/gitspace.go index 02ce515b1..8eca287c7 100644 --- a/app/services/gitspace/gitspace.go +++ b/app/services/gitspace/gitspace.go @@ -18,6 +18,7 @@ import ( "context" "fmt" + "github.com/harness/gitness/app/services/infraprovider" "github.com/harness/gitness/app/store" "github.com/harness/gitness/store/database/dbtx" "github.com/harness/gitness/types" @@ -29,12 +30,14 @@ func NewService( gitspaceStore store.GitspaceConfigStore, gitspaceInstanceStore store.GitspaceInstanceStore, spaceStore store.SpaceStore, + infraProviderSvc *infraprovider.Service, ) *Service { return &Service{ tx: tx, gitspaceConfigStore: gitspaceStore, gitspaceInstanceStore: gitspaceInstanceStore, spaceStore: spaceStore, + infraProviderSvc: infraProviderSvc, } } @@ -43,6 +46,7 @@ type Service struct { gitspaceInstanceStore store.GitspaceInstanceStore spaceStore store.SpaceStore tx dbtx.Transactor + infraProviderSvc *infraprovider.Service } func (c *Service) ListGitspacesForSpace( diff --git a/app/services/gitspace/update_config.go b/app/services/gitspace/update_config.go new file mode 100644 index 000000000..267ef5776 --- /dev/null +++ b/app/services/gitspace/update_config.go @@ -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 gitspace + +import ( + "context" + "fmt" + + "github.com/harness/gitness/types" +) + +func (c *Service) UpdateConfig( + ctx context.Context, + gitspaceConfig *types.GitspaceConfig, +) error { + err := c.gitspaceConfigStore.Update(ctx, gitspaceConfig) + if err != nil { + return fmt.Errorf("failed to update gitspace config: %w", err) + } + return nil +} diff --git a/app/services/gitspace/update_instance.go b/app/services/gitspace/update_instance.go new file mode 100644 index 000000000..564eea939 --- /dev/null +++ b/app/services/gitspace/update_instance.go @@ -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 gitspace + +import ( + "context" + "fmt" + + "github.com/harness/gitness/types" +) + +func (c *Service) UpdateInstance( + ctx context.Context, + gitspaceInstance *types.GitspaceInstance, +) error { + err := c.gitspaceInstanceStore.Update(ctx, gitspaceInstance) + if err != nil { + return fmt.Errorf("failed to update gitspace instance: %w", err) + } + return nil +} diff --git a/app/services/gitspace/wire.go b/app/services/gitspace/wire.go index b35555296..c41f55854 100644 --- a/app/services/gitspace/wire.go +++ b/app/services/gitspace/wire.go @@ -15,6 +15,7 @@ package gitspace import ( + "github.com/harness/gitness/app/services/infraprovider" "github.com/harness/gitness/app/store" "github.com/harness/gitness/store/database/dbtx" @@ -30,6 +31,7 @@ func ProvideGitspace( gitspaceStore store.GitspaceConfigStore, gitspaceInstanceStore store.GitspaceInstanceStore, spaceStore store.SpaceStore, + infraProviderSvc *infraprovider.Service, ) *Service { - return NewService(tx, gitspaceStore, gitspaceInstanceStore, spaceStore) + return NewService(tx, gitspaceStore, gitspaceInstanceStore, spaceStore, infraProviderSvc) } diff --git a/app/services/gitspaceevent/service.go b/app/services/gitspaceevent/service.go index 4b96a0ae4..eb630f703 100644 --- a/app/services/gitspaceevent/service.go +++ b/app/services/gitspaceevent/service.go @@ -51,13 +51,13 @@ func (c *Config) Sanitize() error { } type Service struct { - config Config + config *Config gitspaceEventStore store.GitspaceEventStore } func NewService( ctx context.Context, - config Config, + config *Config, gitspaceEventReaderFactory *events.ReaderFactory[*gitspaceevents.Reader], gitspaceEventStore store.GitspaceEventStore, ) (*Service, error) { diff --git a/app/services/gitspaceevent/wire.go b/app/services/gitspaceevent/wire.go index 2c24452f6..cdb4400d7 100644 --- a/app/services/gitspaceevent/wire.go +++ b/app/services/gitspaceevent/wire.go @@ -30,7 +30,7 @@ var WireSet = wire.NewSet( ) func ProvideService(ctx context.Context, - config Config, + config *Config, gitspaceEventReaderFactory *events.ReaderFactory[*gitspaceevents.Reader], gitspaceEventStore store.GitspaceEventStore, ) (*Service, error) { diff --git a/app/services/gitspaceinfraevent/handler.go b/app/services/gitspaceinfraevent/handler.go new file mode 100644 index 000000000..5fcc83ebe --- /dev/null +++ b/app/services/gitspaceinfraevent/handler.go @@ -0,0 +1,114 @@ +// 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 gitspaceinfraevent + +import ( + "context" + "fmt" + "time" + + gitspaceEvents "github.com/harness/gitness/app/events/gitspace" + gitspaceInfraEvents "github.com/harness/gitness/app/events/gitspaceinfra" + "github.com/harness/gitness/events" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +func (s *Service) handleGitspaceInfraEvent( + ctx context.Context, + event *events.Event[*gitspaceInfraEvents.GitspaceInfraEventPayload], +) error { + payload := event.Payload + + config, fetchErr := s.getConfig(ctx, payload.Infra.SpaceID, payload.Infra.SpacePath, payload.Infra.ResourceKey) + if fetchErr != nil { + return fetchErr + } + + var instance = config.GitspaceInstance + var err error + + switch payload.Type { + case enum.InfraEventProvision: + updatedInstance, err := s.orchestrator.ResumeStartGitspace(ctx, *config, payload.Infra) + if err != nil { + s.emitGitspaceConfigEvent(ctx, config, enum.GitspaceEventTypeGitspaceActionStartFailed) + + return fmt.Errorf("failed to resume start gitspace: %w", err) + } + + instance = &updatedInstance + + case enum.InfraEventStop: + instanceState, err := s.orchestrator.ResumeStopGitspace(ctx, *config, payload.Infra) + if err != nil { + s.emitGitspaceConfigEvent(ctx, config, enum.GitspaceEventTypeGitspaceActionStopFailed) + + return fmt.Errorf("failed to resume stop gitspace: %w", err) + } + + instance.State = instanceState + + case enum.InfraEventDeprovision: + instanceState, err := s.orchestrator.ResumeDeleteGitspace(ctx, *config, payload.Infra) + if err != nil { + return fmt.Errorf("failed to resume delete gitspace: %w", err) + } + + instance.State = instanceState + + config.IsDeleted = true + if err = s.gitspaceSvc.UpdateConfig(ctx, config); err != nil { + return fmt.Errorf("failed to delete gitspace config with ID: %s %w", config.Identifier, err) + } + + default: + return fmt.Errorf("unknown event type: %s", event.Payload.Type) + } + + err = s.gitspaceSvc.UpdateInstance(ctx, instance) + if err != nil { + return fmt.Errorf("failed to update gitspace instance: %w", err) + } + + return nil +} + +func (s *Service) getConfig( + ctx context.Context, + spaceID int64, + spacePath string, + identifier string, +) (*types.GitspaceConfig, error) { + config, err := s.gitspaceSvc.Find(ctx, spaceID, spacePath, identifier) + if err != nil { + return nil, fmt.Errorf( + "failed to find gitspace config during infra event handling, identifier %s: %w", identifier, err) + } + return config, nil +} + +func (s *Service) emitGitspaceConfigEvent(ctx context.Context, + config *types.GitspaceConfig, + eventType enum.GitspaceEventType, +) { + s.eventReporter.EmitGitspaceEvent(ctx, gitspaceEvents.GitspaceEvent, &gitspaceEvents.GitspaceEventPayload{ + QueryKey: config.Identifier, + EntityID: config.ID, + EntityType: enum.GitspaceEntityTypeGitspaceConfig, + EventType: eventType, + Timestamp: time.Now().UnixNano(), + }) +} diff --git a/app/services/gitspaceinfraevent/service.go b/app/services/gitspaceinfraevent/service.go new file mode 100644 index 000000000..f859bc6ad --- /dev/null +++ b/app/services/gitspaceinfraevent/service.go @@ -0,0 +1,77 @@ +// 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 gitspaceinfraevent + +import ( + "context" + "fmt" + "time" + + gitspaceevents "github.com/harness/gitness/app/events/gitspace" + gitspaceinfraevents "github.com/harness/gitness/app/events/gitspaceinfra" + "github.com/harness/gitness/app/gitspace/orchestrator" + "github.com/harness/gitness/app/services/gitspace" + "github.com/harness/gitness/app/services/gitspaceevent" + "github.com/harness/gitness/events" + "github.com/harness/gitness/stream" +) + +const groupGitspaceInfraEvents = "gitness:gitspaceinfra" + +type Service struct { + config *gitspaceevent.Config + orchestrator orchestrator.Orchestrator + gitspaceSvc *gitspace.Service + eventReporter *gitspaceevents.Reporter +} + +func NewService( + ctx context.Context, + config *gitspaceevent.Config, + gitspaceInfraEventReaderFactory *events.ReaderFactory[*gitspaceinfraevents.Reader], + orchestrator orchestrator.Orchestrator, + gitspaceSvc *gitspace.Service, + eventReporter *gitspaceevents.Reporter, +) (*Service, error) { + if err := config.Sanitize(); err != nil { + return nil, fmt.Errorf("provided gitspace infra event service config is invalid: %w", err) + } + service := &Service{ + config: config, + orchestrator: orchestrator, + gitspaceSvc: gitspaceSvc, + eventReporter: eventReporter, + } + + _, err := gitspaceInfraEventReaderFactory.Launch(ctx, groupGitspaceInfraEvents, config.EventReaderName, + func(r *gitspaceinfraevents.Reader) error { + const idleTimeout = 1 * time.Minute + r.Configure( + stream.WithConcurrency(config.Concurrency), + stream.WithHandlerOptions( + stream.WithIdleTimeout(idleTimeout), + stream.WithMaxRetries(config.MaxRetries), + )) + + _ = r.RegisterGitspaceInfraEvent(service.handleGitspaceInfraEvent) + + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to launch gitspace infra event reader: %w", err) + } + + return service, nil +} diff --git a/app/services/gitspaceinfraevent/wire.go b/app/services/gitspaceinfraevent/wire.go new file mode 100644 index 000000000..b6ac14405 --- /dev/null +++ b/app/services/gitspaceinfraevent/wire.go @@ -0,0 +1,51 @@ +// 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 gitspaceinfraevent + +import ( + "context" + + gitspaceevents "github.com/harness/gitness/app/events/gitspace" + gitspaceinfraevents "github.com/harness/gitness/app/events/gitspaceinfra" + "github.com/harness/gitness/app/gitspace/orchestrator" + "github.com/harness/gitness/app/services/gitspace" + "github.com/harness/gitness/app/services/gitspaceevent" + "github.com/harness/gitness/events" + + "github.com/google/wire" +) + +// WireSet provides a wire set for this package. +var WireSet = wire.NewSet( + ProvideService, +) + +func ProvideService( + ctx context.Context, + config *gitspaceevent.Config, + gitspaceInfraEventReaderFactory *events.ReaderFactory[*gitspaceinfraevents.Reader], + orchestrator orchestrator.Orchestrator, + gitspaceSvc *gitspace.Service, + eventReporter *gitspaceevents.Reporter, +) (*Service, error) { + return NewService( + ctx, + config, + gitspaceInfraEventReaderFactory, + orchestrator, + gitspaceSvc, + eventReporter, + ) +} diff --git a/app/services/infraprovider/infraprovider.go b/app/services/infraprovider/infraprovider.go index 6c9635e9e..a48fd5e6b 100644 --- a/app/services/infraprovider/infraprovider.go +++ b/app/services/infraprovider/infraprovider.go @@ -93,7 +93,7 @@ func (c *Service) CreateInfraProvider( if len(infraProvider.TemplateParams()) > 0 { return fmt.Errorf("failed to fetch templates") // TODO Implement } - parameters := []infraprovider.Parameter{} + parameters := []types.InfraProviderParameter{} // TODO logic to populate paramteters as per the provider type err = infraProvider.ValidateParams(parameters) if err != nil { diff --git a/app/services/wire.go b/app/services/wire.go index b04f77386..2d48972ba 100644 --- a/app/services/wire.go +++ b/app/services/wire.go @@ -18,6 +18,7 @@ import ( "github.com/harness/gitness/app/services/cleanup" "github.com/harness/gitness/app/services/gitspace" "github.com/harness/gitness/app/services/gitspaceevent" + "github.com/harness/gitness/app/services/gitspaceinfraevent" "github.com/harness/gitness/app/services/infraprovider" "github.com/harness/gitness/app/services/keywordsearch" "github.com/harness/gitness/app/services/metric" @@ -36,19 +37,20 @@ var WireSet = wire.NewSet( ) type Services struct { - Webhook *webhook.Service - PullReq *pullreq.Service - Trigger *trigger.Service - JobScheduler *job.Scheduler - MetricCollector *metric.Collector - RepoSizeCalculator *repo.SizeCalculator - Repo *repo.Service - Cleanup *cleanup.Service - Notification *notification.Service - Keywordsearch *keywordsearch.Service - GitspaceEvent *gitspaceevent.Service - infraProvider *infraprovider.Service - gitspace *gitspace.Service + Webhook *webhook.Service + PullReq *pullreq.Service + Trigger *trigger.Service + JobScheduler *job.Scheduler + MetricCollector *metric.Collector + RepoSizeCalculator *repo.SizeCalculator + Repo *repo.Service + Cleanup *cleanup.Service + Notification *notification.Service + Keywordsearch *keywordsearch.Service + GitspaceEvent *gitspaceevent.Service + infraProvider *infraprovider.Service + gitspace *gitspace.Service + gitspaceInfraEventSvc *gitspaceinfraevent.Service } func ProvideServices( @@ -65,20 +67,22 @@ func ProvideServices( gitspaceEventSvc *gitspaceevent.Service, infraProviderSvc *infraprovider.Service, gitspaceSvc *gitspace.Service, + gitspaceInfraEventSvc *gitspaceinfraevent.Service, ) Services { return Services{ - Webhook: webhooksSvc, - PullReq: pullReqSvc, - Trigger: triggerSvc, - JobScheduler: jobScheduler, - MetricCollector: metricCollector, - RepoSizeCalculator: repoSizeCalculator, - Repo: repo, - Cleanup: cleanupSvc, - Notification: notificationSvc, - Keywordsearch: keywordsearchSvc, - GitspaceEvent: gitspaceEventSvc, - infraProvider: infraProviderSvc, - gitspace: gitspaceSvc, + Webhook: webhooksSvc, + PullReq: pullReqSvc, + Trigger: triggerSvc, + JobScheduler: jobScheduler, + MetricCollector: metricCollector, + RepoSizeCalculator: repoSizeCalculator, + Repo: repo, + Cleanup: cleanupSvc, + Notification: notificationSvc, + Keywordsearch: keywordsearchSvc, + GitspaceEvent: gitspaceEventSvc, + infraProvider: infraProviderSvc, + gitspace: gitspaceSvc, + gitspaceInfraEventSvc: gitspaceInfraEventSvc, } } diff --git a/app/store/database/infra_provider_config.go b/app/store/database/infra_provider_config.go index e572d65ca..2c6da3f31 100644 --- a/app/store/database/infra_provider_config.go +++ b/app/store/database/infra_provider_config.go @@ -18,10 +18,10 @@ import ( "context" "github.com/harness/gitness/app/store" - "github.com/harness/gitness/infraprovider/enum" "github.com/harness/gitness/store/database" "github.com/harness/gitness/store/database/dbtx" "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" "github.com/jmoiron/sqlx" "github.com/pkg/errors" diff --git a/app/store/database/infra_provider_resource.go b/app/store/database/infra_provider_resource.go index e8747efe1..c9203be4e 100644 --- a/app/store/database/infra_provider_resource.go +++ b/app/store/database/infra_provider_resource.go @@ -19,10 +19,10 @@ import ( "encoding/json" "github.com/harness/gitness/app/store" - "github.com/harness/gitness/infraprovider/enum" "github.com/harness/gitness/store/database" "github.com/harness/gitness/store/database/dbtx" "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" "github.com/guregu/null" "github.com/jmoiron/sqlx" diff --git a/cli/operations/server/config.go b/cli/operations/server/config.go index eba30d402..297cf0d11 100644 --- a/cli/operations/server/config.go +++ b/cli/operations/server/config.go @@ -413,8 +413,8 @@ func ProvideGitspaceOrchestratorConfig(config *types.Config) *orchestrator.Confi } // ProvideGitspaceEventConfig loads the gitspace event service config from the main config. -func ProvideGitspaceEventConfig(config *types.Config) gitspaceevent.Config { - return gitspaceevent.Config{ +func ProvideGitspaceEventConfig(config *types.Config) *gitspaceevent.Config { + return &gitspaceevent.Config{ EventReaderName: config.InstanceID, Concurrency: config.Gitspace.Events.Concurrency, MaxRetries: config.Gitspace.Events.MaxRetries, diff --git a/cmd/gitness/wire.go b/cmd/gitness/wire.go index 19c34ed46..f3f51a993 100644 --- a/cmd/gitness/wire.go +++ b/cmd/gitness/wire.go @@ -42,6 +42,7 @@ import ( "github.com/harness/gitness/app/bootstrap" gitevents "github.com/harness/gitness/app/events/git" gitspaceevents "github.com/harness/gitness/app/events/gitspace" + gitspaceinfraevents "github.com/harness/gitness/app/events/gitspaceinfra" pullreqevents "github.com/harness/gitness/app/events/pullreq" repoevents "github.com/harness/gitness/app/events/repo" infrastructure "github.com/harness/gitness/app/gitspace/infrastructure" @@ -68,6 +69,7 @@ import ( "github.com/harness/gitness/app/services/exporter" gitspaceSvc "github.com/harness/gitness/app/services/gitspace" "github.com/harness/gitness/app/services/gitspaceevent" + gitspaceInfraEventSvc "github.com/harness/gitness/app/services/gitspaceinfraevent" "github.com/harness/gitness/app/services/importer" infraproviderSvc "github.com/harness/gitness/app/services/infraprovider" "github.com/harness/gitness/app/services/keywordsearch" @@ -153,6 +155,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e gitspaceCtrl.WireSet, infraproviderSvc.WireSet, gitspaceSvc.WireSet, + gitspaceInfraEventSvc.WireSet, gitevents.WireSet, pullreqevents.WireSet, repoevents.WireSet, @@ -231,6 +234,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e logutil.WireSet, cliserver.ProvideGitspaceOrchestratorConfig, ide.WireSet, + gitspaceinfraevents.WireSet, ) return &cliserver.System{}, nil } diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index 4283ded1a..a8b80a266 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -39,9 +39,10 @@ import ( "github.com/harness/gitness/app/auth/authn" "github.com/harness/gitness/app/auth/authz" "github.com/harness/gitness/app/bootstrap" - events4 "github.com/harness/gitness/app/events/git" - events5 "github.com/harness/gitness/app/events/gitspace" - events3 "github.com/harness/gitness/app/events/pullreq" + events5 "github.com/harness/gitness/app/events/git" + events6 "github.com/harness/gitness/app/events/gitspace" + events3 "github.com/harness/gitness/app/events/gitspaceinfra" + events4 "github.com/harness/gitness/app/events/pullreq" events2 "github.com/harness/gitness/app/events/repo" "github.com/harness/gitness/app/gitspace/infrastructure" "github.com/harness/gitness/app/gitspace/logutil" @@ -67,6 +68,7 @@ import ( "github.com/harness/gitness/app/services/exporter" "github.com/harness/gitness/app/services/gitspace" "github.com/harness/gitness/app/services/gitspaceevent" + "github.com/harness/gitness/app/services/gitspaceinfraevent" "github.com/harness/gitness/app/services/importer" infraprovider2 "github.com/harness/gitness/app/services/infraprovider" "github.com/harness/gitness/app/services/keywordsearch" @@ -252,7 +254,21 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro } gitspaceConfigStore := database.ProvideGitspaceConfigStore(db) gitspaceInstanceStore := database.ProvideGitspaceInstanceStore(db) - gitspaceService := gitspace.ProvideGitspace(transactor, gitspaceConfigStore, gitspaceInstanceStore, spaceStore) + infraProviderResourceStore := database.ProvideInfraProviderResourceStore(db) + infraProviderConfigStore := database.ProvideInfraProviderConfigStore(db) + dockerConfig, err := server.ProvideDockerConfig(config) + if err != nil { + return nil, err + } + dockerClientFactory := infraprovider.ProvideDockerClientFactory(dockerConfig) + eventsReporter, err := events3.ProvideReporter(eventsSystem) + if err != nil { + return nil, err + } + dockerProvider := infraprovider.ProvideDockerProvider(dockerConfig, dockerClientFactory, eventsReporter) + factory := infraprovider.ProvideFactory(dockerProvider) + infraproviderService := infraprovider2.ProvideInfraProvider(transactor, infraProviderResourceStore, infraProviderConfigStore, factory, spaceStore) + gitspaceService := gitspace.ProvideGitspace(transactor, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, infraproviderService) spaceController := space.ProvideController(config, transactor, provider, streamer, spaceIdentifier, authorizer, spacePathStore, pipelineStore, secretStore, connectorStore, templateStore, spaceStore, repoStore, principalStore, repoController, membershipStore, repository, exporterRepository, resourceLimiter, publicaccessService, auditService, gitspaceService, gitspaceConfigStore, gitspaceInstanceStore, labelService) pipelineController := pipeline.ProvideController(repoStore, triggerStore, authorizer, pipelineStore) secretController := secret.ProvideController(encrypter, secretStore, authorizer, spaceStore) @@ -266,27 +282,27 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro pullReqReviewStore := database.ProvidePullReqReviewStore(db) pullReqReviewerStore := database.ProvidePullReqReviewerStore(db, principalInfoCache) pullReqFileViewStore := database.ProvidePullReqFileViewStore(db) - eventsReporter, err := events3.ProvideReporter(eventsSystem) + reporter2, err := events4.ProvideReporter(eventsSystem) if err != nil { return nil, err } migrator := codecomments.ProvideMigrator(gitInterface) - readerFactory, err := events4.ProvideReaderFactory(eventsSystem) + readerFactory, err := events5.ProvideReaderFactory(eventsSystem) if err != nil { return nil, err } - eventsReaderFactory, err := events3.ProvideReaderFactory(eventsSystem) + eventsReaderFactory, err := events4.ProvideReaderFactory(eventsSystem) if err != nil { return nil, err } repoGitInfoView := database.ProvideRepoGitInfoView(db) repoGitInfoCache := cache.ProvideRepoGitInfoCache(repoGitInfoView) - pullreqService, err := pullreq.ProvideService(ctx, config, readerFactory, eventsReaderFactory, eventsReporter, gitInterface, repoGitInfoCache, repoStore, pullReqStore, pullReqActivityStore, codeCommentView, migrator, pullReqFileViewStore, pubSub, provider, streamer) + pullreqService, err := pullreq.ProvideService(ctx, config, readerFactory, eventsReaderFactory, reporter2, gitInterface, repoGitInfoCache, repoStore, pullReqStore, pullReqActivityStore, codeCommentView, migrator, pullReqFileViewStore, pubSub, provider, streamer) if err != nil { return nil, err } pullReq := importer.ProvidePullReqImporter(provider, gitInterface, principalStore, repoStore, pullReqStore, pullReqActivityStore, transactor) - pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, eventsReporter, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker, pullReq, labelService) + pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, reporter2, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker, pullReq, labelService) webhookConfig := server.ProvideWebhookConfig(config) webhookStore := database.ProvideWebhookStore(db) webhookExecutionStore := database.ProvideWebhookExecutionStore(db) @@ -295,7 +311,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro return nil, err } webhookController := webhook2.ProvideController(webhookConfig, authorizer, webhookStore, webhookExecutionStore, repoStore, webhookService, encrypter) - reporter2, err := events4.ProvideReporter(eventsSystem) + reporter3, err := events5.ProvideReporter(eventsSystem) if err != nil { return nil, err } @@ -311,7 +327,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro if err != nil { return nil, err } - githookController := githook.ProvideController(authorizer, principalStore, repoStore, reporter2, reporter, gitInterface, pullReqStore, provider, protectionManager, clientFactory, resourceLimiter, settingsService, preReceiveExtender, updateExtender, postReceiveExtender) + githookController := githook.ProvideController(authorizer, principalStore, repoStore, reporter3, reporter, gitInterface, pullReqStore, provider, protectionManager, clientFactory, resourceLimiter, settingsService, preReceiveExtender, updateExtender, postReceiveExtender) serviceaccountController := serviceaccount.NewController(principalUID, authorizer, principalStore, spaceStore, repoStore, tokenStore) principalController := principal.ProvideController(principalStore, authorizer) v := check2.ProvideCheckSanitizers() @@ -328,18 +344,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro uploadController := upload.ProvideController(authorizer, repoStore, blobStore) searcher := keywordsearch.ProvideSearcher(localIndexSearcher) keywordsearchController := keywordsearch2.ProvideController(authorizer, searcher, repoController, spaceController) - infraProviderResourceStore := database.ProvideInfraProviderResourceStore(db) - infraProviderConfigStore := database.ProvideInfraProviderConfigStore(db) - dockerConfig, err := server.ProvideDockerConfig(config) - if err != nil { - return nil, err - } - dockerClientFactory := infraprovider.ProvideDockerClientFactory(dockerConfig) - dockerProvider := infraprovider.ProvideDockerProvider(dockerConfig, dockerClientFactory) - factory := infraprovider.ProvideFactory(dockerProvider) - infraproviderService := infraprovider2.ProvideInfraProvider(transactor, infraProviderResourceStore, infraProviderConfigStore, factory, spaceStore) infraproviderController := infraprovider3.ProvideController(authorizer, spaceStore, infraproviderService) - reporter3, err := events5.ProvideReporter(eventsSystem) + reporter4, err := events6.ProvideReporter(eventsSystem) if err != nil { return nil, err } @@ -354,9 +360,9 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro vsCode := ide.ProvideVSCodeService() vsCodeWebConfig := server.ProvideIDEVSCodeWebConfig(config) vsCodeWeb := ide.ProvideVSCodeWebService(vsCodeWebConfig) - orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter3, orchestratorConfig, vsCode, vsCodeWeb) + orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter4, orchestratorConfig, vsCode, vsCodeWeb) gitspaceEventStore := database.ProvideGitspaceEventStore(db) - gitspaceController := gitspace2.ProvideController(transactor, authorizer, infraproviderService, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter3, orchestratorOrchestrator, gitspaceEventStore, statefulLogger, scmSCM, repoStore) + gitspaceController := gitspace2.ProvideController(transactor, authorizer, infraproviderService, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter4, orchestratorOrchestrator, gitspaceEventStore, statefulLogger, scmSCM, repoStore, gitspaceService) 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) @@ -410,7 +416,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro return nil, err } gitspaceeventConfig := server.ProvideGitspaceEventConfig(config) - readerFactory3, err := events5.ProvideReaderFactory(eventsSystem) + readerFactory3, err := events6.ProvideReaderFactory(eventsSystem) if err != nil { return nil, err } @@ -418,7 +424,15 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro if err != nil { return nil, err } - servicesServices := services.ProvideServices(webhookService, pullreqService, triggerService, jobScheduler, collector, sizeCalculator, repoService, cleanupService, notificationService, keywordsearchService, gitspaceeventService, infraproviderService, gitspaceService) + readerFactory4, err := events3.ProvideReaderFactory(eventsSystem) + if err != nil { + return nil, err + } + gitspaceinfraeventService, err := gitspaceinfraevent.ProvideService(ctx, gitspaceeventConfig, readerFactory4, orchestratorOrchestrator, gitspaceService, reporter4) + if err != nil { + return nil, err + } + servicesServices := services.ProvideServices(webhookService, pullreqService, triggerService, jobScheduler, collector, sizeCalculator, repoService, cleanupService, notificationService, keywordsearchService, gitspaceeventService, infraproviderService, gitspaceService, gitspaceinfraeventService) serverSystem := server.NewSystem(bootstrapBootstrap, serverServer, sshServer, poller, resolverManager, servicesServices) return serverSystem, nil } diff --git a/infraprovider/docker_client_factory.go b/infraprovider/docker_client_factory.go index f37d5a1dc..8e0264a9b 100644 --- a/infraprovider/docker_client_factory.go +++ b/infraprovider/docker_client_factory.go @@ -20,7 +20,8 @@ import ( "net/http" "path/filepath" - "github.com/harness/gitness/infraprovider/enum" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" "github.com/docker/docker/client" "github.com/docker/go-connections/tlsconfig" @@ -37,7 +38,7 @@ func NewDockerClientFactory(config *DockerConfig) *DockerClientFactory { // NewDockerClient returns a new docker client created using the docker config and infra. func (d *DockerClientFactory) NewDockerClient( _ context.Context, - infra *Infrastructure, + infra *types.Infrastructure, ) (*client.Client, error) { if infra.ProviderType != enum.InfraProviderTypeDocker { return nil, fmt.Errorf("infra provider type %s not supported", infra.ProviderType) @@ -49,7 +50,7 @@ func (d *DockerClientFactory) NewDockerClient( return dockerClient, nil } -func (d *DockerClientFactory) getClient(_ []Parameter) (*client.Client, error) { +func (d *DockerClientFactory) getClient(_ []types.InfraProviderParameter) (*client.Client, error) { var opts []client.Opt opts = append(opts, client.WithHost(d.config.DockerHost)) diff --git a/infraprovider/docker_provider.go b/infraprovider/docker_provider.go index 231d0178c..e833dbef8 100644 --- a/infraprovider/docker_provider.go +++ b/infraprovider/docker_provider.go @@ -19,7 +19,9 @@ import ( "fmt" "strings" - "github.com/harness/gitness/infraprovider/enum" + events "github.com/harness/gitness/app/events/gitspaceinfra" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" "github.com/docker/docker/api/types/volume" "github.com/docker/docker/client" @@ -31,15 +33,18 @@ var _ InfraProvider = (*DockerProvider)(nil) type DockerProvider struct { config *DockerConfig dockerClientFactory *DockerClientFactory + eventReporter *events.Reporter } func NewDockerProvider( config *DockerConfig, dockerClientFactory *DockerClientFactory, + eventReporter *events.Reporter, ) *DockerProvider { return &DockerProvider{ config: config, dockerClientFactory: dockerClientFactory, + eventReporter: eventReporter, } } @@ -47,17 +52,18 @@ func NewDockerProvider( // It does not start docker engine. It creates a directory in the host machine using the given resource key. func (d DockerProvider) Provision( ctx context.Context, + spaceID int64, spacePath string, resourceKey string, requiredPorts []int, - params []Parameter, -) (*Infrastructure, error) { - dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &Infrastructure{ + params []types.InfraProviderParameter, +) error { + dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &types.Infrastructure{ ProviderType: enum.InfraProviderTypeDocker, Parameters: params, }) if err != nil { - return nil, fmt.Errorf("error getting docker client from docker client factory: %w", err) + return fmt.Errorf("error getting docker client from docker client factory: %w", err) } defer func() { @@ -69,20 +75,24 @@ func (d DockerProvider) Provision( infrastructure, err := d.dockerHostInfo(ctx, dockerClient) if err != nil { - return nil, err + return err } + infrastructure.SpaceID = spaceID + infrastructure.SpacePath = spacePath + infrastructure.ResourceKey = resourceKey + storageName, err := d.createNamedVolume(ctx, spacePath, resourceKey, dockerClient) if err != nil { - return nil, err + return err } infrastructure.Storage = storageName - var portMappings = make(map[int]*PortMapping, len(requiredPorts)) + var portMappings = make(map[int]*types.PortMapping, len(requiredPorts)) for _, requiredPort := range requiredPorts { - portMapping := &PortMapping{ + portMapping := &types.PortMapping{ PublishedPort: 0, ForwardedPort: 0, } @@ -92,55 +102,81 @@ func (d DockerProvider) Provision( infrastructure.PortMappings = portMappings - return infrastructure, nil + event := &events.GitspaceInfraEventPayload{ + Infra: infrastructure, + Type: enum.InfraEventProvision, + } + + d.eventReporter.EmitGitspaceInfraEvent(ctx, events.GitspaceInfraEvent, event) + + return nil } // Find fetches the infrastructure with the current state, the method has no side effects on the infra. func (d DockerProvider) Find( ctx context.Context, + spaceID int64, spacePath string, resourceKey string, - params []Parameter, -) (*Infrastructure, error) { - dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &Infrastructure{ + params []types.InfraProviderParameter, +) (*types.Infrastructure, error) { + dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &types.Infrastructure{ ProviderType: enum.InfraProviderTypeDocker, Parameters: params, }) + if err != nil { return nil, fmt.Errorf("error getting docker client from docker client factory: %w", err) } + defer func() { closingErr := dockerClient.Close() if closingErr != nil { log.Ctx(ctx).Warn().Err(closingErr).Msg("failed to close docker client") } }() + infrastructure, err := d.dockerHostInfo(ctx, dockerClient) if err != nil { return nil, err } + + infrastructure.SpaceID = spaceID + infrastructure.SpacePath = spacePath + infrastructure.ResourceKey = resourceKey + name := volumeName(spacePath, resourceKey) + volumeInspect, err := dockerClient.VolumeInspect(ctx, name) if err != nil { return nil, fmt.Errorf("couldn't find the volume for %s : %w", name, err) } + infrastructure.Storage = volumeInspect.Name + return infrastructure, nil } // Stop is NOOP as this provider uses already running docker engine. It does not stop the docker engine. -func (d DockerProvider) Stop(_ context.Context, infra *Infrastructure) (*Infrastructure, error) { - return infra, nil +func (d DockerProvider) Stop(ctx context.Context, infra *types.Infrastructure) error { + event := &events.GitspaceInfraEventPayload{ + Infra: infra, + Type: enum.InfraEventStop, + } + + d.eventReporter.EmitGitspaceInfraEvent(ctx, events.GitspaceInfraEvent, event) + + return nil } -// Deprovision deletes the host machine directory created by Provision. It does not stop the docker engine. -func (d DockerProvider) Deprovision(ctx context.Context, infra *Infrastructure) (*Infrastructure, error) { - dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &Infrastructure{ +// Deprovision deletes the volume created by Provision. It does not stop the docker engine. +func (d DockerProvider) Deprovision(ctx context.Context, infra *types.Infrastructure) error { + dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, &types.Infrastructure{ ProviderType: enum.InfraProviderTypeDocker, Parameters: infra.Parameters, }) if err != nil { - return nil, fmt.Errorf("error getting docker client from docker client factory: %w", err) + return fmt.Errorf("error getting docker client from docker client factory: %w", err) } defer func() { closingErr := dockerClient.Close() @@ -150,23 +186,31 @@ func (d DockerProvider) Deprovision(ctx context.Context, infra *Infrastructure) }() err = dockerClient.VolumeRemove(ctx, infra.Storage, true) if err != nil { - return nil, fmt.Errorf("couldn't delete volume for %s : %w", infra.Storage, err) + return fmt.Errorf("couldn't delete volume for %s : %w", infra.Storage, err) } - return infra, nil + + event := &events.GitspaceInfraEventPayload{ + Infra: infra, + Type: enum.InfraEventDeprovision, + } + + d.eventReporter.EmitGitspaceInfraEvent(ctx, events.GitspaceInfraEvent, event) + + return nil } // AvailableParams returns empty slice as no params are defined. -func (d DockerProvider) AvailableParams() []ParameterSchema { - return []ParameterSchema{} +func (d DockerProvider) AvailableParams() []types.InfraProviderParameterSchema { + return []types.InfraProviderParameterSchema{} } // ValidateParams returns nil as no params are defined. -func (d DockerProvider) ValidateParams(_ []Parameter) error { +func (d DockerProvider) ValidateParams(_ []types.InfraProviderParameter) error { return nil } // TemplateParams returns nil as no template params are used. -func (d DockerProvider) TemplateParams() []ParameterSchema { +func (d DockerProvider) TemplateParams() []types.InfraProviderParameterSchema { return nil } @@ -175,12 +219,15 @@ func (d DockerProvider) ProvisioningType() enum.InfraProvisioningType { return enum.InfraProvisioningTypeExisting } -func (d DockerProvider) dockerHostInfo(ctx context.Context, dockerClient *client.Client) (*Infrastructure, error) { +func (d DockerProvider) dockerHostInfo( + ctx context.Context, + dockerClient *client.Client, +) (*types.Infrastructure, error) { info, err := dockerClient.Info(ctx) if err != nil { return nil, fmt.Errorf("unable to connect to docker engine: %w", err) } - return &Infrastructure{ + return &types.Infrastructure{ Identifier: info.ID, ProviderType: enum.InfraProviderTypeDocker, Status: enum.InfraStatusProvisioned, diff --git a/infraprovider/infra_provider.go b/infraprovider/infra_provider.go index f5985d6c8..926d181ff 100644 --- a/infraprovider/infra_provider.go +++ b/infraprovider/infra_provider.go @@ -17,30 +17,38 @@ package infraprovider import ( "context" - "github.com/harness/gitness/infraprovider/enum" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" ) type InfraProvider interface { // Provision provisions infrastructure against a resourceKey with the provided parameters. Provision( ctx context.Context, + spaceID int64, spacePath string, resourceKey string, requiredPorts []int, - parameters []Parameter, - ) (*Infrastructure, error) + parameters []types.InfraProviderParameter, + ) error // Find finds infrastructure provisioned against a resourceKey. - Find(ctx context.Context, spacePath string, resourceKey string, parameters []Parameter) (*Infrastructure, error) + Find( + ctx context.Context, + spaceID int64, + spacePath string, + resourceKey string, + parameters []types.InfraProviderParameter, + ) (*types.Infrastructure, error) // Stop frees up the resources allocated against a resourceKey, which can be freed. - Stop(ctx context.Context, infra *Infrastructure) (*Infrastructure, error) + Stop(ctx context.Context, infra *types.Infrastructure) error // Deprovision removes all infrastructure provisioned againest the resourceKey. - Deprovision(ctx context.Context, infra *Infrastructure) (*Infrastructure, error) + Deprovision(ctx context.Context, infra *types.Infrastructure) error // AvailableParams provides a schema to define the infrastructure. - AvailableParams() []ParameterSchema + AvailableParams() []types.InfraProviderParameterSchema // ValidateParams validates the supplied params before defining the infrastructure resource . - ValidateParams(parameters []Parameter) error + ValidateParams(parameters []types.InfraProviderParameter) error // TemplateParams provides a list of params which are of type template. - TemplateParams() []ParameterSchema + TemplateParams() []types.InfraProviderParameterSchema // ProvisioningType specifies whether the provider will provision new infra resources or it will reuse existing. ProvisioningType() enum.InfraProvisioningType } diff --git a/infraprovider/infra_provider_factory.go b/infraprovider/infra_provider_factory.go index acc53cd48..4ba48377b 100644 --- a/infraprovider/infra_provider_factory.go +++ b/infraprovider/infra_provider_factory.go @@ -17,7 +17,7 @@ package infraprovider import ( "fmt" - "github.com/harness/gitness/infraprovider/enum" + "github.com/harness/gitness/types/enum" ) type Factory struct { diff --git a/infraprovider/wire.go b/infraprovider/wire.go index fd3cd1b7a..8900c9f24 100644 --- a/infraprovider/wire.go +++ b/infraprovider/wire.go @@ -15,6 +15,8 @@ package infraprovider import ( + events "github.com/harness/gitness/app/events/gitspaceinfra" + "github.com/google/wire" ) @@ -28,8 +30,9 @@ var WireSet = wire.NewSet( func ProvideDockerProvider( config *DockerConfig, dockerClientFactory *DockerClientFactory, + eventReporter *events.Reporter, ) *DockerProvider { - return NewDockerProvider(config, dockerClientFactory) + return NewDockerProvider(config, dockerClientFactory, eventReporter) } func ProvideFactory(dockerProvider *DockerProvider) Factory { diff --git a/types/enum/gitspace_state_type.go b/types/enum/gitspace_state_type.go index 4401adf62..0e83a13c6 100644 --- a/types/enum/gitspace_state_type.go +++ b/types/enum/gitspace_state_type.go @@ -41,7 +41,8 @@ const ( ) func GetGitspaceStateFromInstance( - instanceState GitspaceInstanceStateType) (GitspaceStateType, error) { + instanceState GitspaceInstanceStateType, +) (GitspaceStateType, error) { switch instanceState { case GitspaceInstanceStateRunning: return GitspaceStateRunning, nil diff --git a/types/enum/infra_event.go b/types/enum/infra_event.go new file mode 100644 index 000000000..50511bfeb --- /dev/null +++ b/types/enum/infra_event.go @@ -0,0 +1,31 @@ +// 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 enum + +type InfraEvent string + +func (InfraEvent) Enum() []interface{} { + return toInterfaceSlice(infraEvents) +} + +var infraEvents = []InfraEvent{ + InfraEventProvision, InfraEventStop, InfraEventDeprovision, +} + +const ( + InfraEventProvision InfraEvent = "provision" + InfraEventStop InfraEvent = "stop" + InfraEventDeprovision InfraEvent = "deprovision" +) diff --git a/infraprovider/enum/infra_provider_type.go b/types/enum/infra_provider_type.go similarity index 100% rename from infraprovider/enum/infra_provider_type.go rename to types/enum/infra_provider_type.go diff --git a/infraprovider/enum/infra_status.go b/types/enum/infra_status.go similarity index 100% rename from infraprovider/enum/infra_status.go rename to types/enum/infra_status.go diff --git a/infraprovider/enum/provisioning_type.go b/types/enum/provisioning_type.go similarity index 100% rename from infraprovider/enum/provisioning_type.go rename to types/enum/provisioning_type.go diff --git a/types/infra_provider.go b/types/infra_provider.go index 3aa195fef..f7ecd724d 100644 --- a/types/infra_provider.go +++ b/types/infra_provider.go @@ -15,7 +15,7 @@ package types import ( - "github.com/harness/gitness/infraprovider/enum" + "github.com/harness/gitness/types/enum" ) type InfraProviderConfig struct { diff --git a/infraprovider/types.go b/types/infrastructure.go similarity index 81% rename from infraprovider/types.go rename to types/infrastructure.go index bb7da21bf..846648ef8 100644 --- a/infraprovider/types.go +++ b/types/infrastructure.go @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -package infraprovider +package types -import "github.com/harness/gitness/infraprovider/enum" +import "github.com/harness/gitness/types/enum" -type ParameterSchema struct { +type InfraProviderParameterSchema struct { Name string Description string DefaultValue string @@ -25,7 +25,7 @@ type ParameterSchema struct { Editable bool } -type Parameter struct { +type InfraProviderParameter struct { Name string Value string } @@ -49,13 +49,15 @@ type Infrastructure struct { // ProviderType specifies the type of the infra provider. ProviderType enum.InfraProviderType // Parameters which are required by the provider to provision the infra. - Parameters []Parameter + Parameters []InfraProviderParameter // Status of the infra. Status enum.InfraStatus - // Host through which the infra can be accessed for all purposes. - Host string - // Port on which the infra can be accessed to orchestrate containers. - Port int + // Host through which the infra can be accessed. + Host string + ProxyHost string + // AgentPort on which the agent can be accessed to orchestrate containers. + AgentPort int + ProxyPort int // Storage is the name of the volume or disk created for the resource. Storage string // PortMappings contains the ports assigned for every requested port.