feat: [CDE-697]: Add async flow when gitspace agent fails to perform action (#3602)

* feat: [CDE-697]: fix stop async flow
* feat: [CDE-697]: Add async flow when gitspace agent fails to perform action
main
Deepak Bhatt 2025-03-28 13:15:20 +00:00 committed by Harness
parent 1ffe29d15a
commit e518dc7f41
11 changed files with 116 additions and 14 deletions

View File

@ -47,7 +47,7 @@ func (r *Reporter) EmitGitspaceOperationsEvent(
}
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, event, payload)
if err != nil {
return fmt.Errorf("failed to send %+v event", event)
return fmt.Errorf("failed to send %s event: %w", event, err)
}
log.Ctx(ctx).Debug().Msgf("reported %v event with id '%s'", event, eventID)

View File

@ -805,6 +805,7 @@ func GetContainerResponse(
codeRepoDir := filepath.Join(homeDir, repoName)
return &response.StartResponse{
Status: response.SuccessStatus,
ContainerID: id,
ContainerName: containerName,
PublishedPorts: ports,

View File

@ -318,12 +318,17 @@ func (e *EmbeddedDockerOrchestrator) StopGitspace(
return fmt.Errorf("gitspace %s is in a bad state: %s", containerName, state)
}
stopResponse := &response.StopResponse{
Status: response.SuccessStatus,
}
err = e.eventReporter.EmitGitspaceOperationsEvent(
ctx,
events.GitspaceOperationsEvent,
&events.GitspaceOperationsEventPayload{
Type: enum.GitspaceOperationsEventStop,
Infra: infra,
Type: enum.GitspaceOperationsEventStop,
Infra: infra,
Response: stopResponse,
},
)
logger.Debug().Msg("stopped gitspace")
@ -411,9 +416,12 @@ func (e *EmbeddedDockerOrchestrator) StopAndRemoveGitspace(
ctx,
events.GitspaceOperationsEvent,
&events.GitspaceOperationsEventPayload{
Type: enum.GitspaceOperationsEventDelete,
Infra: infra,
Response: &response.DeleteResponse{CanDeleteUserData: canDeleteUserData},
Type: enum.GitspaceOperationsEventDelete,
Infra: infra,
Response: &response.DeleteResponse{
Status: response.SuccessStatus,
CanDeleteUserData: canDeleteUserData,
},
},
)
logger.Debug().Msg("removed gitspace")

View File

@ -15,5 +15,7 @@
package response
type DeleteResponse struct {
CanDeleteUserData bool `json:"can_delete_user_data"`
Status Status `json:"status"`
ErrMessage string `json:"err_message"`
CanDeleteUserData bool `json:"can_delete_user_data"`
}

View File

@ -16,6 +16,8 @@ package response
type StartResponse struct {
ContainerID string `json:"container_id"`
Status Status `json:"status"`
ErrMessage string `json:"err_message"`
ContainerName string `json:"container_name"`
PublishedPorts map[int]string `json:"published_ports"`
AbsoluteRepoPath string `json:"absolute_repo_path"`

View File

@ -0,0 +1,22 @@
// 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 response
type Status string
const (
SuccessStatus Status = "success"
FailureStatus Status = "failure"
)

View File

@ -0,0 +1,20 @@
// 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 response
type StopResponse struct {
Status Status `json:"status"`
ErrMessage string `json:"err_message"`
}

View File

@ -149,8 +149,20 @@ func (o Orchestrator) FinishResumeStartGitspace(
provisionedInfra types.Infrastructure,
startResponse *response.StartResponse,
) (types.GitspaceInstance, *types.GitspaceError) {
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationCompleted)
gitspaceInstance := gitspaceConfig.GitspaceInstance
if startResponse == nil || startResponse.Status == response.FailureStatus {
gitspaceInstance.State = enum.GitspaceInstanceStateError
err := fmt.Errorf("gitspace agent does not specify the error for failure")
if startResponse != nil && startResponse.ErrMessage != "" {
err = fmt.Errorf("%s", startResponse.ErrMessage)
}
return *gitspaceInstance, &types.GitspaceError{
Error: err,
ErrorMessage: ptr.String(err.Error()),
}
}
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceCreationCompleted)
ideSvc, err := o.ideFactory.GetIDE(gitspaceConfig.IDE)
if err != nil {

View File

@ -22,6 +22,7 @@ import (
events "github.com/harness/gitness/app/events/gitspace"
"github.com/harness/gitness/app/gitspace/infrastructure"
"github.com/harness/gitness/app/gitspace/orchestrator/container"
"github.com/harness/gitness/app/gitspace/orchestrator/container/response"
"github.com/harness/gitness/app/gitspace/orchestrator/ide"
"github.com/harness/gitness/app/gitspace/platformconnector"
"github.com/harness/gitness/app/gitspace/scm"
@ -206,7 +207,18 @@ func (o Orchestrator) FinishStopGitspaceContainer(
ctx context.Context,
gitspaceConfig types.GitspaceConfig,
infra types.Infrastructure,
stopResponse *response.StopResponse,
) *types.GitspaceError {
if stopResponse == nil || stopResponse.Status == response.FailureStatus {
err := fmt.Errorf("gitspace agent does not specify the error for failure")
if stopResponse != nil && stopResponse.ErrMessage != "" {
err = fmt.Errorf("%s", stopResponse.ErrMessage)
}
return &types.GitspaceError{
Error: err,
ErrorMessage: ptr.String(err.Error()),
}
}
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceStopCompleted)
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraStopStart)
@ -257,19 +269,29 @@ func (o Orchestrator) FinishStopAndRemoveGitspaceContainer(
ctx context.Context,
gitspaceConfig types.GitspaceConfig,
infra types.Infrastructure,
canDeleteUserData bool,
deleteResponse *response.DeleteResponse,
) *types.GitspaceError {
if deleteResponse == nil || deleteResponse.Status == response.FailureStatus {
err := fmt.Errorf("gitspace agent does not specify the error for failure")
if deleteResponse != nil && deleteResponse.ErrMessage != "" {
err = fmt.Errorf("%s", deleteResponse.ErrMessage)
}
return &types.GitspaceError{
Error: err,
ErrorMessage: ptr.String(err.Error()),
}
}
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeAgentGitspaceDeletionCompleted)
if canDeleteUserData {
if deleteResponse.CanDeleteUserData {
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraDeprovisioningStart)
} else {
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraResetStart)
}
opts := infrastructure.InfraEventOpts{CanDeleteUserData: canDeleteUserData}
opts := infrastructure.InfraEventOpts{CanDeleteUserData: deleteResponse.CanDeleteUserData}
err := o.infraProvisioner.TriggerInfraEventWithOpts(ctx, enum.InfraEventDeprovision, gitspaceConfig, &infra, opts)
if err != nil {
if canDeleteUserData {
if deleteResponse.CanDeleteUserData {
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraDeprovisioningFailed)
} else {
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeInfraResetFailed)

View File

@ -86,14 +86,25 @@ func (s *Service) handleGitspaceOperationsEvent(
)
if handleResumeStartErr != nil {
s.emitGitspaceConfigEvent(ctxWithTimedOut, config, enum.GitspaceEventTypeGitspaceActionStartFailed)
instance.State = enum.GitspaceInstanceStateError
updatedInstance.ErrorMessage = handleResumeStartErr.ErrorMessage
err = fmt.Errorf("failed to finish resume start gitspace: %w", handleResumeStartErr.Error)
}
instance = &updatedInstance
case enum.GitspaceOperationsEventStop:
finishStopErr := s.orchestrator.FinishStopGitspaceContainer(ctxWithTimedOut, *config, payload.Infra)
stopResponse, ok := payload.Response.(*response.StopResponse)
if !ok {
return fmt.Errorf("failed to cast stop response")
}
finishStopErr := s.orchestrator.FinishStopGitspaceContainer(
ctxWithTimedOut,
*config,
payload.Infra,
stopResponse,
)
if finishStopErr != nil {
s.emitGitspaceConfigEvent(ctxWithTimedOut, config, enum.GitspaceEventTypeGitspaceActionStopFailed)
instance.State = enum.GitspaceInstanceStateError
instance.ErrorMessage = finishStopErr.ErrorMessage
err = fmt.Errorf("failed to finish trigger start gitspace: %w", finishStopErr.Error)
}
@ -106,10 +117,11 @@ func (s *Service) handleGitspaceOperationsEvent(
ctxWithTimedOut,
*config,
payload.Infra,
deleteResponse.CanDeleteUserData,
deleteResponse,
)
if finishStopAndRemoveErr != nil {
s.emitGitspaceConfigEvent(ctxWithTimedOut, config, enum.GitspaceEventTypeGitspaceActionStopFailed)
instance.State = enum.GitspaceInstanceStateError
instance.ErrorMessage = finishStopAndRemoveErr.ErrorMessage
err = fmt.Errorf("failed to finish trigger start gitspace: %w", finishStopAndRemoveErr.Error)
}

View File

@ -49,6 +49,7 @@ func ReporterSendEvent[T interface{}](reporter *GenericReporter, ctx context.Con
buff := &bytes.Buffer{}
encoder := gob.NewEncoder(buff)
gob.Register((*response.StartResponse)(nil))
gob.Register((*response.StopResponse)(nil))
gob.Register((*response.DeleteResponse)(nil))
if err := encoder.Encode(&event); err != nil {