Skip to content

Commit

Permalink
feat: rerun cli command
Browse files Browse the repository at this point in the history
Signed-off-by: Vladislav Sukhin <vladislav@kubeshop.io>
  • Loading branch information
vsukhin committed Feb 12, 2025
1 parent 593fc3b commit 09a6238
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 3 deletions.
3 changes: 2 additions & 1 deletion cmd/kubectl-testkube/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func NewRunCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "run <resourceName>",
Aliases: []string{"r", "start"},
Short: "Runs tests, test suites or test workflows",
Short: "Runs tests, test suites, test workflows or test workflow executions",
Annotations: map[string]string{cmdGroupAnnotation: cmdGroupCommands},
Run: func(cmd *cobra.Command, args []string) {
err := cmd.Help()
Expand All @@ -33,6 +33,7 @@ func NewRunCmd() *cobra.Command {
cmd.AddCommand(tests.NewRunTestCmd())
cmd.AddCommand(testsuites.NewRunTestSuiteCmd())
cmd.AddCommand(testworkflows.NewRunTestWorkflowCmd())
cmd.AddCommand(testworkflows.NewReRunTestWorkflowExecutionCmd())

cmd.PersistentFlags().StringP("output", "o", "pretty", "output type can be one of json|yaml|pretty|go")
cmd.PersistentFlags().StringP("go-template", "", "{{.}}", "go template to render")
Expand Down
159 changes: 159 additions & 0 deletions cmd/kubectl-testkube/commands/testworkflows/rerun.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package testworkflows

import (
"context"
"errors"
"os"
"strings"

"github.com/spf13/cobra"

"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/render"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/validator"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/tests"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/testworkflows/renderer"
testkubecfg "github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/api/v1/testkube"
tclcmd "github.com/kubeshop/testkube/pkg/tcl/testworkflowstcl/cmd"
"github.com/kubeshop/testkube/pkg/telemetry"
"github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/constants"
"github.com/kubeshop/testkube/pkg/ui"
)

func NewReRunTestWorkflowExecutionCmd() *cobra.Command {
var (
watchEnabled bool
downloadArtifactsEnabled bool
downloadDir string
format string
masks []string
serviceName string
parallelStepName string
serviceIndex int
parallelStepIndex int
)

cmd := &cobra.Command{
Use: "testworkflowexecution [id]",
Aliases: []string{"testworkflowexecutions", "twe"},
Short: "ReRun test workflow execution",
Args: validator.ExecutionName,

Run: func(cmd *cobra.Command, args []string) {
outputFlag := cmd.Flag("output")
outputType := render.OutputPretty
if outputFlag != nil {
outputType = render.OutputType(outputFlag.Value.String())
}

outputPretty := outputType == render.OutputPretty
namespace := cmd.Flag("namespace").Value.String()
client, _, err := common.GetClient(cmd)
ui.ExitOnError("getting client", err)

executionID := args[0]
execution, err := client.GetTestWorkflowExecution(executionID)
ui.ExitOnError("get test workflow execution failed", err)

runContext := telemetry.GetCliRunContext()
interfaceType := testkube.CICD_TestWorkflowRunningContextInterfaceType
if runContext == "others|local" {
runContext = ""
interfaceType = testkube.CLI_TestWorkflowRunningContextInterfaceType
}

cfg, err := testkubecfg.Load()
ui.ExitOnError("loading config file", err)
ui.NL()

var runningContext *testkube.TestWorkflowRunningContext
// Pro edition only (tcl protected code)
if cfg.ContextType == testkubecfg.ContextTypeCloud {
runningContext = tclcmd.GetRunningContext(runContext, cfg.CloudContext.ApiKey, interfaceType)
}

name := ""
if execution.Workflow != nil {
name = execution.Workflow.Name
}

execution, err = client.ReRunTestWorkflowExecution(name, execution.Id, runningContext)
if err != nil {
// User friendly Open Source operation error
errMessage := err.Error()
if strings.Contains(errMessage, constants.OpenSourceOperationErrorMessage) {
startp := strings.LastIndex(errMessage, apiErrorMessage)
endp := strings.Index(errMessage, constants.OpenSourceOperationErrorMessage)
if startp != -1 && endp != -1 {
startp += len(apiErrorMessage)
operation := ""
if endp > startp {
operation = strings.TrimSpace(errMessage[startp:endp])
}

err = errors.New(operation + " " + constants.OpenSourceOperationErrorMessage)
}
}
}

ui.ExitOnError("rerun test workflow execution "+executionID+" from namespace "+namespace, err)

go func() {
<-cmd.Context().Done()
if errors.Is(cmd.Context().Err(), context.Canceled) {
os.Exit(0)
}
}()

err = renderer.PrintTestWorkflowExecution(cmd, os.Stdout, execution)
ui.ExitOnError("render test workflow execution", err)

var exitCode = 0
if outputPretty {
ui.NL()
if !execution.FailedToInitialize() {
if watchEnabled {
var pServiceName, pParallelStepName *string
if cmd.Flag("service-name").Changed || cmd.Flag("service-index").Changed {
pServiceName = &serviceName
}
if cmd.Flag("parallel-step-name").Changed || cmd.Flag("parallel-step-index").Changed {
pParallelStepName = &parallelStepName
}

exitCode = uiWatch(execution, pServiceName, serviceIndex, pParallelStepName, parallelStepIndex, client)
ui.NL()
if downloadArtifactsEnabled {
tests.DownloadTestWorkflowArtifacts(execution.Id, downloadDir, format, masks, client, outputPretty)
}
} else {
uiShellWatchExecution(execution.Id)
}
}

execution, err = client.GetTestWorkflowExecution(execution.Id)
ui.ExitOnError("get test workflow execution failed", err)

render.PrintTestWorkflowExecutionURIs(&execution)
uiShellGetExecution(execution.Id)
}

if exitCode != 0 {
os.Exit(exitCode)
}
},
}

cmd.Flags().BoolVarP(&watchEnabled, "watch", "f", false, "watch for changes after start")
cmd.Flags().StringVar(&downloadDir, "download-dir", "artifacts", "download dir")
cmd.Flags().BoolVarP(&downloadArtifactsEnabled, "download-artifacts", "d", false, "download artifacts automatically")
cmd.Flags().StringVar(&format, "format", "folder", "data format for storing files, one of folder|archive")
cmd.Flags().StringArrayVarP(&masks, "mask", "", []string{}, "regexp to filter downloaded files, single or comma separated, like report/.* or .*\\.json,.*\\.js$")
cmd.Flags().StringVar(&serviceName, "service-name", "", "test workflow service name")
cmd.Flags().IntVar(&serviceIndex, "service-index", 0, "test workflow service index starting from 0")
cmd.Flags().StringVar(&parallelStepName, "parallel-step-name", "", "test workflow parallel step name or reference")
cmd.Flags().IntVar(&parallelStepIndex, "parallel-step-index", 0, "test workflow parallel step index starting from 0")

return cmd
}
1 change: 1 addition & 0 deletions internal/app/api/v1/testworkflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ func (s *TestkubeAPI) ReRunTestWorkflowExecutionHandler() fiber.Handler {
RunningContext: &twrContext,
Tags: execution.Tags,
DisableWebhooks: execution.DisableWebhooks,
Target: execution.RunnerOriginalTarget,
}

request.Config = make(map[string]string)
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/v1/client/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ type TestWorkflowExecutionAPI interface {
GetTestWorkflowExecutionArtifacts(executionID string) (artifacts testkube.Artifacts, err error)
DownloadTestWorkflowArtifact(executionID, fileName, destination string) (artifact string, err error)
DownloadTestWorkflowArtifactArchive(executionID, destination string, masks []string) (archive string, err error)
ReRunTestWorkflowExecution(workflow string, id string, runningContext testkube.TestWorkflowRunningContext) (testkube.TestWorkflowExecution, error)
ReRunTestWorkflowExecution(workflow string, id string, runningContext *testkube.TestWorkflowRunningContext) (testkube.TestWorkflowExecution, error)
}

// TestWorkflowTemplateAPI describes test workflow api methods
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/v1/client/testworkflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ func (c TestWorkflowClient) GetTestWorkflowExecutionLogs(id string) (result []by
}

// ReRunTestWorkflowExecution reruns selected execution
func (c TestWorkflowClient) ReRunTestWorkflowExecution(workflow, id string, runningContext testkube.TestWorkflowRunningContext) (result testkube.TestWorkflowExecution, err error) {
func (c TestWorkflowClient) ReRunTestWorkflowExecution(workflow, id string, runningContext *testkube.TestWorkflowRunningContext) (result testkube.TestWorkflowExecution, err error) {
if workflow == "" {
return result, fmt.Errorf("test workflow name '%s' is not valid", workflow)
}
Expand Down

0 comments on commit 09a6238

Please sign in to comment.