From f493734d787e75e35583b882dde2cb50fd0a7c10 Mon Sep 17 00:00:00 2001 From: Saranya-jena Date: Mon, 16 Oct 2023 17:15:57 +0530 Subject: [PATCH 1/5] Added backend changes for stop experiment Signed-off-by: Saranya-jena --- .../shared/chaos_experiment_run.graphqls | 5 + .../graph/chaos_experiment_run.resolvers.go | 19 ++- .../server/graph/generated/generated.go | 123 ++++++++++++++++++ .../graphql/server/pkg/authorization/roles.go | 59 ++++----- .../pkg/chaos_experiment/handler/handler.go | 61 +++++++++ .../pkg/chaos_experiment_run/service.go | 25 ++++ .../subscriber/pkg/events/chaosengine.go | 25 ++++ .../subscriber/pkg/requests/webhook.go | 2 +- chaoscenter/subscriber/pkg/utils/workflow.go | 17 +++ 9 files changed, 305 insertions(+), 31 deletions(-) diff --git a/chaoscenter/graphql/definitions/shared/chaos_experiment_run.graphqls b/chaoscenter/graphql/definitions/shared/chaos_experiment_run.graphqls index 44f387555f1..15ea5f9bd3e 100644 --- a/chaoscenter/graphql/definitions/shared/chaos_experiment_run.graphqls +++ b/chaoscenter/graphql/definitions/shared/chaos_experiment_run.graphqls @@ -32,4 +32,9 @@ extend type Mutation { experimentID: String! projectID: ID! ): RunChaosExperimentResponse! + + """ + stopExperiment will halt all the ongoing runs of a particular experiment + """ + stopExperimentRuns(projectID: ID!, experimentID:String!, experimentRunID: String, notifyID: String): Boolean! @authorized } \ No newline at end of file diff --git a/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go b/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go index 61b4140c5a9..dc024d84562 100644 --- a/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go +++ b/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go @@ -6,7 +6,6 @@ package graph import ( "context" "errors" - "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model" "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/authorization" data_store "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/data-store" @@ -53,6 +52,24 @@ func (r *mutationResolver) RunChaosExperiment(ctx context.Context, experimentID return &model.RunChaosExperimentResponse{NotifyID: uiResponse.NotifyID}, err } +func (r *mutationResolver) StopExperimentRuns(ctx context.Context, projectID string, experimentID string, experimentRunID *string, notifyID *string) (bool, error) { + logFields := logrus.Fields{ + "projectId": projectID, + "chaosExperimentId": experimentID, + "chaosExperimentRunId": experimentRunID, + "notifyID": notifyID, + } + + logrus.WithFields(logFields).Info("request received to stop chaos experiment") + err := authorization.ValidateRole(ctx, projectID, + authorization.MutationRbacRules[authorization.StopChaosExperiment], + model.InvitationAccepted.String()) + if err != nil { + return false, err + } + +} + func (r *queryResolver) GetExperimentRun(ctx context.Context, projectID string, experimentRunID *string, notifyID *string) (*model.ExperimentRun, error) { logFields := logrus.Fields{ "projectId": projectID, diff --git a/chaoscenter/graphql/server/graph/generated/generated.go b/chaoscenter/graphql/server/graph/generated/generated.go index 2d20f2bdd98..28850be057c 100644 --- a/chaoscenter/graphql/server/graph/generated/generated.go +++ b/chaoscenter/graphql/server/graph/generated/generated.go @@ -501,6 +501,7 @@ type ComplexityRoot struct { RunChaosExperiment func(childComplexity int, experimentID string, projectID string) int SaveChaosExperiment func(childComplexity int, request model.SaveChaosExperimentRequest, projectID string) int SaveChaosHub func(childComplexity int, projectID string, request model.CreateChaosHubRequest) int + StopExperimentRuns func(childComplexity int, projectID string, experimentID string, experimentRunID *string, notifyID *string) int SyncChaosHub func(childComplexity int, id string, projectID string) int UpdateChaosExperiment func(childComplexity int, request *model.ChaosExperimentRequest, projectID string) int UpdateChaosHub func(childComplexity int, projectID string, request model.UpdateChaosHubRequest) int @@ -715,6 +716,7 @@ type MutationResolver interface { DeleteChaosExperiment(ctx context.Context, experimentID string, experimentRunID *string, projectID string) (bool, error) ChaosExperimentRun(ctx context.Context, request model.ExperimentRunRequest) (string, error) RunChaosExperiment(ctx context.Context, experimentID string, projectID string) (*model.RunChaosExperimentResponse, error) + StopExperimentRuns(ctx context.Context, projectID string, experimentID string, experimentRunID *string, notifyID *string) (bool, error) RegisterInfra(ctx context.Context, projectID string, request model.RegisterInfraRequest) (*model.RegisterInfraResponse, error) ConfirmInfraRegistration(ctx context.Context, request model.InfraIdentity) (*model.ConfirmInfraRegistrationResponse, error) DeleteInfra(ctx context.Context, projectID string, infraID string) (string, error) @@ -3143,6 +3145,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.SaveChaosHub(childComplexity, args["projectID"].(string), args["request"].(model.CreateChaosHubRequest)), true + case "Mutation.stopExperimentRuns": + if e.complexity.Mutation.StopExperimentRuns == nil { + break + } + + args, err := ec.field_Mutation_stopExperimentRuns_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.StopExperimentRuns(childComplexity, args["projectID"].(string), args["experimentID"].(string), args["experimentRunID"].(*string), args["notifyID"].(*string)), true + case "Mutation.syncChaosHub": if e.complexity.Mutation.SyncChaosHub == nil { break @@ -5180,6 +5194,11 @@ extend type Mutation { experimentID: String! projectID: ID! ): RunChaosExperimentResponse! + + """ + stopExperiment will halt all the ongoing runs of a particular experiment + """ + stopExperimentRuns(projectID: ID!, experimentID:String!, experimentRunID: String, notifyID: String): Boolean! @authorized }`, BuiltIn: false}, &ast.Source{Name: "../definitions/shared/chaos_infrastructure.graphqls", Input: `directive @authorized on FIELD_DEFINITION @@ -8547,6 +8566,44 @@ func (ec *executionContext) field_Mutation_saveChaosHub_args(ctx context.Context return args, nil } +func (ec *executionContext) field_Mutation_stopExperimentRuns_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["projectID"]; ok { + arg0, err = ec.unmarshalNID2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["projectID"] = arg0 + var arg1 string + if tmp, ok := rawArgs["experimentID"]; ok { + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["experimentID"] = arg1 + var arg2 *string + if tmp, ok := rawArgs["experimentRunID"]; ok { + arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["experimentRunID"] = arg2 + var arg3 *string + if tmp, ok := rawArgs["notifyID"]; ok { + arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["notifyID"] = arg3 + return args, nil +} + func (ec *executionContext) field_Mutation_syncChaosHub_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -19349,6 +19406,67 @@ func (ec *executionContext) _Mutation_runChaosExperiment(ctx context.Context, fi return ec.marshalNRunChaosExperimentResponse2ᚖgithubᚗcomᚋlitmuschaosᚋlitmusᚋchaoscenterᚋgraphqlᚋserverᚋgraphᚋmodelᚐRunChaosExperimentResponse(ctx, field.Selections, res) } +func (ec *executionContext) _Mutation_stopExperimentRuns(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_stopExperimentRuns_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().StopExperimentRuns(rctx, args["projectID"].(string), args["experimentID"].(string), args["experimentRunID"].(*string), args["notifyID"].(*string)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.Authorized == nil { + return nil, errors.New("directive authorized is not implemented") + } + return ec.directives.Authorized(ctx, nil, directive0) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, err + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(bool); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be bool`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + func (ec *executionContext) _Mutation_registerInfra(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -31761,6 +31879,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } + case "stopExperimentRuns": + out.Values[i] = ec._Mutation_stopExperimentRuns(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } case "registerInfra": out.Values[i] = ec._Mutation_registerInfra(ctx, field) if out.Values[i] == graphql.Null { diff --git a/chaoscenter/graphql/server/pkg/authorization/roles.go b/chaoscenter/graphql/server/pkg/authorization/roles.go index 4e06885e5cc..3cdceeaa78e 100644 --- a/chaoscenter/graphql/server/pkg/authorization/roles.go +++ b/chaoscenter/graphql/server/pkg/authorization/roles.go @@ -12,6 +12,7 @@ const ( CreateChaosWorkFlow RoleQuery = "CreateChaosWorkFlow" ReRunChaosWorkFlow RoleQuery = "ReRunChaosWorkFlow" DeleteChaosWorkflow RoleQuery = "DeleteChaosWorkflow" + StopChaosExperiment RoleQuery = "StopChaosExperiment" TerminateChaosWorkflow RoleQuery = "TerminateChaosWorkflow" SyncWorkflow RoleQuery = "SyncWorkflow" SendInvitation RoleQuery = "SendInvitation" @@ -84,35 +85,35 @@ const ( ) var MutationRbacRules = map[RoleQuery][]string{ - UserInfrastructureReg: {MemberRoleOwnerString, MemberRoleEditorString}, - CreateChaosWorkFlow: {MemberRoleOwnerString, MemberRoleEditorString}, - ReRunChaosWorkFlow: {MemberRoleOwnerString, MemberRoleEditorString}, - DeleteChaosWorkflow: {MemberRoleOwnerString, MemberRoleEditorString}, - TerminateChaosWorkflow: {MemberRoleOwnerString, MemberRoleEditorString}, - SyncWorkflow: {MemberRoleOwnerString, MemberRoleEditorString}, - SendInvitation: {MemberRoleOwnerString}, - AcceptInvitation: {MemberRoleViewerString, MemberRoleEditorString}, - DeclineInvitation: {MemberRoleViewerString, MemberRoleEditorString}, - RemoveInvitation: {MemberRoleOwnerString}, - LeaveProject: {MemberRoleViewerString, MemberRoleEditorString}, - UpdateProjectName: {MemberRoleOwnerString}, - AddChaosHub: {MemberRoleOwnerString, MemberRoleEditorString}, - SyncHub: {MemberRoleOwnerString, MemberRoleEditorString}, - UpdateChaosWorkflow: {MemberRoleOwnerString, MemberRoleEditorString}, - DeleteInfrastructures: {MemberRoleOwnerString, MemberRoleEditorString}, - UpdateChaosHub: {MemberRoleOwnerString, MemberRoleEditorString}, - DeleteChaosHub: {MemberRoleOwnerString, MemberRoleEditorString}, - EnableGitOps: {MemberRoleOwnerString}, - DisableGitOps: {MemberRoleOwnerString}, - UpdateGitOps: {MemberRoleOwnerString}, - CreateDataSource: {MemberRoleOwnerString, MemberRoleEditorString}, - CreateDashBoard: {MemberRoleOwnerString, MemberRoleEditorString}, - UpdateDataSource: {MemberRoleOwnerString, MemberRoleEditorString}, - UpdateDashboard: {MemberRoleOwnerString, MemberRoleEditorString}, - DeleteDashboard: {MemberRoleOwnerString, MemberRoleEditorString}, - DeleteDataSource: {MemberRoleOwnerString, MemberRoleEditorString}, - ListWorkflowRuns: {MemberRoleOwnerString, MemberRoleEditorString, MemberRoleViewerString}, - GetWorkflowRun: {MemberRoleOwnerString, MemberRoleEditorString, MemberRoleViewerString}, + UserInfrastructureReg: {MemberRoleOwnerString, MemberRoleEditorString}, + CreateChaosWorkFlow: {MemberRoleOwnerString, MemberRoleEditorString}, + ReRunChaosWorkFlow: {MemberRoleOwnerString, MemberRoleEditorString}, + DeleteChaosWorkflow: {MemberRoleOwnerString, MemberRoleEditorString}, + StopChaosExperiment: {MemberRoleOwnerString, MemberRoleEditorString}, + SyncWorkflow: {MemberRoleOwnerString, MemberRoleEditorString}, + SendInvitation: {MemberRoleOwnerString}, + AcceptInvitation: {MemberRoleViewerString, MemberRoleEditorString}, + DeclineInvitation: {MemberRoleViewerString, MemberRoleEditorString}, + RemoveInvitation: {MemberRoleOwnerString}, + LeaveProject: {MemberRoleViewerString, MemberRoleEditorString}, + UpdateProjectName: {MemberRoleOwnerString}, + AddChaosHub: {MemberRoleOwnerString, MemberRoleEditorString}, + SyncHub: {MemberRoleOwnerString, MemberRoleEditorString}, + UpdateChaosWorkflow: {MemberRoleOwnerString, MemberRoleEditorString}, + DeleteInfrastructures: {MemberRoleOwnerString, MemberRoleEditorString}, + UpdateChaosHub: {MemberRoleOwnerString, MemberRoleEditorString}, + DeleteChaosHub: {MemberRoleOwnerString, MemberRoleEditorString}, + EnableGitOps: {MemberRoleOwnerString}, + DisableGitOps: {MemberRoleOwnerString}, + UpdateGitOps: {MemberRoleOwnerString}, + CreateDataSource: {MemberRoleOwnerString, MemberRoleEditorString}, + CreateDashBoard: {MemberRoleOwnerString, MemberRoleEditorString}, + UpdateDataSource: {MemberRoleOwnerString, MemberRoleEditorString}, + UpdateDashboard: {MemberRoleOwnerString, MemberRoleEditorString}, + DeleteDashboard: {MemberRoleOwnerString, MemberRoleEditorString}, + DeleteDataSource: {MemberRoleOwnerString, MemberRoleEditorString}, + ListWorkflowRuns: {MemberRoleOwnerString, MemberRoleEditorString, MemberRoleViewerString}, + GetWorkflowRun: {MemberRoleOwnerString, MemberRoleEditorString, MemberRoleViewerString}, ListInfrastructures: {MemberRoleOwnerString, MemberRoleEditorString, MemberRoleViewerString}, GetInfrastructure: {MemberRoleOwnerString, MemberRoleEditorString, diff --git a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go index f6e3b2c1f6f..e721c15b677 100644 --- a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go +++ b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "strconv" "time" @@ -1316,3 +1317,63 @@ func (c *ChaosExperimentHandler) validateDuplicateExperimentName(ctx context.Con return nil } + +func (c *ChaosExperimentHandler) StopExperimentRuns(ctx context.Context, projectID string, experimentID string, experimentRunID *string, r *store.StateData) (bool, error) { + + var experimentRunsID []string + + tkn := ctx.Value(authorization.AuthKey).(string) + username, err := authorization.GetUsername(tkn) + + query := bson.D{ + {"experiment_id", experimentID}, + {"project_id", projectID}, + {"is_removed", false}, + } + experiment, err := c.chaosExperimentOperator.GetExperiment(context.TODO(), query) + if err != nil { + return false, err + } + + // if experimentID is provided & no expRunID is present (stop all the corresponding experiment runs) + if experimentRunID == nil { + + // if experiment is of cron type, disable it + if experiment.CronSyntax != "" { + + err = c.DisableCronExperiment(username, experiment, projectID, r) + if err != nil { + return false, err + } + } + + // Fetching all the experiment runs in the experiment + expRuns, err := dbChaosExperimentRun.NewChaosExperimentRunOperator(c.mongodbOperator).GetExperimentRuns(bson.D{ + {"experiment_id", experimentID}, + {"is_removed", false}, + }) + if err != nil { + return false, err + } + + for _, runs := range expRuns { + if (runs.Phase == string(model.ExperimentRunStatusRunning) || runs.Phase == string(model.ExperimentRunStatusTimeout)) && !runs.Completed { + experimentRunsID = append(experimentRunsID, runs.ExperimentRunID) + } + } + + // Check if experiment run count is 0 and if it's not a cron experiment + if len(experimentRunsID) == 0 && experiment.CronSyntax == "" { + return false, fmt.Errorf("no running or timeout experiments found") + } + } else if experimentRunID != nil && *experimentRunID != "" { + experimentRunsID = []string{*experimentRunID} + } + + err = c.chaosExperimentRunService.ProcessExperimentRunStop(ctx, query, experimentRunID, experiment, username, projectID, r) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/chaoscenter/graphql/server/pkg/chaos_experiment_run/service.go b/chaoscenter/graphql/server/pkg/chaos_experiment_run/service.go index cd4a72b4ca3..1a7180ebee9 100644 --- a/chaoscenter/graphql/server/pkg/chaos_experiment_run/service.go +++ b/chaoscenter/graphql/server/pkg/chaos_experiment_run/service.go @@ -27,6 +27,7 @@ import ( type Service interface { ProcessExperimentRunDelete(ctx context.Context, query bson.D, workflowRunID *string, experimentRun dbChaosExperimentRun.ChaosExperimentRun, workflow dbChaosExperiment.ChaosExperimentRequest, username string, r *store.StateData) error ProcessCompletedExperimentRun(execData ExecutionData, wfID string, runID string) (ExperimentRunMetrics, error) + ProcessExperimentRunStop(ctx context.Context, query bson.D, experimentRunID *string, experiment dbChaosExperiment.ChaosExperimentRequest, username string, projectID string, r *store.StateData) error } // chaosWorkflowService is the implementation of the chaos workflow service @@ -70,6 +71,30 @@ func (c *chaosExperimentRunService) ProcessExperimentRunDelete(ctx context.Conte return nil } +// ProcessExperimentRunStop deletes a workflow entry and updates the database +func (c *chaosExperimentRunService) ProcessExperimentRunStop(ctx context.Context, query bson.D, experimentRunID *string, experiment dbChaosExperiment.ChaosExperimentRequest, username string, projectID string, r *store.StateData) error { + update := bson.D{ + {"$set", bson.D{ + {"updated_at", time.Now().UnixMilli()}, + {"updated_by", mongodb.UserDetailResponse{ + Username: username, + }}, + }}, + } + + err := c.chaosExperimentRunOperator.UpdateExperimentRunWithQuery(ctx, query, update) + if err != nil { + return err + } + if r != nil { + chaos_infrastructure.SendExperimentToSubscriber(projectID, &model.ChaosExperimentRequest{ + InfraID: experiment.InfraID, + }, &username, experimentRunID, "workflow_run_stop", r) + } + + return nil +} + // ProcessCompletedExperimentRun calculates the Resiliency Score and returns the updated ExecutionData func (c *chaosExperimentRunService) ProcessCompletedExperimentRun(execData ExecutionData, wfID string, runID string) (ExperimentRunMetrics, error) { weightSum, totalTestResult := 0, 0 diff --git a/chaoscenter/subscriber/pkg/events/chaosengine.go b/chaoscenter/subscriber/pkg/events/chaosengine.go index acf9b476769..02bd2178400 100644 --- a/chaoscenter/subscriber/pkg/events/chaosengine.go +++ b/chaoscenter/subscriber/pkg/events/chaosengine.go @@ -197,6 +197,31 @@ func StopChaosEngineState(namespace string, workflowRunID *string) error { return nil } +// StopWorkflow will patch the workflow based on workflow name using the shutdown strategy +func StopWorkflow(wfName string, namespace string) error { + patch := []byte(`{"spec":{"shutdown":"Stop"}}`) + + //Define the GVR + resourceType := schema.GroupVersionResource{ + Group: "litmuschaos.io", + Version: "v1alpha1", + Resource: "workflows", + } + + _, dynamicClient, err := k8s.GetDynamicAndDiscoveryClient() + if err != nil { + return errors.New("failed to get dynamic client, error: " + err.Error()) + } + wf, err := dynamicClient.Resource(resourceType).Namespace(namespace).Patch(context.TODO(), wfName, mergeType.MergePatchType, patch, v1.PatchOptions{}) + if err != nil { + return fmt.Errorf("error in patching workflow: %w", err) + } + if wf != nil { + logrus.Info("Successfully patched workflow: ", wf.GetName()) + } + return nil +} + func mapStatus(status chaosTypes.EngineStatus) string { switch status { case chaosTypes.EngineStatusInitialized: diff --git a/chaoscenter/subscriber/pkg/requests/webhook.go b/chaoscenter/subscriber/pkg/requests/webhook.go index c4a8b79a2c2..e400f4e87bf 100644 --- a/chaoscenter/subscriber/pkg/requests/webhook.go +++ b/chaoscenter/subscriber/pkg/requests/webhook.go @@ -131,7 +131,7 @@ func RequestProcessor(infraData map[string]string, r types.RawData) error { if err != nil { return errors.New("error performing infra operation: " + err.Error()) } - } else if strings.Index("workflow_delete workflow_run_delete ", strings.ToLower(r.Payload.Data.InfraConnect.Action.RequestType)) >= 0 { + } else if strings.Index("workflow_delete workflow_run_delete workflow_run_stop ", strings.ToLower(r.Payload.Data.InfraConnect.Action.RequestType)) >= 0 { err := utils.WorkflowRequest(infraData, r.Payload.Data.InfraConnect.Action.RequestType, r.Payload.Data.InfraConnect.Action.ExternalData, r.Payload.Data.InfraConnect.Action.Username) if err != nil { diff --git a/chaoscenter/subscriber/pkg/utils/workflow.go b/chaoscenter/subscriber/pkg/utils/workflow.go index b397724e8ff..4f96b95666f 100644 --- a/chaoscenter/subscriber/pkg/utils/workflow.go +++ b/chaoscenter/subscriber/pkg/utils/workflow.go @@ -40,6 +40,23 @@ func WorkflowRequest(agentData map[string]string, requestType string, externalDa } logrus.Info("events delete name: ", wfOb.Name, "namespace: ", wfOb.Namespace) + } else if requestType == "workflow_run_stop" { + wfOb, err := events.ListWorkflowObject(externalData) + if err != nil { + return err + } + for _, wfs := range wfOb.Items { + uid := string(wfs.UID) + err = events.StopChaosEngineState(agentData["AGENT_NAMESPACE"], &uid) + if err != nil { + logrus.Info("failed to stop chaosEngine for : ", wfs.Name, " namespace: ", wfs.Namespace) + } + err = events.StopWorkflow(wfs.Name, wfs.Namespace) + if err != nil { + logrus.Info("failed to stop experiment: ", wfs.Name, " namespace: ", wfs.Namespace) + } + logrus.Info("events stop name: ", wfs.Name, " namespace: ", wfs.Namespace) + } } return nil From 6a19bff99553ca3a653f7e8402edba005abe7e19 Mon Sep 17 00:00:00 2001 From: Saranya-jena Date: Tue, 17 Oct 2023 13:23:11 +0530 Subject: [PATCH 2/5] fixed issues with subscriber for stop experiemnt Signed-off-by: Saranya-jena --- .../graph/chaos_experiment_run.resolvers.go | 6 +++++ .../pkg/chaos_experiment/handler/handler.go | 9 ++++---- .../subscriber/pkg/events/chaosengine.go | 21 ++++++------------ chaoscenter/subscriber/pkg/events/workflow.go | 3 ++- chaoscenter/subscriber/pkg/utils/workflow.go | 22 +++++++++---------- 5 files changed, 29 insertions(+), 32 deletions(-) diff --git a/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go b/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go index dc024d84562..5f621278fe0 100644 --- a/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go +++ b/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go @@ -68,6 +68,12 @@ func (r *mutationResolver) StopExperimentRuns(ctx context.Context, projectID str return false, err } + uiResponse, err := r.chaosExperimentHandler.StopExperimentRuns(ctx, projectID, experimentID, experimentRunID, data_store.Store) + if err != nil { + logrus.WithFields(logFields).Error(err) + return false, err + } + return uiResponse, nil } func (r *queryResolver) GetExperimentRun(ctx context.Context, projectID string, experimentRunID *string, notifyID *string) (*model.ExperimentRun, error) { diff --git a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go index e721c15b677..0f213a9cf31 100644 --- a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go +++ b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go @@ -1368,11 +1368,10 @@ func (c *ChaosExperimentHandler) StopExperimentRuns(ctx context.Context, project } } else if experimentRunID != nil && *experimentRunID != "" { experimentRunsID = []string{*experimentRunID} - } - - err = c.chaosExperimentRunService.ProcessExperimentRunStop(ctx, query, experimentRunID, experiment, username, projectID, r) - if err != nil { - return false, err + err = c.chaosExperimentRunService.ProcessExperimentRunStop(ctx, query, experimentRunID, experiment, username, projectID, r) + if err != nil { + return false, err + } } return true, nil diff --git a/chaoscenter/subscriber/pkg/events/chaosengine.go b/chaoscenter/subscriber/pkg/events/chaosengine.go index 02bd2178400..a0fca1d18a9 100644 --- a/chaoscenter/subscriber/pkg/events/chaosengine.go +++ b/chaoscenter/subscriber/pkg/events/chaosengine.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" "strconv" "subscriber/pkg/k8s" "subscriber/pkg/types" @@ -199,25 +200,17 @@ func StopChaosEngineState(namespace string, workflowRunID *string) error { // StopWorkflow will patch the workflow based on workflow name using the shutdown strategy func StopWorkflow(wfName string, namespace string) error { - patch := []byte(`{"spec":{"shutdown":"Stop"}}`) - - //Define the GVR - resourceType := schema.GroupVersionResource{ - Group: "litmuschaos.io", - Version: "v1alpha1", - Resource: "workflows", - } - _, dynamicClient, err := k8s.GetDynamicAndDiscoveryClient() - if err != nil { - return errors.New("failed to get dynamic client, error: " + err.Error()) - } - wf, err := dynamicClient.Resource(resourceType).Namespace(namespace).Patch(context.TODO(), wfName, mergeType.MergePatchType, patch, v1.PatchOptions{}) + conf, err := k8s.GetKubeConfig() + wfClient := wfclientset.NewForConfigOrDie(conf).ArgoprojV1alpha1().Workflows(namespace) + patch := []byte(`{"spec":{"shutdown":"Stop"}}`) + wf, err := wfClient.Patch(context.TODO(), wfName, mergeType.MergePatchType, patch, v1.PatchOptions{}) if err != nil { return fmt.Errorf("error in patching workflow: %w", err) } if wf != nil { logrus.Info("Successfully patched workflow: ", wf.GetName()) + return nil } return nil } @@ -229,7 +222,7 @@ func mapStatus(status chaosTypes.EngineStatus) string { case chaosTypes.EngineStatusCompleted: return "Succeeded" case chaosTypes.EngineStatusStopped: - return "Skipped" + return "Stopped" default: return "Running" } diff --git a/chaoscenter/subscriber/pkg/events/workflow.go b/chaoscenter/subscriber/pkg/events/workflow.go index ce102d1d249..19efbc3f06e 100644 --- a/chaoscenter/subscriber/pkg/events/workflow.go +++ b/chaoscenter/subscriber/pkg/events/workflow.go @@ -162,7 +162,7 @@ func WorkflowEventHandler(workflowObj *v1alpha1.Workflow, eventType string, star status := updateWorkflowStatus(workflowObj.Status.Phase) finishedTime := StrConvTime(workflowObj.Status.FinishedAt.Unix()) - if eventType == "STOPPED" { + if workflowObj.Spec.Shutdown.Enabled() { status = "Stopped" finishedTime = StrConvTime(time.Now().Unix()) nodes[workflowObj.Name] = types.Node{ @@ -217,6 +217,7 @@ func SendWorkflowUpdates(infraData map[string]string, event types.WorkflowEvent) nodeData := event.Nodes[key] nodeData.Phase = "Stopped" event.Nodes[key] = nodeData + nodeData.FinishedAt = event.FinishedAt } } } diff --git a/chaoscenter/subscriber/pkg/utils/workflow.go b/chaoscenter/subscriber/pkg/utils/workflow.go index 4f96b95666f..ec44e4e9965 100644 --- a/chaoscenter/subscriber/pkg/utils/workflow.go +++ b/chaoscenter/subscriber/pkg/utils/workflow.go @@ -41,22 +41,20 @@ func WorkflowRequest(agentData map[string]string, requestType string, externalDa logrus.Info("events delete name: ", wfOb.Name, "namespace: ", wfOb.Namespace) } else if requestType == "workflow_run_stop" { - wfOb, err := events.ListWorkflowObject(externalData) + wfOb, err := events.GetWorkflowObj(externalData) if err != nil { return err } - for _, wfs := range wfOb.Items { - uid := string(wfs.UID) - err = events.StopChaosEngineState(agentData["AGENT_NAMESPACE"], &uid) - if err != nil { - logrus.Info("failed to stop chaosEngine for : ", wfs.Name, " namespace: ", wfs.Namespace) - } - err = events.StopWorkflow(wfs.Name, wfs.Namespace) - if err != nil { - logrus.Info("failed to stop experiment: ", wfs.Name, " namespace: ", wfs.Namespace) - } - logrus.Info("events stop name: ", wfs.Name, " namespace: ", wfs.Namespace) + err = events.StopChaosEngineState(agentData["INFRA_NAMESPACE"], &externalData) + if err != nil { + logrus.Info("failed to stop chaosEngine for : ", wfOb.Name, " namespace: ", wfOb.Namespace, " : ", err) } + err = events.StopWorkflow(wfOb.Name, wfOb.Namespace) + if err != nil { + logrus.Info("failed to stop experiment: ", wfOb.Name, " namespace: ", wfOb.Namespace, " : ", err) + } + logrus.Info("events stop name: ", wfOb.Name, " namespace: ", wfOb.Namespace) + } return nil From b26fada8695a7ccdb576f88af0d6d243019389b2 Mon Sep 17 00:00:00 2001 From: Saranya-jena Date: Wed, 18 Oct 2023 15:03:13 +0530 Subject: [PATCH 3/5] updated logic Signed-off-by: Saranya-jena --- .../pkg/chaos_experiment/handler/handler.go | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go index 0f213a9cf31..5ba94ebc92c 100644 --- a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go +++ b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go @@ -311,23 +311,23 @@ func (c *ChaosExperimentHandler) GetExperiment(ctx context.Context, projectID st {"let", bson.M{"infraID": "$infra_id"}}, { "pipeline", bson.A{ - bson.D{ - {"$match", bson.D{ - {"$expr", bson.D{ - {"$eq", bson.A{"$infra_id", "$$infraID"}}, - }}, - }}, - }, - bson.D{ - {"$project", bson.D{ - {"token", 0}, - {"infra_ns_exists", 0}, - {"infra_sa_exists", 0}, - {"access_key", 0}, + bson.D{ + {"$match", bson.D{ + {"$expr", bson.D{ + {"$eq", bson.A{"$infra_id", "$$infraID"}}, }}, - }, + }}, + }, + bson.D{ + {"$project", bson.D{ + {"token", 0}, + {"infra_ns_exists", 0}, + {"infra_sa_exists", 0}, + {"access_key", 0}, + }}, }, }, + }, {"as", "kubernetesInfraDetails"}, }}, } @@ -571,23 +571,23 @@ func (c *ChaosExperimentHandler) ListExperiment(projectID string, request model. {"let", bson.M{"infraID": "$infra_id"}}, { "pipeline", bson.A{ - bson.D{ - {"$match", bson.D{ - {"$expr", bson.D{ - {"$eq", bson.A{"$infra_id", "$$infraID"}}, - }}, - }}, - }, - bson.D{ - {"$project", bson.D{ - {"token", 0}, - {"infra_ns_exists", 0}, - {"infra_sa_exists", 0}, - {"access_key", 0}, + bson.D{ + {"$match", bson.D{ + {"$expr", bson.D{ + {"$eq", bson.A{"$infra_id", "$$infraID"}}, }}, - }, + }}, + }, + bson.D{ + {"$project", bson.D{ + {"token", 0}, + {"infra_ns_exists", 0}, + {"infra_sa_exists", 0}, + {"access_key", 0}, + }}, }, }, + }, {"as", "kubernetesInfraDetails"}, }}, } @@ -1073,11 +1073,11 @@ func (c *ChaosExperimentHandler) GetExperimentStats(ctx context.Context, project groupByTotalCount := bson.D{ { "$group", bson.D{ - {"_id", nil}, - {"count", bson.D{ - {"$sum", 1}, - }}, - }, + {"_id", nil}, + {"count", bson.D{ + {"$sum", 1}, + }}, + }, }, } @@ -1368,7 +1368,10 @@ func (c *ChaosExperimentHandler) StopExperimentRuns(ctx context.Context, project } } else if experimentRunID != nil && *experimentRunID != "" { experimentRunsID = []string{*experimentRunID} - err = c.chaosExperimentRunService.ProcessExperimentRunStop(ctx, query, experimentRunID, experiment, username, projectID, r) + } + + for _, runID := range experimentRunsID { + err = c.chaosExperimentRunService.ProcessExperimentRunStop(ctx, query, &runID, experiment, username, projectID, r) if err != nil { return false, err } From 955c09fd13c369a1bf6a2ba5fe1d534d574ce5f4 Mon Sep 17 00:00:00 2001 From: Saranya-jena Date: Thu, 26 Oct 2023 10:27:03 +0530 Subject: [PATCH 4/5] fixed imports Signed-off-by: Saranya-jena --- chaoscenter/subscriber/pkg/events/chaosengine.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chaoscenter/subscriber/pkg/events/chaosengine.go b/chaoscenter/subscriber/pkg/events/chaosengine.go index a0fca1d18a9..e7983297aab 100644 --- a/chaoscenter/subscriber/pkg/events/chaosengine.go +++ b/chaoscenter/subscriber/pkg/events/chaosengine.go @@ -4,11 +4,12 @@ import ( "context" "errors" "fmt" - wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" "strconv" "subscriber/pkg/k8s" "subscriber/pkg/types" + wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" + "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" chaosTypes "github.com/litmuschaos/chaos-operator/api/litmuschaos/v1alpha1" "github.com/litmuschaos/chaos-operator/pkg/client/clientset/versioned" From 6acd480a7f98c7c27828c69a783b21c36977d2dd Mon Sep 17 00:00:00 2001 From: Saranya-jena Date: Thu, 26 Oct 2023 10:29:49 +0530 Subject: [PATCH 5/5] fixed imports Signed-off-by: Saranya-jena --- .../graph/chaos_experiment_run.resolvers.go | 1 + .../pkg/chaos_experiment/handler/handler.go | 66 +++++++++---------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go b/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go index 5f621278fe0..8731d90e0c1 100644 --- a/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go +++ b/chaoscenter/graphql/server/graph/chaos_experiment_run.resolvers.go @@ -6,6 +6,7 @@ package graph import ( "context" "errors" + "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model" "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/authorization" data_store "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/data-store" diff --git a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go index 5ba94ebc92c..35d9a8a0652 100644 --- a/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go +++ b/chaoscenter/graphql/server/pkg/chaos_experiment/handler/handler.go @@ -311,23 +311,23 @@ func (c *ChaosExperimentHandler) GetExperiment(ctx context.Context, projectID st {"let", bson.M{"infraID": "$infra_id"}}, { "pipeline", bson.A{ - bson.D{ - {"$match", bson.D{ - {"$expr", bson.D{ - {"$eq", bson.A{"$infra_id", "$$infraID"}}, + bson.D{ + {"$match", bson.D{ + {"$expr", bson.D{ + {"$eq", bson.A{"$infra_id", "$$infraID"}}, + }}, }}, - }}, - }, - bson.D{ - {"$project", bson.D{ - {"token", 0}, - {"infra_ns_exists", 0}, - {"infra_sa_exists", 0}, - {"access_key", 0}, - }}, + }, + bson.D{ + {"$project", bson.D{ + {"token", 0}, + {"infra_ns_exists", 0}, + {"infra_sa_exists", 0}, + {"access_key", 0}, + }}, + }, }, }, - }, {"as", "kubernetesInfraDetails"}, }}, } @@ -571,23 +571,23 @@ func (c *ChaosExperimentHandler) ListExperiment(projectID string, request model. {"let", bson.M{"infraID": "$infra_id"}}, { "pipeline", bson.A{ - bson.D{ - {"$match", bson.D{ - {"$expr", bson.D{ - {"$eq", bson.A{"$infra_id", "$$infraID"}}, + bson.D{ + {"$match", bson.D{ + {"$expr", bson.D{ + {"$eq", bson.A{"$infra_id", "$$infraID"}}, + }}, }}, - }}, - }, - bson.D{ - {"$project", bson.D{ - {"token", 0}, - {"infra_ns_exists", 0}, - {"infra_sa_exists", 0}, - {"access_key", 0}, - }}, + }, + bson.D{ + {"$project", bson.D{ + {"token", 0}, + {"infra_ns_exists", 0}, + {"infra_sa_exists", 0}, + {"access_key", 0}, + }}, + }, }, }, - }, {"as", "kubernetesInfraDetails"}, }}, } @@ -1073,11 +1073,11 @@ func (c *ChaosExperimentHandler) GetExperimentStats(ctx context.Context, project groupByTotalCount := bson.D{ { "$group", bson.D{ - {"_id", nil}, - {"count", bson.D{ - {"$sum", 1}, - }}, - }, + {"_id", nil}, + {"count", bson.D{ + {"$sum", 1}, + }}, + }, }, }