From 34d76146a459a194d229784ee6a33139d4de518d Mon Sep 17 00:00:00 2001 From: pmahindrakar-oss <77798312+pmahindrakar-oss@users.noreply.github.com> Date: Thu, 13 May 2021 10:26:44 +0530 Subject: [PATCH] Added cluster resource attribute support (#65) * Adding commands for task resource attributes support Signed-off-by: Prafulla Mahindrakar * Added cluster resource attribute support Signed-off-by: Prafulla Mahindrakar * Fixed update command to use cluster default config Signed-off-by: Prafulla Mahindrakar --- .../attrdeleteconfig_flags.go} | 14 +- .../attrdeleteconfig_flags_test.go} | 38 ++--- .../attrfetchconfig_flags.go} | 14 +- .../attrfetchconfig_flags_test.go} | 38 ++--- .../attrupdateconfig_flags.go} | 14 +- .../attrupdateconfig_flags_test.go} | 38 ++--- .../clusterresourceattribute/delete_config.go | 10 ++ .../clusterresourceattribute/fetch_config.go | 9 ++ .../clusterresourceattribute/file_config.go | 47 ++++++ .../file_config_test.go | 46 ++++++ .../clusterresourceattribute/update_config.go | 10 ++ .../matchable_attr_file_config_utils.go | 60 ++++++++ .../matchable_attribute_decorator.go | 13 ++ .../project_domain_workflow_getter.go | 40 +++++ .../task_resource_attribute_delete_config.go | 10 -- .../task_resource_attribute_fetch_config.go | 9 -- .../task_resource_attribute_file_config.go | 86 ----------- .../task_resource_attribute_update_config.go | 10 -- .../attrdeleteconfig_flags.go | 46 ++++++ .../attrdeleteconfig_flags_test.go | 124 ++++++++++++++++ .../attrfetchconfig_flags.go} | 15 +- .../attrfetchconfig_flags_test.go | 124 ++++++++++++++++ .../attrupdateconfig_flags.go | 46 ++++++ .../attrupdateconfig_flags_test.go | 124 ++++++++++++++++ .../taskresourceattribute/delete_config.go | 10 ++ .../taskresourceattribute/fetch_config.go | 9 ++ .../taskresourceattribute/file_config.go | 47 ++++++ .../taskresourceattribute/file_config_test.go | 53 +++++++ .../taskresourceattribute/update_config.go | 10 ++ .../subcommand/workflow/config_flags.go | 47 ++++++ .../config_flags_test.go} | 42 +++--- .../{ => workflow}/workflow_config.go | 10 +- flytectl/cmd/delete/delete.go | 8 +- flytectl/cmd/delete/delete_test.go | 20 +-- .../cmd/delete/matchable_attribute_util.go | 29 ++++ .../matchable_cluster_resource_attribute.go | 79 ++++++++++ ...tchable_cluster_resource_attribute_test.go | 134 +++++++++++++++++ .../matchable_task_resource_attribute.go | 70 ++++----- .../matchable_task_resource_attribute_test.go | 19 +-- ..._attribute.yaml => invalid_attribute.yaml} | 0 ...alid_project_domain_cluster_attribute.yaml | 5 + .../valid_project_domain_task_attribute.yaml | 5 +- .../valid_workflow_cluster_attribute.yaml | 6 + .../valid_workflow_task_attribute.yaml | 6 +- flytectl/cmd/get/get.go | 11 +- flytectl/cmd/get/get_test.go | 35 ++--- flytectl/cmd/get/matchable_attribute_util.go | 32 ++++ .../matchable_cluster_resource_attribute.go | 78 ++++++++++ ...tchable_cluster_resource_attribute_test.go | 138 ++++++++++++++++++ .../get/matchable_task_resource_attribute.go | 58 +++----- .../matchable_task_resource_attribute_test.go | 23 +-- flytectl/cmd/get/workflow.go | 8 +- flytectl/cmd/get/workflow_test.go | 12 +- .../cmd/update/matchable_attribute_util.go | 30 ++++ .../matchable_cluster_resource_attribute.go | 76 ++++++++++ ...tchable_cluster_resource_attribute_test.go | 94 ++++++++++++ .../matchable_task_resource_attribute.go | 62 +++----- .../matchable_task_resource_attribute_test.go | 16 +- ..._attribute.yaml => invalid_attribute.yaml} | 0 ...alid_project_domain_cluster_attribute.yaml | 5 + .../valid_project_domain_task_attribute.yaml | 5 +- .../valid_workflow_cluster_attribute.yaml | 6 + .../valid_workflow_task_attribute.yaml | 6 +- flytectl/cmd/update/update.go | 8 +- flytectl/cmd/update/update_test.go | 13 +- flytectl/docs/source/gen/flytectl_delete.rst | 1 + ...ectl_delete_cluster-resource-attribute.rst | 109 ++++++++++++++ ...lytectl_delete_task-resource-attribute.rst | 15 +- flytectl/docs/source/gen/flytectl_get.rst | 1 + ...lytectl_get_cluster-resource-attribute.rst | 107 ++++++++++++++ .../flytectl_get_task-resource-attribute.rst | 13 +- flytectl/docs/source/gen/flytectl_update.rst | 1 + ...ectl_update_cluster-resource-attribute.rst | 111 ++++++++++++++ ...lytectl_update_task-resource-attribute.rst | 31 ++-- flytectl/docs/source/nouns.rst | 11 ++ flytectl/pkg/ext/attribute_match_fetcher.go | 17 ++- .../pkg/ext/attribute_match_fetcher_test.go | 56 +++++-- 77 files changed, 2300 insertions(+), 483 deletions(-) rename flytectl/cmd/config/subcommand/{taskresourceattrdeleteconfig_flags.go => clusterresourceattribute/attrdeleteconfig_flags.go} (55%) rename flytectl/cmd/config/subcommand/{taskresourceattrfetchconfig_flags_test.go => clusterresourceattribute/attrdeleteconfig_flags_test.go} (64%) rename flytectl/cmd/config/subcommand/{taskresourceattrfetchconfig_flags.go => clusterresourceattribute/attrfetchconfig_flags.go} (55%) rename flytectl/cmd/config/subcommand/{taskresourceattrdeleteconfig_flags_test.go => clusterresourceattribute/attrfetchconfig_flags_test.go} (63%) rename flytectl/cmd/config/subcommand/{taskresourceattrupdateconfig_flags.go => clusterresourceattribute/attrupdateconfig_flags.go} (54%) rename flytectl/cmd/config/subcommand/{taskresourceattrupdateconfig_flags_test.go => clusterresourceattribute/attrupdateconfig_flags_test.go} (63%) create mode 100644 flytectl/cmd/config/subcommand/clusterresourceattribute/delete_config.go create mode 100644 flytectl/cmd/config/subcommand/clusterresourceattribute/fetch_config.go create mode 100644 flytectl/cmd/config/subcommand/clusterresourceattribute/file_config.go create mode 100644 flytectl/cmd/config/subcommand/clusterresourceattribute/file_config_test.go create mode 100644 flytectl/cmd/config/subcommand/clusterresourceattribute/update_config.go create mode 100644 flytectl/cmd/config/subcommand/matchable_attr_file_config_utils.go create mode 100644 flytectl/cmd/config/subcommand/matchable_attribute_decorator.go create mode 100644 flytectl/cmd/config/subcommand/project_domain_workflow_getter.go delete mode 100644 flytectl/cmd/config/subcommand/task_resource_attribute_delete_config.go delete mode 100644 flytectl/cmd/config/subcommand/task_resource_attribute_fetch_config.go delete mode 100644 flytectl/cmd/config/subcommand/task_resource_attribute_file_config.go delete mode 100644 flytectl/cmd/config/subcommand/task_resource_attribute_update_config.go create mode 100755 flytectl/cmd/config/subcommand/taskresourceattribute/attrdeleteconfig_flags.go create mode 100755 flytectl/cmd/config/subcommand/taskresourceattribute/attrdeleteconfig_flags_test.go rename flytectl/cmd/config/subcommand/{workflowconfig_flags.go => taskresourceattribute/attrfetchconfig_flags.go} (52%) create mode 100755 flytectl/cmd/config/subcommand/taskresourceattribute/attrfetchconfig_flags_test.go create mode 100755 flytectl/cmd/config/subcommand/taskresourceattribute/attrupdateconfig_flags.go create mode 100755 flytectl/cmd/config/subcommand/taskresourceattribute/attrupdateconfig_flags_test.go create mode 100644 flytectl/cmd/config/subcommand/taskresourceattribute/delete_config.go create mode 100644 flytectl/cmd/config/subcommand/taskresourceattribute/fetch_config.go create mode 100644 flytectl/cmd/config/subcommand/taskresourceattribute/file_config.go create mode 100644 flytectl/cmd/config/subcommand/taskresourceattribute/file_config_test.go create mode 100644 flytectl/cmd/config/subcommand/taskresourceattribute/update_config.go create mode 100755 flytectl/cmd/config/subcommand/workflow/config_flags.go rename flytectl/cmd/config/subcommand/{workflowconfig_flags_test.go => workflow/config_flags_test.go} (69%) rename flytectl/cmd/config/subcommand/{ => workflow}/workflow_config.go (51%) create mode 100644 flytectl/cmd/delete/matchable_attribute_util.go create mode 100644 flytectl/cmd/delete/matchable_cluster_resource_attribute.go create mode 100644 flytectl/cmd/delete/matchable_cluster_resource_attribute_test.go rename flytectl/cmd/delete/testdata/{invalid_task_attribute.yaml => invalid_attribute.yaml} (100%) create mode 100644 flytectl/cmd/delete/testdata/valid_project_domain_cluster_attribute.yaml create mode 100644 flytectl/cmd/delete/testdata/valid_workflow_cluster_attribute.yaml create mode 100644 flytectl/cmd/get/matchable_attribute_util.go create mode 100644 flytectl/cmd/get/matchable_cluster_resource_attribute.go create mode 100644 flytectl/cmd/get/matchable_cluster_resource_attribute_test.go create mode 100644 flytectl/cmd/update/matchable_attribute_util.go create mode 100644 flytectl/cmd/update/matchable_cluster_resource_attribute.go create mode 100644 flytectl/cmd/update/matchable_cluster_resource_attribute_test.go rename flytectl/cmd/update/testdata/{invalid_task_attribute.yaml => invalid_attribute.yaml} (100%) create mode 100644 flytectl/cmd/update/testdata/valid_project_domain_cluster_attribute.yaml create mode 100644 flytectl/cmd/update/testdata/valid_workflow_cluster_attribute.yaml create mode 100644 flytectl/docs/source/gen/flytectl_delete_cluster-resource-attribute.rst create mode 100644 flytectl/docs/source/gen/flytectl_get_cluster-resource-attribute.rst create mode 100644 flytectl/docs/source/gen/flytectl_update_cluster-resource-attribute.rst diff --git a/flytectl/cmd/config/subcommand/taskresourceattrdeleteconfig_flags.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrdeleteconfig_flags.go similarity index 55% rename from flytectl/cmd/config/subcommand/taskresourceattrdeleteconfig_flags.go rename to flytectl/cmd/config/subcommand/clusterresourceattribute/attrdeleteconfig_flags.go index fadea1f465..2ee35a7887 100755 --- a/flytectl/cmd/config/subcommand/taskresourceattrdeleteconfig_flags.go +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrdeleteconfig_flags.go @@ -1,7 +1,7 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots. -package subcommand +package clusterresourceattribute import ( "encoding/json" @@ -14,7 +14,7 @@ import ( // 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 (TaskResourceAttrDeleteConfig) elemValueOrNil(v interface{}) interface{} { +func (AttrDeleteConfig) elemValueOrNil(v interface{}) interface{} { if t := reflect.TypeOf(v); t.Kind() == reflect.Ptr { if reflect.ValueOf(v).IsNil() { return reflect.Zero(t.Elem()).Interface() @@ -28,7 +28,7 @@ func (TaskResourceAttrDeleteConfig) elemValueOrNil(v interface{}) interface{} { return v } -func (TaskResourceAttrDeleteConfig) mustMarshalJSON(v json.Marshaler) string { +func (AttrDeleteConfig) mustMarshalJSON(v json.Marshaler) string { raw, err := v.MarshalJSON() if err != nil { panic(err) @@ -37,10 +37,10 @@ func (TaskResourceAttrDeleteConfig) mustMarshalJSON(v json.Marshaler) string { return string(raw) } -// GetPFlagSet will return strongly types pflags for all fields in TaskResourceAttrDeleteConfig and its nested types. The format of the +// GetPFlagSet will return strongly types pflags for all fields in AttrDeleteConfig and its nested types. The format of the // flags is json-name.json-sub-name... etc. -func (cfg TaskResourceAttrDeleteConfig) GetPFlagSet(prefix string) *pflag.FlagSet { - cmdFlags := pflag.NewFlagSet("TaskResourceAttrDeleteConfig", pflag.ExitOnError) - cmdFlags.StringVar(&(DefaultTaskResourceDelConfig.AttrFile), fmt.Sprintf("%v%v", prefix, "attrFile"), DefaultTaskResourceDelConfig.AttrFile, "attribute file name to be used for delete attribute for the resource type.") +func (cfg AttrDeleteConfig) GetPFlagSet(prefix string) *pflag.FlagSet { + cmdFlags := pflag.NewFlagSet("AttrDeleteConfig", pflag.ExitOnError) + cmdFlags.StringVar(&(DefaultDelConfig.AttrFile), fmt.Sprintf("%v%v", prefix, "attrFile"), DefaultDelConfig.AttrFile, "attribute file name to be used for delete attribute for the resource type.") return cmdFlags } diff --git a/flytectl/cmd/config/subcommand/taskresourceattrfetchconfig_flags_test.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrdeleteconfig_flags_test.go similarity index 64% rename from flytectl/cmd/config/subcommand/taskresourceattrfetchconfig_flags_test.go rename to flytectl/cmd/config/subcommand/clusterresourceattribute/attrdeleteconfig_flags_test.go index c2c77e82c2..798e81d2cf 100755 --- a/flytectl/cmd/config/subcommand/taskresourceattrfetchconfig_flags_test.go +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrdeleteconfig_flags_test.go @@ -1,7 +1,7 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots. -package subcommand +package clusterresourceattribute import ( "encoding/json" @@ -14,22 +14,22 @@ import ( "github.com/stretchr/testify/assert" ) -var dereferencableKindsTaskResourceAttrFetchConfig = map[reflect.Kind]struct{}{ +var dereferencableKindsAttrDeleteConfig = 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 canGetElementTaskResourceAttrFetchConfig(t reflect.Kind) bool { - _, exists := dereferencableKindsTaskResourceAttrFetchConfig[t] +func canGetElementAttrDeleteConfig(t reflect.Kind) bool { + _, exists := dereferencableKindsAttrDeleteConfig[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 jsonUnmarshalerHookTaskResourceAttrFetchConfig(_, to reflect.Type, data interface{}) (interface{}, error) { +func jsonUnmarshalerHookAttrDeleteConfig(_, to reflect.Type, data interface{}) (interface{}, error) { unmarshalerType := reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() if to.Implements(unmarshalerType) || reflect.PtrTo(to).Implements(unmarshalerType) || - (canGetElementTaskResourceAttrFetchConfig(to.Kind()) && to.Elem().Implements(unmarshalerType)) { + (canGetElementAttrDeleteConfig(to.Kind()) && to.Elem().Implements(unmarshalerType)) { raw, err := json.Marshal(data) if err != nil { @@ -50,7 +50,7 @@ func jsonUnmarshalerHookTaskResourceAttrFetchConfig(_, to reflect.Type, data int return data, nil } -func decode_TaskResourceAttrFetchConfig(input, result interface{}) error { +func decode_AttrDeleteConfig(input, result interface{}) error { config := &mapstructure.DecoderConfig{ TagName: "json", WeaklyTypedInput: true, @@ -58,7 +58,7 @@ func decode_TaskResourceAttrFetchConfig(input, result interface{}) error { DecodeHook: mapstructure.ComposeDecodeHookFunc( mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), - jsonUnmarshalerHookTaskResourceAttrFetchConfig, + jsonUnmarshalerHookAttrDeleteConfig, ), } @@ -70,7 +70,7 @@ func decode_TaskResourceAttrFetchConfig(input, result interface{}) error { return decoder.Decode(input) } -func join_TaskResourceAttrFetchConfig(arr interface{}, sep string) string { +func join_AttrDeleteConfig(arr interface{}, sep string) string { listValue := reflect.ValueOf(arr) strs := make([]string, 0, listValue.Len()) for i := 0; i < listValue.Len(); i++ { @@ -80,22 +80,22 @@ func join_TaskResourceAttrFetchConfig(arr interface{}, sep string) string { return strings.Join(strs, sep) } -func testDecodeJson_TaskResourceAttrFetchConfig(t *testing.T, val, result interface{}) { - assert.NoError(t, decode_TaskResourceAttrFetchConfig(val, result)) +func testDecodeJson_AttrDeleteConfig(t *testing.T, val, result interface{}) { + assert.NoError(t, decode_AttrDeleteConfig(val, result)) } -func testDecodeSlice_TaskResourceAttrFetchConfig(t *testing.T, vStringSlice, result interface{}) { - assert.NoError(t, decode_TaskResourceAttrFetchConfig(vStringSlice, result)) +func testDecodeSlice_AttrDeleteConfig(t *testing.T, vStringSlice, result interface{}) { + assert.NoError(t, decode_AttrDeleteConfig(vStringSlice, result)) } -func TestTaskResourceAttrFetchConfig_GetPFlagSet(t *testing.T) { - val := TaskResourceAttrFetchConfig{} +func TestAttrDeleteConfig_GetPFlagSet(t *testing.T) { + val := AttrDeleteConfig{} cmdFlags := val.GetPFlagSet("") assert.True(t, cmdFlags.HasFlags()) } -func TestTaskResourceAttrFetchConfig_SetFlags(t *testing.T) { - actual := TaskResourceAttrFetchConfig{} +func TestAttrDeleteConfig_SetFlags(t *testing.T) { + actual := AttrDeleteConfig{} cmdFlags := actual.GetPFlagSet("") assert.True(t, cmdFlags.HasFlags()) @@ -103,7 +103,7 @@ func TestTaskResourceAttrFetchConfig_SetFlags(t *testing.T) { t.Run("DefaultValue", func(t *testing.T) { // Test that default value is set properly if vString, err := cmdFlags.GetString("attrFile"); err == nil { - assert.Equal(t, string(DefaultTaskResourceFetchConfig.AttrFile), vString) + assert.Equal(t, string(DefaultDelConfig.AttrFile), vString) } else { assert.FailNow(t, err.Error()) } @@ -114,7 +114,7 @@ func TestTaskResourceAttrFetchConfig_SetFlags(t *testing.T) { cmdFlags.Set("attrFile", testValue) if vString, err := cmdFlags.GetString("attrFile"); err == nil { - testDecodeJson_TaskResourceAttrFetchConfig(t, fmt.Sprintf("%v", vString), &actual.AttrFile) + testDecodeJson_AttrDeleteConfig(t, fmt.Sprintf("%v", vString), &actual.AttrFile) } else { assert.FailNow(t, err.Error()) diff --git a/flytectl/cmd/config/subcommand/taskresourceattrfetchconfig_flags.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrfetchconfig_flags.go similarity index 55% rename from flytectl/cmd/config/subcommand/taskresourceattrfetchconfig_flags.go rename to flytectl/cmd/config/subcommand/clusterresourceattribute/attrfetchconfig_flags.go index ebf6ec6220..477e57689f 100755 --- a/flytectl/cmd/config/subcommand/taskresourceattrfetchconfig_flags.go +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrfetchconfig_flags.go @@ -1,7 +1,7 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots. -package subcommand +package clusterresourceattribute import ( "encoding/json" @@ -14,7 +14,7 @@ import ( // 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 (TaskResourceAttrFetchConfig) elemValueOrNil(v interface{}) interface{} { +func (AttrFetchConfig) elemValueOrNil(v interface{}) interface{} { if t := reflect.TypeOf(v); t.Kind() == reflect.Ptr { if reflect.ValueOf(v).IsNil() { return reflect.Zero(t.Elem()).Interface() @@ -28,7 +28,7 @@ func (TaskResourceAttrFetchConfig) elemValueOrNil(v interface{}) interface{} { return v } -func (TaskResourceAttrFetchConfig) mustMarshalJSON(v json.Marshaler) string { +func (AttrFetchConfig) mustMarshalJSON(v json.Marshaler) string { raw, err := v.MarshalJSON() if err != nil { panic(err) @@ -37,10 +37,10 @@ func (TaskResourceAttrFetchConfig) mustMarshalJSON(v json.Marshaler) string { return string(raw) } -// GetPFlagSet will return strongly types pflags for all fields in TaskResourceAttrFetchConfig and its nested types. The format of the +// GetPFlagSet will return strongly types pflags for all fields in AttrFetchConfig and its nested types. The format of the // flags is json-name.json-sub-name... etc. -func (cfg TaskResourceAttrFetchConfig) GetPFlagSet(prefix string) *pflag.FlagSet { - cmdFlags := pflag.NewFlagSet("TaskResourceAttrFetchConfig", pflag.ExitOnError) - cmdFlags.StringVar(&(DefaultTaskResourceFetchConfig.AttrFile), fmt.Sprintf("%v%v", prefix, "attrFile"), DefaultTaskResourceFetchConfig.AttrFile, "attribute file name to be used for generating attribute for the resource type.") +func (cfg AttrFetchConfig) GetPFlagSet(prefix string) *pflag.FlagSet { + cmdFlags := pflag.NewFlagSet("AttrFetchConfig", pflag.ExitOnError) + cmdFlags.StringVar(&(DefaultFetchConfig.AttrFile), fmt.Sprintf("%v%v", prefix, "attrFile"), DefaultFetchConfig.AttrFile, "attribute file name to be used for generating attribute for the resource type.") return cmdFlags } diff --git a/flytectl/cmd/config/subcommand/taskresourceattrdeleteconfig_flags_test.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrfetchconfig_flags_test.go similarity index 63% rename from flytectl/cmd/config/subcommand/taskresourceattrdeleteconfig_flags_test.go rename to flytectl/cmd/config/subcommand/clusterresourceattribute/attrfetchconfig_flags_test.go index 222fae2c50..4d8ae3e0e6 100755 --- a/flytectl/cmd/config/subcommand/taskresourceattrdeleteconfig_flags_test.go +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrfetchconfig_flags_test.go @@ -1,7 +1,7 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots. -package subcommand +package clusterresourceattribute import ( "encoding/json" @@ -14,22 +14,22 @@ import ( "github.com/stretchr/testify/assert" ) -var dereferencableKindsTaskResourceAttrDeleteConfig = map[reflect.Kind]struct{}{ +var dereferencableKindsAttrFetchConfig = 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 canGetElementTaskResourceAttrDeleteConfig(t reflect.Kind) bool { - _, exists := dereferencableKindsTaskResourceAttrDeleteConfig[t] +func canGetElementAttrFetchConfig(t reflect.Kind) bool { + _, exists := dereferencableKindsAttrFetchConfig[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 jsonUnmarshalerHookTaskResourceAttrDeleteConfig(_, to reflect.Type, data interface{}) (interface{}, error) { +func jsonUnmarshalerHookAttrFetchConfig(_, to reflect.Type, data interface{}) (interface{}, error) { unmarshalerType := reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() if to.Implements(unmarshalerType) || reflect.PtrTo(to).Implements(unmarshalerType) || - (canGetElementTaskResourceAttrDeleteConfig(to.Kind()) && to.Elem().Implements(unmarshalerType)) { + (canGetElementAttrFetchConfig(to.Kind()) && to.Elem().Implements(unmarshalerType)) { raw, err := json.Marshal(data) if err != nil { @@ -50,7 +50,7 @@ func jsonUnmarshalerHookTaskResourceAttrDeleteConfig(_, to reflect.Type, data in return data, nil } -func decode_TaskResourceAttrDeleteConfig(input, result interface{}) error { +func decode_AttrFetchConfig(input, result interface{}) error { config := &mapstructure.DecoderConfig{ TagName: "json", WeaklyTypedInput: true, @@ -58,7 +58,7 @@ func decode_TaskResourceAttrDeleteConfig(input, result interface{}) error { DecodeHook: mapstructure.ComposeDecodeHookFunc( mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), - jsonUnmarshalerHookTaskResourceAttrDeleteConfig, + jsonUnmarshalerHookAttrFetchConfig, ), } @@ -70,7 +70,7 @@ func decode_TaskResourceAttrDeleteConfig(input, result interface{}) error { return decoder.Decode(input) } -func join_TaskResourceAttrDeleteConfig(arr interface{}, sep string) string { +func join_AttrFetchConfig(arr interface{}, sep string) string { listValue := reflect.ValueOf(arr) strs := make([]string, 0, listValue.Len()) for i := 0; i < listValue.Len(); i++ { @@ -80,22 +80,22 @@ func join_TaskResourceAttrDeleteConfig(arr interface{}, sep string) string { return strings.Join(strs, sep) } -func testDecodeJson_TaskResourceAttrDeleteConfig(t *testing.T, val, result interface{}) { - assert.NoError(t, decode_TaskResourceAttrDeleteConfig(val, result)) +func testDecodeJson_AttrFetchConfig(t *testing.T, val, result interface{}) { + assert.NoError(t, decode_AttrFetchConfig(val, result)) } -func testDecodeSlice_TaskResourceAttrDeleteConfig(t *testing.T, vStringSlice, result interface{}) { - assert.NoError(t, decode_TaskResourceAttrDeleteConfig(vStringSlice, result)) +func testDecodeSlice_AttrFetchConfig(t *testing.T, vStringSlice, result interface{}) { + assert.NoError(t, decode_AttrFetchConfig(vStringSlice, result)) } -func TestTaskResourceAttrDeleteConfig_GetPFlagSet(t *testing.T) { - val := TaskResourceAttrDeleteConfig{} +func TestAttrFetchConfig_GetPFlagSet(t *testing.T) { + val := AttrFetchConfig{} cmdFlags := val.GetPFlagSet("") assert.True(t, cmdFlags.HasFlags()) } -func TestTaskResourceAttrDeleteConfig_SetFlags(t *testing.T) { - actual := TaskResourceAttrDeleteConfig{} +func TestAttrFetchConfig_SetFlags(t *testing.T) { + actual := AttrFetchConfig{} cmdFlags := actual.GetPFlagSet("") assert.True(t, cmdFlags.HasFlags()) @@ -103,7 +103,7 @@ func TestTaskResourceAttrDeleteConfig_SetFlags(t *testing.T) { t.Run("DefaultValue", func(t *testing.T) { // Test that default value is set properly if vString, err := cmdFlags.GetString("attrFile"); err == nil { - assert.Equal(t, string(DefaultTaskResourceDelConfig.AttrFile), vString) + assert.Equal(t, string(DefaultFetchConfig.AttrFile), vString) } else { assert.FailNow(t, err.Error()) } @@ -114,7 +114,7 @@ func TestTaskResourceAttrDeleteConfig_SetFlags(t *testing.T) { cmdFlags.Set("attrFile", testValue) if vString, err := cmdFlags.GetString("attrFile"); err == nil { - testDecodeJson_TaskResourceAttrDeleteConfig(t, fmt.Sprintf("%v", vString), &actual.AttrFile) + testDecodeJson_AttrFetchConfig(t, fmt.Sprintf("%v", vString), &actual.AttrFile) } else { assert.FailNow(t, err.Error()) diff --git a/flytectl/cmd/config/subcommand/taskresourceattrupdateconfig_flags.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrupdateconfig_flags.go similarity index 54% rename from flytectl/cmd/config/subcommand/taskresourceattrupdateconfig_flags.go rename to flytectl/cmd/config/subcommand/clusterresourceattribute/attrupdateconfig_flags.go index d94413106f..60a9af145a 100755 --- a/flytectl/cmd/config/subcommand/taskresourceattrupdateconfig_flags.go +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrupdateconfig_flags.go @@ -1,7 +1,7 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots. -package subcommand +package clusterresourceattribute import ( "encoding/json" @@ -14,7 +14,7 @@ import ( // 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 (TaskResourceAttrUpdateConfig) elemValueOrNil(v interface{}) interface{} { +func (AttrUpdateConfig) elemValueOrNil(v interface{}) interface{} { if t := reflect.TypeOf(v); t.Kind() == reflect.Ptr { if reflect.ValueOf(v).IsNil() { return reflect.Zero(t.Elem()).Interface() @@ -28,7 +28,7 @@ func (TaskResourceAttrUpdateConfig) elemValueOrNil(v interface{}) interface{} { return v } -func (TaskResourceAttrUpdateConfig) mustMarshalJSON(v json.Marshaler) string { +func (AttrUpdateConfig) mustMarshalJSON(v json.Marshaler) string { raw, err := v.MarshalJSON() if err != nil { panic(err) @@ -37,10 +37,10 @@ func (TaskResourceAttrUpdateConfig) mustMarshalJSON(v json.Marshaler) string { return string(raw) } -// GetPFlagSet will return strongly types pflags for all fields in TaskResourceAttrUpdateConfig and its nested types. The format of the +// GetPFlagSet will return strongly types pflags for all fields in AttrUpdateConfig and its nested types. The format of the // flags is json-name.json-sub-name... etc. -func (cfg TaskResourceAttrUpdateConfig) GetPFlagSet(prefix string) *pflag.FlagSet { - cmdFlags := pflag.NewFlagSet("TaskResourceAttrUpdateConfig", pflag.ExitOnError) - cmdFlags.StringVar(&(DefaultTaskResourceUpdateConfig.AttrFile), fmt.Sprintf("%v%v", prefix, "attrFile"), DefaultTaskResourceUpdateConfig.AttrFile, "attribute file name to be used for updating attribute for the resource type.") +func (cfg AttrUpdateConfig) GetPFlagSet(prefix string) *pflag.FlagSet { + cmdFlags := pflag.NewFlagSet("AttrUpdateConfig", pflag.ExitOnError) + cmdFlags.StringVar(&(DefaultUpdateConfig.AttrFile), fmt.Sprintf("%v%v", prefix, "attrFile"), DefaultUpdateConfig.AttrFile, "attribute file name to be used for updating attribute for the resource type.") return cmdFlags } diff --git a/flytectl/cmd/config/subcommand/taskresourceattrupdateconfig_flags_test.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrupdateconfig_flags_test.go similarity index 63% rename from flytectl/cmd/config/subcommand/taskresourceattrupdateconfig_flags_test.go rename to flytectl/cmd/config/subcommand/clusterresourceattribute/attrupdateconfig_flags_test.go index 73f272b45e..aa5d18dd07 100755 --- a/flytectl/cmd/config/subcommand/taskresourceattrupdateconfig_flags_test.go +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/attrupdateconfig_flags_test.go @@ -1,7 +1,7 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots. -package subcommand +package clusterresourceattribute import ( "encoding/json" @@ -14,22 +14,22 @@ import ( "github.com/stretchr/testify/assert" ) -var dereferencableKindsTaskResourceAttrUpdateConfig = map[reflect.Kind]struct{}{ +var dereferencableKindsAttrUpdateConfig = 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 canGetElementTaskResourceAttrUpdateConfig(t reflect.Kind) bool { - _, exists := dereferencableKindsTaskResourceAttrUpdateConfig[t] +func canGetElementAttrUpdateConfig(t reflect.Kind) bool { + _, exists := dereferencableKindsAttrUpdateConfig[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 jsonUnmarshalerHookTaskResourceAttrUpdateConfig(_, to reflect.Type, data interface{}) (interface{}, error) { +func jsonUnmarshalerHookAttrUpdateConfig(_, to reflect.Type, data interface{}) (interface{}, error) { unmarshalerType := reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() if to.Implements(unmarshalerType) || reflect.PtrTo(to).Implements(unmarshalerType) || - (canGetElementTaskResourceAttrUpdateConfig(to.Kind()) && to.Elem().Implements(unmarshalerType)) { + (canGetElementAttrUpdateConfig(to.Kind()) && to.Elem().Implements(unmarshalerType)) { raw, err := json.Marshal(data) if err != nil { @@ -50,7 +50,7 @@ func jsonUnmarshalerHookTaskResourceAttrUpdateConfig(_, to reflect.Type, data in return data, nil } -func decode_TaskResourceAttrUpdateConfig(input, result interface{}) error { +func decode_AttrUpdateConfig(input, result interface{}) error { config := &mapstructure.DecoderConfig{ TagName: "json", WeaklyTypedInput: true, @@ -58,7 +58,7 @@ func decode_TaskResourceAttrUpdateConfig(input, result interface{}) error { DecodeHook: mapstructure.ComposeDecodeHookFunc( mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), - jsonUnmarshalerHookTaskResourceAttrUpdateConfig, + jsonUnmarshalerHookAttrUpdateConfig, ), } @@ -70,7 +70,7 @@ func decode_TaskResourceAttrUpdateConfig(input, result interface{}) error { return decoder.Decode(input) } -func join_TaskResourceAttrUpdateConfig(arr interface{}, sep string) string { +func join_AttrUpdateConfig(arr interface{}, sep string) string { listValue := reflect.ValueOf(arr) strs := make([]string, 0, listValue.Len()) for i := 0; i < listValue.Len(); i++ { @@ -80,22 +80,22 @@ func join_TaskResourceAttrUpdateConfig(arr interface{}, sep string) string { return strings.Join(strs, sep) } -func testDecodeJson_TaskResourceAttrUpdateConfig(t *testing.T, val, result interface{}) { - assert.NoError(t, decode_TaskResourceAttrUpdateConfig(val, result)) +func testDecodeJson_AttrUpdateConfig(t *testing.T, val, result interface{}) { + assert.NoError(t, decode_AttrUpdateConfig(val, result)) } -func testDecodeSlice_TaskResourceAttrUpdateConfig(t *testing.T, vStringSlice, result interface{}) { - assert.NoError(t, decode_TaskResourceAttrUpdateConfig(vStringSlice, result)) +func testDecodeSlice_AttrUpdateConfig(t *testing.T, vStringSlice, result interface{}) { + assert.NoError(t, decode_AttrUpdateConfig(vStringSlice, result)) } -func TestTaskResourceAttrUpdateConfig_GetPFlagSet(t *testing.T) { - val := TaskResourceAttrUpdateConfig{} +func TestAttrUpdateConfig_GetPFlagSet(t *testing.T) { + val := AttrUpdateConfig{} cmdFlags := val.GetPFlagSet("") assert.True(t, cmdFlags.HasFlags()) } -func TestTaskResourceAttrUpdateConfig_SetFlags(t *testing.T) { - actual := TaskResourceAttrUpdateConfig{} +func TestAttrUpdateConfig_SetFlags(t *testing.T) { + actual := AttrUpdateConfig{} cmdFlags := actual.GetPFlagSet("") assert.True(t, cmdFlags.HasFlags()) @@ -103,7 +103,7 @@ func TestTaskResourceAttrUpdateConfig_SetFlags(t *testing.T) { t.Run("DefaultValue", func(t *testing.T) { // Test that default value is set properly if vString, err := cmdFlags.GetString("attrFile"); err == nil { - assert.Equal(t, string(DefaultTaskResourceUpdateConfig.AttrFile), vString) + assert.Equal(t, string(DefaultUpdateConfig.AttrFile), vString) } else { assert.FailNow(t, err.Error()) } @@ -114,7 +114,7 @@ func TestTaskResourceAttrUpdateConfig_SetFlags(t *testing.T) { cmdFlags.Set("attrFile", testValue) if vString, err := cmdFlags.GetString("attrFile"); err == nil { - testDecodeJson_TaskResourceAttrUpdateConfig(t, fmt.Sprintf("%v", vString), &actual.AttrFile) + testDecodeJson_AttrUpdateConfig(t, fmt.Sprintf("%v", vString), &actual.AttrFile) } else { assert.FailNow(t, err.Error()) diff --git a/flytectl/cmd/config/subcommand/clusterresourceattribute/delete_config.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/delete_config.go new file mode 100644 index 0000000000..3b81cd0991 --- /dev/null +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/delete_config.go @@ -0,0 +1,10 @@ +package clusterresourceattribute + +//go:generate pflags AttrDeleteConfig --default-var DefaultDelConfig + +// AttrDeleteConfig Matchable resource attributes configuration passed from command line +type AttrDeleteConfig struct { + AttrFile string `json:"attrFile" pflag:",attribute file name to be used for delete attribute for the resource type."` +} + +var DefaultDelConfig = &AttrDeleteConfig{} diff --git a/flytectl/cmd/config/subcommand/clusterresourceattribute/fetch_config.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/fetch_config.go new file mode 100644 index 0000000000..9da6d481f8 --- /dev/null +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/fetch_config.go @@ -0,0 +1,9 @@ +package clusterresourceattribute + +//go:generate pflags AttrFetchConfig --default-var DefaultFetchConfig + +type AttrFetchConfig struct { + AttrFile string `json:"attrFile" pflag:",attribute file name to be used for generating attribute for the resource type."` +} + +var DefaultFetchConfig = &AttrFetchConfig{} diff --git a/flytectl/cmd/config/subcommand/clusterresourceattribute/file_config.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/file_config.go new file mode 100644 index 0000000000..5bfece378d --- /dev/null +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/file_config.go @@ -0,0 +1,47 @@ +package clusterresourceattribute + +import ( + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" +) + +// AttrFileConfig shadow Config for ClusterResourceAttributes. +// 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 AttrFileConfig struct { + Project string `json:"project"` + Domain string `json:"domain"` + Workflow string `json:"workflow,omitempty"` + *admin.ClusterResourceAttributes +} + +// Decorate decorator over ClusterResourceAttributes. +func (c AttrFileConfig) Decorate() *admin.MatchingAttributes { + return &admin.MatchingAttributes{ + Target: &admin.MatchingAttributes_ClusterResourceAttributes{ + ClusterResourceAttributes: c.ClusterResourceAttributes, + }, + } +} + +// UnDecorate to uncover ClusterResourceAttributes. +func (c *AttrFileConfig) UnDecorate(matchingAttribute *admin.MatchingAttributes) { + if matchingAttribute == nil { + return + } + c.ClusterResourceAttributes = matchingAttribute.GetClusterResourceAttributes() +} + +// GetProject from the AttrFileConfig +func (c AttrFileConfig) GetProject() string { + return c.Project +} + +// GetDomain from the AttrFileConfig +func (c AttrFileConfig) GetDomain() string { + return c.Domain +} + +// GetWorkflow from the AttrFileConfig +func (c AttrFileConfig) GetWorkflow() string { + return c.Workflow +} diff --git a/flytectl/cmd/config/subcommand/clusterresourceattribute/file_config_test.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/file_config_test.go new file mode 100644 index 0000000000..f9510c7d07 --- /dev/null +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/file_config_test.go @@ -0,0 +1,46 @@ +package clusterresourceattribute + +import ( + "testing" + + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" + + "github.com/stretchr/testify/assert" +) + +func TestFileConfig(t *testing.T) { + clusterAttrFileConfig := AttrFileConfig{ + Project: "dummyProject", + Domain: "dummyDomain", + ClusterResourceAttributes: &admin.ClusterResourceAttributes{ + Attributes: map[string]string{"foo": "bar"}, + }, + } + matchingAttr := &admin.MatchingAttributes{ + Target: &admin.MatchingAttributes_ClusterResourceAttributes{ + ClusterResourceAttributes: clusterAttrFileConfig.ClusterResourceAttributes, + }, + } + t.Run("decorate", func(t *testing.T) { + assert.Equal(t, matchingAttr, clusterAttrFileConfig.Decorate()) + }) + + t.Run("decorate", func(t *testing.T) { + clusterAttrFileConfigNew := AttrFileConfig{ + Project: "dummyProject", + Domain: "dummyDomain", + } + clusterAttrFileConfigNew.UnDecorate(matchingAttr) + assert.Equal(t, clusterAttrFileConfig, clusterAttrFileConfigNew) + }) + t.Run("get project domain workflow", func(t *testing.T) { + clusterAttrFileConfigNew := AttrFileConfig{ + Project: "dummyProject", + Domain: "dummyDomain", + Workflow: "workflow", + } + assert.Equal(t, "dummyProject", clusterAttrFileConfigNew.GetProject()) + assert.Equal(t, "dummyDomain", clusterAttrFileConfigNew.GetDomain()) + assert.Equal(t, "workflow", clusterAttrFileConfigNew.GetWorkflow()) + }) +} diff --git a/flytectl/cmd/config/subcommand/clusterresourceattribute/update_config.go b/flytectl/cmd/config/subcommand/clusterresourceattribute/update_config.go new file mode 100644 index 0000000000..2ff214af1f --- /dev/null +++ b/flytectl/cmd/config/subcommand/clusterresourceattribute/update_config.go @@ -0,0 +1,10 @@ +package clusterresourceattribute + +//go:generate pflags AttrUpdateConfig --default-var DefaultUpdateConfig + +// AttrUpdateConfig Matchable resource attributes configuration passed from command line +type AttrUpdateConfig struct { + AttrFile string `json:"attrFile" pflag:",attribute file name to be used for updating attribute for the resource type."` +} + +var DefaultUpdateConfig = &AttrUpdateConfig{} diff --git a/flytectl/cmd/config/subcommand/matchable_attr_file_config_utils.go b/flytectl/cmd/config/subcommand/matchable_attr_file_config_utils.go new file mode 100644 index 0000000000..c29b3f23a7 --- /dev/null +++ b/flytectl/cmd/config/subcommand/matchable_attr_file_config_utils.go @@ -0,0 +1,60 @@ +package subcommand + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + + cmdUtil "github.com/flyteorg/flytectl/pkg/commandutils" + "sigs.k8s.io/yaml" +) + +// WriteConfigToFile used for marshaling the Config to a file which can then be used for update/delete +func WriteConfigToFile(matchableAttrConfig interface{}, fileName string) error { + d, err := yaml.Marshal(matchableAttrConfig) + if err != nil { + return fmt.Errorf("error: %v", err) + } + if _, err = os.Stat(fileName); err == nil { + if !cmdUtil.AskForConfirmation(fmt.Sprintf("warning file %v will be overwritten", fileName)) { + return fmt.Errorf("backup the file before continuing") + } + } + return ioutil.WriteFile(fileName, d, 0600) +} + +// String Dumps the json representation of the TaskResourceAttrFileConfig +func String(matchableAttrConfig interface{}) string { + tj, err := json.Marshal(matchableAttrConfig) + if err != nil { + fmt.Println(err) + return "marshaling error" + } + return fmt.Sprintf("%s\n", tj) +} + +// ReadConfigFromFile used for unmarshaling the Config from a file which is used for update/delete +func ReadConfigFromFile(matchableAttrConfig interface{}, fileName string) error { + data, err := ioutil.ReadFile(fileName) + if err != nil { + return fmt.Errorf("unable to read from %v yaml file", fileName) + } + if err = yaml.UnmarshalStrict(data, matchableAttrConfig); err != nil { + return err + } + return nil +} + +func DumpTaskResourceAttr(matchableAttrConfig interface{}, fileName string) error { + // Write Config to file if filename provided in the command + if len(fileName) > 0 { + if err := WriteConfigToFile(matchableAttrConfig, fileName); err != nil { + return fmt.Errorf("error dumping in file due to %v", err) + } + fmt.Printf("wrote the config to file %v", fileName) + } else { + fmt.Printf("%v", String(matchableAttrConfig)) + } + return nil +} diff --git a/flytectl/cmd/config/subcommand/matchable_attribute_decorator.go b/flytectl/cmd/config/subcommand/matchable_attribute_decorator.go new file mode 100644 index 0000000000..728be8582f --- /dev/null +++ b/flytectl/cmd/config/subcommand/matchable_attribute_decorator.go @@ -0,0 +1,13 @@ +package subcommand + +import "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" + +// MatchableAttributeDecorator defines a decorator for any matchable attribute target. +type MatchableAttributeDecorator interface { + Decorate() *admin.MatchingAttributes +} + +// MatchableAttributeUnDecorator defines a undecorator to get the target. +type MatchableAttributeUnDecorator interface { + UnDecorate(*admin.MatchingAttributes) +} diff --git a/flytectl/cmd/config/subcommand/project_domain_workflow_getter.go b/flytectl/cmd/config/subcommand/project_domain_workflow_getter.go new file mode 100644 index 0000000000..ee77f9ca3f --- /dev/null +++ b/flytectl/cmd/config/subcommand/project_domain_workflow_getter.go @@ -0,0 +1,40 @@ +package subcommand + +import rootConfig "github.com/flyteorg/flytectl/cmd/config" + +// ProjectDomainWorkflowGetter defines a interface for getting the project domain workflow. +type ProjectDomainWorkflowGetter interface { + GetProject() string + GetDomain() string + GetWorkflow() string +} + +// PDWGetterCommandLine implements the command line way of getting project domain and workflow +type PDWGetterCommandLine struct { + Config *rootConfig.Config + Args []string +} + +// GetProject returns the cobra parsed Project from the Config +func (g PDWGetterCommandLine) GetProject() string { + if g.Config == nil { + return "" + } + return g.Config.Project +} + +// GetDomain returns the cobra parsed Domain from the Config +func (g PDWGetterCommandLine) GetDomain() string { + if g.Config == nil { + return "" + } + return g.Config.Domain +} + +// GetWorkflow returns the first argument from the commandline +func (g PDWGetterCommandLine) GetWorkflow() string { + if g.Args == nil || len(g.Args) == 0 { + return "" + } + return g.Args[0] +} diff --git a/flytectl/cmd/config/subcommand/task_resource_attribute_delete_config.go b/flytectl/cmd/config/subcommand/task_resource_attribute_delete_config.go deleted file mode 100644 index 34a6a4e466..0000000000 --- a/flytectl/cmd/config/subcommand/task_resource_attribute_delete_config.go +++ /dev/null @@ -1,10 +0,0 @@ -package subcommand - -//go:generate pflags TaskResourceAttrDeleteConfig --default-var DefaultTaskResourceDelConfig - -// TaskResourceAttrDeleteConfig Matchable resource attributes configuration passed from command line -type TaskResourceAttrDeleteConfig struct { - AttrFile string `json:"attrFile" pflag:",attribute file name to be used for delete attribute for the resource type."` -} - -var DefaultTaskResourceDelConfig = &TaskResourceAttrDeleteConfig{} diff --git a/flytectl/cmd/config/subcommand/task_resource_attribute_fetch_config.go b/flytectl/cmd/config/subcommand/task_resource_attribute_fetch_config.go deleted file mode 100644 index 34b1d435f4..0000000000 --- a/flytectl/cmd/config/subcommand/task_resource_attribute_fetch_config.go +++ /dev/null @@ -1,9 +0,0 @@ -package subcommand - -//go:generate pflags TaskResourceAttrFetchConfig --default-var DefaultTaskResourceFetchConfig - -type TaskResourceAttrFetchConfig struct { - AttrFile string `json:"attrFile" pflag:",attribute file name to be used for generating attribute for the resource type."` -} - -var DefaultTaskResourceFetchConfig = &TaskResourceAttrFetchConfig{} diff --git a/flytectl/cmd/config/subcommand/task_resource_attribute_file_config.go b/flytectl/cmd/config/subcommand/task_resource_attribute_file_config.go deleted file mode 100644 index 62fa2f7e3f..0000000000 --- a/flytectl/cmd/config/subcommand/task_resource_attribute_file_config.go +++ /dev/null @@ -1,86 +0,0 @@ -package subcommand - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "os" - - cmdUtil "github.com/flyteorg/flytectl/pkg/commandutils" - "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" - "github.com/flyteorg/flytestdlib/logger" - - "sigs.k8s.io/yaml" -) - -// TaskResourceAttrFileConfig shadow config for TaskResourceAttribute. -// 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 - Domain string - Workflow string - *admin.TaskResourceAttributes -} - -// WriteConfigToFile used for marshaling the config to a file which can then be used for update/delete -func (t TaskResourceAttrFileConfig) WriteConfigToFile(fileName string) error { - d, err := yaml.Marshal(t) - if err != nil { - fmt.Printf("error: %v", err) - } - if _, err = os.Stat(fileName); err == nil { - if !cmdUtil.AskForConfirmation(fmt.Sprintf("warning file %v will be overwritten", fileName)) { - return fmt.Errorf("backup the file before continuing") - } - } - return ioutil.WriteFile(fileName, d, 0600) -} - -// Dumps the json representation of the TaskResourceAttrFileConfig -func (t TaskResourceAttrFileConfig) String() string { - tj, err := json.Marshal(t) - if err != nil { - fmt.Println(err) - return "marshaling error" - } - return fmt.Sprintf("%s\n", tj) -} - -// ReadConfigFromFile used for unmarshaling the config from a file which is used for update/delete -func (t *TaskResourceAttrFileConfig) ReadConfigFromFile(fileName string) error { - data, err := ioutil.ReadFile(fileName) - if err != nil { - return fmt.Errorf("unable to read from %v yaml file", fileName) - } - if err = yaml.UnmarshalStrict(data, t); err != nil { - return err - } - return nil -} - -// MatchableAttributeDecorator decorator over TaskResourceAttributes. Similar decorator would exist for other MatchingAttributes -func (t *TaskResourceAttrFileConfig) MatchableAttributeDecorator() *admin.MatchingAttributes { - return &admin.MatchingAttributes{ - Target: &admin.MatchingAttributes_TaskResourceAttributes{ - TaskResourceAttributes: t.TaskResourceAttributes, - }, - } -} - -func (t TaskResourceAttrFileConfig) DumpTaskResourceAttr(ctx context.Context, fileName string) { - // Dump empty task resource attr for editing - // Write config to file if filename provided in the command - if len(fileName) > 0 { - // Read the config from the file and update the TaskResourceAttrFileConfig with the TaskResourceAttrConfig - if err := t.WriteConfigToFile(fileName); err != nil { - fmt.Printf("error dumping in file due to %v", err) - logger.Warnf(ctx, "error dumping in file due to %v", err) - return - } - fmt.Printf("written the config to file %v", fileName) - } else { - fmt.Printf("%v", t) - } -} diff --git a/flytectl/cmd/config/subcommand/task_resource_attribute_update_config.go b/flytectl/cmd/config/subcommand/task_resource_attribute_update_config.go deleted file mode 100644 index d85944777d..0000000000 --- a/flytectl/cmd/config/subcommand/task_resource_attribute_update_config.go +++ /dev/null @@ -1,10 +0,0 @@ -package subcommand - -//go:generate pflags TaskResourceAttrUpdateConfig --default-var DefaultTaskResourceUpdateConfig - -// TaskResourceAttrUpdateConfig Matchable resource attributes configuration passed from command line -type TaskResourceAttrUpdateConfig struct { - AttrFile string `json:"attrFile" pflag:",attribute file name to be used for updating attribute for the resource type."` -} - -var DefaultTaskResourceUpdateConfig = &TaskResourceAttrUpdateConfig{} diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/attrdeleteconfig_flags.go b/flytectl/cmd/config/subcommand/taskresourceattribute/attrdeleteconfig_flags.go new file mode 100755 index 0000000000..b335257d99 --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/attrdeleteconfig_flags.go @@ -0,0 +1,46 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package taskresourceattribute + +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 (AttrDeleteConfig) 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 (AttrDeleteConfig) 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 AttrDeleteConfig and its nested types. The format of the +// flags is json-name.json-sub-name... etc. +func (cfg AttrDeleteConfig) GetPFlagSet(prefix string) *pflag.FlagSet { + cmdFlags := pflag.NewFlagSet("AttrDeleteConfig", pflag.ExitOnError) + cmdFlags.StringVar(&(DefaultDelConfig.AttrFile), fmt.Sprintf("%v%v", prefix, "attrFile"), DefaultDelConfig.AttrFile, "attribute file name to be used for delete attribute for the resource type.") + return cmdFlags +} diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/attrdeleteconfig_flags_test.go b/flytectl/cmd/config/subcommand/taskresourceattribute/attrdeleteconfig_flags_test.go new file mode 100755 index 0000000000..3c219496ae --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/attrdeleteconfig_flags_test.go @@ -0,0 +1,124 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package taskresourceattribute + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/mitchellh/mapstructure" + "github.com/stretchr/testify/assert" +) + +var dereferencableKindsAttrDeleteConfig = 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 canGetElementAttrDeleteConfig(t reflect.Kind) bool { + _, exists := dereferencableKindsAttrDeleteConfig[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 jsonUnmarshalerHookAttrDeleteConfig(_, to reflect.Type, data interface{}) (interface{}, error) { + unmarshalerType := reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() + if to.Implements(unmarshalerType) || reflect.PtrTo(to).Implements(unmarshalerType) || + (canGetElementAttrDeleteConfig(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_AttrDeleteConfig(input, result interface{}) error { + config := &mapstructure.DecoderConfig{ + TagName: "json", + WeaklyTypedInput: true, + Result: result, + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + jsonUnmarshalerHookAttrDeleteConfig, + ), + } + + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +func join_AttrDeleteConfig(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_AttrDeleteConfig(t *testing.T, val, result interface{}) { + assert.NoError(t, decode_AttrDeleteConfig(val, result)) +} + +func testDecodeSlice_AttrDeleteConfig(t *testing.T, vStringSlice, result interface{}) { + assert.NoError(t, decode_AttrDeleteConfig(vStringSlice, result)) +} + +func TestAttrDeleteConfig_GetPFlagSet(t *testing.T) { + val := AttrDeleteConfig{} + cmdFlags := val.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) +} + +func TestAttrDeleteConfig_SetFlags(t *testing.T) { + actual := AttrDeleteConfig{} + cmdFlags := actual.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) + + t.Run("Test_attrFile", func(t *testing.T) { + t.Run("DefaultValue", func(t *testing.T) { + // Test that default value is set properly + if vString, err := cmdFlags.GetString("attrFile"); err == nil { + assert.Equal(t, string(DefaultDelConfig.AttrFile), vString) + } else { + assert.FailNow(t, err.Error()) + } + }) + + t.Run("Override", func(t *testing.T) { + testValue := "1" + + cmdFlags.Set("attrFile", testValue) + if vString, err := cmdFlags.GetString("attrFile"); err == nil { + testDecodeJson_AttrDeleteConfig(t, fmt.Sprintf("%v", vString), &actual.AttrFile) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) +} diff --git a/flytectl/cmd/config/subcommand/workflowconfig_flags.go b/flytectl/cmd/config/subcommand/taskresourceattribute/attrfetchconfig_flags.go similarity index 52% rename from flytectl/cmd/config/subcommand/workflowconfig_flags.go rename to flytectl/cmd/config/subcommand/taskresourceattribute/attrfetchconfig_flags.go index cc5a1743d4..761baa5c3a 100755 --- a/flytectl/cmd/config/subcommand/workflowconfig_flags.go +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/attrfetchconfig_flags.go @@ -1,7 +1,7 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots. -package subcommand +package taskresourceattribute import ( "encoding/json" @@ -14,7 +14,7 @@ import ( // 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{} { +func (AttrFetchConfig) elemValueOrNil(v interface{}) interface{} { if t := reflect.TypeOf(v); t.Kind() == reflect.Ptr { if reflect.ValueOf(v).IsNil() { return reflect.Zero(t.Elem()).Interface() @@ -28,7 +28,7 @@ func (WorkflowConfig) elemValueOrNil(v interface{}) interface{} { return v } -func (WorkflowConfig) mustMarshalJSON(v json.Marshaler) string { +func (AttrFetchConfig) mustMarshalJSON(v json.Marshaler) string { raw, err := v.MarshalJSON() if err != nil { panic(err) @@ -37,11 +37,10 @@ func (WorkflowConfig) mustMarshalJSON(v json.Marshaler) string { return string(raw) } -// GetPFlagSet will return strongly types pflags for all fields in WorkflowConfig and its nested types. The format of the +// GetPFlagSet will return strongly types pflags for all fields in AttrFetchConfig 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") +func (cfg AttrFetchConfig) GetPFlagSet(prefix string) *pflag.FlagSet { + cmdFlags := pflag.NewFlagSet("AttrFetchConfig", pflag.ExitOnError) + cmdFlags.StringVar(&(DefaultFetchConfig.AttrFile), fmt.Sprintf("%v%v", prefix, "attrFile"), DefaultFetchConfig.AttrFile, "attribute file name to be used for generating attribute for the resource type.") return cmdFlags } diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/attrfetchconfig_flags_test.go b/flytectl/cmd/config/subcommand/taskresourceattribute/attrfetchconfig_flags_test.go new file mode 100755 index 0000000000..1b91152a97 --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/attrfetchconfig_flags_test.go @@ -0,0 +1,124 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package taskresourceattribute + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/mitchellh/mapstructure" + "github.com/stretchr/testify/assert" +) + +var dereferencableKindsAttrFetchConfig = 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 canGetElementAttrFetchConfig(t reflect.Kind) bool { + _, exists := dereferencableKindsAttrFetchConfig[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 jsonUnmarshalerHookAttrFetchConfig(_, to reflect.Type, data interface{}) (interface{}, error) { + unmarshalerType := reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() + if to.Implements(unmarshalerType) || reflect.PtrTo(to).Implements(unmarshalerType) || + (canGetElementAttrFetchConfig(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_AttrFetchConfig(input, result interface{}) error { + config := &mapstructure.DecoderConfig{ + TagName: "json", + WeaklyTypedInput: true, + Result: result, + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + jsonUnmarshalerHookAttrFetchConfig, + ), + } + + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +func join_AttrFetchConfig(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_AttrFetchConfig(t *testing.T, val, result interface{}) { + assert.NoError(t, decode_AttrFetchConfig(val, result)) +} + +func testDecodeSlice_AttrFetchConfig(t *testing.T, vStringSlice, result interface{}) { + assert.NoError(t, decode_AttrFetchConfig(vStringSlice, result)) +} + +func TestAttrFetchConfig_GetPFlagSet(t *testing.T) { + val := AttrFetchConfig{} + cmdFlags := val.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) +} + +func TestAttrFetchConfig_SetFlags(t *testing.T) { + actual := AttrFetchConfig{} + cmdFlags := actual.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) + + t.Run("Test_attrFile", func(t *testing.T) { + t.Run("DefaultValue", func(t *testing.T) { + // Test that default value is set properly + if vString, err := cmdFlags.GetString("attrFile"); err == nil { + assert.Equal(t, string(DefaultFetchConfig.AttrFile), vString) + } else { + assert.FailNow(t, err.Error()) + } + }) + + t.Run("Override", func(t *testing.T) { + testValue := "1" + + cmdFlags.Set("attrFile", testValue) + if vString, err := cmdFlags.GetString("attrFile"); err == nil { + testDecodeJson_AttrFetchConfig(t, fmt.Sprintf("%v", vString), &actual.AttrFile) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) +} diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/attrupdateconfig_flags.go b/flytectl/cmd/config/subcommand/taskresourceattribute/attrupdateconfig_flags.go new file mode 100755 index 0000000000..b94b102bc6 --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/attrupdateconfig_flags.go @@ -0,0 +1,46 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package taskresourceattribute + +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 (AttrUpdateConfig) 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 (AttrUpdateConfig) 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 AttrUpdateConfig and its nested types. The format of the +// flags is json-name.json-sub-name... etc. +func (cfg AttrUpdateConfig) GetPFlagSet(prefix string) *pflag.FlagSet { + cmdFlags := pflag.NewFlagSet("AttrUpdateConfig", pflag.ExitOnError) + cmdFlags.StringVar(&(DefaultUpdateConfig.AttrFile), fmt.Sprintf("%v%v", prefix, "attrFile"), DefaultUpdateConfig.AttrFile, "attribute file name to be used for updating attribute for the resource type.") + return cmdFlags +} diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/attrupdateconfig_flags_test.go b/flytectl/cmd/config/subcommand/taskresourceattribute/attrupdateconfig_flags_test.go new file mode 100755 index 0000000000..2672552de4 --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/attrupdateconfig_flags_test.go @@ -0,0 +1,124 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package taskresourceattribute + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/mitchellh/mapstructure" + "github.com/stretchr/testify/assert" +) + +var dereferencableKindsAttrUpdateConfig = 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 canGetElementAttrUpdateConfig(t reflect.Kind) bool { + _, exists := dereferencableKindsAttrUpdateConfig[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 jsonUnmarshalerHookAttrUpdateConfig(_, to reflect.Type, data interface{}) (interface{}, error) { + unmarshalerType := reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() + if to.Implements(unmarshalerType) || reflect.PtrTo(to).Implements(unmarshalerType) || + (canGetElementAttrUpdateConfig(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_AttrUpdateConfig(input, result interface{}) error { + config := &mapstructure.DecoderConfig{ + TagName: "json", + WeaklyTypedInput: true, + Result: result, + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + jsonUnmarshalerHookAttrUpdateConfig, + ), + } + + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +func join_AttrUpdateConfig(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_AttrUpdateConfig(t *testing.T, val, result interface{}) { + assert.NoError(t, decode_AttrUpdateConfig(val, result)) +} + +func testDecodeSlice_AttrUpdateConfig(t *testing.T, vStringSlice, result interface{}) { + assert.NoError(t, decode_AttrUpdateConfig(vStringSlice, result)) +} + +func TestAttrUpdateConfig_GetPFlagSet(t *testing.T) { + val := AttrUpdateConfig{} + cmdFlags := val.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) +} + +func TestAttrUpdateConfig_SetFlags(t *testing.T) { + actual := AttrUpdateConfig{} + cmdFlags := actual.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) + + t.Run("Test_attrFile", func(t *testing.T) { + t.Run("DefaultValue", func(t *testing.T) { + // Test that default value is set properly + if vString, err := cmdFlags.GetString("attrFile"); err == nil { + assert.Equal(t, string(DefaultUpdateConfig.AttrFile), vString) + } else { + assert.FailNow(t, err.Error()) + } + }) + + t.Run("Override", func(t *testing.T) { + testValue := "1" + + cmdFlags.Set("attrFile", testValue) + if vString, err := cmdFlags.GetString("attrFile"); err == nil { + testDecodeJson_AttrUpdateConfig(t, fmt.Sprintf("%v", vString), &actual.AttrFile) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) +} diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/delete_config.go b/flytectl/cmd/config/subcommand/taskresourceattribute/delete_config.go new file mode 100644 index 0000000000..6c513cafe0 --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/delete_config.go @@ -0,0 +1,10 @@ +package taskresourceattribute + +//go:generate pflags AttrDeleteConfig --default-var DefaultDelConfig + +// AttrDeleteConfig Matchable resource attributes configuration passed from command line +type AttrDeleteConfig struct { + AttrFile string `json:"attrFile" pflag:",attribute file name to be used for delete attribute for the resource type."` +} + +var DefaultDelConfig = &AttrDeleteConfig{} diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/fetch_config.go b/flytectl/cmd/config/subcommand/taskresourceattribute/fetch_config.go new file mode 100644 index 0000000000..6909229c30 --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/fetch_config.go @@ -0,0 +1,9 @@ +package taskresourceattribute + +//go:generate pflags AttrFetchConfig --default-var DefaultFetchConfig + +type AttrFetchConfig struct { + AttrFile string `json:"attrFile" pflag:",attribute file name to be used for generating attribute for the resource type."` +} + +var DefaultFetchConfig = &AttrFetchConfig{} diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/file_config.go b/flytectl/cmd/config/subcommand/taskresourceattribute/file_config.go new file mode 100644 index 0000000000..6c31553f87 --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/file_config.go @@ -0,0 +1,47 @@ +package taskresourceattribute + +import ( + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" +) + +// TaskResourceAttrFileConfig shadow Config for TaskResourceAttribute. +// 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"` + *admin.TaskResourceAttributes +} + +// Decorate decorator over TaskResourceAttributes. +func (t TaskResourceAttrFileConfig) Decorate() *admin.MatchingAttributes { + return &admin.MatchingAttributes{ + Target: &admin.MatchingAttributes_TaskResourceAttributes{ + TaskResourceAttributes: t.TaskResourceAttributes, + }, + } +} + +// UnDecorate to uncover TaskResourceAttributes. +func (t *TaskResourceAttrFileConfig) UnDecorate(matchingAttribute *admin.MatchingAttributes) { + if matchingAttribute == nil { + return + } + t.TaskResourceAttributes = matchingAttribute.GetTaskResourceAttributes() +} + +// GetProject from the TaskResourceAttrFileConfig +func (t TaskResourceAttrFileConfig) GetProject() string { + return t.Project +} + +// GetDomain from the TaskResourceAttrFileConfig +func (t TaskResourceAttrFileConfig) GetDomain() string { + return t.Domain +} + +// GetWorkflow from the TaskResourceAttrFileConfig +func (t TaskResourceAttrFileConfig) GetWorkflow() string { + return t.Workflow +} diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/file_config_test.go b/flytectl/cmd/config/subcommand/taskresourceattribute/file_config_test.go new file mode 100644 index 0000000000..73b2248985 --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/file_config_test.go @@ -0,0 +1,53 @@ +package taskresourceattribute + +import ( + "testing" + + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" + + "github.com/stretchr/testify/assert" +) + +func TestFileConfig(t *testing.T) { + taskAttrFileConfig := TaskResourceAttrFileConfig{ + Project: "dummyProject", + Domain: "dummyDomain", + TaskResourceAttributes: &admin.TaskResourceAttributes{ + Defaults: &admin.TaskResourceSpec{ + Cpu: "1", + Memory: "150Mi", + }, + Limits: &admin.TaskResourceSpec{ + Cpu: "2", + Memory: "350Mi", + }, + }, + } + matchingAttr := &admin.MatchingAttributes{ + Target: &admin.MatchingAttributes_TaskResourceAttributes{ + TaskResourceAttributes: taskAttrFileConfig.TaskResourceAttributes, + }, + } + t.Run("decorate", func(t *testing.T) { + assert.Equal(t, matchingAttr, taskAttrFileConfig.Decorate()) + }) + + t.Run("decorate", func(t *testing.T) { + taskAttrFileConfigNew := TaskResourceAttrFileConfig{ + Project: "dummyProject", + Domain: "dummyDomain", + } + taskAttrFileConfigNew.UnDecorate(matchingAttr) + assert.Equal(t, taskAttrFileConfig, taskAttrFileConfigNew) + }) + t.Run("get project domain workflow", func(t *testing.T) { + taskAttrFileConfigNew := TaskResourceAttrFileConfig{ + Project: "dummyProject", + Domain: "dummyDomain", + Workflow: "workflow", + } + assert.Equal(t, "dummyProject", taskAttrFileConfigNew.GetProject()) + assert.Equal(t, "dummyDomain", taskAttrFileConfigNew.GetDomain()) + assert.Equal(t, "workflow", taskAttrFileConfigNew.GetWorkflow()) + }) +} diff --git a/flytectl/cmd/config/subcommand/taskresourceattribute/update_config.go b/flytectl/cmd/config/subcommand/taskresourceattribute/update_config.go new file mode 100644 index 0000000000..d9ea24ab77 --- /dev/null +++ b/flytectl/cmd/config/subcommand/taskresourceattribute/update_config.go @@ -0,0 +1,10 @@ +package taskresourceattribute + +//go:generate pflags AttrUpdateConfig --default-var DefaultUpdateConfig + +// AttrUpdateConfig Matchable resource attributes configuration passed from command line +type AttrUpdateConfig struct { + AttrFile string `json:"attrFile" pflag:",attribute file name to be used for updating attribute for the resource type."` +} + +var DefaultUpdateConfig = &AttrUpdateConfig{} diff --git a/flytectl/cmd/config/subcommand/workflow/config_flags.go b/flytectl/cmd/config/subcommand/workflow/config_flags.go new file mode 100755 index 0000000000..3db237e852 --- /dev/null +++ b/flytectl/cmd/config/subcommand/workflow/config_flags.go @@ -0,0 +1,47 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package workflow + +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 (Config) 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 (Config) 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 Config and its nested types. The format of the +// flags is json-name.json-sub-name... etc. +func (cfg Config) GetPFlagSet(prefix string) *pflag.FlagSet { + cmdFlags := pflag.NewFlagSet("Config", pflag.ExitOnError) + cmdFlags.StringVar(&(DefaultConfig.Version), fmt.Sprintf("%v%v", prefix, "version"), DefaultConfig.Version, "version of the workflow to be fetched.") + cmdFlags.BoolVar(&(DefaultConfig.Latest),fmt.Sprintf("%v%v", prefix, "latest"), DefaultConfig.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/workflow/config_flags_test.go similarity index 69% rename from flytectl/cmd/config/subcommand/workflowconfig_flags_test.go rename to flytectl/cmd/config/subcommand/workflow/config_flags_test.go index 105f884def..e3d3eae412 100755 --- a/flytectl/cmd/config/subcommand/workflowconfig_flags_test.go +++ b/flytectl/cmd/config/subcommand/workflow/config_flags_test.go @@ -1,7 +1,7 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots. -package subcommand +package workflow import ( "encoding/json" @@ -14,22 +14,22 @@ import ( "github.com/stretchr/testify/assert" ) -var dereferencableKindsWorkflowConfig = map[reflect.Kind]struct{}{ +var dereferencableKindsConfig = 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] +func canGetElementConfig(t reflect.Kind) bool { + _, exists := dereferencableKindsConfig[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) { +func jsonUnmarshalerHookConfig(_, 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)) { + (canGetElementConfig(to.Kind()) && to.Elem().Implements(unmarshalerType)) { raw, err := json.Marshal(data) if err != nil { @@ -50,7 +50,7 @@ func jsonUnmarshalerHookWorkflowConfig(_, to reflect.Type, data interface{}) (in return data, nil } -func decode_WorkflowConfig(input, result interface{}) error { +func decode_Config(input, result interface{}) error { config := &mapstructure.DecoderConfig{ TagName: "json", WeaklyTypedInput: true, @@ -58,7 +58,7 @@ func decode_WorkflowConfig(input, result interface{}) error { DecodeHook: mapstructure.ComposeDecodeHookFunc( mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), - jsonUnmarshalerHookWorkflowConfig, + jsonUnmarshalerHookConfig, ), } @@ -70,7 +70,7 @@ func decode_WorkflowConfig(input, result interface{}) error { return decoder.Decode(input) } -func join_WorkflowConfig(arr interface{}, sep string) string { +func join_Config(arr interface{}, sep string) string { listValue := reflect.ValueOf(arr) strs := make([]string, 0, listValue.Len()) for i := 0; i < listValue.Len(); i++ { @@ -80,22 +80,22 @@ func join_WorkflowConfig(arr interface{}, sep string) string { return strings.Join(strs, sep) } -func testDecodeJson_WorkflowConfig(t *testing.T, val, result interface{}) { - assert.NoError(t, decode_WorkflowConfig(val, result)) +func testDecodeJson_Config(t *testing.T, val, result interface{}) { + assert.NoError(t, decode_Config(val, result)) } -func testDecodeSlice_WorkflowConfig(t *testing.T, vStringSlice, result interface{}) { - assert.NoError(t, decode_WorkflowConfig(vStringSlice, result)) +func testDecodeSlice_Config(t *testing.T, vStringSlice, result interface{}) { + assert.NoError(t, decode_Config(vStringSlice, result)) } -func TestWorkflowConfig_GetPFlagSet(t *testing.T) { - val := WorkflowConfig{} +func TestConfig_GetPFlagSet(t *testing.T) { + val := Config{} cmdFlags := val.GetPFlagSet("") assert.True(t, cmdFlags.HasFlags()) } -func TestWorkflowConfig_SetFlags(t *testing.T) { - actual := WorkflowConfig{} +func TestConfig_SetFlags(t *testing.T) { + actual := Config{} cmdFlags := actual.GetPFlagSet("") assert.True(t, cmdFlags.HasFlags()) @@ -103,7 +103,7 @@ func TestWorkflowConfig_SetFlags(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) + assert.Equal(t, string(DefaultConfig.Version), vString) } else { assert.FailNow(t, err.Error()) } @@ -114,7 +114,7 @@ func TestWorkflowConfig_SetFlags(t *testing.T) { cmdFlags.Set("version", testValue) if vString, err := cmdFlags.GetString("version"); err == nil { - testDecodeJson_WorkflowConfig(t, fmt.Sprintf("%v", vString), &actual.Version) + testDecodeJson_Config(t, fmt.Sprintf("%v", vString), &actual.Version) } else { assert.FailNow(t, err.Error()) @@ -125,7 +125,7 @@ func TestWorkflowConfig_SetFlags(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) + assert.Equal(t, bool(DefaultConfig.Latest), vBool) } else { assert.FailNow(t, err.Error()) } @@ -136,7 +136,7 @@ func TestWorkflowConfig_SetFlags(t *testing.T) { cmdFlags.Set("latest", testValue) if vBool, err := cmdFlags.GetBool("latest"); err == nil { - testDecodeJson_WorkflowConfig(t, fmt.Sprintf("%v", vBool), &actual.Latest) + testDecodeJson_Config(t, fmt.Sprintf("%v", vBool), &actual.Latest) } else { assert.FailNow(t, err.Error()) diff --git a/flytectl/cmd/config/subcommand/workflow_config.go b/flytectl/cmd/config/subcommand/workflow/workflow_config.go similarity index 51% rename from flytectl/cmd/config/subcommand/workflow_config.go rename to flytectl/cmd/config/subcommand/workflow/workflow_config.go index b3c6452a62..d4890f84c2 100644 --- a/flytectl/cmd/config/subcommand/workflow_config.go +++ b/flytectl/cmd/config/subcommand/workflow/workflow_config.go @@ -1,13 +1,13 @@ -package subcommand +package workflow -//go:generate pflags WorkflowConfig --default-var DefaultWorklfowConfig +//go:generate pflags Config --default-var DefaultConfig var ( - DefaultWorklfowConfig = &WorkflowConfig{} + DefaultConfig = &Config{} ) -// WorkflowConfig commandline configuration -type WorkflowConfig struct { +// Config commandline configuration +type Config 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/delete/delete.go b/flytectl/cmd/delete/delete.go index 2c694db819..fe518f9ca0 100644 --- a/flytectl/cmd/delete/delete.go +++ b/flytectl/cmd/delete/delete.go @@ -1,7 +1,8 @@ package delete import ( - "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/clusterresourceattribute" + "github.com/flyteorg/flytectl/cmd/config/subcommand/taskresourceattribute" cmdcore "github.com/flyteorg/flytectl/cmd/core" "github.com/spf13/cobra" @@ -30,7 +31,10 @@ func RemoteDeleteCommand() *cobra.Command { Long: execCmdLong}, "task-resource-attribute": {CmdFunc: deleteTaskResourceAttributes, Aliases: []string{"task-resource-attributes"}, Short: taskResourceAttributesShort, - Long: taskResourceAttributesLong, PFlagProvider: subcommand.DefaultTaskResourceDelConfig, ProjectDomainNotRequired: true}, + Long: taskResourceAttributesLong, PFlagProvider: taskresourceattribute.DefaultDelConfig, ProjectDomainNotRequired: true}, + "cluster-resource-attribute": {CmdFunc: deleteClusterResourceAttributes, Aliases: []string{"cluster-resource-attributes"}, + Short: clusterResourceAttributesShort, + Long: clusterResourceAttributesLong, PFlagProvider: clusterresourceattribute.DefaultDelConfig, ProjectDomainNotRequired: true}, } cmdcore.AddCommands(deleteCmd, terminateResourcesFuncs) return deleteCmd diff --git a/flytectl/cmd/delete/delete_test.go b/flytectl/cmd/delete/delete_test.go index dd3075edac..52ea0e9090 100644 --- a/flytectl/cmd/delete/delete_test.go +++ b/flytectl/cmd/delete/delete_test.go @@ -26,18 +26,20 @@ func TestDeleteCommand(t *testing.T) { assert.Equal(t, deleteCommand.Use, "delete") assert.Equal(t, deleteCommand.Short, deleteCmdShort) assert.Equal(t, deleteCommand.Long, deleteCmdLong) - assert.Equal(t, len(deleteCommand.Commands()), 2) + assert.Equal(t, len(deleteCommand.Commands()), 3) cmdNouns := deleteCommand.Commands() // Sort by Use value. sort.Slice(cmdNouns, func(i, j int) bool { return cmdNouns[i].Use < cmdNouns[j].Use }) - assert.Equal(t, cmdNouns[0].Use, "execution") - assert.Equal(t, cmdNouns[0].Aliases, []string{"executions"}) - assert.Equal(t, cmdNouns[0].Short, execCmdShort) - assert.Equal(t, cmdNouns[0].Long, execCmdLong) - assert.Equal(t, cmdNouns[1].Use, "task-resource-attribute") - assert.Equal(t, cmdNouns[1].Aliases, []string{"task-resource-attributes"}) - assert.Equal(t, cmdNouns[1].Short, taskResourceAttributesShort) - assert.Equal(t, cmdNouns[1].Long, taskResourceAttributesLong) + useArray := []string{"cluster-resource-attribute", "execution", "task-resource-attribute"} + aliases := [][]string{{"cluster-resource-attributes"}, {"executions"}, {"task-resource-attributes"}} + shortArray := []string{clusterResourceAttributesShort, execCmdShort, taskResourceAttributesShort} + longArray := []string{clusterResourceAttributesLong, execCmdLong, taskResourceAttributesLong} + for i := range cmdNouns { + assert.Equal(t, cmdNouns[i].Use, useArray[i]) + assert.Equal(t, cmdNouns[i].Aliases, aliases[i]) + assert.Equal(t, cmdNouns[i].Short, shortArray[i]) + assert.Equal(t, cmdNouns[i].Long, longArray[i]) + } } diff --git a/flytectl/cmd/delete/matchable_attribute_util.go b/flytectl/cmd/delete/matchable_attribute_util.go new file mode 100644 index 0000000000..6621ae85d8 --- /dev/null +++ b/flytectl/cmd/delete/matchable_attribute_util.go @@ -0,0 +1,29 @@ +package delete + +import ( + "context" + + "github.com/flyteorg/flytectl/pkg/ext" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" + "github.com/flyteorg/flytestdlib/logger" +) + +func deleteMatchableAttr(ctx context.Context, project, domain, workflowName string, + deleter ext.AdminDeleterExtInterface, rsType admin.MatchableResource) error { + if len(workflowName) > 0 { + // Delete the workflow attribute from the admin. If the attribute doesn't exist , admin deesn't return an error and same behavior is followed here + err := deleter.DeleteWorkflowAttributes(ctx, project, domain, workflowName, rsType) + if err != nil { + return err + } + logger.Debugf(ctx, "Deleted matchable resources from %v project and domain %v and workflow %v", project, domain, workflowName) + } else { + // Delete the project domain attribute from the admin. If the attribute doesn't exist , admin deesn't return an error and same behavior is followed here + err := deleter.DeleteProjectDomainAttributes(ctx, project, domain, rsType) + if err != nil { + return err + } + logger.Debugf(ctx, "Deleted matchable resources from %v project and domain %v", project, domain) + } + return nil +} diff --git a/flytectl/cmd/delete/matchable_cluster_resource_attribute.go b/flytectl/cmd/delete/matchable_cluster_resource_attribute.go new file mode 100644 index 0000000000..18dfa582e8 --- /dev/null +++ b/flytectl/cmd/delete/matchable_cluster_resource_attribute.go @@ -0,0 +1,79 @@ +package delete + +import ( + "context" + + "github.com/flyteorg/flytectl/cmd/config" + sconfig "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/clusterresourceattribute" + cmdCore "github.com/flyteorg/flytectl/cmd/core" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" +) + +const ( + clusterResourceAttributesShort = "Deletes matchable resources of cluster attributes" + clusterResourceAttributesLong = ` +Deletes cluster resource attributes for given project and domain combination or additionally with workflow name. + +Deletes cluster resource attribute for project and domain +Here the command delete cluster resource attributes for project flytectldemo and development domain. +:: + + flytectl delete cluster-resource-attribute -p flytectldemo -d development + + +Deleting cluster resource attribute using config file which was used for creating it. +Here the command deletes cluster resource attributes from the config file cra.yaml +eg: content of cra.yaml which will use the project domain and workflow name for deleting the resource + +:: + + flytectl delete cluster-resource-attribute --attrFile cra.yaml + + +.. code-block:: yaml + + domain: development + project: flytectldemo + attributes: + foo: "bar" + buzz: "lightyear" + +Deleting cluster resource attribute for a workflow +Here the command deletes cluster resource attributes for a workflow + +:: + + flytectl delete cluster-resource-attribute -p flytectldemo -d development core.control_flow.run_merge_sort.merge_sort + +Usage +` +) + +func deleteClusterResourceAttributes(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error { + var pwdGetter sconfig.ProjectDomainWorkflowGetter + pwdGetter = sconfig.PDWGetterCommandLine{Config: config.GetConfig(), Args: args} + delConfig := clusterresourceattribute.DefaultDelConfig + + // Get the project domain workflowName from the config file or commandline params + if len(delConfig.AttrFile) > 0 { + // Initialize TaskResourceAttrFileConfig which will be used if delConfig.AttrFile is non empty + // And Reads from the attribute file + pwdGetter = &clusterresourceattribute.AttrFileConfig{} + if err := sconfig.ReadConfigFromFile(pwdGetter, delConfig.AttrFile); err != nil { + return err + } + } + // Use the pwdGetter to initialize the project domain and workflow + project := pwdGetter.GetProject() + domain := pwdGetter.GetDomain() + workflowName := pwdGetter.GetWorkflow() + + // Deletes the matchable attributes using the taskResourceAttrFileConfig + if err := deleteMatchableAttr(ctx, project, domain, workflowName, cmdCtx.AdminDeleterExt(), + admin.MatchableResource_CLUSTER_RESOURCE); err != nil { + return err + } + + return nil +} diff --git a/flytectl/cmd/delete/matchable_cluster_resource_attribute_test.go b/flytectl/cmd/delete/matchable_cluster_resource_attribute_test.go new file mode 100644 index 0000000000..ef84023c45 --- /dev/null +++ b/flytectl/cmd/delete/matchable_cluster_resource_attribute_test.go @@ -0,0 +1,134 @@ +package delete + +import ( + "fmt" + "testing" + + "github.com/flyteorg/flytectl/cmd/config" + "github.com/flyteorg/flytectl/cmd/config/subcommand/clusterresourceattribute" + u "github.com/flyteorg/flytectl/cmd/testutils" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func deleteClusterResourceAttributeSetup() { + ctx = u.Ctx + cmdCtx = u.CmdCtx + mockClient = u.MockClient + clusterresourceattribute.DefaultDelConfig = &clusterresourceattribute.AttrDeleteConfig{} + args = []string{} +} + +func TestDeleteClusterResourceAttributes(t *testing.T) { + t.Run("successful project domain attribute deletion commandline", func(t *testing.T) { + setup() + deleteClusterResourceAttributeSetup() + // Empty attribute file + clusterresourceattribute.DefaultDelConfig.AttrFile = "" + // No args implying project domain attribute deletion + u.DeleterExt.OnDeleteProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(nil) + err = deleteClusterResourceAttributes(ctx, args, cmdCtx) + assert.Nil(t, err) + u.DeleterExt.AssertCalled(t, "DeleteProjectDomainAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_CLUSTER_RESOURCE) + }) + t.Run("failed project domain attribute deletion", func(t *testing.T) { + setup() + deleteClusterResourceAttributeSetup() + // No args implying project domain attribute deletion + u.DeleterExt.OnDeleteProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(fmt.Errorf("failed to delte project domain attributes")) + err = deleteClusterResourceAttributes(ctx, args, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("failed to delte project domain attributes"), err) + u.DeleterExt.AssertCalled(t, "DeleteProjectDomainAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_CLUSTER_RESOURCE) + }) + t.Run("successful project domain attribute deletion file", func(t *testing.T) { + setup() + deleteClusterResourceAttributeSetup() + // Empty attribute file + clusterresourceattribute.DefaultDelConfig.AttrFile = "testdata/valid_project_domain_cluster_attribute.yaml" + // No args implying project domain attribute deletion + u.DeleterExt.OnDeleteProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(nil) + err = deleteClusterResourceAttributes(ctx, args, cmdCtx) + assert.Nil(t, err) + u.DeleterExt.AssertCalled(t, "DeleteProjectDomainAttributes", + ctx, "flytectldemo", "development", admin.MatchableResource_CLUSTER_RESOURCE) + }) + t.Run("successful workflow attribute deletion", func(t *testing.T) { + setup() + deleteClusterResourceAttributeSetup() + // Empty attribute file + clusterresourceattribute.DefaultDelConfig.AttrFile = "" + args := []string{"workflow1"} + u.DeleterExt.OnDeleteWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything, mock.Anything).Return(nil) + err = deleteClusterResourceAttributes(ctx, args, cmdCtx) + assert.Nil(t, err) + u.DeleterExt.AssertCalled(t, "DeleteWorkflowAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, "workflow1", + admin.MatchableResource_CLUSTER_RESOURCE) + }) + t.Run("failed workflow attribute deletion", func(t *testing.T) { + setup() + deleteClusterResourceAttributeSetup() + // Empty attribute file + clusterresourceattribute.DefaultDelConfig.AttrFile = "" + args := []string{"workflow1"} + u.DeleterExt.OnDeleteWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything, mock.Anything).Return(fmt.Errorf("failed to delete workflow attribute")) + err = deleteClusterResourceAttributes(ctx, args, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("failed to delete workflow attribute"), err) + u.DeleterExt.AssertCalled(t, "DeleteWorkflowAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, "workflow1", + admin.MatchableResource_CLUSTER_RESOURCE) + }) + t.Run("successful workflow attribute deletion file", func(t *testing.T) { + setup() + deleteClusterResourceAttributeSetup() + // Empty attribute file + clusterresourceattribute.DefaultDelConfig.AttrFile = "testdata/valid_workflow_cluster_attribute.yaml" + // No args implying project domain attribute deletion + u.DeleterExt.OnDeleteWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything, mock.Anything).Return(nil) + err = deleteClusterResourceAttributes(ctx, args, cmdCtx) + assert.Nil(t, err) + u.DeleterExt.AssertCalled(t, "DeleteWorkflowAttributes", + ctx, "flytectldemo", "development", "core.control_flow.run_merge_sort.merge_sort", + admin.MatchableResource_CLUSTER_RESOURCE) + }) + t.Run("workflow attribute deletion non existent file", func(t *testing.T) { + setup() + deleteClusterResourceAttributeSetup() + // Empty attribute file + clusterresourceattribute.DefaultDelConfig.AttrFile = "testdata/non-existent" + // No args implying project domain attribute deletion + u.DeleterExt.OnDeleteWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything, mock.Anything).Return(nil) + err = deleteClusterResourceAttributes(ctx, args, cmdCtx) + assert.NotNil(t, err) + u.DeleterExt.AssertNotCalled(t, "DeleteWorkflowAttributes", + ctx, "flytectldemo", "development", "core.control_flow.run_merge_sort.merge_sort", + admin.MatchableResource_CLUSTER_RESOURCE) + }) + t.Run("attribute deletion invalid file", func(t *testing.T) { + setup() + deleteClusterResourceAttributeSetup() + // Empty attribute file + clusterresourceattribute.DefaultDelConfig.AttrFile = "testdata/invalid_attribute.yaml" + // No args implying project domain attribute deletion + err = deleteClusterResourceAttributes(ctx, args, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, + fmt.Errorf("error unmarshaling JSON: while decoding JSON: json: unknown field \"InvalidDomain\""), + err) + u.DeleterExt.AssertNotCalled(t, "DeleteProjectDomainAttributes", + ctx, "flytectldemo", "development", admin.MatchableResource_CLUSTER_RESOURCE) + }) +} diff --git a/flytectl/cmd/delete/matchable_task_resource_attribute.go b/flytectl/cmd/delete/matchable_task_resource_attribute.go index 00e8e51148..dbd8293cfe 100644 --- a/flytectl/cmd/delete/matchable_task_resource_attribute.go +++ b/flytectl/cmd/delete/matchable_task_resource_attribute.go @@ -4,10 +4,10 @@ import ( "context" "github.com/flyteorg/flytectl/cmd/config" - "github.com/flyteorg/flytectl/cmd/config/subcommand" + sconfig "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/taskresourceattribute" cmdCore "github.com/flyteorg/flytectl/cmd/core" "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" - "github.com/flyteorg/flytestdlib/logger" ) const ( @@ -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 +Deleting task resource attribute using config file which was used for creating it. 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,15 +33,14 @@ eg: content of tra.yaml which will use the project domain and workflow name for .. code-block:: yaml - Domain: development - Project: flytectldemo - Workflow: "" + domain: development + project: flytectldemo defaults: - cpu: "1" - memory: 150Mi + cpu: "1" + memory: "150Mi" limits: - cpu: "2" - memory: 450Mi + cpu: "2" + memory: "450Mi" Deleting task resource attribute for a workflow Here the command deletes task resource attributes for a workflow @@ -55,44 +54,29 @@ Usage ) func deleteTaskResourceAttributes(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error { - delConfig := subcommand.DefaultTaskResourceDelConfig - var project string - var domain string - var workflowName string + var pwdGetter sconfig.ProjectDomainWorkflowGetter + pwdGetter = sconfig.PDWGetterCommandLine{Config: config.GetConfig(), Args: args} + delConfig := taskresourceattribute.DefaultDelConfig + // Get the project domain workflowName from the config file or commandline params if len(delConfig.AttrFile) > 0 { - // Read the config from the file - taskResourceAttrFileConfig := subcommand.TaskResourceAttrFileConfig{} - if err := taskResourceAttrFileConfig.ReadConfigFromFile(delConfig.AttrFile); err != nil { + // Initialize TaskResourceAttrFileConfig which will be used if delConfig.AttrFile is non empty + // And Reads from the attribute file + pwdGetter = &taskresourceattribute.TaskResourceAttrFileConfig{} + if err := sconfig.ReadConfigFromFile(pwdGetter, delConfig.AttrFile); err != nil { return err } - // Get project domain workflow name from the read file. - project = taskResourceAttrFileConfig.Project - domain = taskResourceAttrFileConfig.Domain - workflowName = taskResourceAttrFileConfig.Workflow - } else { - // Get all the parameters for deletion from the command line - project = config.GetConfig().Project - domain = config.GetConfig().Domain - if len(args) == 1 { - workflowName = args[0] - } } - - if len(workflowName) > 0 { - // Delete the workflow attribute from the admin. If the attribute doesn't exist , admin deesn't return an error and same behavior is followed here - err := cmdCtx.AdminDeleterExt().DeleteWorkflowAttributes(ctx, project, domain, workflowName, admin.MatchableResource_TASK_RESOURCE) - if err != nil { - return err - } - logger.Debugf(ctx, "Deleted task resource attributes from %v project and domain %v and workflow %v", project, domain, workflowName) - } else { - // Delete the project domain attribute from the admin. If the attribute doesn't exist , admin deesn't return an error and same behavior is followed here - err := cmdCtx.AdminDeleterExt().DeleteProjectDomainAttributes(ctx, project, domain, admin.MatchableResource_TASK_RESOURCE) - if err != nil { - return err - } - logger.Debugf(ctx, "Deleted task resource attributes from %v project and domain %v", project, domain) + // Use the pwdGetter to initialize the project domain and workflow + project := pwdGetter.GetProject() + domain := pwdGetter.GetDomain() + workflowName := pwdGetter.GetWorkflow() + + // Deletes the matchable attributes using the taskResourceAttrFileConfig + if err := deleteMatchableAttr(ctx, project, domain, workflowName, cmdCtx.AdminDeleterExt(), + admin.MatchableResource_TASK_RESOURCE); err != nil { + return err } + return nil } diff --git a/flytectl/cmd/delete/matchable_task_resource_attribute_test.go b/flytectl/cmd/delete/matchable_task_resource_attribute_test.go index 211aeb694b..16443ef270 100644 --- a/flytectl/cmd/delete/matchable_task_resource_attribute_test.go +++ b/flytectl/cmd/delete/matchable_task_resource_attribute_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/flyteorg/flytectl/cmd/config" - "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/taskresourceattribute" u "github.com/flyteorg/flytectl/cmd/testutils" "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" @@ -17,7 +17,8 @@ func deleteTaskResourceAttributeSetup() { ctx = u.Ctx cmdCtx = u.CmdCtx mockClient = u.MockClient - subcommand.DefaultTaskResourceDelConfig = &subcommand.TaskResourceAttrDeleteConfig{} + taskresourceattribute.DefaultDelConfig = &taskresourceattribute.AttrDeleteConfig{} + args = []string{} } func TestDeleteTaskResourceAttributes(t *testing.T) { @@ -25,7 +26,7 @@ func TestDeleteTaskResourceAttributes(t *testing.T) { setup() deleteTaskResourceAttributeSetup() // Empty attribute file - subcommand.DefaultTaskResourceDelConfig.AttrFile = "" + taskresourceattribute.DefaultDelConfig.AttrFile = "" // No args implying project domain attribute deletion u.DeleterExt.OnDeleteProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) @@ -50,7 +51,7 @@ func TestDeleteTaskResourceAttributes(t *testing.T) { setup() deleteTaskResourceAttributeSetup() // Empty attribute file - subcommand.DefaultTaskResourceDelConfig.AttrFile = "testdata/valid_project_domain_task_attribute.yaml" + taskresourceattribute.DefaultDelConfig.AttrFile = "testdata/valid_project_domain_task_attribute.yaml" // No args implying project domain attribute deletion u.DeleterExt.OnDeleteProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) @@ -63,7 +64,7 @@ func TestDeleteTaskResourceAttributes(t *testing.T) { setup() deleteTaskResourceAttributeSetup() // Empty attribute file - subcommand.DefaultTaskResourceDelConfig.AttrFile = "" + taskresourceattribute.DefaultDelConfig.AttrFile = "" args := []string{"workflow1"} u.DeleterExt.OnDeleteWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) @@ -77,7 +78,7 @@ func TestDeleteTaskResourceAttributes(t *testing.T) { setup() deleteTaskResourceAttributeSetup() // Empty attribute file - subcommand.DefaultTaskResourceDelConfig.AttrFile = "" + taskresourceattribute.DefaultDelConfig.AttrFile = "" args := []string{"workflow1"} u.DeleterExt.OnDeleteWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("failed to delete workflow attribute")) @@ -92,7 +93,7 @@ func TestDeleteTaskResourceAttributes(t *testing.T) { setup() deleteTaskResourceAttributeSetup() // Empty attribute file - subcommand.DefaultTaskResourceDelConfig.AttrFile = "testdata/valid_workflow_task_attribute.yaml" + taskresourceattribute.DefaultDelConfig.AttrFile = "testdata/valid_workflow_task_attribute.yaml" // No args implying project domain attribute deletion u.DeleterExt.OnDeleteWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) @@ -106,7 +107,7 @@ func TestDeleteTaskResourceAttributes(t *testing.T) { setup() deleteTaskResourceAttributeSetup() // Empty attribute file - subcommand.DefaultTaskResourceDelConfig.AttrFile = "testdata/non-existent" + taskresourceattribute.DefaultDelConfig.AttrFile = "testdata/non-existent" // No args implying project domain attribute deletion u.DeleterExt.OnDeleteWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) @@ -120,7 +121,7 @@ func TestDeleteTaskResourceAttributes(t *testing.T) { setup() deleteTaskResourceAttributeSetup() // Empty attribute file - subcommand.DefaultTaskResourceDelConfig.AttrFile = "testdata/invalid_task_attribute.yaml" + taskresourceattribute.DefaultDelConfig.AttrFile = "testdata/invalid_attribute.yaml" // No args implying project domain attribute deletion err = deleteTaskResourceAttributes(ctx, args, cmdCtx) assert.NotNil(t, err) diff --git a/flytectl/cmd/delete/testdata/invalid_task_attribute.yaml b/flytectl/cmd/delete/testdata/invalid_attribute.yaml similarity index 100% rename from flytectl/cmd/delete/testdata/invalid_task_attribute.yaml rename to flytectl/cmd/delete/testdata/invalid_attribute.yaml diff --git a/flytectl/cmd/delete/testdata/valid_project_domain_cluster_attribute.yaml b/flytectl/cmd/delete/testdata/valid_project_domain_cluster_attribute.yaml new file mode 100644 index 0000000000..c6c25fa904 --- /dev/null +++ b/flytectl/cmd/delete/testdata/valid_project_domain_cluster_attribute.yaml @@ -0,0 +1,5 @@ +domain: development +project: flytectldemo +attributes: + "foo": "bar" + "buzz": "lightyear" \ No newline at end of file diff --git a/flytectl/cmd/delete/testdata/valid_project_domain_task_attribute.yaml b/flytectl/cmd/delete/testdata/valid_project_domain_task_attribute.yaml index 0051a4c2cc..a16186c80b 100644 --- a/flytectl/cmd/delete/testdata/valid_project_domain_task_attribute.yaml +++ b/flytectl/cmd/delete/testdata/valid_project_domain_task_attribute.yaml @@ -1,6 +1,5 @@ -Domain: development -Project: flytectldemo -Workflow: "" +domain: development +project: flytectldemo defaults: cpu: "1" memory: 150Mi diff --git a/flytectl/cmd/delete/testdata/valid_workflow_cluster_attribute.yaml b/flytectl/cmd/delete/testdata/valid_workflow_cluster_attribute.yaml new file mode 100644 index 0000000000..f8e7b17f65 --- /dev/null +++ b/flytectl/cmd/delete/testdata/valid_workflow_cluster_attribute.yaml @@ -0,0 +1,6 @@ +Domain: development +Project: flytectldemo +Workflow: core.control_flow.run_merge_sort.merge_sort +attributes: + "foo": "bar" + "buzz": "lightyear" \ No newline at end of file diff --git a/flytectl/cmd/delete/testdata/valid_workflow_task_attribute.yaml b/flytectl/cmd/delete/testdata/valid_workflow_task_attribute.yaml index 2593cad309..3b8d08cc79 100644 --- a/flytectl/cmd/delete/testdata/valid_workflow_task_attribute.yaml +++ b/flytectl/cmd/delete/testdata/valid_workflow_task_attribute.yaml @@ -1,6 +1,6 @@ -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: "2" memory: 250Mi diff --git a/flytectl/cmd/get/get.go b/flytectl/cmd/get/get.go index f5b414013f..a03e4931b3 100644 --- a/flytectl/cmd/get/get.go +++ b/flytectl/cmd/get/get.go @@ -1,7 +1,9 @@ package get import ( - "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/clusterresourceattribute" + "github.com/flyteorg/flytectl/cmd/config/subcommand/taskresourceattribute" + "github.com/flyteorg/flytectl/cmd/config/subcommand/workflow" cmdcore "github.com/flyteorg/flytectl/cmd/core" "github.com/spf13/cobra" @@ -33,14 +35,17 @@ 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, PFlagProvider: subcommand.DefaultWorklfowConfig}, + Long: workflowLong, PFlagProvider: workflow.DefaultConfig}, "launchplan": {CmdFunc: getLaunchPlanFunc, Aliases: []string{"launchplans"}, Short: launchPlanShort, Long: launchPlanLong, PFlagProvider: launchPlanConfig}, "execution": {CmdFunc: getExecutionFunc, Aliases: []string{"executions"}, Short: executionShort, Long: executionLong}, "task-resource-attribute": {CmdFunc: getTaskResourceAttributes, Aliases: []string{"task-resource-attributes"}, Short: taskResourceAttributesShort, - Long: taskResourceAttributesLong, PFlagProvider: subcommand.DefaultTaskResourceFetchConfig}, + Long: taskResourceAttributesLong, PFlagProvider: taskresourceattribute.DefaultFetchConfig}, + "cluster-resource-attribute": {CmdFunc: getClusterResourceAttributes, Aliases: []string{"cluster-resource-attributes"}, + Short: clusterResourceAttributesShort, + Long: clusterResourceAttributesLong, PFlagProvider: clusterresourceattribute.DefaultFetchConfig}, } cmdcore.AddCommands(getCmd, getResourcesFuncs) diff --git a/flytectl/cmd/get/get_test.go b/flytectl/cmd/get/get_test.go index e277ba1624..1d0bebe97b 100644 --- a/flytectl/cmd/get/get_test.go +++ b/flytectl/cmd/get/get_test.go @@ -37,29 +37,24 @@ func TestCreateGetCommand(t *testing.T) { assert.Equal(t, getCommand.Use, "get") assert.Equal(t, getCommand.Short, "Used for fetching various flyte resources including tasks/workflows/launchplans/executions/project.") fmt.Println(getCommand.Commands()) - assert.Equal(t, len(getCommand.Commands()), 6) + assert.Equal(t, len(getCommand.Commands()), 7) cmdNouns := getCommand.Commands() // Sort by Use value. sort.Slice(cmdNouns, func(i, j int) bool { return cmdNouns[i].Use < cmdNouns[j].Use }) - assert.Equal(t, cmdNouns[0].Use, "execution") - assert.Equal(t, cmdNouns[0].Aliases, []string{"executions"}) - assert.Equal(t, cmdNouns[0].Short, "Gets execution resources") - assert.Equal(t, cmdNouns[1].Use, "launchplan") - assert.Equal(t, cmdNouns[1].Aliases, []string{"launchplans"}) - assert.Equal(t, cmdNouns[1].Short, "Gets launch plan resources") - assert.Equal(t, cmdNouns[2].Use, "project") - assert.Equal(t, cmdNouns[2].Aliases, []string{"projects"}) - assert.Equal(t, cmdNouns[2].Short, "Gets project resources") - assert.Equal(t, cmdNouns[3].Use, "task") - assert.Equal(t, cmdNouns[3].Aliases, []string{"tasks"}) - assert.Equal(t, cmdNouns[3].Short, "Gets task resources") - assert.Equal(t, cmdNouns[4].Use, "task-resource-attribute") - assert.Equal(t, cmdNouns[4].Aliases, []string{"task-resource-attributes"}) - assert.Equal(t, cmdNouns[4].Short, taskResourceAttributesShort) - assert.Equal(t, cmdNouns[4].Long, taskResourceAttributesLong) - assert.Equal(t, cmdNouns[5].Use, "workflow") - assert.Equal(t, cmdNouns[5].Aliases, []string{"workflows"}) - assert.Equal(t, cmdNouns[5].Short, "Gets workflow resources") + useArray := []string{"cluster-resource-attribute", "execution", "launchplan", "project", + "task", "task-resource-attribute", "workflow"} + aliases := [][]string{{"cluster-resource-attributes"}, {"executions"}, {"launchplans"}, {"projects"}, {"tasks"}, + {"task-resource-attributes"}, {"workflows"}} + shortArray := []string{clusterResourceAttributesShort, executionShort, launchPlanShort, projectShort, + taskShort, taskResourceAttributesShort, workflowShort} + longArray := []string{clusterResourceAttributesLong, executionLong, launchPlanLong, projectLong, taskLong, + taskResourceAttributesLong, workflowLong} + for i := range cmdNouns { + assert.Equal(t, cmdNouns[i].Use, useArray[i]) + assert.Equal(t, cmdNouns[i].Aliases, aliases[i]) + assert.Equal(t, cmdNouns[i].Short, shortArray[i]) + assert.Equal(t, cmdNouns[i].Long, longArray[i]) + } } diff --git a/flytectl/cmd/get/matchable_attribute_util.go b/flytectl/cmd/get/matchable_attribute_util.go new file mode 100644 index 0000000000..cddfdab348 --- /dev/null +++ b/flytectl/cmd/get/matchable_attribute_util.go @@ -0,0 +1,32 @@ +package get + +import ( + "context" + + sconfig "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/pkg/ext" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" +) + +func FetchAndUnDecorateMatchableAttr(ctx context.Context, project, domain, workflowName string, + fetcher ext.AdminFetcherExtInterface, unDecorator sconfig.MatchableAttributeUnDecorator, rsType admin.MatchableResource) error { + if len(workflowName) > 0 { + // Fetch the workflow attribute from the admin + workflowAttr, err := fetcher.FetchWorkflowAttributes(ctx, + project, domain, workflowName, rsType) + if err != nil { + return err + } + // Update the shadow config with the fetched taskResourceAttribute which can then be written to a file which can then be called for an update. + unDecorator.UnDecorate(workflowAttr.GetAttributes().GetMatchingAttributes()) + } else { + // Fetch the project domain attribute from the admin + projectDomainAttr, err := fetcher.FetchProjectDomainAttributes(ctx, project, domain, rsType) + if err != nil { + return err + } + // Update the shadow config with the fetched taskResourceAttribute which can then be written to a file which can then be called for an update. + unDecorator.UnDecorate(projectDomainAttr.GetAttributes().GetMatchingAttributes()) + } + return nil +} diff --git a/flytectl/cmd/get/matchable_cluster_resource_attribute.go b/flytectl/cmd/get/matchable_cluster_resource_attribute.go new file mode 100644 index 0000000000..4ca0934cd4 --- /dev/null +++ b/flytectl/cmd/get/matchable_cluster_resource_attribute.go @@ -0,0 +1,78 @@ +package get + +import ( + "context" + + "github.com/flyteorg/flytectl/cmd/config" + sconfig "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/clusterresourceattribute" + cmdCore "github.com/flyteorg/flytectl/cmd/core" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" +) + +const ( + clusterResourceAttributesShort = "Gets matchable resources of cluster resource attributes" + clusterResourceAttributesLong = ` +Retrieves cluster resource attributes for given project and domain combination or additionally with workflow name. + +Retrieves cluster resource attribute for project and domain +Here the command get cluster resource attributes for project flytectldemo and development domain. +:: + + flytectl get cluster-resource-attribute -p flytectldemo -d development + +eg : O/P + +.. code-block:: json + + {"project":"flytectldemo","domain":"development","attributes":{"buzz":"lightyear","foo":"bar"}} + +Writing the cluster resource attribute to a file. If there are no cluster resource attributes , command would return an error. +Here the command gets task resource attributes and writes the config file to tra.yaml +eg: content of tra.yaml + +:: + + flytectl get task-resource-attribute --attrFile tra.yaml + + +.. code-block:: yaml + + domain: development + project: flytectldemo + attributes: + foo: "bar" + buzz: "lightyear" + +Usage +` +) + +func getClusterResourceAttributes(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error { + var project string + var domain string + var workflowName string + + // Get the project domain workflow name parameters from the command line. Project and domain are mandatory for this command + project = config.GetConfig().Project + domain = config.GetConfig().Domain + if len(args) == 1 { + workflowName = args[0] + } + // Construct a shadow config for ClusterResourceAttribute. The shadow config is not using ProjectDomainAttribute/Workflowattribute directly inorder to simplify the inputs. + clusterResourceAttrFileConfig := clusterresourceattribute.AttrFileConfig{Project: project, Domain: domain, Workflow: workflowName} + // Get the attribute file name from the command line config + fileName := clusterresourceattribute.DefaultFetchConfig.AttrFile + + // Updates the taskResourceAttrFileConfig with the fetched matchable attribute + if err := FetchAndUnDecorateMatchableAttr(ctx, project, domain, workflowName, cmdCtx.AdminFetcherExt(), + &clusterResourceAttrFileConfig, admin.MatchableResource_CLUSTER_RESOURCE); err != nil { + return err + } + + // Write the config to the file which can be used for update + if err := sconfig.DumpTaskResourceAttr(clusterResourceAttrFileConfig, fileName); err != nil { + return err + } + return nil +} diff --git a/flytectl/cmd/get/matchable_cluster_resource_attribute_test.go b/flytectl/cmd/get/matchable_cluster_resource_attribute_test.go new file mode 100644 index 0000000000..dd93934a91 --- /dev/null +++ b/flytectl/cmd/get/matchable_cluster_resource_attribute_test.go @@ -0,0 +1,138 @@ +package get + +import ( + "fmt" + "os" + "testing" + + "github.com/flyteorg/flytectl/cmd/config" + "github.com/flyteorg/flytectl/cmd/config/subcommand/clusterresourceattribute" + u "github.com/flyteorg/flytectl/cmd/testutils" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func getClusterResourceAttributeSetup() { + ctx = u.Ctx + cmdCtx = u.CmdCtx + mockClient = u.MockClient + clusterresourceattribute.DefaultFetchConfig = &clusterresourceattribute.AttrFetchConfig{} + // Clean up the temp directory. + _ = os.Remove("temp-output-file") +} + +func TestGetClusterResourceAttributes(t *testing.T) { + clusterResourceAttr := &admin.ClusterResourceAttributes{ + Attributes: map[string]string{"foo": "bar"}, + } + projectDomainResp := &admin.ProjectDomainAttributesGetResponse{ + Attributes: &admin.ProjectDomainAttributes{ + Project: config.GetConfig().Project, + Domain: config.GetConfig().Domain, + MatchingAttributes: &admin.MatchingAttributes{ + Target: &admin.MatchingAttributes_ClusterResourceAttributes{ + ClusterResourceAttributes: clusterResourceAttr, + }, + }, + }, + } + workflowResp := &admin.WorkflowAttributesGetResponse{ + Attributes: &admin.WorkflowAttributes{ + Project: config.GetConfig().Project, + Domain: config.GetConfig().Domain, + Workflow: "workflow", + MatchingAttributes: &admin.MatchingAttributes{ + Target: &admin.MatchingAttributes_ClusterResourceAttributes{ + ClusterResourceAttributes: clusterResourceAttr, + }, + }, + }, + } + t.Run("successful get project domain attribute", func(t *testing.T) { + var args []string + setup() + getClusterResourceAttributeSetup() + // No args implying project domain attribute deletion + u.FetcherExt.OnFetchProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(projectDomainResp, nil) + err = getClusterResourceAttributes(ctx, args, cmdCtx) + assert.Nil(t, err) + u.FetcherExt.AssertCalled(t, "FetchProjectDomainAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_CLUSTER_RESOURCE) + tearDownAndVerify(t, `{"project":"dummyProject","domain":"dummyDomain","attributes":{"foo":"bar"}}`) + }) + t.Run("successful get project domain attribute and write to file", func(t *testing.T) { + var args []string + setup() + getClusterResourceAttributeSetup() + clusterresourceattribute.DefaultFetchConfig.AttrFile = "temp-output-file" + // No args implying project domain attribute deletion + u.FetcherExt.OnFetchProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(projectDomainResp, nil) + err = getClusterResourceAttributes(ctx, args, cmdCtx) + assert.Nil(t, err) + u.FetcherExt.AssertCalled(t, "FetchProjectDomainAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_CLUSTER_RESOURCE) + tearDownAndVerify(t, `wrote 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 + setup() + getClusterResourceAttributeSetup() + clusterresourceattribute.DefaultFetchConfig.AttrFile = "non-existent-dir/temp-output-file" + // No args implying project domain attribute deletion + u.FetcherExt.OnFetchProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(projectDomainResp, nil) + err = getClusterResourceAttributes(ctx, args, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("error dumping in file due to open non-existent-dir/temp-output-file: no such file or directory"), err) + u.FetcherExt.AssertCalled(t, "FetchProjectDomainAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_CLUSTER_RESOURCE) + tearDownAndVerify(t, ``) + }) + t.Run("failed get project domain attribute", func(t *testing.T) { + var args []string + setup() + getClusterResourceAttributeSetup() + // No args implying project domain attribute deletion + u.FetcherExt.OnFetchProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(nil, fmt.Errorf("failed to fetch response")) + err = getClusterResourceAttributes(ctx, args, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("failed to fetch response"), err) + u.FetcherExt.AssertCalled(t, "FetchProjectDomainAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_CLUSTER_RESOURCE) + tearDownAndVerify(t, ``) + }) + t.Run("successful get workflow attribute", func(t *testing.T) { + var args []string + setup() + getClusterResourceAttributeSetup() + args = []string{"workflow"} + u.FetcherExt.OnFetchWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything, mock.Anything).Return(workflowResp, nil) + err = getClusterResourceAttributes(ctx, args, cmdCtx) + assert.Nil(t, err) + u.FetcherExt.AssertCalled(t, "FetchWorkflowAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, "workflow", + admin.MatchableResource_CLUSTER_RESOURCE) + tearDownAndVerify(t, `{"project":"dummyProject","domain":"dummyDomain","workflow":"workflow","attributes":{"foo":"bar"}}`) + }) + t.Run("failed get workflow attribute", func(t *testing.T) { + var args []string + setup() + getClusterResourceAttributeSetup() + args = []string{"workflow"} + u.FetcherExt.OnFetchWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything, mock.Anything).Return(nil, fmt.Errorf("failed to fetch response")) + err = getClusterResourceAttributes(ctx, args, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("failed to fetch response"), err) + u.FetcherExt.AssertCalled(t, "FetchWorkflowAttributes", + ctx, config.GetConfig().Project, config.GetConfig().Domain, "workflow", + admin.MatchableResource_CLUSTER_RESOURCE) + tearDownAndVerify(t, ``) + }) +} diff --git a/flytectl/cmd/get/matchable_task_resource_attribute.go b/flytectl/cmd/get/matchable_task_resource_attribute.go index bfa68d6edc..e27367e8a2 100644 --- a/flytectl/cmd/get/matchable_task_resource_attribute.go +++ b/flytectl/cmd/get/matchable_task_resource_attribute.go @@ -2,10 +2,10 @@ package get import ( "context" - "fmt" "github.com/flyteorg/flytectl/cmd/config" - "github.com/flyteorg/flytectl/cmd/config/subcommand" + sconfig "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/taskresourceattribute" cmdCore "github.com/flyteorg/flytectl/cmd/core" "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" ) @@ -38,15 +38,14 @@ eg: content of tra.yaml .. code-block:: yaml - Domain: development - Project: flytectldemo - Workflow: "" + domain: development + project: flytectldemo defaults: - cpu: "1" - memory: 150Mi + cpu: "1" + memory: "150Mi" limits: - cpu: "2" - memory: 450Mi + cpu: "2" + memory: "450Mi" Usage ` @@ -64,38 +63,19 @@ func getTaskResourceAttributes(ctx context.Context, args []string, cmdCtx cmdCor workflowName = args[0] } // Construct a shadow config for TaskResourceAttribute. The shadow config is not using ProjectDomainAttribute/Workflowattribute directly inorder to simplify the inputs. - taskResourceAttrFileConfig := subcommand.TaskResourceAttrFileConfig{Project: project, Domain: domain, Workflow: workflowName} + taskResourceAttrFileConfig := taskresourceattribute.TaskResourceAttrFileConfig{Project: project, Domain: domain, Workflow: workflowName} // Get the attribute file name from the command line config - fileName := subcommand.DefaultTaskResourceFetchConfig.AttrFile - - if len(workflowName) > 0 { - // Fetch the workflow attribute from the admin - 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("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() - } else { - // Fetch the project domain attribute from the admin - 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("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() + fileName := taskresourceattribute.DefaultFetchConfig.AttrFile + + // Updates the taskResourceAttrFileConfig with the fetched matchable attribute + if err := FetchAndUnDecorateMatchableAttr(ctx, project, domain, workflowName, cmdCtx.AdminFetcherExt(), + &taskResourceAttrFileConfig, admin.MatchableResource_TASK_RESOURCE); err != nil { + return err } + // Write the config to the file which can be used for update - taskResourceAttrFileConfig.DumpTaskResourceAttr(ctx, fileName) + if err := sconfig.DumpTaskResourceAttr(taskResourceAttrFileConfig, fileName); err != nil { + return err + } return nil } diff --git a/flytectl/cmd/get/matchable_task_resource_attribute_test.go b/flytectl/cmd/get/matchable_task_resource_attribute_test.go index 22d82df983..2477661d70 100644 --- a/flytectl/cmd/get/matchable_task_resource_attribute_test.go +++ b/flytectl/cmd/get/matchable_task_resource_attribute_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/flyteorg/flytectl/cmd/config" - "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/taskresourceattribute" u "github.com/flyteorg/flytectl/cmd/testutils" "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" @@ -18,7 +18,7 @@ func getTaskResourceAttributeSetup() { ctx = u.Ctx cmdCtx = u.CmdCtx mockClient = u.MockClient - subcommand.DefaultTaskResourceFetchConfig = &subcommand.TaskResourceAttrFetchConfig{} + taskresourceattribute.DefaultFetchConfig = &taskresourceattribute.AttrFetchConfig{} // Clean up the temp directory. _ = os.Remove("temp-output-file") } @@ -68,13 +68,13 @@ 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","Workflow":"","defaults":{"cpu":"1","memory":"150Mi"},"limits":{"cpu":"2","memory":"350Mi"}}`) + tearDownAndVerify(t, `{"project":"dummyProject","domain":"dummyDomain","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 setup() getTaskResourceAttributeSetup() - subcommand.DefaultTaskResourceFetchConfig.AttrFile = "temp-output-file" + taskresourceattribute.DefaultFetchConfig.AttrFile = "temp-output-file" // No args implying project domain attribute deletion u.FetcherExt.OnFetchProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(projectDomainResp, nil) @@ -82,21 +82,22 @@ 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, `written the config to file temp-output-file`) + tearDownAndVerify(t, `wrote 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 setup() getTaskResourceAttributeSetup() - subcommand.DefaultTaskResourceFetchConfig.AttrFile = "non-existent-dir/temp-output-file" + taskresourceattribute.DefaultFetchConfig.AttrFile = "non-existent-dir/temp-output-file" // No args implying project domain attribute deletion u.FetcherExt.OnFetchProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(projectDomainResp, nil) err = getTaskResourceAttributes(ctx, args, cmdCtx) - assert.Nil(t, err) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("error dumping in file due to open non-existent-dir/temp-output-file: no such file or directory"), err) u.FetcherExt.AssertCalled(t, "FetchProjectDomainAttributes", ctx, config.GetConfig().Project, config.GetConfig().Domain, admin.MatchableResource_TASK_RESOURCE) - tearDownAndVerify(t, `error dumping in file due to open non-existent-dir/temp-output-file: no such file or directory`) + tearDownAndVerify(t, ``) }) t.Run("failed get project domain attribute", func(t *testing.T) { var args []string @@ -110,7 +111,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, `{"Project":"dummyProject","Domain":"dummyDomain","Workflow":""}`) + tearDownAndVerify(t, ``) }) t.Run("successful get workflow attribute", func(t *testing.T) { var args []string @@ -124,7 +125,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 +140,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, `{"Project":"dummyProject","Domain":"dummyDomain","Workflow":"workflow"}`) + tearDownAndVerify(t, ``) }) } diff --git a/flytectl/cmd/get/workflow.go b/flytectl/cmd/get/workflow.go index 3640a5f342..4de25ace61 100644 --- a/flytectl/cmd/get/workflow.go +++ b/flytectl/cmd/get/workflow.go @@ -3,7 +3,7 @@ package get import ( "context" - "github.com/flyteorg/flytectl/cmd/config/subcommand" + workflowconfig "github.com/flyteorg/flytectl/cmd/config/subcommand/workflow" "github.com/flyteorg/flytectl/pkg/ext" "github.com/flyteorg/flytestdlib/logger" "github.com/golang/protobuf/proto" @@ -108,13 +108,13 @@ func FetchWorkflowForName(ctx context.Context, fetcher ext.AdminFetcherExtInterf var workflows []*admin.Workflow var workflow *admin.Workflow var err error - if subcommand.DefaultWorklfowConfig.Latest { + if workflowconfig.DefaultConfig.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 { + } else if workflowconfig.DefaultConfig.Version != "" { + if workflow, err = fetcher.FetchWorkflowVersion(ctx, name, workflowconfig.DefaultConfig.Version, project, domain); err != nil { return nil, err } workflows = append(workflows, workflow) diff --git a/flytectl/cmd/get/workflow_test.go b/flytectl/cmd/get/workflow_test.go index 93928eebc4..bb9f88d356 100644 --- a/flytectl/cmd/get/workflow_test.go +++ b/flytectl/cmd/get/workflow_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/workflow" u "github.com/flyteorg/flytectl/cmd/testutils" "github.com/flyteorg/flytectl/pkg/ext/mocks" "github.com/stretchr/testify/assert" @@ -15,8 +15,8 @@ func getWorkflowSetup() { ctx = u.Ctx mockClient = u.MockClient cmdCtx = u.CmdCtx - subcommand.DefaultWorklfowConfig.Latest = false - subcommand.DefaultWorklfowConfig.Version = "" + workflow.DefaultConfig.Latest = false + workflow.DefaultConfig.Version = "" } func TestGetWorkflowFuncWithError(t *testing.T) { @@ -24,7 +24,7 @@ func TestGetWorkflowFuncWithError(t *testing.T) { setup() getWorkflowSetup() mockFetcher := new(mocks.AdminFetcherExtInterface) - subcommand.DefaultWorklfowConfig.Latest = true + workflow.DefaultConfig.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) @@ -35,7 +35,7 @@ func TestGetWorkflowFuncWithError(t *testing.T) { setup() getWorkflowSetup() mockFetcher := new(mocks.AdminFetcherExtInterface) - subcommand.DefaultWorklfowConfig.Version = "v1" + workflow.DefaultConfig.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) @@ -55,7 +55,7 @@ func TestGetWorkflowFuncWithError(t *testing.T) { t.Run("failure fetching ", func(t *testing.T) { setup() getWorkflowSetup() - subcommand.DefaultWorklfowConfig.Latest = true + workflow.DefaultConfig.Latest = true args := []string{"workflowName"} u.FetcherExt.OnFetchWorkflowLatestVersionMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("error fetching latest version")) diff --git a/flytectl/cmd/update/matchable_attribute_util.go b/flytectl/cmd/update/matchable_attribute_util.go new file mode 100644 index 0000000000..8717789574 --- /dev/null +++ b/flytectl/cmd/update/matchable_attribute_util.go @@ -0,0 +1,30 @@ +package update + +import ( + "context" + + sconfig "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/pkg/ext" + "github.com/flyteorg/flytestdlib/logger" +) + +func DecorateAndUpdateMatchableAttr(ctx context.Context, project, domain, workflowName string, + updater ext.AdminUpdaterExtInterface, mcDecorator sconfig.MatchableAttributeDecorator) error { + matchingAttr := mcDecorator.Decorate() + if len(workflowName) > 0 { + // Update the workflow attribute using the admin. + err := updater.UpdateWorkflowAttributes(ctx, project, domain, workflowName, matchingAttr) + if err != nil { + return err + } + logger.Debugf(ctx, "Updated attributes from %v project and domain %v and workflow %v", project, domain, workflowName) + } else { + // Update the project domain attribute using the admin. + err := updater.UpdateProjectDomainAttributes(ctx, project, domain, matchingAttr) + if err != nil { + return err + } + logger.Debugf(ctx, "Updated attributes from %v project and domain %v", project, domain) + } + return nil +} diff --git a/flytectl/cmd/update/matchable_cluster_resource_attribute.go b/flytectl/cmd/update/matchable_cluster_resource_attribute.go new file mode 100644 index 0000000000..ff0b42c9ef --- /dev/null +++ b/flytectl/cmd/update/matchable_cluster_resource_attribute.go @@ -0,0 +1,76 @@ +package update + +import ( + "context" + "fmt" + + sconfig "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/clusterresourceattribute" + cmdCore "github.com/flyteorg/flytectl/cmd/core" +) + +const ( + clusterResourceAttributesShort = "Updates matchable resources of cluster attributes" + clusterResourceAttributesLong = ` +Updates cluster resource attributes for given project and domain combination or additionally with workflow name. + +Updating to the cluster 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 cluster resource attributes from the config file cra.yaml +eg: content of tra.yaml + +.. code-block:: yaml + + domain: development + project: flytectldemo + attributes: + foo: "bar" + buzz: "lightyear" + +:: + + flytectl update cluster-resource-attribute -attrFile cra.yaml + +Updating cluster 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 cluster 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 + attributes: + foo: "bar" + buzz: "lightyear" + +:: + + flytectl update cluster-resource-attribute -attrFile cra.yaml + +Usage + +` +) + +func updateClusterResourceAttributesFunc(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error { + updateConfig := clusterresourceattribute.DefaultUpdateConfig + if len(updateConfig.AttrFile) == 0 { + return fmt.Errorf("attrFile is mandatory while calling update for cluster resource attribute") + } + + clustrResourceAttrFileConfig := clusterresourceattribute.AttrFileConfig{} + if err := sconfig.ReadConfigFromFile(&clustrResourceAttrFileConfig, updateConfig.AttrFile); err != nil { + return err + } + + // Get project domain workflow name from the read file. + project := clustrResourceAttrFileConfig.Project + domain := clustrResourceAttrFileConfig.Domain + workflowName := clustrResourceAttrFileConfig.Workflow + + // Updates the admin matchable attribute from taskResourceAttrFileConfig + if err := DecorateAndUpdateMatchableAttr(ctx, project, domain, workflowName, cmdCtx.AdminUpdaterExt(), + clustrResourceAttrFileConfig); err != nil { + return err + } + return nil +} diff --git a/flytectl/cmd/update/matchable_cluster_resource_attribute_test.go b/flytectl/cmd/update/matchable_cluster_resource_attribute_test.go new file mode 100644 index 0000000000..1819c6cec4 --- /dev/null +++ b/flytectl/cmd/update/matchable_cluster_resource_attribute_test.go @@ -0,0 +1,94 @@ +package update + +import ( + "fmt" + "testing" + + "github.com/flyteorg/flytectl/cmd/config/subcommand/clusterresourceattribute" + u "github.com/flyteorg/flytectl/cmd/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func updateClusterResourceAttributeSetup() { + ctx = u.Ctx + cmdCtx = u.CmdCtx + mockClient = u.MockClient + clusterresourceattribute.DefaultUpdateConfig = &clusterresourceattribute.AttrUpdateConfig{} +} + +func TestUpdateClusterResourceAttributes(t *testing.T) { + t.Run("no input file for update", func(t *testing.T) { + setup() + updateClusterResourceAttributeSetup() + err = updateClusterResourceAttributesFunc(ctx, args, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("attrFile is mandatory while calling update for cluster resource attribute"), err) + tearDownAndVerify(t, ``) + }) + t.Run("successful update project domain attribute", func(t *testing.T) { + setup() + updateClusterResourceAttributeSetup() + clusterresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/valid_project_domain_cluster_attribute.yaml" + // No args implying project domain attribute deletion + u.UpdaterExt.OnUpdateProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(nil) + err = updateClusterResourceAttributesFunc(ctx, args, cmdCtx) + assert.Nil(t, err) + tearDownAndVerify(t, ``) + }) + t.Run("failed update project domain attribute", func(t *testing.T) { + setup() + updateClusterResourceAttributeSetup() + clusterresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/valid_project_domain_cluster_attribute.yaml" + // No args implying project domain attribute deletion + u.UpdaterExt.OnUpdateProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything).Return(fmt.Errorf("failed to update attributes")) + err = updateClusterResourceAttributesFunc(ctx, args, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("failed to update attributes"), err) + tearDownAndVerify(t, ``) + }) + t.Run("successful update workflow attribute", func(t *testing.T) { + setup() + updateClusterResourceAttributeSetup() + clusterresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/valid_workflow_cluster_attribute.yaml" + // No args implying project domain attribute deletion + u.UpdaterExt.OnUpdateWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything, mock.Anything).Return(nil) + err = updateClusterResourceAttributesFunc(ctx, nil, cmdCtx) + assert.Nil(t, err) + tearDownAndVerify(t, ``) + }) + t.Run("failed update workflow attribute", func(t *testing.T) { + setup() + updateClusterResourceAttributeSetup() + clusterresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/valid_workflow_cluster_attribute.yaml" + // No args implying project domain attribute deletion + u.UpdaterExt.OnUpdateWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, + mock.Anything, mock.Anything).Return(fmt.Errorf("failed to update attributes")) + err = updateClusterResourceAttributesFunc(ctx, nil, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("failed to update attributes"), err) + tearDownAndVerify(t, ``) + }) + t.Run("non existent file", func(t *testing.T) { + setup() + updateClusterResourceAttributeSetup() + clusterresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/non-existent-filel" + err = updateClusterResourceAttributesFunc(ctx, nil, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("unable to read from testdata/non-existent-filel yaml file"), err) + tearDownAndVerify(t, ``) + }) + t.Run("invalid update file", func(t *testing.T) { + setup() + updateClusterResourceAttributeSetup() + clusterresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/invalid_attribute.yaml" + err = updateClusterResourceAttributesFunc(ctx, nil, cmdCtx) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("error unmarshaling JSON: while decoding JSON: json: unknown field \"InvalidDomain\""), err) + tearDownAndVerify(t, ``) + }) +} diff --git a/flytectl/cmd/update/matchable_task_resource_attribute.go b/flytectl/cmd/update/matchable_task_resource_attribute.go index 598eb98531..84e3128145 100644 --- a/flytectl/cmd/update/matchable_task_resource_attribute.go +++ b/flytectl/cmd/update/matchable_task_resource_attribute.go @@ -4,15 +4,15 @@ import ( "context" "fmt" - "github.com/flyteorg/flytectl/cmd/config/subcommand" + sconfig "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/taskresourceattribute" cmdCore "github.com/flyteorg/flytectl/cmd/core" - "github.com/flyteorg/flytestdlib/logger" ) const ( taskResourceAttributesShort = "Updates matchable resources of task attributes" taskResourceAttributesLong = ` -Updates task resource attributes for given project,domain combination or additionally with workflow name. +Updates task resource attributes for given project and 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,34 +20,33 @@ eg: content of tra.yaml .. code-block:: yaml - Domain: development - Project: flytectldemo - Workflow: "" + domain: development + project: flytectldemo defaults: - cpu: "1" - memory: 150Mi + cpu: "1" + memory: "150Mi" limits: - cpu: "2" - memory: 450Mi + cpu: "2" + memory: "450Mi" :: 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 + cpu: "1" + memory: "150Mi" limits: - cpu: "2" - memory: 450Mi + cpu: "2" + memory: "450Mi" :: @@ -59,13 +58,13 @@ Usage ) func updateTaskResourceAttributesFunc(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error { - updateConfig := subcommand.DefaultTaskResourceUpdateConfig + updateConfig := taskresourceattribute.DefaultUpdateConfig if len(updateConfig.AttrFile) == 0 { return fmt.Errorf("attrFile is mandatory while calling update for task resource attribute") } - taskResourceAttrFileConfig := subcommand.TaskResourceAttrFileConfig{} - if err := taskResourceAttrFileConfig.ReadConfigFromFile(updateConfig.AttrFile); err != nil { + taskResourceAttrFileConfig := taskresourceattribute.TaskResourceAttrFileConfig{} + if err := sconfig.ReadConfigFromFile(&taskResourceAttrFileConfig, updateConfig.AttrFile); err != nil { return err } @@ -74,23 +73,10 @@ func updateTaskResourceAttributesFunc(ctx context.Context, args []string, cmdCtx domain := taskResourceAttrFileConfig.Domain workflowName := taskResourceAttrFileConfig.Workflow - // decorate the taskresource Attributes with MatchingAttributes - matchingAttr := taskResourceAttrFileConfig.MatchableAttributeDecorator() - - if len(workflowName) > 0 { - // Update the workflow attribute using the admin. - err := cmdCtx.AdminUpdaterExt().UpdateWorkflowAttributes(ctx, project, domain, workflowName, matchingAttr) - if err != nil { - return err - } - logger.Debugf(ctx, "Updated task resource attributes from %v project and domain %v and workflow %v", project, domain, workflowName) - } else { - // Update the project domain attribute using the admin. - err := cmdCtx.AdminUpdaterExt().UpdateProjectDomainAttributes(ctx, project, domain, matchingAttr) - if err != nil { - return err - } - logger.Debugf(ctx, "Updated task resource attributes from %v project and domain %v", project, domain) + // Updates the admin matchable attribute from taskResourceAttrFileConfig + if err := DecorateAndUpdateMatchableAttr(ctx, project, domain, workflowName, cmdCtx.AdminUpdaterExt(), + taskResourceAttrFileConfig); err != nil { + return err } return nil } diff --git a/flytectl/cmd/update/matchable_task_resource_attribute_test.go b/flytectl/cmd/update/matchable_task_resource_attribute_test.go index 4ab7b84cab..75aca56c29 100644 --- a/flytectl/cmd/update/matchable_task_resource_attribute_test.go +++ b/flytectl/cmd/update/matchable_task_resource_attribute_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/taskresourceattribute" u "github.com/flyteorg/flytectl/cmd/testutils" "github.com/stretchr/testify/assert" @@ -15,7 +15,7 @@ func updateTaskResourceAttributeSetup() { ctx = u.Ctx cmdCtx = u.CmdCtx mockClient = u.MockClient - subcommand.DefaultTaskResourceUpdateConfig = &subcommand.TaskResourceAttrUpdateConfig{} + taskresourceattribute.DefaultUpdateConfig = &taskresourceattribute.AttrUpdateConfig{} } func TestUpdateTaskResourceAttributes(t *testing.T) { @@ -30,7 +30,7 @@ func TestUpdateTaskResourceAttributes(t *testing.T) { t.Run("successful update project domain attribute", func(t *testing.T) { setup() updateTaskResourceAttributeSetup() - subcommand.DefaultTaskResourceUpdateConfig.AttrFile = "testdata/valid_project_domain_task_attribute.yaml" + taskresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/valid_project_domain_task_attribute.yaml" // No args implying project domain attribute deletion u.UpdaterExt.OnUpdateProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) @@ -41,7 +41,7 @@ func TestUpdateTaskResourceAttributes(t *testing.T) { t.Run("failed update project domain attribute", func(t *testing.T) { setup() updateTaskResourceAttributeSetup() - subcommand.DefaultTaskResourceUpdateConfig.AttrFile = "testdata/valid_project_domain_task_attribute.yaml" + taskresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/valid_project_domain_task_attribute.yaml" // No args implying project domain attribute deletion u.UpdaterExt.OnUpdateProjectDomainAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("failed to update attributes")) @@ -53,7 +53,7 @@ func TestUpdateTaskResourceAttributes(t *testing.T) { t.Run("successful update workflow attribute", func(t *testing.T) { setup() updateTaskResourceAttributeSetup() - subcommand.DefaultTaskResourceUpdateConfig.AttrFile = "testdata/valid_workflow_task_attribute.yaml" + taskresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/valid_workflow_task_attribute.yaml" // No args implying project domain attribute deletion u.UpdaterExt.OnUpdateWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) @@ -64,7 +64,7 @@ func TestUpdateTaskResourceAttributes(t *testing.T) { t.Run("failed update workflow attribute", func(t *testing.T) { setup() updateTaskResourceAttributeSetup() - subcommand.DefaultTaskResourceUpdateConfig.AttrFile = "testdata/valid_workflow_task_attribute.yaml" + taskresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/valid_workflow_task_attribute.yaml" // No args implying project domain attribute deletion u.UpdaterExt.OnUpdateWorkflowAttributesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("failed to update attributes")) @@ -76,7 +76,7 @@ func TestUpdateTaskResourceAttributes(t *testing.T) { t.Run("non existent file", func(t *testing.T) { setup() updateTaskResourceAttributeSetup() - subcommand.DefaultTaskResourceUpdateConfig.AttrFile = "testdata/non-existent-filel" + taskresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/non-existent-filel" err = updateTaskResourceAttributesFunc(ctx, nil, cmdCtx) assert.NotNil(t, err) assert.Equal(t, fmt.Errorf("unable to read from testdata/non-existent-filel yaml file"), err) @@ -85,7 +85,7 @@ func TestUpdateTaskResourceAttributes(t *testing.T) { t.Run("invalid update file", func(t *testing.T) { setup() updateTaskResourceAttributeSetup() - subcommand.DefaultTaskResourceUpdateConfig.AttrFile = "testdata/invalid_task_attribute.yaml" + taskresourceattribute.DefaultUpdateConfig.AttrFile = "testdata/invalid_attribute.yaml" err = updateTaskResourceAttributesFunc(ctx, nil, cmdCtx) assert.NotNil(t, err) assert.Equal(t, fmt.Errorf("error unmarshaling JSON: while decoding JSON: json: unknown field \"InvalidDomain\""), err) diff --git a/flytectl/cmd/update/testdata/invalid_task_attribute.yaml b/flytectl/cmd/update/testdata/invalid_attribute.yaml similarity index 100% rename from flytectl/cmd/update/testdata/invalid_task_attribute.yaml rename to flytectl/cmd/update/testdata/invalid_attribute.yaml diff --git a/flytectl/cmd/update/testdata/valid_project_domain_cluster_attribute.yaml b/flytectl/cmd/update/testdata/valid_project_domain_cluster_attribute.yaml new file mode 100644 index 0000000000..c6c25fa904 --- /dev/null +++ b/flytectl/cmd/update/testdata/valid_project_domain_cluster_attribute.yaml @@ -0,0 +1,5 @@ +domain: development +project: flytectldemo +attributes: + "foo": "bar" + "buzz": "lightyear" \ No newline at end of file diff --git a/flytectl/cmd/update/testdata/valid_project_domain_task_attribute.yaml b/flytectl/cmd/update/testdata/valid_project_domain_task_attribute.yaml index 0051a4c2cc..a16186c80b 100644 --- a/flytectl/cmd/update/testdata/valid_project_domain_task_attribute.yaml +++ b/flytectl/cmd/update/testdata/valid_project_domain_task_attribute.yaml @@ -1,6 +1,5 @@ -Domain: development -Project: flytectldemo -Workflow: "" +domain: development +project: flytectldemo defaults: cpu: "1" memory: 150Mi diff --git a/flytectl/cmd/update/testdata/valid_workflow_cluster_attribute.yaml b/flytectl/cmd/update/testdata/valid_workflow_cluster_attribute.yaml new file mode 100644 index 0000000000..f8e7b17f65 --- /dev/null +++ b/flytectl/cmd/update/testdata/valid_workflow_cluster_attribute.yaml @@ -0,0 +1,6 @@ +Domain: development +Project: flytectldemo +Workflow: core.control_flow.run_merge_sort.merge_sort +attributes: + "foo": "bar" + "buzz": "lightyear" \ No newline at end of file diff --git a/flytectl/cmd/update/testdata/valid_workflow_task_attribute.yaml b/flytectl/cmd/update/testdata/valid_workflow_task_attribute.yaml index 2593cad309..3b8d08cc79 100644 --- a/flytectl/cmd/update/testdata/valid_workflow_task_attribute.yaml +++ b/flytectl/cmd/update/testdata/valid_workflow_task_attribute.yaml @@ -1,6 +1,6 @@ -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: "2" memory: 250Mi diff --git a/flytectl/cmd/update/update.go b/flytectl/cmd/update/update.go index c842c0855b..203407d306 100644 --- a/flytectl/cmd/update/update.go +++ b/flytectl/cmd/update/update.go @@ -1,8 +1,10 @@ package update import ( - "github.com/flyteorg/flytectl/cmd/config/subcommand" + "github.com/flyteorg/flytectl/cmd/config/subcommand/clusterresourceattribute" + "github.com/flyteorg/flytectl/cmd/config/subcommand/taskresourceattribute" cmdCore "github.com/flyteorg/flytectl/cmd/core" + "github.com/spf13/cobra" ) @@ -36,8 +38,10 @@ func CreateUpdateCommand() *cobra.Command { Short: updateTaskShort, Long: updateTaskLong}, "workflow": {CmdFunc: updateWorkflowFunc, Aliases: []string{}, ProjectDomainNotRequired: false, PFlagProvider: namedEntityConfig, Short: updateWorkflowShort, Long: updateWorkflowLong}, - "task-resource-attribute": {CmdFunc: updateTaskResourceAttributesFunc, Aliases: []string{}, PFlagProvider: subcommand.DefaultTaskResourceUpdateConfig, + "task-resource-attribute": {CmdFunc: updateTaskResourceAttributesFunc, Aliases: []string{}, PFlagProvider: taskresourceattribute.DefaultUpdateConfig, Short: taskResourceAttributesShort, Long: taskResourceAttributesLong, ProjectDomainNotRequired: true}, + "cluster-resource-attribute": {CmdFunc: updateClusterResourceAttributesFunc, Aliases: []string{}, PFlagProvider: clusterresourceattribute.DefaultUpdateConfig, + Short: clusterResourceAttributesShort, Long: clusterResourceAttributesLong, ProjectDomainNotRequired: true}, } cmdCore.AddCommands(updateCmd, updateResourcesFuncs) return updateCmd diff --git a/flytectl/cmd/update/update_test.go b/flytectl/cmd/update/update_test.go index 33e9b9eb5e..abebad5236 100644 --- a/flytectl/cmd/update/update_test.go +++ b/flytectl/cmd/update/update_test.go @@ -26,16 +26,19 @@ func TestUpdateCommand(t *testing.T) { assert.Equal(t, updateCommand.Use, updateUse) assert.Equal(t, updateCommand.Short, updateShort) assert.Equal(t, updateCommand.Long, updatecmdLong) - assert.Equal(t, len(updateCommand.Commands()), 5) + assert.Equal(t, len(updateCommand.Commands()), 6) cmdNouns := updateCommand.Commands() // Sort by Use value. sort.Slice(cmdNouns, func(i, j int) bool { return cmdNouns[i].Use < cmdNouns[j].Use }) - useArray := []string{"launchplan", "project", "task", "task-resource-attribute", "workflow"} - aliases := [][]string{{}, {}, {}, {}, {}} - shortArray := []string{updateLPShort, projectShort, updateTaskShort, taskResourceAttributesShort, updateWorkflowShort} - longArray := []string{updateLPLong, projectLong, updateTaskLong, taskResourceAttributesLong, updateWorkflowLong} + useArray := []string{"cluster-resource-attribute", "launchplan", "project", "task", + "task-resource-attribute", "workflow"} + aliases := [][]string{{}, {}, {}, {}, {}, {}} + shortArray := []string{clusterResourceAttributesShort, updateLPShort, projectShort, updateTaskShort, + taskResourceAttributesShort, updateWorkflowShort} + longArray := []string{clusterResourceAttributesLong, updateLPLong, projectLong, updateTaskLong, + taskResourceAttributesLong, updateWorkflowLong} for i := range cmdNouns { assert.Equal(t, cmdNouns[i].Use, useArray[i]) assert.Equal(t, cmdNouns[i].Aliases, aliases[i]) diff --git a/flytectl/docs/source/gen/flytectl_delete.rst b/flytectl/docs/source/gen/flytectl_delete.rst index 49006715a4..be95495573 100644 --- a/flytectl/docs/source/gen/flytectl_delete.rst +++ b/flytectl/docs/source/gen/flytectl_delete.rst @@ -71,6 +71,7 @@ SEE ALSO ~~~~~~~~ * :doc:`flytectl` - flyetcl CLI tool +* :doc:`flytectl_delete_cluster-resource-attribute` - Deletes matchable resources of cluster attributes * :doc:`flytectl_delete_execution` - Terminate/Delete execution resources. * :doc:`flytectl_delete_task-resource-attribute` - Deletes matchable resources of task attributes diff --git a/flytectl/docs/source/gen/flytectl_delete_cluster-resource-attribute.rst b/flytectl/docs/source/gen/flytectl_delete_cluster-resource-attribute.rst new file mode 100644 index 0000000000..bb342e1ed9 --- /dev/null +++ b/flytectl/docs/source/gen/flytectl_delete_cluster-resource-attribute.rst @@ -0,0 +1,109 @@ +.. _flytectl_delete_cluster-resource-attribute: + +flytectl delete cluster-resource-attribute +------------------------------------------ + +Deletes matchable resources of cluster attributes + +Synopsis +~~~~~~~~ + + + +Deletes cluster resource attributes for given project and domain combination or additionally with workflow name. + +Deletes cluster resource attribute for project and domain +Here the command delete cluster resource attributes for project flytectldemo and development domain. +:: + + flytectl delete cluster-resource-attribute -p flytectldemo -d development + + +Deleting cluster resource attribute using config file which was used for creating it. +Here the command deletes cluster resource attributes from the config file cra.yaml +eg: content of cra.yaml which will use the project domain and workflow name for deleting the resource + +:: + + flytectl delete cluster-resource-attribute --attrFile cra.yaml + + +.. code-block:: yaml + + domain: development + project: flytectldemo + attributes: + foo: "bar" + buzz: "lightyear" + +Deleting cluster resource attribute for a workflow +Here the command deletes cluster resource attributes for a workflow + +:: + + flytectl delete cluster-resource-attribute -p flytectldemo -d development core.control_flow.run_merge_sort.merge_sort + +Usage + + +:: + + flytectl delete cluster-resource-attribute [flags] + +Options +~~~~~~~ + +:: + + --attrFile string attribute file name to be used for delete attribute for the resource type. + -h, --help help for cluster-resource-attribute + +Options inherited from parent commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + --admin.authorizationHeader string Custom metadata header to pass JWT + --admin.authorizationServerUrl string This is the URL to your IdP's authorization server. It'll default to Endpoint + --admin.clientId string Client ID (default "flytepropeller") + --admin.clientSecretLocation string File containing the client secret (default "/etc/secrets/client_secret") + --admin.endpoint string For admin types, specify where the uri of the service is located. + --admin.insecure Use insecure connection. + --admin.maxBackoffDelay string Max delay for grpc backoff (default "8s") + --admin.maxRetries int Max number of gRPC retries (default 4) + --admin.perRetryTimeout string gRPC per retry timeout (default "15s") + --admin.scopes strings List of scopes to request + --admin.tokenUrl string OPTIONAL: Your IdP's token endpoint. It'll be discovered from flyte admin's OAuth Metadata endpoint if not provided. + --admin.useAuth Deprecated: Auth will be enabled/disabled based on admin's dynamically discovered information. + --adminutils.batchSize int Maximum number of records to retrieve per call. (default 100) + --adminutils.maxRecords int Maximum number of records to retrieve. (default 500) + --config string config file (default is $HOME/.flyte/config.yaml) + -d, --domain string Specifies the Flyte project's domain. + --logger.formatter.type string Sets logging format type. (default "json") + --logger.level int Sets the minimum logging level. (default 4) + --logger.mute Mutes all logs regardless of severity. Intended for benchmarks/tests only. + --logger.show-source Includes source code location in logs. + -o, --output string Specifies the output type - supported formats [TABLE JSON YAML] (default "TABLE") + -p, --project string Specifies the Flyte project. + --root.domain string Specified the domain to work on. + --root.output string Specified the output type. + --root.project string Specifies the project to work on. + --storage.cache.max_size_mbs int Maximum size of the cache where the Blob store data is cached in-memory. If not specified or set to 0, cache is not used + --storage.cache.target_gc_percent int Sets the garbage collection target percentage. + --storage.connection.access-key string Access key to use. Only required when authtype is set to accesskey. + --storage.connection.auth-type string Auth Type to use [iam, accesskey]. (default "iam") + --storage.connection.disable-ssl Disables SSL connection. Should only be used for development. + --storage.connection.endpoint string URL for storage client to connect to. + --storage.connection.region string Region to connect to. (default "us-east-1") + --storage.connection.secret-key string Secret to use when accesskey is set. + --storage.container string Initial container to create -if it doesn't exist-.' + --storage.defaultHttpClient.timeout string Sets time out on the http client. (default "0s") + --storage.enable-multicontainer If this is true, then the container argument is overlooked and redundant. This config will automatically open new connections to new containers/buckets as they are encountered + --storage.limits.maxDownloadMBs int Maximum allowed download size (in MBs) per call. (default 2) + --storage.type string Sets the type of storage to configure [s3/minio/local/mem/stow]. (default "s3") + +SEE ALSO +~~~~~~~~ + +* :doc:`flytectl_delete` - Used for terminating/deleting various flyte resources including tasks/workflows/launchplans/executions/project. + 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 da97151fef..d31faa8b9f 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 +Deleting task resource attribute using config file which was used for creating it. 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,15 +30,14 @@ eg: content of tra.yaml which will use the project domain and workflow name for .. code-block:: yaml - Domain: development - Project: flytectldemo - Workflow: "" + domain: development + project: flytectldemo defaults: - cpu: "1" - memory: 150Mi + cpu: "1" + memory: "150Mi" limits: - cpu: "2" - memory: 450Mi + cpu: "2" + memory: "450Mi" Deleting task resource attribute for a workflow Here the command deletes task resource attributes for a workflow diff --git a/flytectl/docs/source/gen/flytectl_get.rst b/flytectl/docs/source/gen/flytectl_get.rst index add9e81c9b..7a37881b50 100644 --- a/flytectl/docs/source/gen/flytectl_get.rst +++ b/flytectl/docs/source/gen/flytectl_get.rst @@ -71,6 +71,7 @@ SEE ALSO ~~~~~~~~ * :doc:`flytectl` - flyetcl CLI tool +* :doc:`flytectl_get_cluster-resource-attribute` - Gets matchable resources of cluster resource attributes * :doc:`flytectl_get_execution` - Gets execution resources * :doc:`flytectl_get_launchplan` - Gets launch plan resources * :doc:`flytectl_get_project` - Gets project resources diff --git a/flytectl/docs/source/gen/flytectl_get_cluster-resource-attribute.rst b/flytectl/docs/source/gen/flytectl_get_cluster-resource-attribute.rst new file mode 100644 index 0000000000..db51d52098 --- /dev/null +++ b/flytectl/docs/source/gen/flytectl_get_cluster-resource-attribute.rst @@ -0,0 +1,107 @@ +.. _flytectl_get_cluster-resource-attribute: + +flytectl get cluster-resource-attribute +--------------------------------------- + +Gets matchable resources of cluster resource attributes + +Synopsis +~~~~~~~~ + + + +Retrieves cluster resource attributes for given project and domain combination or additionally with workflow name. + +Retrieves cluster resource attribute for project and domain +Here the command get cluster resource attributes for project flytectldemo and development domain. +:: + + flytectl get cluster-resource-attribute -p flytectldemo -d development + +eg : O/P + +.. code-block:: json + + {"project":"flytectldemo","domain":"development","attributes":{"buzz":"lightyear","foo":"bar"}} + +Writing the cluster resource attribute to a file. If there are no cluster resource attributes , command would return an error. +Here the command gets task resource attributes and writes the config file to tra.yaml +eg: content of tra.yaml + +:: + + flytectl get task-resource-attribute --attrFile tra.yaml + + +.. code-block:: yaml + + domain: development + project: flytectldemo + attributes: + foo: "bar" + buzz: "lightyear" + +Usage + + +:: + + flytectl get cluster-resource-attribute [flags] + +Options +~~~~~~~ + +:: + + --attrFile string attribute file name to be used for generating attribute for the resource type. + -h, --help help for cluster-resource-attribute + +Options inherited from parent commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + --admin.authorizationHeader string Custom metadata header to pass JWT + --admin.authorizationServerUrl string This is the URL to your IdP's authorization server. It'll default to Endpoint + --admin.clientId string Client ID (default "flytepropeller") + --admin.clientSecretLocation string File containing the client secret (default "/etc/secrets/client_secret") + --admin.endpoint string For admin types, specify where the uri of the service is located. + --admin.insecure Use insecure connection. + --admin.maxBackoffDelay string Max delay for grpc backoff (default "8s") + --admin.maxRetries int Max number of gRPC retries (default 4) + --admin.perRetryTimeout string gRPC per retry timeout (default "15s") + --admin.scopes strings List of scopes to request + --admin.tokenUrl string OPTIONAL: Your IdP's token endpoint. It'll be discovered from flyte admin's OAuth Metadata endpoint if not provided. + --admin.useAuth Deprecated: Auth will be enabled/disabled based on admin's dynamically discovered information. + --adminutils.batchSize int Maximum number of records to retrieve per call. (default 100) + --adminutils.maxRecords int Maximum number of records to retrieve. (default 500) + --config string config file (default is $HOME/.flyte/config.yaml) + -d, --domain string Specifies the Flyte project's domain. + --logger.formatter.type string Sets logging format type. (default "json") + --logger.level int Sets the minimum logging level. (default 4) + --logger.mute Mutes all logs regardless of severity. Intended for benchmarks/tests only. + --logger.show-source Includes source code location in logs. + -o, --output string Specifies the output type - supported formats [TABLE JSON YAML] (default "TABLE") + -p, --project string Specifies the Flyte project. + --root.domain string Specified the domain to work on. + --root.output string Specified the output type. + --root.project string Specifies the project to work on. + --storage.cache.max_size_mbs int Maximum size of the cache where the Blob store data is cached in-memory. If not specified or set to 0, cache is not used + --storage.cache.target_gc_percent int Sets the garbage collection target percentage. + --storage.connection.access-key string Access key to use. Only required when authtype is set to accesskey. + --storage.connection.auth-type string Auth Type to use [iam, accesskey]. (default "iam") + --storage.connection.disable-ssl Disables SSL connection. Should only be used for development. + --storage.connection.endpoint string URL for storage client to connect to. + --storage.connection.region string Region to connect to. (default "us-east-1") + --storage.connection.secret-key string Secret to use when accesskey is set. + --storage.container string Initial container to create -if it doesn't exist-.' + --storage.defaultHttpClient.timeout string Sets time out on the http client. (default "0s") + --storage.enable-multicontainer If this is true, then the container argument is overlooked and redundant. This config will automatically open new connections to new containers/buckets as they are encountered + --storage.limits.maxDownloadMBs int Maximum allowed download size (in MBs) per call. (default 2) + --storage.type string Sets the type of storage to configure [s3/minio/local/mem/stow]. (default "s3") + +SEE ALSO +~~~~~~~~ + +* :doc:`flytectl_get` - Used for fetching various flyte resources including tasks/workflows/launchplans/executions/project. + 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 ea2356a8ca..5c1d307eae 100644 --- a/flytectl/docs/source/gen/flytectl_get_task-resource-attribute.rst +++ b/flytectl/docs/source/gen/flytectl_get_task-resource-attribute.rst @@ -35,15 +35,14 @@ eg: content of tra.yaml .. code-block:: yaml - Domain: development - Project: flytectldemo - Workflow: "" + domain: development + project: flytectldemo defaults: - cpu: "1" - memory: 150Mi + cpu: "1" + memory: "150Mi" limits: - cpu: "2" - memory: 450Mi + cpu: "2" + memory: "450Mi" Usage diff --git a/flytectl/docs/source/gen/flytectl_update.rst b/flytectl/docs/source/gen/flytectl_update.rst index a30c1a9337..4692575169 100644 --- a/flytectl/docs/source/gen/flytectl_update.rst +++ b/flytectl/docs/source/gen/flytectl_update.rst @@ -73,6 +73,7 @@ SEE ALSO ~~~~~~~~ * :doc:`flytectl` - flyetcl CLI tool +* :doc:`flytectl_update_cluster-resource-attribute` - Updates matchable resources of cluster attributes * :doc:`flytectl_update_launchplan` - Updates launch plan metadata * :doc:`flytectl_update_project` - Updates project resources * :doc:`flytectl_update_task` - Updates task metadata diff --git a/flytectl/docs/source/gen/flytectl_update_cluster-resource-attribute.rst b/flytectl/docs/source/gen/flytectl_update_cluster-resource-attribute.rst new file mode 100644 index 0000000000..96be5e1fa7 --- /dev/null +++ b/flytectl/docs/source/gen/flytectl_update_cluster-resource-attribute.rst @@ -0,0 +1,111 @@ +.. _flytectl_update_cluster-resource-attribute: + +flytectl update cluster-resource-attribute +------------------------------------------ + +Updates matchable resources of cluster attributes + +Synopsis +~~~~~~~~ + + + +Updates cluster resource attributes for given project and domain combination or additionally with workflow name. + +Updating to the cluster 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 cluster resource attributes from the config file cra.yaml +eg: content of tra.yaml + +.. code-block:: yaml + + domain: development + project: flytectldemo + attributes: + foo: "bar" + buzz: "lightyear" + +:: + + flytectl update cluster-resource-attribute -attrFile cra.yaml + +Updating cluster 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 cluster 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 + attributes: + foo: "bar" + buzz: "lightyear" + +:: + + flytectl update cluster-resource-attribute -attrFile cra.yaml + +Usage + + + +:: + + flytectl update cluster-resource-attribute [flags] + +Options +~~~~~~~ + +:: + + --attrFile string attribute file name to be used for updating attribute for the resource type. + -h, --help help for cluster-resource-attribute + +Options inherited from parent commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + --admin.authorizationHeader string Custom metadata header to pass JWT + --admin.authorizationServerUrl string This is the URL to your IdP's authorization server. It'll default to Endpoint + --admin.clientId string Client ID (default "flytepropeller") + --admin.clientSecretLocation string File containing the client secret (default "/etc/secrets/client_secret") + --admin.endpoint string For admin types, specify where the uri of the service is located. + --admin.insecure Use insecure connection. + --admin.maxBackoffDelay string Max delay for grpc backoff (default "8s") + --admin.maxRetries int Max number of gRPC retries (default 4) + --admin.perRetryTimeout string gRPC per retry timeout (default "15s") + --admin.scopes strings List of scopes to request + --admin.tokenUrl string OPTIONAL: Your IdP's token endpoint. It'll be discovered from flyte admin's OAuth Metadata endpoint if not provided. + --admin.useAuth Deprecated: Auth will be enabled/disabled based on admin's dynamically discovered information. + --adminutils.batchSize int Maximum number of records to retrieve per call. (default 100) + --adminutils.maxRecords int Maximum number of records to retrieve. (default 500) + --config string config file (default is $HOME/.flyte/config.yaml) + -d, --domain string Specifies the Flyte project's domain. + --logger.formatter.type string Sets logging format type. (default "json") + --logger.level int Sets the minimum logging level. (default 4) + --logger.mute Mutes all logs regardless of severity. Intended for benchmarks/tests only. + --logger.show-source Includes source code location in logs. + -o, --output string Specifies the output type - supported formats [TABLE JSON YAML] (default "TABLE") + -p, --project string Specifies the Flyte project. + --root.domain string Specified the domain to work on. + --root.output string Specified the output type. + --root.project string Specifies the project to work on. + --storage.cache.max_size_mbs int Maximum size of the cache where the Blob store data is cached in-memory. If not specified or set to 0, cache is not used + --storage.cache.target_gc_percent int Sets the garbage collection target percentage. + --storage.connection.access-key string Access key to use. Only required when authtype is set to accesskey. + --storage.connection.auth-type string Auth Type to use [iam, accesskey]. (default "iam") + --storage.connection.disable-ssl Disables SSL connection. Should only be used for development. + --storage.connection.endpoint string URL for storage client to connect to. + --storage.connection.region string Region to connect to. (default "us-east-1") + --storage.connection.secret-key string Secret to use when accesskey is set. + --storage.container string Initial container to create -if it doesn't exist-.' + --storage.defaultHttpClient.timeout string Sets time out on the http client. (default "0s") + --storage.enable-multicontainer If this is true, then the container argument is overlooked and redundant. This config will automatically open new connections to new containers/buckets as they are encountered + --storage.limits.maxDownloadMBs int Maximum allowed download size (in MBs) per call. (default 2) + --storage.type string Sets the type of storage to configure [s3/minio/local/mem/stow]. (default "s3") + +SEE ALSO +~~~~~~~~ + +* :doc:`flytectl_update` - Used for updating flyte resources eg: project. + 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 6f60b7d50d..fd4b141361 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,domain combination or additionally with workflow name. +Updates task resource attributes for given project and 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,34 +18,33 @@ eg: content of tra.yaml .. code-block:: yaml - Domain: development - Project: flytectldemo - Workflow: "" + domain: development + project: flytectldemo defaults: - cpu: "1" - memory: 150Mi + cpu: "1" + memory: "150Mi" limits: - cpu: "2" - memory: 450Mi + cpu: "2" + memory: "450Mi" :: 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 + cpu: "1" + memory: "150Mi" limits: - cpu: "2" - memory: 450Mi + cpu: "2" + memory: "450Mi" :: diff --git a/flytectl/docs/source/nouns.rst b/flytectl/docs/source/nouns.rst index 78996e38ce..e65582ff28 100644 --- a/flytectl/docs/source/nouns.rst +++ b/flytectl/docs/source/nouns.rst @@ -12,8 +12,19 @@ Nouns gen/flytectl_get_project gen/flytectl_get_workflow gen/flytectl_get_task + gen/flytectl_get_task-resource-attribute + gen/flytectl_get_cluster-resource-attribute gen/flytectl_get_launchplan + gen/flytectl_update_launchplan + gen/flytectl_update_workflow gen/flytectl_update_project + gen/flytectl_update_task + gen/flytectl_update_task-resource-attribute + gen/flytectl_update_cluster-resource-attribute gen/flytectl_register_files + gen/flytectl_delete_execution + gen/flytectl_delete_task-resource-attribute + gen/flytectl_delete_cluster-resource-attribute gen/flytectl_version gen/flytectl_config_validate + gen/flytectl_config_discover diff --git a/flytectl/pkg/ext/attribute_match_fetcher.go b/flytectl/pkg/ext/attribute_match_fetcher.go index f3ea76b369..0af146e8e1 100644 --- a/flytectl/pkg/ext/attribute_match_fetcher.go +++ b/flytectl/pkg/ext/attribute_match_fetcher.go @@ -2,6 +2,7 @@ package ext import ( "context" + "fmt" "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" ) @@ -14,7 +15,13 @@ func (a *AdminFetcherExtClient) FetchWorkflowAttributes(ctx context.Context, pro Workflow: name, ResourceType: rsType, }) - return workflowAttr, err + if err != nil { + return nil, err + } + if workflowAttr.GetAttributes() == nil || workflowAttr.GetAttributes().GetMatchingAttributes() == nil { + return nil, fmt.Errorf("attribute doesn't exist") + } + return workflowAttr, nil } func (a *AdminFetcherExtClient) FetchProjectDomainAttributes(ctx context.Context, project, domain string, @@ -25,5 +32,11 @@ func (a *AdminFetcherExtClient) FetchProjectDomainAttributes(ctx context.Context Domain: domain, ResourceType: rsType, }) - return projectDomainAttr, err + if err != nil { + return nil, err + } + if projectDomainAttr.GetAttributes() == nil || projectDomainAttr.GetAttributes().GetMatchingAttributes() == nil { + return nil, fmt.Errorf("attribute doesn't exist") + } + return projectDomainAttr, nil } diff --git a/flytectl/pkg/ext/attribute_match_fetcher_test.go b/flytectl/pkg/ext/attribute_match_fetcher_test.go index b91d18fe3a..ea7f29929a 100644 --- a/flytectl/pkg/ext/attribute_match_fetcher_test.go +++ b/flytectl/pkg/ext/attribute_match_fetcher_test.go @@ -12,38 +12,70 @@ import ( "github.com/stretchr/testify/mock" ) +var wResp *admin.WorkflowAttributesGetResponse + +var pResp *admin.ProjectDomainAttributesGetResponse + func getAttributeMatchFetcherSetup() { ctx = context.Background() adminClient = new(mocks.AdminServiceClient) adminFetcherExt = AdminFetcherExtClient{AdminClient: adminClient} + wResp = &admin.WorkflowAttributesGetResponse{Attributes: &admin.WorkflowAttributes{ + MatchingAttributes: &admin.MatchingAttributes{ + Target: nil, + }}} + pResp = &admin.ProjectDomainAttributesGetResponse{Attributes: &admin.ProjectDomainAttributes{ + Project: "dummyProject", + Domain: "dummyDomain", + MatchingAttributes: &admin.MatchingAttributes{ + Target: nil, + }}} } func TestFetchWorkflowAttributes(t *testing.T) { getAttributeMatchFetcherSetup() - resp := &admin.WorkflowAttributesGetResponse{} - adminClient.OnGetWorkflowAttributesMatch(mock.Anything, mock.Anything).Return(resp, nil) + adminClient.OnGetWorkflowAttributesMatch(mock.Anything, mock.Anything).Return(wResp, nil) _, err := adminFetcherExt.FetchWorkflowAttributes(ctx, "dummyProject", "domainValue", "workflowName", admin.MatchableResource_TASK_RESOURCE) assert.Nil(t, err) } func TestFetchWorkflowAttributesError(t *testing.T) { - getAttributeMatchFetcherSetup() - adminClient.OnGetWorkflowAttributesMatch(mock.Anything, mock.Anything).Return(nil, fmt.Errorf("failed")) - _, err := adminFetcherExt.FetchWorkflowAttributes(ctx, "dummyProject", "domainValue", "workflowName", admin.MatchableResource_TASK_RESOURCE) - assert.Equal(t, fmt.Errorf("failed"), err) + t.Run("failed api", func(t *testing.T) { + getAttributeMatchFetcherSetup() + adminClient.OnGetWorkflowAttributesMatch(mock.Anything, mock.Anything).Return(nil, fmt.Errorf("failed")) + _, err := adminFetcherExt.FetchWorkflowAttributes(ctx, "dummyProject", "domainValue", "workflowName", admin.MatchableResource_TASK_RESOURCE) + assert.Equal(t, fmt.Errorf("failed"), err) + }) + t.Run("empty data from api", func(t *testing.T) { + getAttributeMatchFetcherSetup() + wResp := &admin.WorkflowAttributesGetResponse{} + adminClient.OnGetWorkflowAttributesMatch(mock.Anything, mock.Anything).Return(wResp, nil) + _, err := adminFetcherExt.FetchWorkflowAttributes(ctx, "dummyProject", "domainValue", "workflowName", admin.MatchableResource_TASK_RESOURCE) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("attribute doesn't exist"), err) + }) } func TestFetchProjectDomainAttributes(t *testing.T) { getAttributeMatchFetcherSetup() - resp := &admin.ProjectDomainAttributesGetResponse{} - adminClient.OnGetProjectDomainAttributesMatch(mock.Anything, mock.Anything).Return(resp, nil) + adminClient.OnGetProjectDomainAttributesMatch(mock.Anything, mock.Anything).Return(pResp, nil) _, err := adminFetcherExt.FetchProjectDomainAttributes(ctx, "dummyProject", "domainValue", admin.MatchableResource_TASK_RESOURCE) assert.Nil(t, err) } func TestFetchProjectDomainAttributesError(t *testing.T) { - getAttributeMatchFetcherSetup() - adminClient.OnGetProjectDomainAttributesMatch(mock.Anything, mock.Anything).Return(nil, fmt.Errorf("failed")) - _, err := adminFetcherExt.FetchProjectDomainAttributes(ctx, "dummyProject", "domainValue", admin.MatchableResource_TASK_RESOURCE) - assert.Equal(t, fmt.Errorf("failed"), err) + t.Run("failed api", func(t *testing.T) { + getAttributeMatchFetcherSetup() + adminClient.OnGetProjectDomainAttributesMatch(mock.Anything, mock.Anything).Return(nil, fmt.Errorf("failed")) + _, err := adminFetcherExt.FetchProjectDomainAttributes(ctx, "dummyProject", "domainValue", admin.MatchableResource_TASK_RESOURCE) + assert.Equal(t, fmt.Errorf("failed"), err) + }) + t.Run("empty data from api", func(t *testing.T) { + getAttributeMatchFetcherSetup() + pResp := &admin.ProjectDomainAttributesGetResponse{} + adminClient.OnGetProjectDomainAttributesMatch(mock.Anything, mock.Anything).Return(pResp, nil) + _, err := adminFetcherExt.FetchProjectDomainAttributes(ctx, "dummyProject", "domainValue", admin.MatchableResource_TASK_RESOURCE) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("attribute doesn't exist"), err) + }) }