From 0da0daeb297426120ff6a9363196a1f3cb6e5079 Mon Sep 17 00:00:00 2001 From: Haytham AbuelFutuh Date: Thu, 4 Jun 2020 13:57:45 -0700 Subject: [PATCH] Fix up config --- .idea/$CACHE_FILE$ | 6 ++ .idea/.gitignore | 8 ++ .idea/dictionaries | 6 ++ .idea/flytectl.iml | 8 ++ .idea/misc.xml | 6 ++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ cmd/config/config.go | 19 +++++ cmd/config/config_flags.go | 47 ++++++++++ cmd/config/config_flags_test.go | 146 ++++++++++++++++++++++++++++++++ cmd/root.go | 25 +++--- cmd/testdata/config.yaml | 3 +- cmd/version.go | 20 +++++ go.mod | 1 + 14 files changed, 296 insertions(+), 13 deletions(-) create mode 100644 .idea/$CACHE_FILE$ create mode 100644 .idea/.gitignore create mode 100644 .idea/dictionaries create mode 100644 .idea/flytectl.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 cmd/config/config.go create mode 100755 cmd/config/config_flags.go create mode 100755 cmd/config/config_flags_test.go create mode 100644 cmd/version.go diff --git a/.idea/$CACHE_FILE$ b/.idea/$CACHE_FILE$ new file mode 100644 index 00000000..6cb8985e --- /dev/null +++ b/.idea/$CACHE_FILE$ @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..73f69e09 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/dictionaries b/.idea/dictionaries new file mode 100644 index 00000000..6ccf20ea --- /dev/null +++ b/.idea/dictionaries @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/flytectl.iml b/.idea/flytectl.iml new file mode 100644 index 00000000..c956989b --- /dev/null +++ b/.idea/flytectl.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..28a804d8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..2471589a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cmd/config/config.go b/cmd/config/config.go new file mode 100644 index 00000000..9e5bf79c --- /dev/null +++ b/cmd/config/config.go @@ -0,0 +1,19 @@ +package config + +import "github.com/lyft/flytestdlib/config" + +//go:generate pflags Config + +var ( + defaultConfig = &Config{} + section = config.MustRegisterSection("root", defaultConfig) +) + +type Config struct { + Project string `json:"project" pflag:",Specifies the project to work on."` + Domain string `json:"domain" pflag:",Specified the domain to work on."` +} + +func GetConfig() *Config { + return section.GetConfig().(*Config) +} diff --git a/cmd/config/config_flags.go b/cmd/config/config_flags.go new file mode 100755 index 00000000..a6986a23 --- /dev/null +++ b/cmd/config/config_flags.go @@ -0,0 +1,47 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package config + +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.String(fmt.Sprintf("%v%v", prefix, "project"), defaultConfig.Project, "Specifies the project to work on.") + cmdFlags.String(fmt.Sprintf("%v%v", prefix, "domain"), defaultConfig.Domain, "Specified the domain to work on.") + return cmdFlags +} diff --git a/cmd/config/config_flags_test.go b/cmd/config/config_flags_test.go new file mode 100755 index 00000000..1297f830 --- /dev/null +++ b/cmd/config/config_flags_test.go @@ -0,0 +1,146 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots. + +package config + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/mitchellh/mapstructure" + "github.com/stretchr/testify/assert" +) + +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 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 jsonUnmarshalerHookConfig(_, to reflect.Type, data interface{}) (interface{}, error) { + unmarshalerType := reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() + if to.Implements(unmarshalerType) || reflect.PtrTo(to).Implements(unmarshalerType) || + (canGetElementConfig(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_Config(input, result interface{}) error { + config := &mapstructure.DecoderConfig{ + TagName: "json", + WeaklyTypedInput: true, + Result: result, + DecodeHook: mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + jsonUnmarshalerHookConfig, + ), + } + + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +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++ { + strs = append(strs, fmt.Sprintf("%v", listValue.Index(i))) + } + + return strings.Join(strs, sep) +} + +func testDecodeJson_Config(t *testing.T, val, result interface{}) { + assert.NoError(t, decode_Config(val, result)) +} + +func testDecodeSlice_Config(t *testing.T, vStringSlice, result interface{}) { + assert.NoError(t, decode_Config(vStringSlice, result)) +} + +func TestConfig_GetPFlagSet(t *testing.T) { + val := Config{} + cmdFlags := val.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) +} + +func TestConfig_SetFlags(t *testing.T) { + actual := Config{} + cmdFlags := actual.GetPFlagSet("") + assert.True(t, cmdFlags.HasFlags()) + + t.Run("Test_project", func(t *testing.T) { + t.Run("DefaultValue", func(t *testing.T) { + // Test that default value is set properly + if vString, err := cmdFlags.GetString("project"); err == nil { + assert.Equal(t, string(defaultConfig.Project), vString) + } else { + assert.FailNow(t, err.Error()) + } + }) + + t.Run("Override", func(t *testing.T) { + testValue := "1" + + cmdFlags.Set("project", testValue) + if vString, err := cmdFlags.GetString("project"); err == nil { + testDecodeJson_Config(t, fmt.Sprintf("%v", vString), &actual.Project) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) + t.Run("Test_domain", func(t *testing.T) { + t.Run("DefaultValue", func(t *testing.T) { + // Test that default value is set properly + if vString, err := cmdFlags.GetString("domain"); err == nil { + assert.Equal(t, string(defaultConfig.Domain), vString) + } else { + assert.FailNow(t, err.Error()) + } + }) + + t.Run("Override", func(t *testing.T) { + testValue := "1" + + cmdFlags.Set("domain", testValue) + if vString, err := cmdFlags.GetString("domain"); err == nil { + testDecodeJson_Config(t, fmt.Sprintf("%v", vString), &actual.Domain) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) +} diff --git a/cmd/root.go b/cmd/root.go index 3953bd7f..98ce44bc 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,23 +3,19 @@ package cmd import ( "context" - "github.com/lyft/flytestdlib/config" + "github.com/lyft/flytectl/cmd/config" + + stdConfig "github.com/lyft/flytestdlib/config" "github.com/lyft/flytestdlib/config/viper" "github.com/spf13/cobra" ) var ( cfgFile string - configAccessor = viper.NewAccessor(config.Options{StrictMode: true}) + configAccessor = viper.NewAccessor(stdConfig.Options{StrictMode: true}) ) -type persistentFlags struct { - Project *string - Domain *string -} - func newRootCmd() *cobra.Command { - persistentFlags := persistentFlags{} rootCmd := &cobra.Command{ PersistentPreRunE: initConfig, } @@ -29,15 +25,20 @@ func newRootCmd() *cobra.Command { configAccessor.InitializePflags(rootCmd.PersistentFlags()) - persistentFlags.Project = rootCmd.PersistentFlags().String("project", "", "Specifies the Flyte project.") - persistentFlags.Domain = rootCmd.PersistentFlags().String("domain", "", "Specifies the Flyte project's domain.") + // Due to https://github.com/lyft/flyte/issues/341, project flag will have to be specified as + // --root.project, this adds a convenience on top to allow --project to be used + rootCmd.PersistentFlags().StringVarP(&(config.GetConfig().Project), "project", "p", "", "Specifies the Flyte project.") + rootCmd.PersistentFlags().StringVarP(&(config.GetConfig().Domain), "domain", "d", "", "Specifies the Flyte project's domain.") + + rootCmd.AddCommand(viper.GetConfigCommand()) + rootCmd.AddCommand(versionCmd) + config.GetConfig() - rootCmd.AddCommand(newTimelineCmd(persistentFlags)) return rootCmd } func initConfig(_ *cobra.Command, _ []string) error { - configAccessor = viper.NewAccessor(config.Options{ + configAccessor = viper.NewAccessor(stdConfig.Options{ StrictMode: true, SearchPaths: []string{cfgFile}, }) diff --git a/cmd/testdata/config.yaml b/cmd/testdata/config.yaml index 202261b1..5aa315c5 100644 --- a/cmd/testdata/config.yaml +++ b/cmd/testdata/config.yaml @@ -1,2 +1,3 @@ admin: - endpoint: dns:///flyte.lyft.net + endpoint: http://localhost:30082 + insecure: true diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 00000000..ba6f63d4 --- /dev/null +++ b/cmd/version.go @@ -0,0 +1,20 @@ +package cmd + +import ( + "github.com/lyft/flytectl/cmd/config" + "github.com/lyft/flytestdlib/logger" + "github.com/lyft/flytestdlib/version" + "github.com/spf13/cobra" +) + +var ( + versionCmd = &cobra.Command{ + Use: "version", + Short: "Displays version information for the client and server.", + Run: func(cmd *cobra.Command, args []string) { + version.LogBuildInformation("flytectl") + logger.InfofNoCtx(config.GetConfig().Project) + logger.InfofNoCtx(config.GetConfig().Domain) + }, + } +) diff --git a/go.mod b/go.mod index 1091e73a..06f543db 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/lyft/flytestdlib v0.2.31 github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/spf13/cobra v0.0.5 + github.com/spf13/pflag v1.0.5 github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.4.0 github.com/wcharczuk/go-chart v2.0.1+incompatible