From 36c1e4f86047bb3a23aacc0c8f817d0c3af42efb Mon Sep 17 00:00:00 2001 From: pmahindrakar-oss <77798312+pmahindrakar-oss@users.noreply.github.com> Date: Wed, 12 May 2021 00:34:00 +0530 Subject: [PATCH] Added version support to workflow command (#64) Signed-off-by: Ketan Umare --- .../task_resource_attribute_file_config.go | 10 +- .../cmd/config/subcommand/workflow_config.go | 13 ++ .../config/subcommand/workflowconfig_flags.go | 47 ++++++ .../subcommand/workflowconfig_flags_test.go | 146 ++++++++++++++++++ .../matchable_task_resource_attribute.go | 7 +- flytectl/cmd/get/get.go | 2 +- flytectl/cmd/get/launch_plan.go | 13 ++ .../get/matchable_task_resource_attribute.go | 11 +- .../matchable_task_resource_attribute_test.go | 10 +- flytectl/cmd/get/task.go | 12 ++ flytectl/cmd/get/workflow.go | 72 ++++++--- flytectl/cmd/get/workflow_test.go | 65 ++++++++ .../matchable_task_resource_attribute.go | 15 +- ...lytectl_delete_task-resource-attribute.rst | 7 +- .../source/gen/flytectl_get_launchplan.rst | 13 ++ .../flytectl_get_task-resource-attribute.rst | 5 +- .../docs/source/gen/flytectl_get_task.rst | 12 ++ .../docs/source/gen/flytectl_get_workflow.rst | 24 ++- ...lytectl_update_task-resource-attribute.rst | 15 +- flytectl/pkg/ext/fetcher.go | 9 ++ .../ext/mocks/admin_fetcher_ext_interface.go | 123 +++++++++++++++ flytectl/pkg/ext/workflow_fetcher.go | 60 +++++++ flytectl/pkg/ext/workflow_fetcher_test.go | 120 ++++++++++++++ 23 files changed, 749 insertions(+), 62 deletions(-) create mode 100644 flytectl/cmd/config/subcommand/workflow_config.go create mode 100755 flytectl/cmd/config/subcommand/workflowconfig_flags.go create mode 100755 flytectl/cmd/config/subcommand/workflowconfig_flags_test.go create mode 100644 flytectl/cmd/get/workflow_test.go create mode 100644 flytectl/pkg/ext/workflow_fetcher.go create mode 100644 flytectl/pkg/ext/workflow_fetcher_test.go diff --git a/flytectl/cmd/config/subcommand/task_resource_attribute_file_config.go b/flytectl/cmd/config/subcommand/task_resource_attribute_file_config.go index d0c0a0656c..62fa2f7e3f 100644 --- a/flytectl/cmd/config/subcommand/task_resource_attribute_file_config.go +++ b/flytectl/cmd/config/subcommand/task_resource_attribute_file_config.go @@ -18,9 +18,9 @@ import ( // The shadow config is not using ProjectDomainAttribute/Workflowattribute directly inorder to simplify the inputs. // As the same structure is being used for both ProjectDomainAttribute/Workflowattribute type TaskResourceAttrFileConfig struct { - Project string `json:"project"` - Domain string `json:"domain"` - Workflow string `json:"workflow,omitempty"` + Project string + Domain string + Workflow string *admin.TaskResourceAttributes } @@ -28,7 +28,7 @@ type TaskResourceAttrFileConfig struct { func (t TaskResourceAttrFileConfig) WriteConfigToFile(fileName string) error { d, err := yaml.Marshal(t) if err != nil { - return fmt.Errorf("error: %v", err) + fmt.Printf("error: %v", err) } if _, err = os.Stat(fileName); err == nil { if !cmdUtil.AskForConfirmation(fmt.Sprintf("warning file %v will be overwritten", fileName)) { @@ -79,7 +79,7 @@ func (t TaskResourceAttrFileConfig) DumpTaskResourceAttr(ctx context.Context, fi logger.Warnf(ctx, "error dumping in file due to %v", err) return } - fmt.Printf("wrote the config to file %v", fileName) + fmt.Printf("written the config to file %v", fileName) } else { fmt.Printf("%v", t) } diff --git a/flytectl/cmd/config/subcommand/workflow_config.go b/flytectl/cmd/config/subcommand/workflow_config.go new file mode 100644 index 0000000000..b3c6452a62 --- /dev/null +++ b/flytectl/cmd/config/subcommand/workflow_config.go @@ -0,0 +1,13 @@ +package subcommand + +//go:generate pflags WorkflowConfig --default-var DefaultWorklfowConfig + +var ( + DefaultWorklfowConfig = &WorkflowConfig{} +) + +// WorkflowConfig commandline configuration +type WorkflowConfig struct { + Version string `json:"version" pflag:",version of the workflow to be fetched."` + Latest bool `json:"latest" pflag:", flag to indicate to fetch the latest version, version flag will be ignored in this case"` +} diff --git a/flytectl/cmd/config/subcommand/workflowconfig_flags.go b/flytectl/cmd/config/subcommand/workflowconfig_flags.go new file mode 100755 index 0000000000..cc5a1743d4 --- /dev/null +++ b/flytectl/cmd/config/subcommand/workflowconfig_flags.go @@ -0,0 +1,47 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package subcommand + +import ( + "encoding/json" + "reflect" + + "fmt" + + "github.com/spf13/pflag" +) + +// If v is a pointer, it will get its element value or the zero value of the element type. +// If v is not a pointer, it will return it as is. +func (WorkflowConfig) elemValueOrNil(v interface{}) interface{} { + if t := reflect.TypeOf(v); t.Kind() == reflect.Ptr { + if reflect.ValueOf(v).IsNil() { + return reflect.Zero(t.Elem()).Interface() + } else { + return reflect.ValueOf(v).Interface() + } + } else if v == nil { + return reflect.Zero(t).Interface() + } + + return v +} + +func (WorkflowConfig) mustMarshalJSON(v json.Marshaler) string { + raw, err := v.MarshalJSON() + if err != nil { + panic(err) + } + + return string(raw) +} + +// GetPFlagSet will return strongly types pflags for all fields in WorkflowConfig and its nested types. The format of the +// flags is json-name.json-sub-name... etc. +func (cfg WorkflowConfig) GetPFlagSet(prefix string) *pflag.FlagSet { + cmdFlags := pflag.NewFlagSet("WorkflowConfig", pflag.ExitOnError) + cmdFlags.StringVar(&(DefaultWorklfowConfig.Version), fmt.Sprintf("%v%v", prefix, "version"), DefaultWorklfowConfig.Version, "version of the workflow to be fetched.") + cmdFlags.BoolVar(&(DefaultWorklfowConfig.Latest),fmt.Sprintf("%v%v", prefix, "latest"), DefaultWorklfowConfig.Latest, " flag to indicate to fetch the latest version, version flag will be ignored in this case") + return cmdFlags +} diff --git a/flytectl/cmd/config/subcommand/workflowconfig_flags_test.go b/flytectl/cmd/config/subcommand/workflowconfig_flags_test.go new file mode 100755 index 0000000000..105f884def --- /dev/null +++ b/flytectl/cmd/config/subcommand/workflowconfig_flags_test.go @@ -0,0 +1,146 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package subcommand + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/mitchellh/mapstructure" + "github.com/stretchr/testify/assert" +) + +var dereferencableKindsWorkflowConfig = map[reflect.Kind]struct{}{ + reflect.Array: {}, reflect.Chan: {}, reflect.Map: {}, reflect.Ptr: {}, reflect.Slice: {}, +} + +// Checks if t is a kind that can be dereferenced to get its underlying type. +func canGetElementWorkflowConfig(t reflect.Kind) bool { + _, exists := dereferencableKindsWorkflowConfig[t] + return exists +} + +// This decoder hook tests types for json unmarshaling capability. If implemented, it uses json unmarshal to build the +// object. Otherwise, it'll just pass on the original data. +func jsonUnmarshalerHookWorkflowConfig(_, to reflect.Type, data interface{}) (interface{}, error) { + unmarshalerType := reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() + if to.Implements(unmarshalerType) || reflect.PtrTo(to).Implements(unmarshalerType) || + (canGetElementWorkflowConfig(to.Kind()) && to.Elem().Implements(unmarshalerType)) { + + raw, err := json.Marshal(data) + if err != nil { + fmt.Printf("Failed to marshal Data: %v. Error: %v. Skipping jsonUnmarshalHook", data, err) + return data, nil + } + + res := reflect.New(to).Interface() + err = json.Unmarshal(raw, &res) + if err != nil { + fmt.Printf("Failed to umarshal Data: %v. Error: %v. Skipping jsonUnmarshalHook", data, err) + return data, nil + } + + return res, nil + } + + return data, nil +} + +func decode_WorkflowConfig(input, result interface{}) error { + config := &mapstructure.DecoderConfig{ + TagName: "json", + WeaklyTypedInput: true, + Result: result, + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + jsonUnmarshalerHookWorkflowConfig, + ), + } + + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +func join_WorkflowConfig(arr interface{}, sep string) string { + listValue := reflect.ValueOf(arr) + strs := make([]string, 0, listValue.Len()) + for i := 0; i < listValue.Len(); i++ { + strs = append(strs, fmt.Sprintf("%v", listValue.Index(i))) + } + + return strings.Join(strs, sep) +} + +func testDecodeJson_WorkflowConfig(t *testing.T, val, result interface{}) { + assert.NoError(t, decode_WorkflowConfig(val, result)) +} + +func testDecodeSlice_WorkflowConfig(t *testing.T, vStringSlice, result interface{}) { + assert.NoError(t, decode_WorkflowConfig(vStringSlice, result)) +} + +func TestWorkflowConfig_GetPFlagSet(t *testing.T) { + val := WorkflowConfig{} + cmdFlags := val.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) +} + +func TestWorkflowConfig_SetFlags(t *testing.T) { + actual := WorkflowConfig{} + cmdFlags := actual.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) + + t.Run("Test_version", func(t *testing.T) { + t.Run("DefaultValue", func(t *testing.T) { + // Test that default value is set properly + if vString, err := cmdFlags.GetString("version"); err == nil { + assert.Equal(t, string(DefaultWorklfowConfig.Version), vString) + } else { + assert.FailNow(t, err.Error()) + } + }) + + t.Run("Override", func(t *testing.T) { + testValue := "1" + + cmdFlags.Set("version", testValue) + if vString, err := cmdFlags.GetString("version"); err == nil { + testDecodeJson_WorkflowConfig(t, fmt.Sprintf("%v", vString), &actual.Version) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) + t.Run("Test_latest", func(t *testing.T) { + t.Run("DefaultValue", func(t *testing.T) { + // Test that default value is set properly + if vBool, err := cmdFlags.GetBool("latest"); err == nil { + assert.Equal(t, bool(DefaultWorklfowConfig.Latest), vBool) + } else { + assert.FailNow(t, err.Error()) + } + }) + + t.Run("Override", func(t *testing.T) { + testValue := "1" + + cmdFlags.Set("latest", testValue) + if vBool, err := cmdFlags.GetBool("latest"); err == nil { + testDecodeJson_WorkflowConfig(t, fmt.Sprintf("%v", vBool), &actual.Latest) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) +} diff --git a/flytectl/cmd/delete/matchable_task_resource_attribute.go b/flytectl/cmd/delete/matchable_task_resource_attribute.go index 57791d033d..00e8e51148 100644 --- a/flytectl/cmd/delete/matchable_task_resource_attribute.go +++ b/flytectl/cmd/delete/matchable_task_resource_attribute.go @@ -22,7 +22,7 @@ Here the command delete task resource attributes for project flytectldemo and d flytectl delete task-resource-attribute -p flytectldemo -d development -Deleting task resource attribute using config file which was used for creating it. +Deleting task resource attribute using config file Here the command deletes task resource attributes from the config file tra.yaml eg: content of tra.yaml which will use the project domain and workflow name for deleting the resource @@ -33,8 +33,9 @@ eg: content of tra.yaml which will use the project domain and workflow name for .. code-block:: yaml - domain: development - project: flytectldemo + Domain: development + Project: flytectldemo + Workflow: "" defaults: cpu: "1" memory: 150Mi diff --git a/flytectl/cmd/get/get.go b/flytectl/cmd/get/get.go index 0b7bd5da93..f5b414013f 100644 --- a/flytectl/cmd/get/get.go +++ b/flytectl/cmd/get/get.go @@ -33,7 +33,7 @@ func CreateGetCommand() *cobra.Command { "task": {CmdFunc: getTaskFunc, Aliases: []string{"tasks"}, Short: taskShort, Long: taskLong, PFlagProvider: taskConfig}, "workflow": {CmdFunc: getWorkflowFunc, Aliases: []string{"workflows"}, Short: workflowShort, - Long: workflowLong}, + Long: workflowLong, PFlagProvider: subcommand.DefaultWorklfowConfig}, "launchplan": {CmdFunc: getLaunchPlanFunc, Aliases: []string{"launchplans"}, Short: launchPlanShort, Long: launchPlanLong, PFlagProvider: launchPlanConfig}, "execution": {CmdFunc: getExecutionFunc, Aliases: []string{"executions"}, Short: executionShort, diff --git a/flytectl/cmd/get/launch_plan.go b/flytectl/cmd/get/launch_plan.go index e98029483c..22ff6660fe 100644 --- a/flytectl/cmd/get/launch_plan.go +++ b/flytectl/cmd/get/launch_plan.go @@ -28,6 +28,19 @@ Retrieves launch plan by name within project and domain. flytectl get launchplan -p flytesnacks -d development core.basic.lp.go_greet + +Retrieves latest version of task by name within project and domain. + +:: + + flytectl get launchplan -p flytesnacks -d development core.basic.lp.go_greet --latest + +Retrieves particular version of launchplan by name within project and domain. + +:: + + flytectl get launchplan -p flytesnacks -d development core.basic.lp.go_greet --version v2 + Retrieves launchplan by filters. :: diff --git a/flytectl/cmd/get/matchable_task_resource_attribute.go b/flytectl/cmd/get/matchable_task_resource_attribute.go index 09e8b5df99..bfa68d6edc 100644 --- a/flytectl/cmd/get/matchable_task_resource_attribute.go +++ b/flytectl/cmd/get/matchable_task_resource_attribute.go @@ -38,8 +38,9 @@ eg: content of tra.yaml .. code-block:: yaml - domain: development - project: flytectldemo + Domain: development + Project: flytectldemo + Workflow: "" defaults: cpu: "1" memory: 150Mi @@ -72,10 +73,11 @@ func getTaskResourceAttributes(ctx context.Context, args []string, cmdCtx cmdCor workflowAttr, err := cmdCtx.AdminFetcherExt().FetchWorkflowAttributes(ctx, project, domain, workflowName, admin.MatchableResource_TASK_RESOURCE) if err != nil { + taskResourceAttrFileConfig.DumpTaskResourceAttr(ctx, fileName) return err } if workflowAttr.GetAttributes() == nil || workflowAttr.GetAttributes().GetMatchingAttributes() == nil { - return fmt.Errorf("attribute doesn't exist") + return fmt.Errorf("invalid matching attribute returned with nil data") } // Update the shadow config with the fetched taskResourceAttribute which can then be written to a file which can then be called for an update. taskResourceAttrFileConfig.TaskResourceAttributes = workflowAttr.GetAttributes().GetMatchingAttributes().GetTaskResourceAttributes() @@ -84,10 +86,11 @@ func getTaskResourceAttributes(ctx context.Context, args []string, cmdCtx cmdCor projectDomainAttr, err := cmdCtx.AdminFetcherExt().FetchProjectDomainAttributes(ctx, project, domain, admin.MatchableResource_TASK_RESOURCE) if err != nil { + taskResourceAttrFileConfig.DumpTaskResourceAttr(ctx, fileName) return err } if projectDomainAttr.GetAttributes() == nil || projectDomainAttr.GetAttributes().GetMatchingAttributes() == nil { - return fmt.Errorf("attribute doesn't exist") + return fmt.Errorf("invalid matching attribute returned with nil data") } // Update the shadow config with the fetched taskResourceAttribute which can then be written to a file which can then be called for an update. taskResourceAttrFileConfig.TaskResourceAttributes = projectDomainAttr.GetAttributes().GetMatchingAttributes().GetTaskResourceAttributes() diff --git a/flytectl/cmd/get/matchable_task_resource_attribute_test.go b/flytectl/cmd/get/matchable_task_resource_attribute_test.go index 00f3efad26..22d82df983 100644 --- a/flytectl/cmd/get/matchable_task_resource_attribute_test.go +++ b/flytectl/cmd/get/matchable_task_resource_attribute_test.go @@ -68,7 +68,7 @@ func TestGetTaskResourceAttributes(t *testing.T) { assert.Nil(t, err) u.FetcherExt.AssertCalled(t, "FetchProjectDomainAttributes", ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_TASK_RESOURCE) - tearDownAndVerify(t, `{"project":"dummyProject","domain":"dummyDomain","defaults":{"cpu":"1","memory":"150Mi"},"limits":{"cpu":"2","memory":"350Mi"}}`) + tearDownAndVerify(t, `{"Project":"dummyProject","Domain":"dummyDomain","Workflow":"","defaults":{"cpu":"1","memory":"150Mi"},"limits":{"cpu":"2","memory":"350Mi"}}`) }) t.Run("successful get project domain attribute and write to file", func(t *testing.T) { var args []string @@ -82,7 +82,7 @@ func TestGetTaskResourceAttributes(t *testing.T) { assert.Nil(t, err) u.FetcherExt.AssertCalled(t, "FetchProjectDomainAttributes", ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_TASK_RESOURCE) - tearDownAndVerify(t, `wrote the config to file temp-output-file`) + tearDownAndVerify(t, `written the config to file temp-output-file`) }) t.Run("successful get project domain attribute and write to file failure", func(t *testing.T) { var args []string @@ -110,7 +110,7 @@ func TestGetTaskResourceAttributes(t *testing.T) { assert.Equal(t, fmt.Errorf("failed to fetch response"), err) u.FetcherExt.AssertCalled(t, "FetchProjectDomainAttributes", ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_TASK_RESOURCE) - tearDownAndVerify(t, ``) + tearDownAndVerify(t, `{"Project":"dummyProject","Domain":"dummyDomain","Workflow":""}`) }) t.Run("successful get workflow attribute", func(t *testing.T) { var args []string @@ -124,7 +124,7 @@ func TestGetTaskResourceAttributes(t *testing.T) { u.FetcherExt.AssertCalled(t, "FetchWorkflowAttributes", ctx, config.GetConfig().Project, config.GetConfig().Domain, "workflow", admin.MatchableResource_TASK_RESOURCE) - tearDownAndVerify(t, `{"project":"dummyProject","domain":"dummyDomain","workflow":"workflow","defaults":{"cpu":"1","memory":"150Mi"},"limits":{"cpu":"2","memory":"350Mi"}}`) + tearDownAndVerify(t, `{"Project":"dummyProject","Domain":"dummyDomain","Workflow":"workflow","defaults":{"cpu":"1","memory":"150Mi"},"limits":{"cpu":"2","memory":"350Mi"}}`) }) t.Run("failed get workflow attribute", func(t *testing.T) { var args []string @@ -139,6 +139,6 @@ func TestGetTaskResourceAttributes(t *testing.T) { u.FetcherExt.AssertCalled(t, "FetchWorkflowAttributes", ctx, config.GetConfig().Project, config.GetConfig().Domain, "workflow", admin.MatchableResource_TASK_RESOURCE) - tearDownAndVerify(t, ``) + tearDownAndVerify(t, `{"Project":"dummyProject","Domain":"dummyDomain","Workflow":"workflow"}`) }) } diff --git a/flytectl/cmd/get/task.go b/flytectl/cmd/get/task.go index 7be143d232..eb40e9cb23 100644 --- a/flytectl/cmd/get/task.go +++ b/flytectl/cmd/get/task.go @@ -28,6 +28,18 @@ Retrieves task by name within project and domain. bin/flytectl task -p flytesnacks -d development core.basic.lp.greet +Retrieves latest version of task by name within project and domain. + +:: + + flytectl get task -p flytesnacks -d development core.basic.lp.greet --latest + +Retrieves particular version of task by name within project and domain. + +:: + + flytectl get workflow -p flytesnacks -d development core.basic.lp.greet --version v2 + Retrieves project by filters. :: diff --git a/flytectl/cmd/get/workflow.go b/flytectl/cmd/get/workflow.go index 2736fe9b11..3640a5f342 100644 --- a/flytectl/cmd/get/workflow.go +++ b/flytectl/cmd/get/workflow.go @@ -3,6 +3,8 @@ package get import ( "context" + "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/pkg/ext" "github.com/flyteorg/flytestdlib/logger" "github.com/golang/protobuf/proto" @@ -20,13 +22,25 @@ const ( Retrieves all the workflows within project and domain.(workflow,workflows can be used interchangeably in these commands) :: - bin/flytectl get workflow -p flytesnacks -d development + flytectl get workflow -p flytesnacks -d development Retrieves workflow by name within project and domain. :: - bin/flytectl get workflow -p flytesnacks -d development core.basic.lp.go_greet + flytectl get workflow -p flytesnacks -d development core.basic.lp.go_greet + +Retrieves latest version of workflow by name within project and domain. + +:: + + flytectl get workflow -p flytesnacks -d development core.basic.lp.go_greet --latest + +Retrieves particular version of workflow by name within project and domain. + +:: + + flytectl get workflow -p flytesnacks -d development core.basic.lp.go_greet --version v2 Retrieves workflow by filters. :: @@ -37,13 +51,13 @@ Retrieves all the workflow within project and domain in yaml format. :: - bin/flytectl get workflow -p flytesnacks -d development -o yaml + flytectl get workflow -p flytesnacks -d development -o yaml Retrieves all the workflow within project and domain in json format. :: - bin/flytectl get workflow -p flytesnacks -d development -o json + flytectl get workflow -p flytesnacks -d development -o json Usage ` @@ -66,25 +80,18 @@ func WorkflowToProtoMessages(l []*admin.Workflow) []proto.Message { func getWorkflowFunc(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error { adminPrinter := printer.Printer{} if len(args) > 0 { - workflows, err := cmdCtx.AdminClient().ListWorkflows(ctx, &admin.ResourceListRequest{ - Id: &admin.NamedEntityIdentifier{ - Project: config.GetConfig().Project, - Domain: config.GetConfig().Domain, - Name: args[0], - }, - // TODO Sorting and limits should be parameters - SortBy: &admin.Sort{ - Key: "created_at", - Direction: admin.Sort_DESCENDING, - }, - Limit: 100, - }) + name := args[0] + var workflows []*admin.Workflow + var err error + if workflows, err = FetchWorkflowForName(ctx, cmdCtx.AdminFetcherExt(), name, config.GetConfig().Project, config.GetConfig().Domain); err != nil { + return err + } + logger.Debugf(ctx, "Retrieved %v workflow", len(workflows)) + err = adminPrinter.Print(config.GetConfig().MustOutputFormat(), workflowColumns, WorkflowToProtoMessages(workflows)...) if err != nil { return err } - logger.Debugf(ctx, "Retrieved %v workflows", len(workflows.Workflows)) - - return adminPrinter.Print(config.GetConfig().MustOutputFormat(), workflowColumns, WorkflowToProtoMessages(workflows.Workflows)...) + return nil } workflows, err := adminutils.GetAllNamedEntities(ctx, cmdCtx.AdminClient().ListWorkflowIds, adminutils.ListRequest{Project: config.GetConfig().Project, Domain: config.GetConfig().Domain}) @@ -94,3 +101,28 @@ func getWorkflowFunc(ctx context.Context, args []string, cmdCtx cmdCore.CommandC logger.Debugf(ctx, "Retrieved %v workflows", len(workflows)) return adminPrinter.Print(config.GetConfig().MustOutputFormat(), entityColumns, adminutils.NamedEntityToProtoMessage(workflows)...) } + +// FetchWorkflowForName fetches the workflow give it name. +func FetchWorkflowForName(ctx context.Context, fetcher ext.AdminFetcherExtInterface, name, project, + domain string) ([]*admin.Workflow, error) { + var workflows []*admin.Workflow + var workflow *admin.Workflow + var err error + if subcommand.DefaultWorklfowConfig.Latest { + if workflow, err = fetcher.FetchWorkflowLatestVersion(ctx, name, project, domain); err != nil { + return nil, err + } + workflows = append(workflows, workflow) + } else if subcommand.DefaultWorklfowConfig.Version != "" { + if workflow, err = fetcher.FetchWorkflowVersion(ctx, name, subcommand.DefaultWorklfowConfig.Version, project, domain); err != nil { + return nil, err + } + workflows = append(workflows, workflow) + } else { + workflows, err = fetcher.FetchAllVerOfWorkflow(ctx, name, project, domain) + if err != nil { + return nil, err + } + } + return workflows, nil +} diff --git a/flytectl/cmd/get/workflow_test.go b/flytectl/cmd/get/workflow_test.go new file mode 100644 index 0000000000..93928eebc4 --- /dev/null +++ b/flytectl/cmd/get/workflow_test.go @@ -0,0 +1,65 @@ +package get + +import ( + "fmt" + "testing" + + "github.com/flyteorg/flytectl/cmd/config/subcommand" + u "github.com/flyteorg/flytectl/cmd/testutils" + "github.com/flyteorg/flytectl/pkg/ext/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func getWorkflowSetup() { + ctx = u.Ctx + mockClient = u.MockClient + cmdCtx = u.CmdCtx + subcommand.DefaultWorklfowConfig.Latest = false + subcommand.DefaultWorklfowConfig.Version = "" +} + +func TestGetWorkflowFuncWithError(t *testing.T) { + t.Run("failure fetch latest", func(t *testing.T) { + setup() + getWorkflowSetup() + mockFetcher := new(mocks.AdminFetcherExtInterface) + subcommand.DefaultWorklfowConfig.Latest = true + mockFetcher.OnFetchWorkflowLatestVersionMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(nil, fmt.Errorf("error fetching latest version")) + _, err = FetchWorkflowForName(ctx, mockFetcher, "workflowName", projectValue, domainValue) + assert.NotNil(t, err) + }) + + t.Run("failure fetching version ", func(t *testing.T) { + setup() + getWorkflowSetup() + mockFetcher := new(mocks.AdminFetcherExtInterface) + subcommand.DefaultWorklfowConfig.Version = "v1" + mockFetcher.OnFetchWorkflowVersionMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(nil, fmt.Errorf("error fetching version")) + _, err = FetchWorkflowForName(ctx, mockFetcher, "workflowName", projectValue, domainValue) + assert.NotNil(t, err) + }) + + t.Run("failure fetching all version ", func(t *testing.T) { + setup() + getWorkflowSetup() + mockFetcher := new(mocks.AdminFetcherExtInterface) + mockFetcher.OnFetchAllVerOfWorkflowMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(nil, fmt.Errorf("error fetching all version")) + _, err = FetchWorkflowForName(ctx, mockFetcher, "workflowName", projectValue, domainValue) + assert.NotNil(t, err) + }) + + t.Run("failure fetching ", func(t *testing.T) { + setup() + getWorkflowSetup() + subcommand.DefaultWorklfowConfig.Latest = true + args := []string{"workflowName"} + u.FetcherExt.OnFetchWorkflowLatestVersionMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(nil, fmt.Errorf("error fetching latest version")) + err = getWorkflowFunc(ctx, args, cmdCtx) + assert.NotNil(t, err) + }) +} diff --git a/flytectl/cmd/update/matchable_task_resource_attribute.go b/flytectl/cmd/update/matchable_task_resource_attribute.go index 239a81d736..598eb98531 100644 --- a/flytectl/cmd/update/matchable_task_resource_attribute.go +++ b/flytectl/cmd/update/matchable_task_resource_attribute.go @@ -12,7 +12,7 @@ import ( const ( taskResourceAttributesShort = "Updates matchable resources of task attributes" taskResourceAttributesLong = ` -Updates task resource attributes for given project and domain combination or additionally with workflow name. +Updates task resource attributes for given project,domain combination or additionally with workflow name. Updating the task resource attribute is only available from a generated file. See the get section for generating this file. Here the command updates takes the input for task resource attributes from the config file tra.yaml @@ -20,8 +20,9 @@ eg: content of tra.yaml .. code-block:: yaml - domain: development - project: flytectldemo + Domain: development + Project: flytectldemo + Workflow: "" defaults: cpu: "1" memory: 150Mi @@ -33,14 +34,14 @@ eg: content of tra.yaml flytectl update task-resource-attribute -attrFile tra.yaml -Updating task resource attribute for project and domain and workflow combination. This will take precedence over any other +Updating task resource attribute for project and domain and workflow combination. This will take precedence over any other resource attribute defined at project domain level. Update the resource attributes for workflow core.control_flow.run_merge_sort.merge_sort in flytectldemo , development domain .. code-block:: yaml - domain: development - project: flytectldemo - workflow: core.control_flow.run_merge_sort.merge_sort + Domain: development + Project: flytectldemo + Workflow: core.control_flow.run_merge_sort.merge_sort defaults: cpu: "1" memory: 150Mi diff --git a/flytectl/docs/source/gen/flytectl_delete_task-resource-attribute.rst b/flytectl/docs/source/gen/flytectl_delete_task-resource-attribute.rst index 4f19d5b32c..da97151fef 100644 --- a/flytectl/docs/source/gen/flytectl_delete_task-resource-attribute.rst +++ b/flytectl/docs/source/gen/flytectl_delete_task-resource-attribute.rst @@ -19,7 +19,7 @@ Here the command delete task resource attributes for project flytectldemo and d flytectl delete task-resource-attribute -p flytectldemo -d development -Deleting task resource attribute using config file which was used for creating it. +Deleting task resource attribute using config file Here the command deletes task resource attributes from the config file tra.yaml eg: content of tra.yaml which will use the project domain and workflow name for deleting the resource @@ -30,8 +30,9 @@ eg: content of tra.yaml which will use the project domain and workflow name for .. code-block:: yaml - domain: development - project: flytectldemo + Domain: development + Project: flytectldemo + Workflow: "" defaults: cpu: "1" memory: 150Mi diff --git a/flytectl/docs/source/gen/flytectl_get_launchplan.rst b/flytectl/docs/source/gen/flytectl_get_launchplan.rst index 2f97de100f..fa45e3f85f 100644 --- a/flytectl/docs/source/gen/flytectl_get_launchplan.rst +++ b/flytectl/docs/source/gen/flytectl_get_launchplan.rst @@ -21,6 +21,19 @@ Retrieves launch plan by name within project and domain. flytectl get launchplan -p flytesnacks -d development core.basic.lp.go_greet + +Retrieves latest version of task by name within project and domain. + +:: + + flytectl get launchplan -p flytesnacks -d development core.basic.lp.go_greet --latest + +Retrieves particular version of launchplan by name within project and domain. + +:: + + flytectl get launchplan -p flytesnacks -d development core.basic.lp.go_greet --version v2 + Retrieves launchplan by filters. :: diff --git a/flytectl/docs/source/gen/flytectl_get_task-resource-attribute.rst b/flytectl/docs/source/gen/flytectl_get_task-resource-attribute.rst index 05046e2469..ea2356a8ca 100644 --- a/flytectl/docs/source/gen/flytectl_get_task-resource-attribute.rst +++ b/flytectl/docs/source/gen/flytectl_get_task-resource-attribute.rst @@ -35,8 +35,9 @@ eg: content of tra.yaml .. code-block:: yaml - domain: development - project: flytectldemo + Domain: development + Project: flytectldemo + Workflow: "" defaults: cpu: "1" memory: 150Mi diff --git a/flytectl/docs/source/gen/flytectl_get_task.rst b/flytectl/docs/source/gen/flytectl_get_task.rst index 8a770670f9..494ee1b7a1 100644 --- a/flytectl/docs/source/gen/flytectl_get_task.rst +++ b/flytectl/docs/source/gen/flytectl_get_task.rst @@ -21,6 +21,18 @@ Retrieves task by name within project and domain. bin/flytectl task -p flytesnacks -d development core.basic.lp.greet +Retrieves latest version of task by name within project and domain. + +:: + + flytectl get task -p flytesnacks -d development core.basic.lp.greet --latest + +Retrieves particular version of task by name within project and domain. + +:: + + flytectl get workflow -p flytesnacks -d development core.basic.lp.greet --version v2 + Retrieves project by filters. :: diff --git a/flytectl/docs/source/gen/flytectl_get_workflow.rst b/flytectl/docs/source/gen/flytectl_get_workflow.rst index 9a2288c75f..89e4c688db 100644 --- a/flytectl/docs/source/gen/flytectl_get_workflow.rst +++ b/flytectl/docs/source/gen/flytectl_get_workflow.rst @@ -13,13 +13,25 @@ Synopsis Retrieves all the workflows within project and domain.(workflow,workflows can be used interchangeably in these commands) :: - bin/flytectl get workflow -p flytesnacks -d development + flytectl get workflow -p flytesnacks -d development Retrieves workflow by name within project and domain. :: - bin/flytectl get workflow -p flytesnacks -d development core.basic.lp.go_greet + flytectl get workflow -p flytesnacks -d development core.basic.lp.go_greet + +Retrieves latest version of workflow by name within project and domain. + +:: + + flytectl get workflow -p flytesnacks -d development core.basic.lp.go_greet --latest + +Retrieves particular version of workflow by name within project and domain. + +:: + + flytectl get workflow -p flytesnacks -d development core.basic.lp.go_greet --version v2 Retrieves workflow by filters. :: @@ -30,13 +42,13 @@ Retrieves all the workflow within project and domain in yaml format. :: - bin/flytectl get workflow -p flytesnacks -d development -o yaml + flytectl get workflow -p flytesnacks -d development -o yaml Retrieves all the workflow within project and domain in json format. :: - bin/flytectl get workflow -p flytesnacks -d development -o json + flytectl get workflow -p flytesnacks -d development -o json Usage @@ -50,7 +62,9 @@ Options :: - -h, --help help for workflow + -h, --help help for workflow + --latest flag to indicate to fetch the latest version, version flag will be ignored in this case + --version string version of the workflow to be fetched. Options inherited from parent commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/flytectl/docs/source/gen/flytectl_update_task-resource-attribute.rst b/flytectl/docs/source/gen/flytectl_update_task-resource-attribute.rst index a0aef81d04..6f60b7d50d 100644 --- a/flytectl/docs/source/gen/flytectl_update_task-resource-attribute.rst +++ b/flytectl/docs/source/gen/flytectl_update_task-resource-attribute.rst @@ -10,7 +10,7 @@ Synopsis -Updates task resource attributes for given project and domain combination or additionally with workflow name. +Updates task resource attributes for given project,domain combination or additionally with workflow name. Updating the task resource attribute is only available from a generated file. See the get section for generating this file. Here the command updates takes the input for task resource attributes from the config file tra.yaml @@ -18,8 +18,9 @@ eg: content of tra.yaml .. code-block:: yaml - domain: development - project: flytectldemo + Domain: development + Project: flytectldemo + Workflow: "" defaults: cpu: "1" memory: 150Mi @@ -31,14 +32,14 @@ eg: content of tra.yaml flytectl update task-resource-attribute -attrFile tra.yaml -Updating task resource attribute for project and domain and workflow combination. This will take precedence over any other +Updating task resource attribute for project and domain and workflow combination. This will take precedence over any other resource attribute defined at project domain level. Update the resource attributes for workflow core.control_flow.run_merge_sort.merge_sort in flytectldemo , development domain .. code-block:: yaml - domain: development - project: flytectldemo - workflow: core.control_flow.run_merge_sort.merge_sort + Domain: development + Project: flytectldemo + Workflow: core.control_flow.run_merge_sort.merge_sort defaults: cpu: "1" memory: 150Mi diff --git a/flytectl/pkg/ext/fetcher.go b/flytectl/pkg/ext/fetcher.go index d32421156b..8601ec7e71 100644 --- a/flytectl/pkg/ext/fetcher.go +++ b/flytectl/pkg/ext/fetcher.go @@ -35,6 +35,15 @@ type AdminFetcherExtInterface interface { // FetchTaskVersion fetches particular version of task in a project, domain FetchTaskVersion(ctx context.Context, name, version, project, domain string) (*admin.Task, error) + // FetchAllVerOfWorkflow fetches all versions of task in a project, domain + FetchAllVerOfWorkflow(ctx context.Context, name, project, domain string) ([]*admin.Workflow, error) + + // FetchWorkflowLatestVersion fetches latest version of workflow in a project, domain + FetchWorkflowLatestVersion(ctx context.Context, name, project, domain string) (*admin.Workflow, error) + + // FetchWorkflowVersion fetches particular version of workflow in a project, domain + FetchWorkflowVersion(ctx context.Context, name, version, project, domain string) (*admin.Workflow, error) + // FetchWorkflowAttributes fetches workflow attributes particular resource type in a project, domain and workflow FetchWorkflowAttributes(ctx context.Context, project, domain, name string, rsType admin.MatchableResource) (*admin.WorkflowAttributesGetResponse, error) diff --git a/flytectl/pkg/ext/mocks/admin_fetcher_ext_interface.go b/flytectl/pkg/ext/mocks/admin_fetcher_ext_interface.go index 95d3c99c92..902121ef88 100644 --- a/flytectl/pkg/ext/mocks/admin_fetcher_ext_interface.go +++ b/flytectl/pkg/ext/mocks/admin_fetcher_ext_interface.go @@ -133,6 +133,47 @@ func (_m *AdminFetcherExtInterface) FetchAllVerOfTask(ctx context.Context, name return r0, r1 } +type AdminFetcherExtInterface_FetchAllVerOfWorkflow struct { + *mock.Call +} + +func (_m AdminFetcherExtInterface_FetchAllVerOfWorkflow) Return(_a0 []*admin.Workflow, _a1 error) *AdminFetcherExtInterface_FetchAllVerOfWorkflow { + return &AdminFetcherExtInterface_FetchAllVerOfWorkflow{Call: _m.Call.Return(_a0, _a1)} +} + +func (_m *AdminFetcherExtInterface) OnFetchAllVerOfWorkflow(ctx context.Context, name string, project string, domain string) *AdminFetcherExtInterface_FetchAllVerOfWorkflow { + c := _m.On("FetchAllVerOfWorkflow", ctx, name, project, domain) + return &AdminFetcherExtInterface_FetchAllVerOfWorkflow{Call: c} +} + +func (_m *AdminFetcherExtInterface) OnFetchAllVerOfWorkflowMatch(matchers ...interface{}) *AdminFetcherExtInterface_FetchAllVerOfWorkflow { + c := _m.On("FetchAllVerOfWorkflow", matchers...) + return &AdminFetcherExtInterface_FetchAllVerOfWorkflow{Call: c} +} + +// FetchAllVerOfWorkflow provides a mock function with given fields: ctx, name, project, domain +func (_m *AdminFetcherExtInterface) FetchAllVerOfWorkflow(ctx context.Context, name string, project string, domain string) ([]*admin.Workflow, error) { + ret := _m.Called(ctx, name, project, domain) + + var r0 []*admin.Workflow + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) []*admin.Workflow); ok { + r0 = rf(ctx, name, project, domain) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*admin.Workflow) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, name, project, domain) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + type AdminFetcherExtInterface_FetchExecution struct { *mock.Call } @@ -419,3 +460,85 @@ func (_m *AdminFetcherExtInterface) FetchWorkflowAttributes(ctx context.Context, return r0, r1 } + +type AdminFetcherExtInterface_FetchWorkflowLatestVersion struct { + *mock.Call +} + +func (_m AdminFetcherExtInterface_FetchWorkflowLatestVersion) Return(_a0 *admin.Workflow, _a1 error) *AdminFetcherExtInterface_FetchWorkflowLatestVersion { + return &AdminFetcherExtInterface_FetchWorkflowLatestVersion{Call: _m.Call.Return(_a0, _a1)} +} + +func (_m *AdminFetcherExtInterface) OnFetchWorkflowLatestVersion(ctx context.Context, name string, project string, domain string) *AdminFetcherExtInterface_FetchWorkflowLatestVersion { + c := _m.On("FetchWorkflowLatestVersion", ctx, name, project, domain) + return &AdminFetcherExtInterface_FetchWorkflowLatestVersion{Call: c} +} + +func (_m *AdminFetcherExtInterface) OnFetchWorkflowLatestVersionMatch(matchers ...interface{}) *AdminFetcherExtInterface_FetchWorkflowLatestVersion { + c := _m.On("FetchWorkflowLatestVersion", matchers...) + return &AdminFetcherExtInterface_FetchWorkflowLatestVersion{Call: c} +} + +// FetchWorkflowLatestVersion provides a mock function with given fields: ctx, name, project, domain +func (_m *AdminFetcherExtInterface) FetchWorkflowLatestVersion(ctx context.Context, name string, project string, domain string) (*admin.Workflow, error) { + ret := _m.Called(ctx, name, project, domain) + + var r0 *admin.Workflow + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *admin.Workflow); ok { + r0 = rf(ctx, name, project, domain) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*admin.Workflow) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, name, project, domain) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type AdminFetcherExtInterface_FetchWorkflowVersion struct { + *mock.Call +} + +func (_m AdminFetcherExtInterface_FetchWorkflowVersion) Return(_a0 *admin.Workflow, _a1 error) *AdminFetcherExtInterface_FetchWorkflowVersion { + return &AdminFetcherExtInterface_FetchWorkflowVersion{Call: _m.Call.Return(_a0, _a1)} +} + +func (_m *AdminFetcherExtInterface) OnFetchWorkflowVersion(ctx context.Context, name string, version string, project string, domain string) *AdminFetcherExtInterface_FetchWorkflowVersion { + c := _m.On("FetchWorkflowVersion", ctx, name, version, project, domain) + return &AdminFetcherExtInterface_FetchWorkflowVersion{Call: c} +} + +func (_m *AdminFetcherExtInterface) OnFetchWorkflowVersionMatch(matchers ...interface{}) *AdminFetcherExtInterface_FetchWorkflowVersion { + c := _m.On("FetchWorkflowVersion", matchers...) + return &AdminFetcherExtInterface_FetchWorkflowVersion{Call: c} +} + +// FetchWorkflowVersion provides a mock function with given fields: ctx, name, version, project, domain +func (_m *AdminFetcherExtInterface) FetchWorkflowVersion(ctx context.Context, name string, version string, project string, domain string) (*admin.Workflow, error) { + ret := _m.Called(ctx, name, version, project, domain) + + var r0 *admin.Workflow + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) *admin.Workflow); ok { + r0 = rf(ctx, name, version, project, domain) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*admin.Workflow) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string) error); ok { + r1 = rf(ctx, name, version, project, domain) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/flytectl/pkg/ext/workflow_fetcher.go b/flytectl/pkg/ext/workflow_fetcher.go new file mode 100644 index 0000000000..3abcb04fab --- /dev/null +++ b/flytectl/pkg/ext/workflow_fetcher.go @@ -0,0 +1,60 @@ +package ext + +import ( + "context" + "fmt" + + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/core" +) + +// FetchAllVerOfWorkflow fetches all the versions for give workflow name +func (a *AdminFetcherExtClient) FetchAllVerOfWorkflow(ctx context.Context, workflowName, project, domain string) ([]*admin.Workflow, error) { + wList, err := a.AdminServiceClient().ListWorkflows(ctx, &admin.ResourceListRequest{ + Id: &admin.NamedEntityIdentifier{ + Project: project, + Domain: domain, + Name: workflowName, + }, + SortBy: &admin.Sort{ + Key: "created_at", + Direction: admin.Sort_DESCENDING, + }, + Limit: 100, + }) + if err != nil { + return nil, err + } + if len(wList.Workflows) == 0 { + return nil, fmt.Errorf("no workflow retrieved for %v", workflowName) + } + return wList.Workflows, nil +} + +// FetchWorkflowLatestVersion fetches latest version for given workflow name +func (a *AdminFetcherExtClient) FetchWorkflowLatestVersion(ctx context.Context, name, project, domain string) (*admin.Workflow, error) { + // Fetch the latest version of the workflow. + wVersions, err := a.FetchAllVerOfWorkflow(ctx, name, project, domain) + if err != nil { + return nil, err + } + w := wVersions[0] + return w, nil +} + +// FetchWorkflowVersion fetches particular version of workflow +func (a *AdminFetcherExtClient) FetchWorkflowVersion(ctx context.Context, name, version, project, domain string) (*admin.Workflow, error) { + lp, err := a.AdminServiceClient().GetWorkflow(ctx, &admin.ObjectGetRequest{ + Id: &core.Identifier{ + ResourceType: core.ResourceType_WORKFLOW, + Project: project, + Domain: domain, + Name: name, + Version: version, + }, + }) + if err != nil { + return nil, err + } + return lp, nil +} diff --git a/flytectl/pkg/ext/workflow_fetcher_test.go b/flytectl/pkg/ext/workflow_fetcher_test.go new file mode 100644 index 0000000000..91c6818c72 --- /dev/null +++ b/flytectl/pkg/ext/workflow_fetcher_test.go @@ -0,0 +1,120 @@ +package ext + +import ( + "context" + "fmt" + "testing" + + "github.com/flyteorg/flyteidl/clients/go/admin/mocks" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/core" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "google.golang.org/protobuf/types/known/timestamppb" +) + +var ( + workflowListResponse *admin.WorkflowList +) + +func getWorkflowFetcherSetup() { + ctx = context.Background() + adminClient = new(mocks.AdminServiceClient) + adminFetcherExt = AdminFetcherExtClient{AdminClient: adminClient} + + sortedListLiteralType := core.Variable{ + Type: &core.LiteralType{ + Type: &core.LiteralType_CollectionType{ + CollectionType: &core.LiteralType{ + Type: &core.LiteralType_Simple{ + Simple: core.SimpleType_INTEGER, + }, + }, + }, + }, + } + variableMap := map[string]*core.Variable{ + "sorted_list1": &sortedListLiteralType, + "sorted_list2": &sortedListLiteralType, + } + + var compiledTasks []*core.CompiledTask + compiledTasks = append(compiledTasks, &core.CompiledTask{ + Template: &core.TaskTemplate{ + Interface: &core.TypedInterface{ + Inputs: &core.VariableMap{ + Variables: variableMap, + }, + }, + }, + }) + + workflow1 := &admin.Workflow{ + Id: &core.Identifier{ + Name: "task1", + Version: "v1", + }, + Closure: &admin.WorkflowClosure{ + CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 0}, + CompiledWorkflow: &core.CompiledWorkflowClosure{ + Tasks: compiledTasks, + }, + }, + } + workflow2 := &admin.Workflow{ + Id: &core.Identifier{ + Name: "workflow", + Version: "v2", + }, + Closure: &admin.WorkflowClosure{ + CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 0}, + CompiledWorkflow: &core.CompiledWorkflowClosure{ + Tasks: compiledTasks, + }, + }, + } + + workflows := []*admin.Workflow{workflow2, workflow1} + + workflowListResponse = &admin.WorkflowList{ + Workflows: workflows, + } +} + +func TestFetchAllVerOfWorkflow(t *testing.T) { + getWorkflowFetcherSetup() + adminClient.OnListWorkflowsMatch(mock.Anything, mock.Anything).Return(workflowListResponse, nil) + _, err := adminFetcherExt.FetchAllVerOfWorkflow(ctx, "workflowName", "project", "domain") + assert.Nil(t, err) +} + +func TestFetchAllVerOfWorkflowError(t *testing.T) { + getWorkflowFetcherSetup() + adminClient.OnListWorkflowsMatch(mock.Anything, mock.Anything).Return(nil, fmt.Errorf("failed")) + _, err := adminFetcherExt.FetchAllVerOfWorkflow(ctx, "workflowName", "project", "domain") + assert.Equal(t, fmt.Errorf("failed"), err) +} + +func TestFetchAllVerOfWorkflowEmptyResponse(t *testing.T) { + workflowListResponse := &admin.WorkflowList{} + getWorkflowFetcherSetup() + adminClient.OnListWorkflowsMatch(mock.Anything, mock.Anything).Return(workflowListResponse, nil) + _, err := adminFetcherExt.FetchAllVerOfWorkflow(ctx, "workflowName", "project", "domain") + assert.Equal(t, fmt.Errorf("no workflow retrieved for workflowName"), err) +} + +func TestFetchWorkflowLatestVersion(t *testing.T) { + getWorkflowFetcherSetup() + adminClient.OnListWorkflowsMatch(mock.Anything, mock.Anything).Return(workflowListResponse, nil) + _, err := adminFetcherExt.FetchWorkflowLatestVersion(ctx, "workflowName", "project", "domain") + assert.Nil(t, err) +} + +func TestFetchWorkflowLatestVersionError(t *testing.T) { + workflowListResponse := &admin.WorkflowList{} + getWorkflowFetcherSetup() + adminClient.OnListWorkflowsMatch(mock.Anything, mock.Anything).Return(workflowListResponse, nil) + _, err := adminFetcherExt.FetchWorkflowLatestVersion(ctx, "workflowName", "project", "domain") + assert.Equal(t, fmt.Errorf("no workflow retrieved for workflowName"), err) +}