diff --git a/api/v1/testkube.yaml b/api/v1/testkube.yaml index 1a7068d0e5..26fafe0525 100644 --- a/api/v1/testkube.yaml +++ b/api/v1/testkube.yaml @@ -4065,6 +4065,57 @@ paths: items: $ref: "#/components/schemas/Problem" /test-workflow-executions: + post: + parameters: + - $ref: "#/components/parameters/Selector" + - $ref: "#/components/parameters/ConcurrencyLevel" + tags: + - api + - test-workflows + - pro + summary: "Execute test workflows" + description: "Execute test workflows in the kubernetes cluster" + operationId: executeTestWorkflows + requestBody: + description: test workflow execution request + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TestWorkflowExecutionRequest" + responses: + 200: + description: successful execution + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TestWorkflowExecution" + 400: + description: "problem with body parsing - probably some bad input occurs" + content: + application/problem+json: + schema: + type: array + items: + $ref: "#/components/schemas/Problem" + 402: + description: "missing Pro subscription for a commercial feature" + content: + application/problem+json: + schema: + type: array + items: + $ref: "#/components/schemas/Problem" + 502: + description: problem communicating with kubernetes cluster + content: + application/problem+json: + schema: + type: array + items: + $ref: "#/components/schemas/Problem" get: tags: - test-workflows @@ -8990,6 +9041,9 @@ components: $ref: "#/components/schemas/TestWorkflowTarballRequest" config: $ref: "#/components/schemas/TestWorkflowConfigValue" + selector: + $ref: "#/components/schemas/LabelSelector" + description: label selector for test workflow TestWorkflowStepExecuteTestRef: type: object diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index d0f10c51ba..5c96c690d3 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -3,6 +3,7 @@ package testworkflows import ( "bufio" "bytes" + "context" "errors" "fmt" "os" @@ -45,12 +46,12 @@ func NewRunTestWorkflowCmd() *cobra.Command { format string masks []string tags map[string]string + selectors []string ) cmd := &cobra.Command{ Use: "testworkflow [name]", Aliases: []string{"testworkflows", "tw"}, - Args: cobra.ExactArgs(1), Short: "Starts test workflow execution", Run: func(cmd *cobra.Command, args []string) { @@ -65,7 +66,6 @@ func NewRunTestWorkflowCmd() *cobra.Command { client, _, err := common.GetClient(cmd) ui.ExitOnError("getting client", err) - name := args[0] runContext := telemetry.GetCliRunContext() interfaceType := testkube.CICD_TestWorkflowRunningContextInterfaceType if runContext == "others|local" { @@ -83,13 +83,29 @@ func NewRunTestWorkflowCmd() *cobra.Command { runningContext = tclcmd.GetRunningContext(runContext, cfg.CloudContext.ApiKey, interfaceType) } - execution, err := client.ExecuteTestWorkflow(name, testkube.TestWorkflowExecutionRequest{ + request := testkube.TestWorkflowExecutionRequest{ Name: executionName, Config: config, DisableWebhooks: disableWebhooks, Tags: tags, RunningContext: runningContext, - }) + } + + var executions []testkube.TestWorkflowExecution + switch { + case len(args) > 0: + name := args[0] + + var execution testkube.TestWorkflowExecution + execution, err = client.ExecuteTestWorkflow(name, request) + executions = append(executions, execution) + case len(selectors) != 0: + selector := strings.Join(selectors, ",") + executions, err = client.ExecuteTestWorkflows(selector, request) + default: + ui.Failf("Pass Test workflow name or labels to run by labels ") + } + if err != nil { // User friendly Open Source operation error errMessage := err.Error() @@ -108,33 +124,49 @@ func NewRunTestWorkflowCmd() *cobra.Command { } } - ui.ExitOnError("execute test workflow "+name+" from namespace "+namespace, err) - 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 { - exitCode = uiWatch(execution, client) - ui.NL() - if downloadArtifactsEnabled { - tests.DownloadTestWorkflowArtifacts(execution.Id, downloadDir, format, masks, client, outputPretty) + if len(args) > 0 { + ui.ExitOnError("execute test workflow "+args[0]+" from namespace "+namespace, err) + } else { + ui.ExitOnError("execute test workflows "+strings.Join(selectors, ",")+" from namespace "+namespace, err) + } + + go func() { + <-cmd.Context().Done() + if errors.Is(cmd.Context().Err(), context.Canceled) { + os.Exit(0) + } + }() + + for _, execution := range executions { + 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 && len(args) > 0 { + exitCode = uiWatch(execution, client) + ui.NL() + if downloadArtifactsEnabled { + tests.DownloadTestWorkflowArtifacts(execution.Id, downloadDir, format, masks, client, outputPretty) + } + } else { + uiShellWatchExecution(execution.Id) } - } else { - uiShellWatchExecution(execution.Id) } - } - execution, err = client.GetTestWorkflowExecution(execution.Id) - ui.ExitOnError("get execution failed", err) + execution, err = client.GetTestWorkflowExecution(execution.Id) + ui.ExitOnError("get execution failed", err) - render.PrintTestWorkflowExecutionURIs(&execution) - uiShellGetExecution(execution.Id) - } + render.PrintTestWorkflowExecutionURIs(&execution) + uiShellGetExecution(execution.Id) + } - os.Exit(exitCode) + if exitCode != 0 { + os.Exit(exitCode) + } + } }, } @@ -148,6 +180,7 @@ func NewRunTestWorkflowCmd() *cobra.Command { 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().StringToStringVarP(&tags, "tag", "", map[string]string{}, "execution tags in a form of name1=val1 passed to executor") + cmd.Flags().StringSliceVarP(&selectors, "label", "l", nil, "label key value pair: --label key1=value1 or label expression") return cmd } diff --git a/cmd/tcl/testworkflow-toolkit/commands/execute.go b/cmd/tcl/testworkflow-toolkit/commands/execute.go index 2079b69e20..ce47f57f83 100644 --- a/cmd/tcl/testworkflow-toolkit/commands/execute.go +++ b/cmd/tcl/testworkflow-toolkit/commands/execute.go @@ -18,6 +18,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" testworkflowsv1 "github.com/kubeshop/testkube-operator/api/testworkflows/v1" commontcl "github.com/kubeshop/testkube/cmd/tcl/testworkflow-toolkit/common" @@ -368,6 +369,8 @@ func NewExecuteCmd() *cobra.Command { operations = append(operations, fn) } } + + c := env.Testkube() for _, s := range workflows { var w testworkflowsv1.StepExecuteWorkflow err := json.Unmarshal([]byte(s), &w) @@ -375,35 +378,65 @@ func NewExecuteCmd() *cobra.Command { ui.Fail(errors.Wrap(err, "unmarshal workflow definition")) } - // Resolve the params - params, err := commontcl.GetParamsSpec(w.Matrix, w.Shards, w.Count, w.MaxCount, baseMachine) - if err != nil { - ui.Fail(errors.Wrap(err, "matrix and sharding")) + if w.Name == "" && w.Selector == nil { + ui.Fail(errors.New("either workflow name or selector should be specified")) } - fmt.Printf("%s: %s\n", commontcl.ServiceLabel(w.Name), params.Humanize()) - // Create operations for each expected execution - for i := int64(0); i < params.Count; i++ { - // Clone the spec - spec := w.DeepCopy() + var testWorkflowNames []string + if w.Name != "" { + testWorkflowNames = []string{w.Name} + } - // Build files for transfer - tarballMachine, err := registerTransfer(transferSrv, spec.Tarball, baseMachine, params.MachineAt(i)) + if w.Selector != nil { + selector, err := metav1.LabelSelectorAsSelector(w.Selector) if err != nil { - ui.Fail(errors.Wrapf(err, "'%s' workflow", spec.Name)) + ui.Fail(errors.Wrap(err, "error creating selector from test workflow selector")) } - spec.Tarball = nil - // Prepare the operation to run - err = expressions.Finalize(&spec, baseMachine, tarballMachine, params.MachineAt(i)) + stringifiedSelector := selector.String() + testWorkflowsList, err := c.ListTestWorkflows(stringifiedSelector) if err != nil { - ui.Fail(errors.Wrapf(err, "'%s' workflow: computing execution", spec.Name)) + ui.Fail(errors.Wrap(err, "error listing test workflows using selector")) } - fn, err := buildWorkflowExecution(*spec, async) - if err != nil { - ui.Fail(err) + + for _, item := range testWorkflowsList { + testWorkflowNames = append(testWorkflowNames, item.Name) + } + } + + // Resolve the params + params, err := commontcl.GetParamsSpec(w.Matrix, w.Shards, w.Count, w.MaxCount, baseMachine) + if err != nil { + ui.Fail(errors.Wrap(err, "matrix and sharding")) + } + + for _, testWorkflowName := range testWorkflowNames { + fmt.Printf("%s: %s\n", commontcl.ServiceLabel(testWorkflowName), params.Humanize()) + + // Create operations for each expected execution + for i := int64(0); i < params.Count; i++ { + // Clone the spec + spec := w.DeepCopy() + spec.Name = testWorkflowName + + // Build files for transfer + tarballMachine, err := registerTransfer(transferSrv, spec.Tarball, baseMachine, params.MachineAt(i)) + if err != nil { + ui.Fail(errors.Wrapf(err, "'%s' workflow", spec.Name)) + } + spec.Tarball = nil + + // Prepare the operation to run + err = expressions.Finalize(&spec, baseMachine, tarballMachine, params.MachineAt(i)) + if err != nil { + ui.Fail(errors.Wrapf(err, "'%s' workflow: computing execution", spec.Name)) + } + fn, err := buildWorkflowExecution(*spec, async) + if err != nil { + ui.Fail(err) + } + operations = append(operations, fn) } - operations = append(operations, fn) } } diff --git a/go.mod b/go.mod index 9cee87d6da..2d1b1bf494 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kelseyhightower/envconfig v1.4.0 github.com/kubepug/kubepug v1.7.1 - github.com/kubeshop/testkube-operator v1.17.55-0.20241030092155-2a57f6e797e9 + github.com/kubeshop/testkube-operator v1.17.55-0.20241118133003-70462ac10f4a github.com/minio/minio-go/v7 v7.0.47 github.com/montanaflynn/stats v0.6.6 github.com/moogar0880/problems v0.1.1 @@ -55,6 +55,7 @@ require ( github.com/prometheus/client_golang v1.18.0 github.com/pterm/pterm v0.12.79 github.com/robfig/cron v1.2.0 + github.com/savioxavier/termlink v1.4.1 github.com/segmentio/analytics-go/v3 v3.2.1 github.com/shirou/gopsutil/v3 v3.24.3 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 @@ -185,7 +186,6 @@ require ( github.com/rs/xid v1.4.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect - github.com/savioxavier/termlink v1.4.1 // indirect github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899 // indirect github.com/segmentio/backo-go v1.0.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect diff --git a/go.sum b/go.sum index cd0cd7ac9e..a16cd51848 100644 --- a/go.sum +++ b/go.sum @@ -402,8 +402,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubepug/kubepug v1.7.1 h1:LKhfSxS8Y5mXs50v+3Lpyec+cogErDLcV7CMUuiaisw= github.com/kubepug/kubepug v1.7.1/go.mod h1:lv+HxD0oTFL7ZWjj0u6HKhMbbTIId3eG7aWIW0gyF8g= -github.com/kubeshop/testkube-operator v1.17.55-0.20241030092155-2a57f6e797e9 h1:0v4W4kPfuDBJxvfkgKhDFA71AgmV0B5Jdb8dR7n4bV4= -github.com/kubeshop/testkube-operator v1.17.55-0.20241030092155-2a57f6e797e9/go.mod h1:P47tw1nKQFufdsZndyq2HG2MSa0zK/lU0XpRfZtEmIk= +github.com/kubeshop/testkube-operator v1.17.55-0.20241118133003-70462ac10f4a h1:xget2cwwqOL+K2Op9FPbMgfzj9lSVJAzZ9p48yxuFrE= +github.com/kubeshop/testkube-operator v1.17.55-0.20241118133003-70462ac10f4a/go.mod h1:P47tw1nKQFufdsZndyq2HG2MSa0zK/lU0XpRfZtEmIk= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= diff --git a/internal/app/api/v1/server.go b/internal/app/api/v1/server.go index e555f095ec..615f1e52c7 100644 --- a/internal/app/api/v1/server.go +++ b/internal/app/api/v1/server.go @@ -147,6 +147,7 @@ func (s *TestkubeAPI) Init(server server.HTTPServer) { testWorkflowExecutions := root.Group("/test-workflow-executions") testWorkflowExecutions.Get("/", s.ListTestWorkflowExecutionsHandler()) + testWorkflowExecutions.Post("/", s.ExecuteTestWorkflowHandler()) testWorkflowExecutions.Get("/:executionID", s.GetTestWorkflowExecutionHandler()) testWorkflowExecutions.Get("/:executionID/notifications", s.StreamTestWorkflowExecutionNotificationsHandler()) testWorkflowExecutions.Get("/:executionID/notifications/stream", s.StreamTestWorkflowExecutionNotificationsWebSocketHandler()) diff --git a/internal/app/api/v1/testworkflows.go b/internal/app/api/v1/testworkflows.go index 4602eb555a..4a741e24b3 100644 --- a/internal/app/api/v1/testworkflows.go +++ b/internal/app/api/v1/testworkflows.go @@ -2,13 +2,13 @@ package v1 import ( "context" + "errors" "fmt" "net/http" "strconv" "strings" "github.com/gofiber/fiber/v2" - "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" testworkflowsv1 "github.com/kubeshop/testkube-operator/api/testworkflows/v1" @@ -348,10 +348,27 @@ func (s *TestkubeAPI) ExecuteTestWorkflowHandler() fiber.Handler { return func(c *fiber.Ctx) (err error) { ctx := c.Context() name := c.Params("id") - errPrefix := fmt.Sprintf("failed to execute test workflow '%s'", name) - workflow, err := s.TestWorkflowsClient.Get(name) - if err != nil { - return s.ClientError(c, errPrefix, err) + selector := c.Query("selector") + s.Log.Debugw("getting test workflow", "name", name, "selector", selector) + + errPrefix := "failed to execute test workflow" + + var testWorkflows []testworkflowsv1.TestWorkflow + if name != "" { + errPrefix = errPrefix + " " + name + testWorkflow, err := s.TestWorkflowsClient.Get(name) + if err != nil { + return s.ClientError(c, errPrefix, err) + } + + testWorkflows = append(testWorkflows, *testWorkflow) + } else { + testWorkflowList, err := s.TestWorkflowsClient.List(selector) + if err != nil { + return s.ClientError(c, errPrefix, err) + } + + testWorkflows = append(testWorkflows, testWorkflowList.Items...) } // Load the execution request @@ -371,34 +388,32 @@ func (s *TestkubeAPI) ExecuteTestWorkflowHandler() fiber.Handler { var results []testkube.TestWorkflowExecution var errs []error - request.TestWorkflowExecutionName = strings.Clone(c.Query("testWorkflowExecutionName")) - concurrencyLevel := scheduler.DefaultConcurrencyLevel - workerpoolService := workerpool.New[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, - testkube.TestWorkflowExecution](concurrencyLevel) - requests := []workerpool.Request[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, testkube.TestWorkflowExecution]{ - { - Object: *workflow, - Options: request, - ExecFn: s.TestWorkflowExecutor.Execute, - }, - } - - go workerpoolService.SendRequests(requests) - go workerpoolService.Run(ctx) - - for r := range workerpoolService.GetResponses() { - results = append(results, r.Result) - if r.Err != nil { - errs = append(errs, r.Err) + if len(testWorkflows) != 0 { + request.TestWorkflowExecutionName = strings.Clone(c.Query("testWorkflowExecutionName")) + workerpoolService := workerpool.New[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, testkube.TestWorkflowExecution](scheduler.DefaultConcurrencyLevel) + + go workerpoolService.SendRequests(s.prepareTestWorkflowRequests(testWorkflows, request)) + go workerpoolService.Run(ctx) + + for r := range workerpoolService.GetResponses() { + results = append(results, r.Result) + if r.Err != nil { + errs = append(errs, r.Err) + } } } if len(errs) != 0 { - return s.InternalError(c, errPrefix, "execution error", errs[0]) + return s.InternalError(c, errPrefix, "execution error", errors.Join(errs...)) } + s.Log.Debugw("executing test workflow", "name", name, "selector", selector) if len(results) != 0 { - return c.JSON(results[0]) + if name != "" { + return c.JSON(results[0]) + } + + return c.JSON(results) } return s.InternalError(c, errPrefix, "error", errors.New("no execution results")) @@ -423,3 +438,20 @@ func (s *TestkubeAPI) getFilteredTestWorkflowList(c *fiber.Ctx) (*testworkflowsv return crWorkflows, nil } + +func (s *TestkubeAPI) prepareTestWorkflowRequests(work []testworkflowsv1.TestWorkflow, request testkube.TestWorkflowExecutionRequest) []workerpool.Request[ + testworkflowsv1.TestWorkflow, + testkube.TestWorkflowExecutionRequest, + testkube.TestWorkflowExecution, +] { + requests := make([]workerpool.Request[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, testkube.TestWorkflowExecution], len(work)) + for i := range work { + requests[i] = workerpool.Request[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, testkube.TestWorkflowExecution]{ + Object: work[i], + Options: request, + ExecFn: s.TestWorkflowExecutor.Execute, + } + } + + return requests +} diff --git a/pkg/api/v1/client/interface.go b/pkg/api/v1/client/interface.go index 554a3faaf6..82f1357377 100644 --- a/pkg/api/v1/client/interface.go +++ b/pkg/api/v1/client/interface.go @@ -151,6 +151,7 @@ type TestWorkflowAPI interface { UpdateTestWorkflow(workflow testkube.TestWorkflow) (testkube.TestWorkflow, error) DeleteTestWorkflow(name string) error ExecuteTestWorkflow(name string, request testkube.TestWorkflowExecutionRequest) (testkube.TestWorkflowExecution, error) + ExecuteTestWorkflows(selector string, request testkube.TestWorkflowExecutionRequest) ([]testkube.TestWorkflowExecution, error) GetTestWorkflowExecutionNotifications(id string) (chan testkube.TestWorkflowExecutionNotification, error) GetTestWorkflowExecutionLogs(id string) ([]byte, error) } diff --git a/pkg/api/v1/client/testworkflow.go b/pkg/api/v1/client/testworkflow.go index 3c929be660..d878f93565 100644 --- a/pkg/api/v1/client/testworkflow.go +++ b/pkg/api/v1/client/testworkflow.go @@ -121,6 +121,23 @@ func (c TestWorkflowClient) ExecuteTestWorkflow(name string, request testkube.Te return c.testWorkflowExecutionTransport.Execute(http.MethodPost, uri, body, nil) } +// ExecuteTestWorkflows starts new external test workflow executions, reads data and returns IDs +// Executions are started asynchronously client can check later for results +func (c TestWorkflowClient) ExecuteTestWorkflows(selector string, request testkube.TestWorkflowExecutionRequest) (executions []testkube.TestWorkflowExecution, err error) { + uri := c.testWorkflowExecutionTransport.GetURI("/test-workflow-executions") + + body, err := json.Marshal(request) + if err != nil { + return executions, err + } + + params := map[string]string{ + "selector": selector, + } + + return c.testWorkflowExecutionTransport.ExecuteMultiple(http.MethodPost, uri, body, params) +} + // GetTestWorkflowExecutionNotifications returns events stream from job pods, based on job pods logs func (c TestWorkflowClient) GetTestWorkflowExecutionNotifications(id string) (notifications chan testkube.TestWorkflowExecutionNotification, err error) { notifications = make(chan testkube.TestWorkflowExecutionNotification) diff --git a/pkg/api/v1/testkube/model_test_workflow_step_execute_test_workflow_ref.go b/pkg/api/v1/testkube/model_test_workflow_step_execute_test_workflow_ref.go index c2bcd5b1d1..be1e4454f7 100644 --- a/pkg/api/v1/testkube/model_test_workflow_step_execute_test_workflow_ref.go +++ b/pkg/api/v1/testkube/model_test_workflow_step_execute_test_workflow_ref.go @@ -23,5 +23,6 @@ type TestWorkflowStepExecuteTestWorkflowRef struct { // matrix of parameters to spawn instances Matrix map[string]interface{} `json:"matrix,omitempty"` // parameters that should be distributed across sharded instances - Shards map[string]interface{} `json:"shards,omitempty"` + Shards map[string]interface{} `json:"shards,omitempty"` + Selector *LabelSelector `json:"selector,omitempty"` } diff --git a/pkg/mapper/testworkflows/kube_openapi.go b/pkg/mapper/testworkflows/kube_openapi.go index c0664f9d93..9076451da8 100644 --- a/pkg/mapper/testworkflows/kube_openapi.go +++ b/pkg/mapper/testworkflows/kube_openapi.go @@ -806,6 +806,21 @@ func MapStepExecuteTestKubeToAPI(v testworkflowsv1.StepExecuteTest) testkube.Tes } } +func MapLabelSelectorRequirementToAPI(v metav1.LabelSelectorRequirement) testkube.LabelSelectorRequirement { + return testkube.LabelSelectorRequirement{ + Key: v.Key, + Operator: string(v.Operator), + Values: v.Values, + } +} + +func MapSelectorToAPI(v metav1.LabelSelector) testkube.LabelSelector { + return testkube.LabelSelector{ + MatchLabels: v.MatchLabels, + MatchExpressions: common.MapSlice(v.MatchExpressions, MapLabelSelectorRequirementToAPI), + } +} + func MapStepExecuteTestWorkflowKubeToAPI(v testworkflowsv1.StepExecuteWorkflow) testkube.TestWorkflowStepExecuteTestWorkflowRef { return testkube.TestWorkflowStepExecuteTestWorkflowRef{ Name: v.Name, @@ -817,6 +832,7 @@ func MapStepExecuteTestWorkflowKubeToAPI(v testworkflowsv1.StepExecuteWorkflow) MaxCount: MapIntOrStringToBoxedString(v.MaxCount), Matrix: MapDynamicListMapKubeToAPI(v.Matrix), Shards: MapDynamicListMapKubeToAPI(v.Shards), + Selector: common.MapPtr(v.Selector, MapSelectorToAPI), } } diff --git a/pkg/mapper/testworkflows/openapi_kube.go b/pkg/mapper/testworkflows/openapi_kube.go index 844e6da049..86b9bf7ede 100644 --- a/pkg/mapper/testworkflows/openapi_kube.go +++ b/pkg/mapper/testworkflows/openapi_kube.go @@ -846,6 +846,21 @@ func MapStepExecuteTestAPIToKube(v testkube.TestWorkflowStepExecuteTestRef) test } } +func MapLabelSelectorRequirementToCRD(v testkube.LabelSelectorRequirement) metav1.LabelSelectorRequirement { + return metav1.LabelSelectorRequirement{ + Key: v.Key, + Operator: metav1.LabelSelectorOperator(v.Operator), + Values: v.Values, + } +} + +func MapSelectorToCRD(v testkube.LabelSelector) metav1.LabelSelector { + return metav1.LabelSelector{ + MatchLabels: v.MatchLabels, + MatchExpressions: common.MapSlice(v.MatchExpressions, MapLabelSelectorRequirementToCRD), + } +} + func MapStepExecuteTestWorkflowAPIToKube(v testkube.TestWorkflowStepExecuteTestWorkflowRef) testworkflowsv1.StepExecuteWorkflow { return testworkflowsv1.StepExecuteWorkflow{ Name: v.Name, @@ -859,6 +874,7 @@ func MapStepExecuteTestWorkflowAPIToKube(v testkube.TestWorkflowStepExecuteTestW Matrix: MapDynamicListMapAPIToKube(v.Matrix), Shards: MapDynamicListMapAPIToKube(v.Shards), }, + Selector: common.MapPtr(v.Selector, MapSelectorToCRD), } }