From 6983438ec2da4a3194181a80045c2bf1b74d8f22 Mon Sep 17 00:00:00 2001 From: Deepak Bhatt Date: Mon, 28 Oct 2024 05:30:14 +0000 Subject: [PATCH] feat: [CDE-391]:add infrastruce and orchestrator funcs (#2841) * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into add-force-delete-job-funcs * change name * increase scope of delete * change func name * modify delete api * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into add-force-delete-job-funcs * add delete and handler condition * address comment * handle cleanup event * update enum mapping * find destroyed infra as well * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into add-force-delete-job-funcs * modify find signature * Merge branch 'add-force-delete-job-funcs' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into add-force-delete-job-funcs * address feedback 2 * address feedback * Apply suggestion from code review * Apply suggestion from code review * remove std out * add infrastruce and orchestrator funcs * add and modify infra provider methods Add cleanupInstanceResources method Modify Deprovision method and make it idempotent * address feedback 2 * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into add-force-delete-job-funcs * Merge branch 'add-force-delete-job-funcs' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into add-force-delete-job-funcs * address feedback * Apply suggestion from code review * Apply suggestion from code review * remove std out * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into add-force-delete-job-funcs * add infrastruce and orchestrator funcs * add and modify infra provider methods Add cleanupInstanceResources method Modify Deprovision method and make it idempotent --- app/api/controller/gitspace/controller.go | 3 + app/api/controller/gitspace/delete.go | 7 ++ .../cleanup_instance_resources.go | 70 ++++++++++++++++ app/gitspace/infrastructure/deprovision.go | 11 ++- app/gitspace/infrastructure/provisioner.go | 23 ++++- app/gitspace/orchestrator/orchestrator.go | 16 +++- .../orchestrator/orchestrator_impl.go | 81 +++++++++++++++++- app/services/gitspace/delete.go | 2 +- app/services/gitspace/find.go | 3 +- app/services/gitspaceinfraevent/handler.go | 11 ++- app/store/database.go | 2 +- app/store/database/gitspace_config.go | 10 ++- infraprovider/docker_provider.go | 84 +++++++++++++++---- infraprovider/infra_provider.go | 9 +- types/enum/gitspace_event_type.go | 5 ++ types/enum/gitspace_instance_state_type.go | 8 +- types/enum/gitspace_state_type.go | 7 ++ types/enum/infra_event.go | 1 + 18 files changed, 317 insertions(+), 36 deletions(-) create mode 100644 app/gitspace/infrastructure/cleanup_instance_resources.go diff --git a/app/api/controller/gitspace/controller.go b/app/api/controller/gitspace/controller.go index e60801e99..b26c4e1b5 100644 --- a/app/api/controller/gitspace/controller.go +++ b/app/api/controller/gitspace/controller.go @@ -25,6 +25,9 @@ import ( "github.com/harness/gitness/store/database/dbtx" ) +// gitspaceInstanceCleaningTimedOutMins is timeout for which a gitspace instance can be in cleaning state. +const gitspaceInstanceCleaningTimedOutMins = 10 + type Controller struct { authorizer authz.Authorizer infraProviderSvc *infraprovider.Service diff --git a/app/api/controller/gitspace/delete.go b/app/api/controller/gitspace/delete.go index de7a40476..205bc4b01 100644 --- a/app/api/controller/gitspace/delete.go +++ b/app/api/controller/gitspace/delete.go @@ -55,6 +55,7 @@ func (c *Controller) Delete( instance, _ := c.gitspaceInstanceStore.FindLatestByGitspaceConfigID(ctx, gitspaceConfig.ID) gitspaceConfig.GitspaceInstance = instance if instance == nil || instance.State == enum.GitspaceInstanceStateUninitialized { + gitspaceConfig.IsMarkedForDeletion = true gitspaceConfig.IsDeleted = true if err = c.gitspaceSvc.UpdateConfig(ctx, gitspaceConfig); err != nil { return fmt.Errorf("failed to mark gitspace config as deleted: %w", err) @@ -87,6 +88,12 @@ func (c *Controller) removeGitspace(ctx context.Context, config types.GitspaceCo config.GitspaceInstance.Identifier) return } + } else if config.GitspaceInstance.State == enum.GitSpaceInstanceStateCleaning && + time.Since(time.UnixMilli(config.GitspaceInstance.Updated)).Milliseconds() <= + (gitspaceInstanceCleaningTimedOutMins*60*1000) { + log.Ctx(ctx).Warn().Msgf("gitspace start/stop is already pending for : %q", + config.GitspaceInstance.Identifier) + return } if err := c.gitspaceSvc.TriggerDelete(ctx, config); err != nil { log.Ctx(ctx).Err(err).Msgf("error during triggering delete for gitspace instance %s", diff --git a/app/gitspace/infrastructure/cleanup_instance_resources.go b/app/gitspace/infrastructure/cleanup_instance_resources.go new file mode 100644 index 000000000..77192cb77 --- /dev/null +++ b/app/gitspace/infrastructure/cleanup_instance_resources.go @@ -0,0 +1,70 @@ +// 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 infrastructure + +import ( + "context" + "fmt" + + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +func (i infraProvisioner) TriggerCleanupInstance( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + infra types.Infrastructure, +) error { + infraProviderEntity, err := i.getConfigFromResource(ctx, gitspaceConfig.InfraProviderResource) + if err != nil { + return err + } + + infraProvider, err := i.getInfraProvider(infraProviderEntity.Type) + if err != nil { + return err + } + + return infraProvider.CleanupInstanceResources(ctx, infra) +} + +func (i infraProvisioner) ResumeCleanupInstance( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + cleanedInfra types.Infrastructure, +) error { + infraProvider, err := i.getInfraProvider(cleanedInfra.ProviderType) + if err != nil { + return err + } + + if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { + return i.resumeCleanupForNewProvisioning(ctx, gitspaceConfig, cleanedInfra) + } + return nil +} + +func (i infraProvisioner) resumeCleanupForNewProvisioning( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + cleanedInfra types.Infrastructure, +) error { + err := i.updateInfraProvisionedRecord(ctx, gitspaceConfig, cleanedInfra) + if err != nil { + return fmt.Errorf("unable to update provisioned record after cleanup: %w", err) + } + + return nil +} diff --git a/app/gitspace/infrastructure/deprovision.go b/app/gitspace/infrastructure/deprovision.go index 8257d68ef..be25d3871 100644 --- a/app/gitspace/infrastructure/deprovision.go +++ b/app/gitspace/infrastructure/deprovision.go @@ -27,6 +27,7 @@ func (i infraProvisioner) TriggerDeprovision( ctx context.Context, gitspaceConfig types.GitspaceConfig, infra types.Infrastructure, + canDeleteUserData bool, ) error { infraProviderEntity, err := i.getConfigFromResource(ctx, gitspaceConfig.InfraProviderResource) if err != nil { @@ -39,9 +40,9 @@ func (i infraProvisioner) TriggerDeprovision( } if infraProvider.ProvisioningType() == enum.InfraProvisioningTypeNew { - return i.triggerDeprovisionForNewProvisioning(ctx, infraProvider, gitspaceConfig, infra) + return i.triggerDeprovisionForNewProvisioning(ctx, infraProvider, gitspaceConfig, infra, canDeleteUserData) } - return i.triggerDeprovisionForExistingProvisioning(ctx, infraProvider, infra) + return i.triggerDeprovisionForExistingProvisioning(ctx, infraProvider, infra, canDeleteUserData) } func (i infraProvisioner) triggerDeprovisionForNewProvisioning( @@ -49,6 +50,7 @@ func (i infraProvisioner) triggerDeprovisionForNewProvisioning( infraProvider infraprovider.InfraProvider, gitspaceConfig types.GitspaceConfig, infra types.Infrastructure, + canDeleteUserData bool, ) error { infraProvisionedLatest, err := i.infraProvisionedStore.FindLatestByGitspaceInstanceID( ctx, gitspaceConfig.SpaceID, gitspaceConfig.GitspaceInstance.ID) @@ -62,7 +64,7 @@ func (i infraProvisioner) triggerDeprovisionForNewProvisioning( return nil } - err = infraProvider.Deprovision(ctx, infra) + err = infraProvider.Deprovision(ctx, infra, canDeleteUserData) if err != nil { return fmt.Errorf("unable to trigger deprovision infra %+v: %w", infra, err) } @@ -74,8 +76,9 @@ func (i infraProvisioner) triggerDeprovisionForExistingProvisioning( ctx context.Context, infraProvider infraprovider.InfraProvider, infra types.Infrastructure, + canDeleteUserData bool, ) error { - err := infraProvider.Deprovision(ctx, infra) + err := infraProvider.Deprovision(ctx, infra, canDeleteUserData) if err != nil { return fmt.Errorf("unable to trigger deprovision infra %+v: %w", infra, err) } diff --git a/app/gitspace/infrastructure/provisioner.go b/app/gitspace/infrastructure/provisioner.go index b3aeb5ba1..9c506f6e3 100644 --- a/app/gitspace/infrastructure/provisioner.go +++ b/app/gitspace/infrastructure/provisioner.go @@ -52,15 +52,32 @@ type InfraProvisioner interface { deprovisionedInfra types.Infrastructure, ) error - // TriggerDeprovision triggers deprovisionign of all the resources created for the Gitspace. + // TriggerDeprovision triggers deprovisionign of resources created for a Gitspace. + // canDeleteUserData = true -> triggers deprovision of all resources + // canDeleteUserData = false -> triggers deprovision of all resources except storage associated to user data. TriggerDeprovision( ctx context.Context, gitspaceConfig types.GitspaceConfig, infra types.Infrastructure, + canDeleteUserData bool, + ) error + + // ResumeDeprovision stores the deprovisioned infra details in the db depending on the provisioning type. + ResumeDeprovision( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + deprovisionedInfra types.Infrastructure, + ) error + + // TriggerCleanupInstance cleans up resources exclusive for a gitspace instance + TriggerCleanupInstance( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + infra types.Infrastructure, ) error - // ResumeDeprovision stores the deprovisioned infra details in the db depending on the provisioning type. - ResumeDeprovision( + // ResumeCleanupInstance stores the deprovisioned infra details in the db depending on the provisioning type. + ResumeCleanupInstance( ctx context.Context, gitspaceConfig types.GitspaceConfig, deprovisionedInfra types.Infrastructure, diff --git a/app/gitspace/orchestrator/orchestrator.go b/app/gitspace/orchestrator/orchestrator.go index 7b814fe56..434749cff 100644 --- a/app/gitspace/orchestrator/orchestrator.go +++ b/app/gitspace/orchestrator/orchestrator.go @@ -43,9 +43,21 @@ type Orchestrator interface { stoppedInfra types.Infrastructure, ) (enum.GitspaceInstanceStateType, error) + // TriggerCleanupInstanceResources cleans up all the resources exclusive to gitspace instance. + TriggerCleanupInstanceResources(ctx context.Context, gitspaceConfig types.GitspaceConfig) error + + // ResumeCleanupInstanceResources saves the cleaned up infra details. + ResumeCleanupInstanceResources( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + cleanedUpInfra types.Infrastructure, + ) (enum.GitspaceInstanceStateType, error) + // TriggerDeleteGitspace removes the Gitspace container and triggers infra deprovisioning to deprovision - // all the infra resources. - TriggerDeleteGitspace(ctx context.Context, gitspaceConfig types.GitspaceConfig) error + // the infra resources. + // canDeleteUserData = false -> trigger deprovision of all resources except storage associated to user data. + // canDeleteUserData = true -> trigger deprovision of all resources. + TriggerDeleteGitspace(ctx context.Context, gitspaceConfig types.GitspaceConfig, canDeleteUserData bool) error // ResumeDeleteGitspace saves the deprovisioned infra details. ResumeDeleteGitspace( diff --git a/app/gitspace/orchestrator/orchestrator_impl.go b/app/gitspace/orchestrator/orchestrator_impl.go index 387b42c3f..4a898dc57 100644 --- a/app/gitspace/orchestrator/orchestrator_impl.go +++ b/app/gitspace/orchestrator/orchestrator_impl.go @@ -199,12 +199,89 @@ func (o orchestrator) stopAndRemoveGitspaceContainer( return nil } +func (o orchestrator) TriggerCleanupInstanceResources(ctx context.Context, gitspaceConfig types.GitspaceConfig) error { + infra, err := o.getProvisionedInfra(ctx, gitspaceConfig, + []enum.InfraStatus{ + enum.InfraStatusProvisioned, + enum.InfraStatusStopped, + enum.InfraStatusPending, + enum.InfraStatusUnknown, + enum.InfraStatusDestroyed, + }) + if err != nil { + return fmt.Errorf( + "unable to find provisioned infra while triggering cleanup for gitspace instance %s: %w", + gitspaceConfig.GitspaceInstance.Identifier, err) + } + + if gitspaceConfig.GitspaceInstance.State != enum.GitSpaceInstanceStateCleaning { + return fmt.Errorf("cannot trigger cleanup, expected state: %s, actual state: %s ", + enum.GitSpaceInstanceStateCleaning, + gitspaceConfig.GitspaceInstance.State, + ) + } + + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraCleanupStart) + + err = o.infraProvisioner.TriggerCleanupInstance(ctx, gitspaceConfig, *infra) + if err != nil { + return fmt.Errorf("cannot trigger cleanup infrastructure with ID %s: %w", + gitspaceConfig.InfraProviderResource.UID, + err, + ) + } + + return nil +} + +func (o orchestrator) ResumeCleanupInstanceResources( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + cleanedUpInfra types.Infrastructure, +) (enum.GitspaceInstanceStateType, error) { + instanceState := enum.GitspaceInstanceStateError + + err := o.infraProvisioner.ResumeCleanupInstance(ctx, gitspaceConfig, cleanedUpInfra) + if err != nil { + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraCleanupFailed) + + return instanceState, fmt.Errorf( + "cannot clenup provisioned infrastructure with ID %s: %w", + gitspaceConfig.InfraProviderResource.UID, + err, + ) + } + + if cleanedUpInfra.Status != enum.InfraStatusDestroyed && cleanedUpInfra.Status != enum.InfraStatusStopped { + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraCleanupFailed) + + return instanceState, fmt.Errorf( + "infra state is %v, should be %v for gitspace instance identifier %s", + cleanedUpInfra.Status, + []enum.InfraStatus{enum.InfraStatusDestroyed, enum.InfraStatusStopped}, + gitspaceConfig.GitspaceInstance.Identifier) + } + + o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraCleanupCompleted) + + instanceState = enum.GitspaceInstanceStateCleaned + + return instanceState, nil +} + func (o orchestrator) TriggerDeleteGitspace( ctx context.Context, gitspaceConfig types.GitspaceConfig, + canDeleteUserData bool, ) error { infra, err := o.getProvisionedInfra(ctx, gitspaceConfig, - []enum.InfraStatus{enum.InfraStatusProvisioned, enum.InfraStatusStopped, enum.InfraStatusDestroyed}) + []enum.InfraStatus{ + enum.InfraStatusProvisioned, + enum.InfraStatusStopped, + enum.InfraStatusDestroyed, + enum.InfraStatusError, + enum.InfraStatusUnknown, + }) if err != nil { return fmt.Errorf( "unable to find provisioned infra while triggering delete for gitspace instance %s: %w", @@ -217,7 +294,7 @@ func (o orchestrator) TriggerDeleteGitspace( } o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraDeprovisioningStart) - err = o.infraProvisioner.TriggerDeprovision(ctx, gitspaceConfig, *infra) + err = o.infraProvisioner.TriggerDeprovision(ctx, gitspaceConfig, *infra, canDeleteUserData) if err != nil { o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraDeprovisioningFailed) diff --git a/app/services/gitspace/delete.go b/app/services/gitspace/delete.go index dbf93fa3d..162c68c58 100644 --- a/app/services/gitspace/delete.go +++ b/app/services/gitspace/delete.go @@ -24,5 +24,5 @@ func (c *Service) TriggerDelete( ctx context.Context, config types.GitspaceConfig, ) error { - return c.orchestrator.TriggerDeleteGitspace(ctx, config) + return c.orchestrator.TriggerDeleteGitspace(ctx, config, true) } diff --git a/app/services/gitspace/find.go b/app/services/gitspace/find.go index 709aa7f81..426062ad9 100644 --- a/app/services/gitspace/find.go +++ b/app/services/gitspace/find.go @@ -80,10 +80,11 @@ func (c *Service) setInstance( func (c *Service) FindByID( ctx context.Context, id int64, + includeDeleted bool, ) (*types.GitspaceConfig, error) { var gitspaceConfigResult *types.GitspaceConfig txErr := c.tx.WithTx(ctx, func(ctx context.Context) error { - gitspaceConfig, err := c.gitspaceConfigStore.Find(ctx, id) + gitspaceConfig, err := c.gitspaceConfigStore.Find(ctx, id, includeDeleted) gitspaceConfigResult = gitspaceConfig if err != nil { return fmt.Errorf("failed to find gitspace config: %w", err) diff --git a/app/services/gitspaceinfraevent/handler.go b/app/services/gitspaceinfraevent/handler.go index f6766d5a3..dd79effcc 100644 --- a/app/services/gitspaceinfraevent/handler.go +++ b/app/services/gitspaceinfraevent/handler.go @@ -68,7 +68,7 @@ func (s *Service) handleGitspaceInfraEvent( instanceState, resumeDeleteErr := s.orchestrator.ResumeDeleteGitspace(ctx, *config, payload.Infra) if resumeDeleteErr != nil { err = fmt.Errorf("failed to resume delete gitspace: %w", resumeDeleteErr) - } else { + } else if config.IsMarkedForDeletion { config.IsDeleted = true updateErr := s.gitspaceSvc.UpdateConfig(ctx, config) if updateErr != nil { @@ -76,6 +76,15 @@ func (s *Service) handleGitspaceInfraEvent( } } + instance.State = instanceState + case enum.InfraEventCleanup: + instanceState, resumeCleanupErr := s.orchestrator.ResumeCleanupInstanceResources(ctx, *config, payload.Infra) + if resumeCleanupErr != nil { + s.emitGitspaceConfigEvent(ctx, config, enum.GitspaceEventTypeInfraCleanupFailed) + + err = fmt.Errorf("failed to resume cleanup gitspace: %w", resumeCleanupErr) + } + instance.State = instanceState default: return fmt.Errorf("unknown event type: %s", event.Payload.Type) diff --git a/app/store/database.go b/app/store/database.go index a4106b719..3ca224552 100644 --- a/app/store/database.go +++ b/app/store/database.go @@ -648,7 +648,7 @@ type ( GitspaceConfigStore interface { // Find returns a gitspace config given a ID from the datastore. - Find(ctx context.Context, id int64) (*types.GitspaceConfig, error) + Find(ctx context.Context, id int64, includeDeleted bool) (*types.GitspaceConfig, error) // FindAll returns list of gitspace configs given a IDs from the datastore. FindAll(ctx context.Context, id []int64) ([]*types.GitspaceConfig, error) diff --git a/app/store/database/gitspace_config.go b/app/store/database/gitspace_config.go index 2ac8786f6..e6cbd7433 100644 --- a/app/store/database/gitspace_config.go +++ b/app/store/database/gitspace_config.go @@ -133,12 +133,16 @@ func (s gitspaceConfigStore) Count(ctx context.Context, filter *types.GitspaceFi return count, nil } -func (s gitspaceConfigStore) Find(ctx context.Context, id int64) (*types.GitspaceConfig, error) { +func (s gitspaceConfigStore) Find(ctx context.Context, id int64, includeDeleted bool) (*types.GitspaceConfig, error) { stmt := database.Builder. Select(gitspaceConfigSelectColumns). From(gitspaceConfigsTable). - Where("gconf_id = ?", id). //nolint:goconst - Where("gconf_is_deleted = ?", false) + Where("gconf_id = ?", id) //nolint:goconst + + if !includeDeleted { + stmt = stmt.Where("gconf_is_deleted = ?", false) + } + dst := new(gitspaceConfig) sql, args, err := stmt.ToSql() if err != nil { diff --git a/infraprovider/docker_provider.go b/infraprovider/docker_provider.go index d702b6579..9c84ca8fb 100644 --- a/infraprovider/docker_provider.go +++ b/infraprovider/docker_provider.go @@ -182,8 +182,51 @@ func (d DockerProvider) Stop(ctx context.Context, infra types.Infrastructure) er return nil } -// 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 { +// CleanupInstanceResources is NOOP as this provider does not utilise infra exclusively associated to a gitspace +// instance. +func (d DockerProvider) CleanupInstanceResources(ctx context.Context, infra types.Infrastructure) error { + event := &events.GitspaceInfraEventPayload{ + Infra: infra, + Type: enum.InfraEventCleanup, + } + + infra.Status = enum.InfraStatusStopped + + err := d.eventReporter.EmitGitspaceInfraEvent(ctx, events.GitspaceInfraEvent, event) + if err != nil { + return fmt.Errorf("error emitting gitspace infra event for cleanup: %w", err) + } + + return nil +} + +// Deprovision is NOOP if canDeleteUserData = false +// Deprovision deletes the volume created by Provision method if canDeleteUserData = false. +// Deprovision does not stop the docker engine in any case. +func (d DockerProvider) Deprovision(ctx context.Context, infra types.Infrastructure, canDeleteUserData bool) error { + if canDeleteUserData { + err := d.deleteVolume(ctx, infra) + if err != nil { + return fmt.Errorf("couldn't delete volume for %s : %w", infra.Storage, err) + } + } + + infra.Status = enum.InfraStatusDestroyed + + event := &events.GitspaceInfraEventPayload{ + Infra: infra, + Type: enum.InfraEventDeprovision, + } + + err := d.eventReporter.EmitGitspaceInfraEvent(ctx, events.GitspaceInfraEvent, event) + if err != nil { + return fmt.Errorf("error emitting gitspace infra event for deprovisioning: %w", err) + } + + return nil +} + +func (d DockerProvider) deleteVolume(ctx context.Context, infra types.Infrastructure) error { dockerClient, err := d.dockerClientFactory.NewDockerClient(ctx, types.Infrastructure{ ProviderType: enum.InfraProviderTypeDocker, InputParameters: infra.InputParameters, @@ -199,26 +242,39 @@ func (d DockerProvider) Deprovision(ctx context.Context, infra types.Infrastruct } }() + // check if volume is available + volumeList, err := dockerClient.VolumeList(ctx, volume.ListOptions{}) + if err != nil { + return fmt.Errorf("couldn't list the volume: %w", err) + } + + if !findVolume(infra.Storage, volumeList.Volumes) { + // given volume does not exist, return nil + return nil + } + err = dockerClient.VolumeRemove(ctx, infra.Storage, true) if err != nil { return fmt.Errorf("couldn't delete volume for %s : %w", infra.Storage, err) } - infra.Status = enum.InfraStatusDestroyed - - event := &events.GitspaceInfraEventPayload{ - Infra: infra, - Type: enum.InfraEventDeprovision, - } - - err = d.eventReporter.EmitGitspaceInfraEvent(ctx, events.GitspaceInfraEvent, event) - if err != nil { - return fmt.Errorf("error emitting gitspace infra event for deprovisioning: %w", err) - } - return nil } +func findVolume(target string, volumes []*volume.Volume) bool { + for _, vol := range volumes { + if vol == nil { + continue + } + + if vol.Name == target { + return true + } + } + + return false +} + // AvailableParams returns empty slice as no params are defined. func (d DockerProvider) AvailableParams() []types.InfraProviderParameterSchema { return []types.InfraProviderParameterSchema{} diff --git a/infraprovider/infra_provider.go b/infraprovider/infra_provider.go index 93c287489..e58eeab6c 100644 --- a/infraprovider/infra_provider.go +++ b/infraprovider/infra_provider.go @@ -49,8 +49,13 @@ type InfraProvider interface { // Stop frees up the resources allocated against a gitspace, which can be freed. Stop(ctx context.Context, infra types.Infrastructure) error - // Deprovision removes all infrastructure provisioned against the gitspace. - Deprovision(ctx context.Context, infra types.Infrastructure) error + // CleanupInstanceResources cleans up resources exclusively allocated to a gitspace instance. + CleanupInstanceResources(ctx context.Context, infra types.Infrastructure) error + + // Deprovision removes infrastructure provisioned against a gitspace. + // canDeleteUserData = false -> remove all resources except storage where user has stored it's data. + // canDeleteUserData = true -> remove all resources including storage. + Deprovision(ctx context.Context, infra types.Infrastructure, canDeleteUserData bool) error // AvailableParams provides a schema to define the infrastructure. AvailableParams() []types.InfraProviderParameterSchema diff --git a/types/enum/gitspace_event_type.go b/types/enum/gitspace_event_type.go index a16ceb4cc..597396489 100644 --- a/types/enum/gitspace_event_type.go +++ b/types/enum/gitspace_event_type.go @@ -95,6 +95,11 @@ const ( GitspaceEventTypeInfraStopCompleted GitspaceEventType = "infra_stop_completed" GitspaceEventTypeInfraStopFailed GitspaceEventType = "infra_stop_failed" + // Infra cleanup events. + GitspaceEventTypeInfraCleanupStart GitspaceEventType = "infra_cleanup_start" + GitspaceEventTypeInfraCleanupCompleted GitspaceEventType = "infra_cleanup_completed" + GitspaceEventTypeInfraCleanupFailed GitspaceEventType = "infra_cleanup_failed" + // Infra deprovisioning events. GitspaceEventTypeInfraDeprovisioningStart GitspaceEventType = "infra_deprovisioning_start" GitspaceEventTypeInfraDeprovisioningCompleted GitspaceEventType = "infra_deprovisioning_completed" diff --git a/types/enum/gitspace_instance_state_type.go b/types/enum/gitspace_instance_state_type.go index 1a3ddde42..ad5b7cc76 100644 --- a/types/enum/gitspace_instance_state_type.go +++ b/types/enum/gitspace_instance_state_type.go @@ -36,16 +36,19 @@ const ( GitspaceInstanceStateUnknown GitspaceInstanceStateType = "unknown" GitspaceInstanceStateError GitspaceInstanceStateType = "error" GitspaceInstanceStateDeleted GitspaceInstanceStateType = "deleted" + GitspaceInstanceStateCleaned GitspaceInstanceStateType = "cleaned" GitspaceInstanceStateStarting GitspaceInstanceStateType = "starting" GitspaceInstanceStateStopping GitspaceInstanceStateType = "stopping" + GitSpaceInstanceStateCleaning GitspaceInstanceStateType = "cleaning" ) func (g GitspaceInstanceStateType) IsFinalStatus() bool { //nolint:exhaustive switch g { case GitspaceInstanceStateDeleted, - GitspaceInstanceStateError: + GitspaceInstanceStateError, + GitspaceInstanceStateCleaned: return true default: return false @@ -56,7 +59,8 @@ func (g GitspaceInstanceStateType) IsBusyStatus() bool { //nolint:exhaustive switch g { case GitspaceInstanceStateStarting, - GitspaceInstanceStateStopping: + GitspaceInstanceStateStopping, + GitSpaceInstanceStateCleaning: return true default: return false diff --git a/types/enum/gitspace_state_type.go b/types/enum/gitspace_state_type.go index b7adae842..c853f978e 100644 --- a/types/enum/gitspace_state_type.go +++ b/types/enum/gitspace_state_type.go @@ -67,6 +67,13 @@ func GetGitspaceStateFromInstance( return GitspaceStateError, nil } return GitspaceStateStopping, nil + case GitSpaceInstanceStateCleaning: + if lastUpdateTimeExceeded(lastUpdateTime) { + return GitspaceStateError, nil + } + return GitspaceStateStopping, nil + case GitspaceInstanceStateCleaned: + return GitspaceStateStopped, nil default: return GitspaceStateError, fmt.Errorf("unsupported gitspace instance state %s", string(instanceState)) } diff --git a/types/enum/infra_event.go b/types/enum/infra_event.go index 50511bfeb..ab78c6fd4 100644 --- a/types/enum/infra_event.go +++ b/types/enum/infra_event.go @@ -27,5 +27,6 @@ var infraEvents = []InfraEvent{ const ( InfraEventProvision InfraEvent = "provision" InfraEventStop InfraEvent = "stop" + InfraEventCleanup InfraEvent = "cleanup" InfraEventDeprovision InfraEvent = "deprovision" )