Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cli): Adding run by id #3775

Merged
merged 6 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cli/cloud/cmd/run_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ func RunMultipleFiles(ctx context.Context, httpClient *resourcemanager.HTTPClien
)

return orchestrator.Run(ctx, runner.RunOptions{
IDs: runParams.IDs,
ResourceName: runParams.ResourceName,
DefinitionFiles: runParams.DefinitionFiles,
VarsID: runParams.VarsID,
SkipResultWait: runParams.SkipResultWait,
Expand Down
99 changes: 76 additions & 23 deletions cli/cloud/runner/multifile_orchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net/http"
"os"
"sync"

"github.com/kubeshop/tracetest/cli/formatters"
"github.com/kubeshop/tracetest/cli/metadata"
Expand All @@ -22,6 +23,10 @@ import (
// RunOptions defines options for running a resource
// IDs and DefinitionFiles are mutually exclusive and the only required options
type RunOptions struct {
// if IDs is used it needs to have the ResourceName defined
IDs []string
ResourceName string

// path to the file with resource definition
// the file will be applied before running
DefinitionFiles []string
Expand Down Expand Up @@ -101,36 +106,27 @@ func (o orchestrator) Run(ctx context.Context, opts RunOptions, outputFormat str
}
o.logger.Debug("env resolved", zap.String("ID", varsID))

hasDefinitionFilesDefined := opts.DefinitionFiles != nil && len(opts.DefinitionFiles) > 0
resourceFetcher := runner.GetResourceFetcher(o.logger, o.runnerRegistry)

if !hasDefinitionFilesDefined {
return ExitCodeGeneralError, fmt.Errorf("you must define at least two files to use the multifile orchestrator")
}

vars := varset.VarSets{}
var resources []any
resourceFetcher := runner.GetResourceFetcher(o.logger, o.runnerRegistry)

runGroupID := opts.RunGroupID
if runGroupID == "" {
runGroupID = id.GenerateID().String()
}
runsResults := make([]runner.RunResult, 0)
definitionFiles, err := o.getDefinitionFiles(opts.DefinitionFiles)

if err != nil {
return ExitCodeGeneralError, fmt.Errorf("cannot read definition files: %w", err)
}
var resources []any
var runsResults []runner.RunResult

// 1. create runs
for _, definitionFile := range definitionFiles {
result, resource, err := o.createRun(ctx, resourceFetcher, &vars, opts.RequiredGates, definitionFile, varsID, runGroupID)
if len(opts.DefinitionFiles) > 0 {
resources, runsResults, err = o.runByFiles(ctx, opts, resourceFetcher, &vars, varsID, runGroupID)
if err != nil {
return ExitCodeGeneralError, fmt.Errorf("cannot run test: %w", err)
return ExitCodeGeneralError, fmt.Errorf("cannot run files: %w", err)
}
} else {
resources, runsResults, err = o.runByIDs(ctx, opts, resourceFetcher, &vars, varsID, runGroupID)
if err != nil {
return ExitCodeGeneralError, fmt.Errorf("cannot run by id: %w", err)
}

runsResults = append(runsResults, result)
resources = append(resources, resource)
}

runnerGetter := func(resource any) (formatters.Runner[runner.RunResult], error) {
Expand Down Expand Up @@ -224,12 +220,69 @@ func (o orchestrator) getDefinitionFiles(file []string) ([]string, error) {
return files, nil
}

func (o orchestrator) createRun(ctx context.Context, resourceFetcher runner.ResourceFetcher, vars *varset.VarSets, requiredGates []string, definitionFile string, varsID string, runGroupID string) (runner.RunResult, any, error) {
resource, err := resourceFetcher.FetchWithDefinitionFile(ctx, definitionFile)
func (o orchestrator) runByFiles(ctx context.Context, opts RunOptions, resourceFetcher runner.ResourceFetcher, vars *varset.VarSets, varsID string, runGroupID string) ([]any, []runner.RunResult, error) {
resources := make([]any, 0)
runsResults := make([]runner.RunResult, 0)
var mainErr error

hasDefinitionFilesDefined := opts.DefinitionFiles != nil && len(opts.DefinitionFiles) > 0
if !hasDefinitionFilesDefined {
return resources, runsResults, fmt.Errorf("no definition files defined")
}

definitionFiles, err := o.getDefinitionFiles(opts.DefinitionFiles)
if err != nil {
return runner.RunResult{}, nil, err
return resources, runsResults, fmt.Errorf("cannot get definition files: %w", err)
}

var wg sync.WaitGroup
wg.Add(len(definitionFiles))
for _, definitionFile := range definitionFiles {
go func(def string) {
defer wg.Done()
resource, err := resourceFetcher.FetchWithDefinitionFile(ctx, def)
if err != nil {
mainErr = fmt.Errorf("cannot fetch resource from definition file: %w", err)
return
}
result, resource, err := o.createRun(ctx, resource, vars, opts.RequiredGates, varsID, runGroupID)
if err != nil {
mainErr = fmt.Errorf("cannot run test: %w", err)
return
}

runsResults = append(runsResults, result)
resources = append(resources, resource)
}(definitionFile)
}

wg.Wait()
return resources, runsResults, mainErr
}

func (o orchestrator) runByIDs(ctx context.Context, opts RunOptions, resourceFetcher runner.ResourceFetcher, vars *varset.VarSets, varsID string, runGroupID string) ([]any, []runner.RunResult, error) {
resources := make([]any, 0)
runsResults := make([]runner.RunResult, 0)

for _, id := range opts.IDs {
resource, err := resourceFetcher.FetchWithID(ctx, opts.ResourceName, id)
if err != nil {
return resources, runsResults, err
}

result, resource, err := o.createRun(ctx, resource, vars, opts.RequiredGates, varsID, runGroupID)
if err != nil {
return resources, runsResults, fmt.Errorf("cannot run test: %w", err)
}

runsResults = append(runsResults, result)
resources = append(resources, resource)
}

return resources, runsResults, nil
}

func (o orchestrator) createRun(ctx context.Context, resource any, vars *varset.VarSets, requiredGates []string, varsID, runGroupID string) (runner.RunResult, any, error) {
resourceType, err := resourcemanager.GetResourceType(resource)
if err != nil {
return runner.RunResult{}, nil, fmt.Errorf("cannot extract type from resource: %w", err)
Expand Down
10 changes: 8 additions & 2 deletions cli/cmd/resource_run_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func init() {
Long: "Run tests and test suites",
PreRun: setupCommand(WithOptionalResourceName()),
Run: WithResourceMiddleware(func(ctx context.Context, _ *cobra.Command, args []string) (string, error) {
runParams.ResourceName = resourceParams.ResourceName
if cliConfig.Jwt != "" {
exitCode, err := cloudCmd.RunMultipleFiles(ctx, httpClient, runParams, &cliConfig, runnerRegistry, output)
ExitCLI(exitCode)
Expand All @@ -40,7 +41,7 @@ func init() {
}

runCmd.Flags().StringSliceVarP(&runParams.DefinitionFiles, "file", "f", []string{}, "path to the definition file (can be defined multiple times)")
runCmd.Flags().StringVarP(&runParams.ID, "id", "", "", "id of the resource to run (can be defined multiple times)")
runCmd.Flags().StringSliceVarP(&runParams.IDs, "id", "", []string{}, "id of the resource to run (can be defined multiple times)")
runCmd.Flags().StringVarP(&runParams.VarsID, "vars", "", "", "variable set file or ID to be used")
runCmd.Flags().StringVarP(&runParams.RunGroupID, "group", "g", "", "Sets the Run Group ID for the run. This is used to group multiple runs together.")
runCmd.Flags().BoolVarP(&runParams.SkipResultWait, "skip-result-wait", "W", false, "do not wait for results. exit immediately after test run started")
Expand Down Expand Up @@ -72,8 +73,13 @@ func runSingleFile(ctx context.Context) (string, error) {
definitionFile = runParams.DefinitionFiles[0]
}

ID := ""
if len(runParams.IDs) > 0 {
ID = runParams.IDs[0]
}

runParams := runner.RunOptions{
ID: runParams.ID,
ID: ID,
DefinitionFile: definitionFile,
VarsID: runParams.VarsID,
SkipResultWait: runParams.SkipResultWait,
Expand Down
13 changes: 11 additions & 2 deletions cli/cmdutil/run_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ import (
)

type RunParameters struct {
ID string
IDs []string
DefinitionFiles []string
VarsID string
EnvID string
SkipResultWait bool
JUnitOuptutFile string
RequiredGates []string
RunGroupID string
ResourceName string
}

func (p RunParameters) Validate(cmd *cobra.Command, args []string) []error {
errs := []error{}

hasDefinitionFilesSpecified := p.DefinitionFiles != nil && len(p.DefinitionFiles) > 0
hasFileIDsSpecified := p.ID != "" && len(p.ID) > 0
hasFileIDsSpecified := len(p.IDs) > 0

if !hasDefinitionFilesSpecified && !hasFileIDsSpecified {
errs = append(errs, ParamError{
Expand All @@ -32,6 +33,14 @@ func (p RunParameters) Validate(cmd *cobra.Command, args []string) []error {
})
}

isResourceNameSpecified := len(args) > 0
if hasFileIDsSpecified && !isResourceNameSpecified {
errs = append(errs, ParamError{
Parameter: "resource",
Message: "you must specify a resource name (test|testsuite) when providing a resource IDs",
})
}

if hasDefinitionFilesSpecified && hasFileIDsSpecified {
errs = append(errs, ParamError{
Parameter: "resource",
Expand Down
Loading