From d2209d161af9e64c9253c6d456f66f7ceeab7e55 Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Wed, 30 Dec 2020 18:52:15 -0500 Subject: [PATCH 01/13] initial default flags work todo, make context aware --- README.md | 21 ++-- cmd/cloudNetworkVipCreate.go | 8 +- cmd/cloudPrivateParentCreate.go | 18 ++-- cmd/cloudServerCreate.go | 5 +- cmd/cloudTemplateRestore.go | 7 +- cmd/defaultFlags.go | 43 ++++++++ cmd/defaultFlagsDelete.go | 50 +++++++++ cmd/defaultFlagsGet.go | 52 +++++++++ cmd/defaultFlagsList.go | 43 ++++++++ cmd/defaultFlagsSet.go | 55 ++++++++++ cmd/networkIpPoolCreate.go | 16 ++- cmd/root.go | 7 +- flags/defaults/constants.go | 4 + flags/defaults/core.go | 182 ++++++++++++++++++++++++++++++++ flags/defaults/errors.go | 12 +++ flags/defaults/types.go | 30 ++++++ instance/instance.go | 7 +- 17 files changed, 512 insertions(+), 48 deletions(-) create mode 100644 cmd/defaultFlags.go create mode 100644 cmd/defaultFlagsDelete.go create mode 100644 cmd/defaultFlagsGet.go create mode 100644 cmd/defaultFlagsList.go create mode 100644 cmd/defaultFlagsSet.go create mode 100644 flags/defaults/constants.go create mode 100644 flags/defaults/core.go create mode 100644 flags/defaults/errors.go create mode 100644 flags/defaults/types.go diff --git a/README.md b/README.md index 7beb881..8c14ec4 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,17 @@ Usage: lw [command] Available Commands: - asset All things assets - auth authentication actions - cloud Interact with LiquidWeb's Cloud platform - completion Generate completion script - dedicated All things dedicated server - help Help about any command - network network actions - plan Process YAML plan file - ssh SSH to a Server - version show build information + asset All things assets + auth authentication actions + cloud Interact with LiquidWeb's Cloud platform + completion Generate completion script + dedicated All things dedicated server + default-flags Manage default flags + help Help about any command + network network actions + plan Process YAML plan file + ssh SSH to a Server + version show build information Flags: --config string config file (default is $HOME/.liquidweb-cli.yaml) diff --git a/cmd/cloudNetworkVipCreate.go b/cmd/cloudNetworkVipCreate.go index 1daa41b..b653cec 100644 --- a/cmd/cloudNetworkVipCreate.go +++ b/cmd/cloudNetworkVipCreate.go @@ -18,8 +18,10 @@ package cmd import ( "fmt" + "github.com/spf13/cast" "github.com/spf13/cobra" + "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/types/api" "github.com/liquidweb/liquidweb-cli/utils" "github.com/liquidweb/liquidweb-cli/validate" @@ -93,10 +95,6 @@ func init() { cloudNetworkVipCmd.AddCommand(cloudNetworkVipCreateCmd) cloudNetworkVipCreateCmd.Flags().String("name", fmt.Sprintf("vip-%s", utils.RandomString(8)), "name for the new VIP") - cloudNetworkVipCreateCmd.Flags().Int64("zone", -1, + cloudNetworkVipCreateCmd.Flags().Int64("zone", cast.ToInt64(defaults.GetOrNag("zone")), "zone id to create VIP in (see: 'cloud server options --zones')") - - if err := cloudNetworkVipCreateCmd.MarkFlagRequired("zone"); err != nil { - lwCliInst.Die(err) - } } diff --git a/cmd/cloudPrivateParentCreate.go b/cmd/cloudPrivateParentCreate.go index 7afd519..76ede78 100644 --- a/cmd/cloudPrivateParentCreate.go +++ b/cmd/cloudPrivateParentCreate.go @@ -18,8 +18,10 @@ package cmd import ( "fmt" + "github.com/spf13/cast" "github.com/spf13/cobra" + "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/types/api" "github.com/liquidweb/liquidweb-cli/validate" ) @@ -71,15 +73,13 @@ func init() { cloudPrivateParentCreateCmd.Flags().Int64("config-id", -1, "config-id (category must be bare-metal or bare-metal-r)") cloudPrivateParentCreateCmd.Flags().String("name", "", "name for your Private Parent") - cloudPrivateParentCreateCmd.Flags().Int64("zone", -1, "id number of the zone to provision the Private Parent in ('cloud server options --zones')") + cloudPrivateParentCreateCmd.Flags().Int64("zone", cast.ToInt64(defaults.GetOrNag("zone")), + "id number of the zone to provision the Private Parent in ('cloud server options --zones')") - if err := cloudPrivateParentCreateCmd.MarkFlagRequired("config-id"); err != nil { - lwCliInst.Die(err) - } - if err := cloudPrivateParentCreateCmd.MarkFlagRequired("zone"); err != nil { - lwCliInst.Die(err) - } - if err := cloudPrivateParentCreateCmd.MarkFlagRequired("name"); err != nil { - lwCliInst.Die(err) + reqs := []string{"config-id", "name"} + for _, req := range reqs { + if err := cloudPrivateParentCreateCmd.MarkFlagRequired(req); err != nil { + lwCliInst.Die(err) + } } } diff --git a/cmd/cloudServerCreate.go b/cmd/cloudServerCreate.go index de32890..9ee1f45 100644 --- a/cmd/cloudServerCreate.go +++ b/cmd/cloudServerCreate.go @@ -24,6 +24,7 @@ import ( "github.com/spf13/cast" "github.com/spf13/cobra" + "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/instance" ) @@ -134,7 +135,7 @@ func init() { sshPubKeyFile = fmt.Sprintf("%s/.ssh/id_rsa.pub", home) } - cloudServerCreateCmd.Flags().String("template", "", "template to use (see 'cloud server options --templates')") + cloudServerCreateCmd.Flags().String("template", cast.ToString(defaults.GetOrNag("template")), "template to use (see 'cloud server options --templates')") cloudServerCreateCmd.Flags().String("type", "SS.VPS", "some examples of types; SS.VPS, SS.VPS.WIN, SS.VM, SS.VM.WIN") cloudServerCreateCmd.Flags().String("hostname", "", "hostname to set") cloudServerCreateCmd.Flags().Int("ips", 1, "amount of IP addresses") @@ -144,7 +145,7 @@ func init() { cloudServerCreateCmd.Flags().Int("backup-days", -1, "Enable daily backup plan. This is the amount of days to keep a backup") cloudServerCreateCmd.Flags().Int("backup-quota", -1, "Enable quota backup plan. This is the total amount of GB to keep.") cloudServerCreateCmd.Flags().String("bandwidth", "SS.10000", "bandwidth package to use") - cloudServerCreateCmd.Flags().Int64("zone", 0, "zone (id) to create new Cloud Server in (see 'cloud server options --zones')") + cloudServerCreateCmd.Flags().Int64("zone", cast.ToInt64(defaults.GetOrNag("zone")), "zone (id) to create new Cloud Server in (see 'cloud server options --zones')") cloudServerCreateCmd.Flags().String("password", "", "root or administrator password to set") cloudServerCreateCmd.Flags().Int("backup-id", -1, "id of cloud backup to create from (see 'cloud backup list')") diff --git a/cmd/cloudTemplateRestore.go b/cmd/cloudTemplateRestore.go index d3e2b1c..d7959cb 100644 --- a/cmd/cloudTemplateRestore.go +++ b/cmd/cloudTemplateRestore.go @@ -18,8 +18,10 @@ package cmd import ( "fmt" + "github.com/spf13/cast" "github.com/spf13/cobra" + "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/instance" ) @@ -59,12 +61,9 @@ func init() { cloudTemplateCmd.AddCommand(cloudTemplateRestoreCmd) cloudTemplateRestoreCmd.Flags().String("uniq-id", "", "uniq-id of Cloud Server") - cloudTemplateRestoreCmd.Flags().String("template", "", "name of template to restore") + cloudTemplateRestoreCmd.Flags().String("template", cast.ToString(defaults.GetOrNag("template")), "name of template to restore") if err := cloudTemplateRestoreCmd.MarkFlagRequired("uniq-id"); err != nil { lwCliInst.Die(err) } - if err := cloudTemplateRestoreCmd.MarkFlagRequired("template"); err != nil { - lwCliInst.Die(err) - } } diff --git a/cmd/defaultFlags.go b/cmd/defaultFlags.go new file mode 100644 index 0000000..0e1114a --- /dev/null +++ b/cmd/defaultFlags.go @@ -0,0 +1,43 @@ +/* +Copyright © LiquidWeb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +var defaultFlagsCmd = &cobra.Command{ + Use: "default-flags", + Short: "Manage default flags", + Long: `Manage the configured default flags. + +If a default flag is set (such as for "zone") then any subcommand will use its +value in place if omitted. + +For a full list of capabilities, please refer to the "Available Commands" section.`, + Run: func(cmd *cobra.Command, args []string) { + if err := cmd.Help(); err != nil { + lwCliInst.Die(err) + } + os.Exit(1) + }, +} + +func init() { + rootCmd.AddCommand(defaultFlagsCmd) +} diff --git a/cmd/defaultFlagsDelete.go b/cmd/defaultFlagsDelete.go new file mode 100644 index 0000000..f4f6bc5 --- /dev/null +++ b/cmd/defaultFlagsDelete.go @@ -0,0 +1,50 @@ +/* +Copyright © 2019 LiquidWeb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/liquidweb/liquidweb-cli/flags/defaults" +) + +var defaultFlagsDeleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete a default flag", + Long: `Delete a default flag. + +When a default flag is set (such as "zone") then any subcommand will use its +value in place if omitted.`, + Run: func(cmd *cobra.Command, args []string) { + flagName, _ := cmd.Flags().GetString("flag") + + if err := defaults.Delete(flagName); err != nil { + lwCliInst.Die(err) + } + + fmt.Printf("deleted default flag [%s]\n", flagName) + }, +} + +func init() { + defaultFlagsCmd.AddCommand(defaultFlagsDeleteCmd) + defaultFlagsDeleteCmd.Flags().String("flag", "", "name of the default flag to delete") + if err := defaultFlagsDeleteCmd.MarkFlagRequired("flag"); err != nil { + lwCliInst.Die(err) + } +} diff --git a/cmd/defaultFlagsGet.go b/cmd/defaultFlagsGet.go new file mode 100644 index 0000000..d15f893 --- /dev/null +++ b/cmd/defaultFlagsGet.go @@ -0,0 +1,52 @@ +/* +Copyright © 2019 LiquidWeb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/liquidweb/liquidweb-cli/flags/defaults" +) + +var defaultFlagsGetCmd = &cobra.Command{ + Use: "get", + Short: "Get details on a flag", + Long: `Get details on a flag. + +When a default flag is set (such as "zone") then any subcommand will use its +value in place if omitted.`, + Run: func(cmd *cobra.Command, args []string) { + flagName, _ := cmd.Flags().GetString("flag") + + value, err := defaults.Get(flagName) + if err != nil { + lwCliInst.Die(err) + } + + fmt.Printf("flag: %s\n", flagName) + fmt.Printf("\tvalue: %+v\n", value) + }, +} + +func init() { + defaultFlagsCmd.AddCommand(defaultFlagsGetCmd) + defaultFlagsGetCmd.Flags().String("flag", "", "name of the default flag") + if err := defaultFlagsGetCmd.MarkFlagRequired("flag"); err != nil { + lwCliInst.Die(err) + } +} diff --git a/cmd/defaultFlagsList.go b/cmd/defaultFlagsList.go new file mode 100644 index 0000000..a1d85aa --- /dev/null +++ b/cmd/defaultFlagsList.go @@ -0,0 +1,43 @@ +/* +Copyright © 2019 LiquidWeb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/liquidweb/liquidweb-cli/flags/defaults" +) + +var defaultFlagsListCmd = &cobra.Command{ + Use: "list", + Short: "Display your set default flags", + Long: `Display your set default flags. + +If you've never created any default flags, check "default-flags add".`, + Run: func(cmd *cobra.Command, args []string) { + all, err := defaults.GetAll() + if err != nil { + lwCliInst.Die(err) + } + fmt.Print(all) + }, +} + +func init() { + defaultFlagsCmd.AddCommand(defaultFlagsListCmd) +} diff --git a/cmd/defaultFlagsSet.go b/cmd/defaultFlagsSet.go new file mode 100644 index 0000000..4eeed7b --- /dev/null +++ b/cmd/defaultFlagsSet.go @@ -0,0 +1,55 @@ +/* +Copyright © 2019 LiquidWeb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/liquidweb/liquidweb-cli/flags/defaults" +) + +var defaultFlagsSetCmd = &cobra.Command{ + Use: "set", + Short: "Set a default flag", + Long: `Set a default flag. + +When a default flag is set (such as "zone") then any subcommand will use its +value in place if omitted.`, + Run: func(cmd *cobra.Command, args []string) { + flagName, _ := cmd.Flags().GetString("flag") + flagValue, _ := cmd.Flags().GetString("value") + + if err := defaults.Set(flagName, flagValue); err != nil { + lwCliInst.Die(err) + } + + fmt.Printf("default flag [%s] set with value [%s]\n", flagName, flagValue) + }, +} + +func init() { + defaultFlagsCmd.AddCommand(defaultFlagsSetCmd) + defaultFlagsSetCmd.Flags().String("flag", "", "name of the default flag to set") + defaultFlagsSetCmd.Flags().String("value", "", "value for the default flag") + reqs := []string{"flag", "value"} + for _, req := range reqs { + if err := defaultFlagsSetCmd.MarkFlagRequired(req); err != nil { + lwCliInst.Die(err) + } + } +} diff --git a/cmd/networkIpPoolCreate.go b/cmd/networkIpPoolCreate.go index d31ae1f..df6d816 100644 --- a/cmd/networkIpPoolCreate.go +++ b/cmd/networkIpPoolCreate.go @@ -18,8 +18,10 @@ package cmd import ( "fmt" + "github.com/spf13/cast" "github.com/spf13/cobra" + "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/types/api" ) @@ -36,11 +38,8 @@ your account.`, zoneFlag, _ := cmd.Flags().GetInt64("zone") newIpsFlag, _ := cmd.Flags().GetInt64("new-ips") - //fmt.Printf("networkIpPoolCreateCmdAddIpsFlag: %+v\n", networkIpPoolCreateCmdAddIpsFlag) - //fmt.Printf("%d %d\n", newIpsFlag, zoneFlag) - - if len(networkIpPoolCreateCmdAddIpsFlag) == 0 && newIpsFlag == -1 { - lwCliInst.Die(fmt.Errorf("flags --new-ips --add-ips cannot both be empty")) + if len(networkIpPoolCreateCmdAddIpsFlag) == 0 && newIpsFlag == -1 || zoneFlag == 0 { + lwCliInst.Die(fmt.Errorf("flags --new-ips --add-ips cannot both be empty. --zone cannot be empty")) } apiArgs := map[string]interface{}{ @@ -68,9 +67,6 @@ func init() { networkIpPoolCreateCmd.Flags().StringSliceVar(&networkIpPoolCreateCmdAddIpsFlag, "add-ips", []string{}, "ips separated by ',' to add to created IP Pool") networkIpPoolCreateCmd.Flags().Int64("new-ips", -1, "amount of IPs to assign to the created IP Pool") - networkIpPoolCreateCmd.Flags().Int64("zone", -1, "zone id to create the IP Pool in") - - if err := networkIpPoolCreateCmd.MarkFlagRequired("zone"); err != nil { - lwCliInst.Die(err) - } + networkIpPoolCreateCmd.Flags().Int64("zone", cast.ToInt64(defaults.GetOrNag("zone")), + "zone id to create the IP Pool in") } diff --git a/cmd/root.go b/cmd/root.go index b7c3766..3b4a464 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -30,7 +30,7 @@ import ( ) var cfgFile string -var lwCliInst instance.Client +var lwCliInst *instance.Client var useContext string var rootCmd = &cobra.Command{ @@ -70,17 +70,16 @@ func init() { func initConfig() { vp := viper.New() + if cfgFile != "" { // Use config file from the flag. vp.SetConfigFile(cfgFile) } else { - // Find home directory. + // Search config in home directory with name ".liquidweb-cli" (without extension). home, err := homedir.Dir() if err != nil { lwCliInst.Die(err) } - - // Search config in home directory with name ".liquidweb-cli" (without extension). vp.AddConfigPath(home) vp.SetConfigName(".liquidweb-cli") } diff --git a/flags/defaults/constants.go b/flags/defaults/constants.go new file mode 100644 index 0000000..28293bc --- /dev/null +++ b/flags/defaults/constants.go @@ -0,0 +1,4 @@ +package defaults + +const DefFlagsKey = "defaults" +const DefaultFlagsFileKey = "liquidweb.flags.defaults.file" diff --git a/flags/defaults/core.go b/flags/defaults/core.go new file mode 100644 index 0000000..a808c3c --- /dev/null +++ b/flags/defaults/core.go @@ -0,0 +1,182 @@ +package defaults + +import ( + "errors" + "fmt" + "os" + + homedir "github.com/mitchellh/go-homedir" + "github.com/spf13/viper" + + "github.com/liquidweb/liquidweb-cli/utils" +) + +func init() { + home, err := homedir.Dir() + if err != nil { + utils.PrintYellow("failed fetching homedir: %s\n", err) + return + } + viper.SetDefault("liquidweb.flags.defaults.file", fmt.Sprintf("%s/.liquidweb-cli-flag-defaults.yaml", home)) +} + +//var flagDefaultsFile = viper.GetString("liquidweb.flags.defaults.file") + +func GetOrNag(flag string) (value interface{}) { + var err error + value, err = Get(flag) + if err != nil { + if errors.Is(err, ErrorNotFound) { + utils.PrintTeal("No default for flag [%s] set. See 'help default-flags set' for details.\n", flag) + } else { + utils.PrintYellow("Unexpected error when fetching value for default flag [%s]: %s\n", flag, err) + } + } + return +} + +func Get(flag string) (value interface{}, err error) { + if err = permittedFlagOrError(flag); err != nil { + return + } + + var flags map[string]interface{} + flags, err = getFlagsMap() + if err != nil { + return + } + + if v, exists := flags[flag]; exists { + value = v + return + } + + err = fmt.Errorf("%s %w", flag, ErrorNotFound) + return +} + +func GetAll() (all AllFlags, err error) { + all, err = getFlagsMap() + + return +} + +func Set(flag string, value interface{}) (err error) { + if err = permittedFlagOrError(flag); err != nil { + return + } + + var ( + vp *viper.Viper + flags map[string]interface{} + ) + vp, flags, err = getFlagsViperAndMap() + if err != nil { + return + } + + flags[flag] = value + vp.Set(DefFlagsKey, flags) + + if err = vp.WriteConfig(); err != nil { + err = fmt.Errorf("%w: %s", ErrorUnwritable, err) + } + + return +} + +func Delete(flag string) (err error) { + if err = permittedFlagOrError(flag); err != nil { + return + } + + var ( + vp *viper.Viper + flags map[string]interface{} + ) + vp, flags, err = getFlagsViperAndMap() + if err != nil { + return + } + + delete(flags, flag) + vp.Set(DefFlagsKey, flags) + err = vp.WriteConfig() + + return +} + +func permittedFlagOrError(flag string) (err error) { + if flag == "" { + err = ErrorInvalidFlagName + return + } + + if _, exists := permittedFlags[flag]; !exists { + err = fmt.Errorf("%s %w", flag, ErrorForbiddenFlag) + } + + return +} + +func getFlagsViperAndMap() (vp *viper.Viper, flags map[string]interface{}, err error) { + vp, err = getFlagsViper() + if err != nil { + return + } + + flags, err = getFlagsMap(vp) + + return +} + +func getFlagsMap(vpL ...*viper.Viper) (flags map[string]interface{}, err error) { + var vp *viper.Viper + if len(vpL) == 0 { + if vp, err = getFlagsViper(); err != nil { + return + } + } else { + vp = vpL[0] + } + + flags = vp.GetStringMap(DefFlagsKey) + + return +} + +func getFlagsViper() (vp *viper.Viper, err error) { + var file string + file, err = getFlagsFile() + if err != nil { + return + } + + vp = viper.New() + vp.SetConfigFile(file) + if err = vp.ReadInConfig(); err != nil { + err = fmt.Errorf("%w: %s", ErrorUnreadable, err) + return + } + + return +} + +func getFlagsFile() (file string, err error) { + file = viper.GetString(DefaultFlagsFileKey) + if file == "" { + err = ErrorFileKeyMissing + } + + if _, err = os.Stat(file); os.IsNotExist(err) { + err = nil + f, ferr := os.Create(file) + if ferr != nil { + err = ferr + return + } + err = f.Close() + } + + return +} diff --git a/flags/defaults/errors.go b/flags/defaults/errors.go new file mode 100644 index 0000000..09bdc98 --- /dev/null +++ b/flags/defaults/errors.go @@ -0,0 +1,12 @@ +package defaults + +import ( + "errors" +) + +var ErrorForbiddenFlag = errors.New("is a forbidden default flag") +var ErrorInvalidFlagName = errors.New("the given flag name is invalid") +var ErrorFileKeyMissing = errors.New("flag defaults file key is missing") +var ErrorUnwritable = errors.New("flag defaults cannot be written") +var ErrorUnreadable = errors.New("flag defaults cannot be read") +var ErrorNotFound = errors.New("flag default not found") diff --git a/flags/defaults/types.go b/flags/defaults/types.go new file mode 100644 index 0000000..d8bd16b --- /dev/null +++ b/flags/defaults/types.go @@ -0,0 +1,30 @@ +package defaults + +import ( + "fmt" + "strings" +) + +type AllFlags map[string]interface{} + +func (self AllFlags) String() string { + var slice []string + + if len(self) == 0 { + slice = append(slice, "No configured default flags. Set some with 'default-flags set'.\n") + } else { + slice = append(slice, "Configured default flags:\n\n") + + for flag, value := range self { + slice = append(slice, fmt.Sprintf("\tFlag: %s\n", flag)) + slice = append(slice, fmt.Sprintf("\t\tValue: %+v\n", value)) + } + } + + return strings.Join(slice[:], "") +} + +var permittedFlags = map[string]interface{}{ + "zone": true, + "template": true, +} diff --git a/instance/instance.go b/instance/instance.go index 53bba7e..2114ba9 100644 --- a/instance/instance.go +++ b/instance/instance.go @@ -32,16 +32,15 @@ import ( "github.com/liquidweb/liquidweb-cli/utils" ) -func New(viper *viper.Viper) (Client, error) { - +func New(viper *viper.Viper) (*Client, error) { lwCliApiClient, err := lwCliInstApi.New(viper) if err != nil { - return Client{}, fmt.Errorf( + return &Client{}, fmt.Errorf( "Failed creating an lwApi client. Error was:\n%s\nPlease check your liquidweb-cli config file for errors or ommissions\n", err) } - client := Client{ + client := &Client{ LwCliApiClient: lwCliApiClient, Viper: viper, } From 32a4ad46b8f24194a49bdce0e40cf681cca204ad Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Thu, 31 Dec 2020 11:18:16 -0500 Subject: [PATCH 02/13] default flags should obey context --- cmd/cloudNetworkVipCreate.go | 3 +- cmd/cloudPrivateParentCreate.go | 3 +- cmd/cloudServerCreate.go | 7 +-- cmd/cloudTemplateRestore.go | 3 +- cmd/defaultFlags.go | 3 +- cmd/defaultFlagsDelete.go | 9 ++-- cmd/defaultFlagsGet.go | 7 +-- cmd/defaultFlagsList.go | 6 ++- cmd/defaultFlagsNagOff.go | 45 +++++++++++++++++ cmd/defaultFlagsNagOn.go | 45 +++++++++++++++++ cmd/defaultFlagsPermitted.go | 50 +++++++++++++++++++ cmd/defaultFlagsSet.go | 13 ++--- cmd/networkIpPoolCreate.go | 3 +- cmd/root.go | 67 ++++++++++++++++---------- config/core.go | 52 ++++++++++++++++++++ flags/defaults/constants.go | 1 + flags/defaults/core.go | 85 ++++++++++++++++++++++++++++----- flags/defaults/types.go | 6 +-- 18 files changed, 342 insertions(+), 66 deletions(-) create mode 100644 cmd/defaultFlagsNagOff.go create mode 100644 cmd/defaultFlagsNagOn.go create mode 100644 cmd/defaultFlagsPermitted.go create mode 100644 config/core.go diff --git a/cmd/cloudNetworkVipCreate.go b/cmd/cloudNetworkVipCreate.go index b653cec..acddc49 100644 --- a/cmd/cloudNetworkVipCreate.go +++ b/cmd/cloudNetworkVipCreate.go @@ -21,7 +21,6 @@ import ( "github.com/spf13/cast" "github.com/spf13/cobra" - "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/types/api" "github.com/liquidweb/liquidweb-cli/utils" "github.com/liquidweb/liquidweb-cli/validate" @@ -95,6 +94,6 @@ func init() { cloudNetworkVipCmd.AddCommand(cloudNetworkVipCreateCmd) cloudNetworkVipCreateCmd.Flags().String("name", fmt.Sprintf("vip-%s", utils.RandomString(8)), "name for the new VIP") - cloudNetworkVipCreateCmd.Flags().Int64("zone", cast.ToInt64(defaults.GetOrNag("zone")), + cloudNetworkVipCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone")), "zone id to create VIP in (see: 'cloud server options --zones')") } diff --git a/cmd/cloudPrivateParentCreate.go b/cmd/cloudPrivateParentCreate.go index 76ede78..91a52bd 100644 --- a/cmd/cloudPrivateParentCreate.go +++ b/cmd/cloudPrivateParentCreate.go @@ -21,7 +21,6 @@ import ( "github.com/spf13/cast" "github.com/spf13/cobra" - "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/types/api" "github.com/liquidweb/liquidweb-cli/validate" ) @@ -73,7 +72,7 @@ func init() { cloudPrivateParentCreateCmd.Flags().Int64("config-id", -1, "config-id (category must be bare-metal or bare-metal-r)") cloudPrivateParentCreateCmd.Flags().String("name", "", "name for your Private Parent") - cloudPrivateParentCreateCmd.Flags().Int64("zone", cast.ToInt64(defaults.GetOrNag("zone")), + cloudPrivateParentCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone")), "id number of the zone to provision the Private Parent in ('cloud server options --zones')") reqs := []string{"config-id", "name"} diff --git a/cmd/cloudServerCreate.go b/cmd/cloudServerCreate.go index 9ee1f45..35c3deb 100644 --- a/cmd/cloudServerCreate.go +++ b/cmd/cloudServerCreate.go @@ -24,7 +24,7 @@ import ( "github.com/spf13/cast" "github.com/spf13/cobra" - "github.com/liquidweb/liquidweb-cli/flags/defaults" + "github.com/liquidweb/liquidweb-cli/config" "github.com/liquidweb/liquidweb-cli/instance" ) @@ -88,6 +88,7 @@ cloud: lw plan --file /tmp/cloud.server.create.yaml `, Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("config.CurrentContext is now: %s\n", config.CurrentContext) params := &instance.CloudServerCreateParams{} params.Template, _ = cmd.Flags().GetString("template") @@ -135,7 +136,7 @@ func init() { sshPubKeyFile = fmt.Sprintf("%s/.ssh/id_rsa.pub", home) } - cloudServerCreateCmd.Flags().String("template", cast.ToString(defaults.GetOrNag("template")), "template to use (see 'cloud server options --templates')") + cloudServerCreateCmd.Flags().String("template", cast.ToString(defaultFlag("template")), "template to use (see 'cloud server options --templates')") cloudServerCreateCmd.Flags().String("type", "SS.VPS", "some examples of types; SS.VPS, SS.VPS.WIN, SS.VM, SS.VM.WIN") cloudServerCreateCmd.Flags().String("hostname", "", "hostname to set") cloudServerCreateCmd.Flags().Int("ips", 1, "amount of IP addresses") @@ -145,7 +146,7 @@ func init() { cloudServerCreateCmd.Flags().Int("backup-days", -1, "Enable daily backup plan. This is the amount of days to keep a backup") cloudServerCreateCmd.Flags().Int("backup-quota", -1, "Enable quota backup plan. This is the total amount of GB to keep.") cloudServerCreateCmd.Flags().String("bandwidth", "SS.10000", "bandwidth package to use") - cloudServerCreateCmd.Flags().Int64("zone", cast.ToInt64(defaults.GetOrNag("zone")), "zone (id) to create new Cloud Server in (see 'cloud server options --zones')") + cloudServerCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone")), "zone (id) to create new Cloud Server in (see 'cloud server options --zones')") cloudServerCreateCmd.Flags().String("password", "", "root or administrator password to set") cloudServerCreateCmd.Flags().Int("backup-id", -1, "id of cloud backup to create from (see 'cloud backup list')") diff --git a/cmd/cloudTemplateRestore.go b/cmd/cloudTemplateRestore.go index d7959cb..39f9554 100644 --- a/cmd/cloudTemplateRestore.go +++ b/cmd/cloudTemplateRestore.go @@ -21,7 +21,6 @@ import ( "github.com/spf13/cast" "github.com/spf13/cobra" - "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/instance" ) @@ -61,7 +60,7 @@ func init() { cloudTemplateCmd.AddCommand(cloudTemplateRestoreCmd) cloudTemplateRestoreCmd.Flags().String("uniq-id", "", "uniq-id of Cloud Server") - cloudTemplateRestoreCmd.Flags().String("template", cast.ToString(defaults.GetOrNag("template")), "name of template to restore") + cloudTemplateRestoreCmd.Flags().String("template", cast.ToString(defaultFlag("template")), "name of template to restore") if err := cloudTemplateRestoreCmd.MarkFlagRequired("uniq-id"); err != nil { lwCliInst.Die(err) diff --git a/cmd/defaultFlags.go b/cmd/defaultFlags.go index 0e1114a..f83e19e 100644 --- a/cmd/defaultFlags.go +++ b/cmd/defaultFlags.go @@ -27,7 +27,8 @@ var defaultFlagsCmd = &cobra.Command{ Long: `Manage the configured default flags. If a default flag is set (such as for "zone") then any subcommand will use its -value in place if omitted. +value in place if omitted. Default flags are auth context aware. For details +on auth contexts, see 'help auth'. For a full list of capabilities, please refer to the "Available Commands" section.`, Run: func(cmd *cobra.Command, args []string) { diff --git a/cmd/defaultFlagsDelete.go b/cmd/defaultFlagsDelete.go index f4f6bc5..a8abc65 100644 --- a/cmd/defaultFlagsDelete.go +++ b/cmd/defaultFlagsDelete.go @@ -25,11 +25,12 @@ import ( var defaultFlagsDeleteCmd = &cobra.Command{ Use: "delete", - Short: "Delete a default flag", - Long: `Delete a default flag. + Short: "Delete a flag", + Long: `Delete a flag for the current context. When a default flag is set (such as "zone") then any subcommand will use its -value in place if omitted.`, +value in place if omitted. Default flags are auth context aware. For details +on auth contexts, see 'help auth'.`, Run: func(cmd *cobra.Command, args []string) { flagName, _ := cmd.Flags().GetString("flag") @@ -43,7 +44,7 @@ value in place if omitted.`, func init() { defaultFlagsCmd.AddCommand(defaultFlagsDeleteCmd) - defaultFlagsDeleteCmd.Flags().String("flag", "", "name of the default flag to delete") + defaultFlagsDeleteCmd.Flags().String("flag", "", "name of the flag to delete") if err := defaultFlagsDeleteCmd.MarkFlagRequired("flag"); err != nil { lwCliInst.Die(err) } diff --git a/cmd/defaultFlagsGet.go b/cmd/defaultFlagsGet.go index d15f893..5636101 100644 --- a/cmd/defaultFlagsGet.go +++ b/cmd/defaultFlagsGet.go @@ -26,10 +26,11 @@ import ( var defaultFlagsGetCmd = &cobra.Command{ Use: "get", Short: "Get details on a flag", - Long: `Get details on a flag. + Long: `Get details on a flag in the current context. When a default flag is set (such as "zone") then any subcommand will use its -value in place if omitted.`, +value in place if omitted. Default flags are auth context aware. For details +on auth contexts, see 'help auth'.`, Run: func(cmd *cobra.Command, args []string) { flagName, _ := cmd.Flags().GetString("flag") @@ -45,7 +46,7 @@ value in place if omitted.`, func init() { defaultFlagsCmd.AddCommand(defaultFlagsGetCmd) - defaultFlagsGetCmd.Flags().String("flag", "", "name of the default flag") + defaultFlagsGetCmd.Flags().String("flag", "", "name of the flag") if err := defaultFlagsGetCmd.MarkFlagRequired("flag"); err != nil { lwCliInst.Die(err) } diff --git a/cmd/defaultFlagsList.go b/cmd/defaultFlagsList.go index a1d85aa..587b2e0 100644 --- a/cmd/defaultFlagsList.go +++ b/cmd/defaultFlagsList.go @@ -28,7 +28,11 @@ var defaultFlagsListCmd = &cobra.Command{ Short: "Display your set default flags", Long: `Display your set default flags. -If you've never created any default flags, check "default-flags add".`, +If you've never created any default flags, see 'help default-flags set'. + +When a default flag is set (such as "zone") then any subcommand will use its +value in place if omitted. Default flags are auth context aware. For details +on auth contexts, see 'help auth'.`, Run: func(cmd *cobra.Command, args []string) { all, err := defaults.GetAll() if err != nil { diff --git a/cmd/defaultFlagsNagOff.go b/cmd/defaultFlagsNagOff.go new file mode 100644 index 0000000..2e5c25a --- /dev/null +++ b/cmd/defaultFlagsNagOff.go @@ -0,0 +1,45 @@ +/* +Copyright © 2019 LiquidWeb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/liquidweb/liquidweb-cli/flags/defaults" +) + +var defaultFlagsNagOffCmd = &cobra.Command{ + Use: "nags-off", + Short: "Turn nags off", + Long: `Turn nags off for unset default flags. + +When a default flag is set (such as "zone") then any subcommand will use its +value in place if omitted. Default flags are auth context aware. For details +on auth contexts, see 'help auth'.`, + Run: func(cmd *cobra.Command, args []string) { + if err := defaults.NagsOff(); err != nil { + lwCliInst.Die(err) + } + + fmt.Println("Nags turned off.") + }, +} + +func init() { + defaultFlagsCmd.AddCommand(defaultFlagsNagOffCmd) +} diff --git a/cmd/defaultFlagsNagOn.go b/cmd/defaultFlagsNagOn.go new file mode 100644 index 0000000..2ff0522 --- /dev/null +++ b/cmd/defaultFlagsNagOn.go @@ -0,0 +1,45 @@ +/* +Copyright © 2019 LiquidWeb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/liquidweb/liquidweb-cli/flags/defaults" +) + +var defaultFlagsNagOnCmd = &cobra.Command{ + Use: "nags-on", + Short: "Turn nags on", + Long: `Turn nags on for unset default flags. + +When a default flag is set (such as "zone") then any subcommand will use its +value in place if omitted. Default flags are auth context aware. For details +on auth contexts, see 'help auth'.`, + Run: func(cmd *cobra.Command, args []string) { + if err := defaults.NagsOn(); err != nil { + lwCliInst.Die(err) + } + + fmt.Println("Nags turned on.") + }, +} + +func init() { + defaultFlagsCmd.AddCommand(defaultFlagsNagOnCmd) +} diff --git a/cmd/defaultFlagsPermitted.go b/cmd/defaultFlagsPermitted.go new file mode 100644 index 0000000..ab70cba --- /dev/null +++ b/cmd/defaultFlagsPermitted.go @@ -0,0 +1,50 @@ +/* +Copyright © 2019 LiquidWeb + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/liquidweb/liquidweb-cli/flags/defaults" +) + +var defaultFlagsPermittedCmd = &cobra.Command{ + Use: "permitted", + Short: "Display permitted default flags", + Long: `Display permitted default flags. + +If you've never created any default flags, see 'help default-flags set'. + +When a default flag is set (such as "zone") then any subcommand will use its +value in place if omitted. Default flags are auth context aware. For details +on auth contexts, see 'help auth'.`, + Run: func(cmd *cobra.Command, args []string) { + permitted := defaults.GetPermitted() + fmt.Println("Permitted flags:") + for flag, v := range permitted { + if !v { + continue + } + fmt.Printf(" %s\n", flag) + } + }, +} + +func init() { + defaultFlagsCmd.AddCommand(defaultFlagsPermittedCmd) +} diff --git a/cmd/defaultFlagsSet.go b/cmd/defaultFlagsSet.go index 4eeed7b..48949a6 100644 --- a/cmd/defaultFlagsSet.go +++ b/cmd/defaultFlagsSet.go @@ -25,11 +25,12 @@ import ( var defaultFlagsSetCmd = &cobra.Command{ Use: "set", - Short: "Set a default flag", - Long: `Set a default flag. + Short: "Set a flag", + Long: `Set a flag for the current context. When a default flag is set (such as "zone") then any subcommand will use its -value in place if omitted.`, +value in place if omitted. Default flags are auth context aware. For details +on auth contexts, see 'help auth'.`, Run: func(cmd *cobra.Command, args []string) { flagName, _ := cmd.Flags().GetString("flag") flagValue, _ := cmd.Flags().GetString("value") @@ -38,14 +39,14 @@ value in place if omitted.`, lwCliInst.Die(err) } - fmt.Printf("default flag [%s] set with value [%s]\n", flagName, flagValue) + fmt.Printf("flag [%s] set with value [%s]\n", flagName, flagValue) }, } func init() { defaultFlagsCmd.AddCommand(defaultFlagsSetCmd) - defaultFlagsSetCmd.Flags().String("flag", "", "name of the default flag to set") - defaultFlagsSetCmd.Flags().String("value", "", "value for the default flag") + defaultFlagsSetCmd.Flags().String("flag", "", "name of the flag to set") + defaultFlagsSetCmd.Flags().String("value", "", "value for the flag") reqs := []string{"flag", "value"} for _, req := range reqs { if err := defaultFlagsSetCmd.MarkFlagRequired(req); err != nil { diff --git a/cmd/networkIpPoolCreate.go b/cmd/networkIpPoolCreate.go index df6d816..90b7cf5 100644 --- a/cmd/networkIpPoolCreate.go +++ b/cmd/networkIpPoolCreate.go @@ -21,7 +21,6 @@ import ( "github.com/spf13/cast" "github.com/spf13/cobra" - "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/types/api" ) @@ -67,6 +66,6 @@ func init() { networkIpPoolCreateCmd.Flags().StringSliceVar(&networkIpPoolCreateCmdAddIpsFlag, "add-ips", []string{}, "ips separated by ',' to add to created IP Pool") networkIpPoolCreateCmd.Flags().Int64("new-ips", -1, "amount of IPs to assign to the created IP Pool") - networkIpPoolCreateCmd.Flags().Int64("zone", cast.ToInt64(defaults.GetOrNag("zone")), + networkIpPoolCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone")), "zone id to create the IP Pool in") } diff --git a/cmd/root.go b/cmd/root.go index 3b4a464..e0fd80a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,13 +18,14 @@ package cmd import ( "fmt" "os" + "regexp" "strings" "github.com/c-bata/go-prompt" - homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" - "github.com/spf13/viper" + "github.com/liquidweb/liquidweb-cli/config" + "github.com/liquidweb/liquidweb-cli/flags/defaults" "github.com/liquidweb/liquidweb-cli/instance" "github.com/liquidweb/liquidweb-cli/utils" ) @@ -68,34 +69,50 @@ func init() { rootCmd.PersistentFlags().StringVar(&useContext, "use-context", "", "forces current context, without persisting the context change") } -func initConfig() { - vp := viper.New() - - if cfgFile != "" { - // Use config file from the flag. - vp.SetConfigFile(cfgFile) - } else { - // Search config in home directory with name ".liquidweb-cli" (without extension). - home, err := homedir.Dir() - if err != nil { - lwCliInst.Die(err) +func setConfigArgs() { + config.UseContextArg = useContext + config.ConfigFileArg = cfgFile + + if config.UseContextArg == "" { + osArgsForContext(regexp.MustCompile(`use-context\s+[A-z]+`)) + if config.UseContextArg == "" { + osArgsForContext(regexp.MustCompile(`use-context=[A-z]+`)) } - vp.AddConfigPath(home) - vp.SetConfigName(".liquidweb-cli") } +} - vp.AutomaticEnv() - if err := vp.ReadInConfig(); err != nil { - utils.PrintYellow("no config\n") +// this gets called early on before cobra fully initializes, so have to +// parse os.Args directly. +func osArgsForContext(re *regexp.Regexp) { + var searchStr string + for _, str := range os.Args { + searchStr = searchStr + " " + str } - if useContext != "" { - if err := instance.ValidateContext(useContext, vp); err != nil { - utils.PrintRed("error using auth context:\n\n") - fmt.Printf("%s\n\n", err) - os.Exit(1) - } - vp.Set("liquidweb.api.current_context", useContext) + delimiter := " " + if strings.Contains(searchStr, "=") { + delimiter = "=" + } + + slice := strings.Split(re.FindString(searchStr), delimiter) + if len(slice) > 1 && slice[1] != "" { + config.UseContextArg = slice[1] + config.CurrentContext = slice[1] + } +} + +func defaultFlag(flag string) (value interface{}) { + // calling config.InitConfig() here so default context gets set + _, _ = config.InitConfig() + setConfigArgs() + value = defaults.GetOrNag(flag) + return +} + +func initConfig() { + vp, err := config.InitConfig() + if err != nil { + lwCliInst.Die(err) } var lwCliInstErr error diff --git a/config/core.go b/config/core.go new file mode 100644 index 0000000..ee79fb7 --- /dev/null +++ b/config/core.go @@ -0,0 +1,52 @@ +package config + +import ( + "fmt" + + homedir "github.com/mitchellh/go-homedir" + "github.com/spf13/viper" + + "github.com/liquidweb/liquidweb-cli/instance" + "github.com/liquidweb/liquidweb-cli/utils" +) + +var ( + CurrentContext string + ConfigFileArg string + UseContextArg string +) + +func InitConfig() (vp *viper.Viper, err error) { + vp = viper.New() + + if ConfigFileArg != "" { + // Use config file from the flag. + vp.SetConfigFile(ConfigFileArg) + } else { + // Search config in home directory with name ".liquidweb-cli" (without extension). + var home string + home, err = homedir.Dir() + if err != nil { + return + } + vp.AddConfigPath(home) + vp.SetConfigName(".liquidweb-cli") + } + + vp.AutomaticEnv() + if err := vp.ReadInConfig(); err != nil { + utils.PrintYellow("no config\n") + } + + if UseContextArg != "" { + if err = instance.ValidateContext(UseContextArg, vp); err != nil { + err = fmt.Errorf("error using auth context: %s\n", err) + return + } + vp.Set("liquidweb.api.current_context", UseContextArg) + } + + CurrentContext = vp.GetString("liquidweb.api.current_context") + + return +} diff --git a/flags/defaults/constants.go b/flags/defaults/constants.go index 28293bc..371093e 100644 --- a/flags/defaults/constants.go +++ b/flags/defaults/constants.go @@ -1,4 +1,5 @@ package defaults +const NagsKey = "nags" const DefFlagsKey = "defaults" const DefaultFlagsFileKey = "liquidweb.flags.defaults.file" diff --git a/flags/defaults/core.go b/flags/defaults/core.go index a808c3c..2787654 100644 --- a/flags/defaults/core.go +++ b/flags/defaults/core.go @@ -8,10 +8,19 @@ import ( homedir "github.com/mitchellh/go-homedir" "github.com/spf13/viper" + "github.com/liquidweb/liquidweb-cli/config" "github.com/liquidweb/liquidweb-cli/utils" ) +var ( + nagged map[string]bool + nags bool + tipped bool +) + func init() { + nagged = map[string]bool{} + home, err := homedir.Dir() if err != nil { utils.PrintYellow("failed fetching homedir: %s\n", err) @@ -20,16 +29,40 @@ func init() { viper.SetDefault("liquidweb.flags.defaults.file", fmt.Sprintf("%s/.liquidweb-cli-flag-defaults.yaml", home)) } -//var flagDefaultsFile = viper.GetString("liquidweb.flags.defaults.file") +func NagsOff() (err error) { + err = toggleNags(false) + + return +} + +func NagsOn() (err error) { + err = toggleNags(true) + + return +} + +func GetPermitted() (permitted map[string]bool) { + permitted = permittedFlags + return +} func GetOrNag(flag string) (value interface{}) { var err error value, err = Get(flag) if err != nil { - if errors.Is(err, ErrorNotFound) { - utils.PrintTeal("No default for flag [%s] set. See 'help default-flags set' for details.\n", flag) - } else { - utils.PrintYellow("Unexpected error when fetching value for default flag [%s]: %s\n", flag, err) + if !nagged[flag] { + if errors.Is(err, ErrorNotFound) { + if nags { + fmt.Printf("No default value for flag [%s] set. See 'help default-flags set' for details.\n", flag) + if !tipped { + utils.PrintTeal("TIP: You can silence undefined default flag notices with 'default-flags nags-off'\n") + tipped = true + } + } + } else { + utils.PrintYellow("WARNING: Unexpected error when fetching value for default flag [%s]: %s\n", flag, err) + } + nagged[flag] = true } } return @@ -76,11 +109,9 @@ func Set(flag string, value interface{}) (err error) { } flags[flag] = value - vp.Set(DefFlagsKey, flags) + vp.Set(contextFlagKey(), flags) - if err = vp.WriteConfig(); err != nil { - err = fmt.Errorf("%w: %s", ErrorUnwritable, err) - } + err = writeViperConfig(vp) return } @@ -100,8 +131,8 @@ func Delete(flag string) (err error) { } delete(flags, flag) - vp.Set(DefFlagsKey, flags) - err = vp.WriteConfig() + vp.Set(contextFlagKey(), flags) + err = writeViperConfig(vp) return } @@ -140,7 +171,7 @@ func getFlagsMap(vpL ...*viper.Viper) (flags map[string]interface{}, err error) vp = vpL[0] } - flags = vp.GetStringMap(DefFlagsKey) + flags = vp.GetStringMap(contextFlagKey()) return } @@ -154,11 +185,14 @@ func getFlagsViper() (vp *viper.Viper, err error) { vp = viper.New() vp.SetConfigFile(file) + vp.SetDefault(NagsKey, true) if err = vp.ReadInConfig(); err != nil { err = fmt.Errorf("%w: %s", ErrorUnreadable, err) return } + nags = vp.GetBool(NagsKey) + return } @@ -180,3 +214,30 @@ func getFlagsFile() (file string, err error) { return } + +func contextFlagKey() (k string) { + k = fmt.Sprintf("%s.%s", DefFlagsKey, config.CurrentContext) + + return +} + +func toggleNags(on bool) error { + vp, err := getFlagsViper() + if err != nil { + return err + } + + vp.Set(NagsKey, on) + + err = writeViperConfig(vp) + + return err +} + +func writeViperConfig(vp *viper.Viper) (err error) { + if err = vp.WriteConfig(); err != nil { + err = fmt.Errorf("%w: %s", ErrorUnwritable, err) + } + + return +} diff --git a/flags/defaults/types.go b/flags/defaults/types.go index d8bd16b..cc0f7c1 100644 --- a/flags/defaults/types.go +++ b/flags/defaults/types.go @@ -16,15 +16,15 @@ func (self AllFlags) String() string { slice = append(slice, "Configured default flags:\n\n") for flag, value := range self { - slice = append(slice, fmt.Sprintf("\tFlag: %s\n", flag)) - slice = append(slice, fmt.Sprintf("\t\tValue: %+v\n", value)) + slice = append(slice, fmt.Sprintf(" Flag: %s\n", flag)) + slice = append(slice, fmt.Sprintf(" Value: %+v\n", value)) } } return strings.Join(slice[:], "") } -var permittedFlags = map[string]interface{}{ +var permittedFlags = map[string]bool{ "zone": true, "template": true, } From a9c05d6eb72a7f63e4a0eca1f6a3fcb30d67ae6f Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Thu, 31 Dec 2020 11:20:20 -0500 Subject: [PATCH 03/13] rm leftover debug --- cmd/cloudServerCreate.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/cloudServerCreate.go b/cmd/cloudServerCreate.go index 35c3deb..7219c05 100644 --- a/cmd/cloudServerCreate.go +++ b/cmd/cloudServerCreate.go @@ -88,7 +88,6 @@ cloud: lw plan --file /tmp/cloud.server.create.yaml `, Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("config.CurrentContext is now: %s\n", config.CurrentContext) params := &instance.CloudServerCreateParams{} params.Template, _ = cmd.Flags().GetString("template") From f845694b97997450666193612b434c313d97c434 Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Thu, 31 Dec 2020 11:25:33 -0500 Subject: [PATCH 04/13] account for false bool value in permittedFlags --- cmd/cloudServerCreate.go | 1 - flags/defaults/core.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/cloudServerCreate.go b/cmd/cloudServerCreate.go index 7219c05..3a97a22 100644 --- a/cmd/cloudServerCreate.go +++ b/cmd/cloudServerCreate.go @@ -24,7 +24,6 @@ import ( "github.com/spf13/cast" "github.com/spf13/cobra" - "github.com/liquidweb/liquidweb-cli/config" "github.com/liquidweb/liquidweb-cli/instance" ) diff --git a/flags/defaults/core.go b/flags/defaults/core.go index 2787654..87dbff2 100644 --- a/flags/defaults/core.go +++ b/flags/defaults/core.go @@ -143,7 +143,7 @@ func permittedFlagOrError(flag string) (err error) { return } - if _, exists := permittedFlags[flag]; !exists { + if v, exists := permittedFlags[flag]; !exists || !v { err = fmt.Errorf("%s %w", flag, ErrorForbiddenFlag) } From 97d0e47b607c4a5cad977b02711ddabb0b17415d Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Thu, 31 Dec 2020 11:53:17 -0500 Subject: [PATCH 05/13] When config file isn't found, this shouldn't be fatal new installs wont have this before they add contexts. --- config/core.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/config/core.go b/config/core.go index ee79fb7..bcf1319 100644 --- a/config/core.go +++ b/config/core.go @@ -34,8 +34,13 @@ func InitConfig() (vp *viper.Viper, err error) { } vp.AutomaticEnv() - if err := vp.ReadInConfig(); err != nil { - utils.PrintYellow("no config\n") + if err = vp.ReadInConfig(); err != nil { + if _, notFound := err.(viper.ConfigFileNotFoundError); notFound { + err = nil + return + } + utils.PrintYellow("error reading config: %s\n", err) + return } if UseContextArg != "" { From 42ae4808e391784bdcf2d926b7a6e3f308122e3b Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Mon, 4 Jan 2021 11:08:36 -0500 Subject: [PATCH 06/13] allow for optional custom zero values for defaultFlags and add config-id --- cmd/cloudNetworkVipCreate.go | 2 +- cmd/cloudPrivateParentCreate.go | 2 +- cmd/cloudServerClone.go | 3 ++- cmd/cloudServerCreate.go | 4 ++-- cmd/networkIpPoolCreate.go | 4 ++-- cmd/root.go | 5 ++++- flags/defaults/types.go | 5 +++-- 7 files changed, 15 insertions(+), 10 deletions(-) diff --git a/cmd/cloudNetworkVipCreate.go b/cmd/cloudNetworkVipCreate.go index acddc49..c4b7cb3 100644 --- a/cmd/cloudNetworkVipCreate.go +++ b/cmd/cloudNetworkVipCreate.go @@ -94,6 +94,6 @@ func init() { cloudNetworkVipCmd.AddCommand(cloudNetworkVipCreateCmd) cloudNetworkVipCreateCmd.Flags().String("name", fmt.Sprintf("vip-%s", utils.RandomString(8)), "name for the new VIP") - cloudNetworkVipCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone")), + cloudNetworkVipCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone", -1)), "zone id to create VIP in (see: 'cloud server options --zones')") } diff --git a/cmd/cloudPrivateParentCreate.go b/cmd/cloudPrivateParentCreate.go index 91a52bd..72a52af 100644 --- a/cmd/cloudPrivateParentCreate.go +++ b/cmd/cloudPrivateParentCreate.go @@ -72,7 +72,7 @@ func init() { cloudPrivateParentCreateCmd.Flags().Int64("config-id", -1, "config-id (category must be bare-metal or bare-metal-r)") cloudPrivateParentCreateCmd.Flags().String("name", "", "name for your Private Parent") - cloudPrivateParentCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone")), + cloudPrivateParentCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone", -1)), "id number of the zone to provision the Private Parent in ('cloud server options --zones')") reqs := []string{"config-id", "name"} diff --git a/cmd/cloudServerClone.go b/cmd/cloudServerClone.go index fddf100..9cada3b 100644 --- a/cmd/cloudServerClone.go +++ b/cmd/cloudServerClone.go @@ -18,6 +18,7 @@ package cmd import ( "fmt" + "github.com/spf13/cast" "github.com/spf13/cobra" "github.com/liquidweb/liquidweb-cli/types/api" @@ -162,7 +163,7 @@ func init() { cloudServerCloneCmd.Flags().Int64("vcpu", -1, "amount of vcpus for new Cloud Server (when private-parent)") // Non Private Parent - cloudServerCloneCmd.Flags().Int64("config-id", -1, + cloudServerCloneCmd.Flags().Int64("config-id", cast.ToInt64(defaultFlag("config-id", -1)), "config-id for new Cloud Server (when !private-parent) (see: 'cloud server options --configs')") if err := cloudServerCloneCmd.MarkFlagRequired("uniq-id"); err != nil { diff --git a/cmd/cloudServerCreate.go b/cmd/cloudServerCreate.go index 3a97a22..606f2b3 100644 --- a/cmd/cloudServerCreate.go +++ b/cmd/cloudServerCreate.go @@ -140,11 +140,11 @@ func init() { cloudServerCreateCmd.Flags().Int("ips", 1, "amount of IP addresses") cloudServerCreateCmd.Flags().String("public-ssh-key", sshPubKeyFile, "path to file containing the public ssh key you wish to be on the new Cloud Server") - cloudServerCreateCmd.Flags().Int("config-id", 0, "config-id to use") + cloudServerCreateCmd.Flags().Int("config-id", cast.ToInt(defaultFlag("config-id", -1)), "config-id to use") cloudServerCreateCmd.Flags().Int("backup-days", -1, "Enable daily backup plan. This is the amount of days to keep a backup") cloudServerCreateCmd.Flags().Int("backup-quota", -1, "Enable quota backup plan. This is the total amount of GB to keep.") cloudServerCreateCmd.Flags().String("bandwidth", "SS.10000", "bandwidth package to use") - cloudServerCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone")), "zone (id) to create new Cloud Server in (see 'cloud server options --zones')") + cloudServerCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone", -1)), "zone (id) to create new Cloud Server in (see 'cloud server options --zones')") cloudServerCreateCmd.Flags().String("password", "", "root or administrator password to set") cloudServerCreateCmd.Flags().Int("backup-id", -1, "id of cloud backup to create from (see 'cloud backup list')") diff --git a/cmd/networkIpPoolCreate.go b/cmd/networkIpPoolCreate.go index 90b7cf5..0e72703 100644 --- a/cmd/networkIpPoolCreate.go +++ b/cmd/networkIpPoolCreate.go @@ -37,7 +37,7 @@ your account.`, zoneFlag, _ := cmd.Flags().GetInt64("zone") newIpsFlag, _ := cmd.Flags().GetInt64("new-ips") - if len(networkIpPoolCreateCmdAddIpsFlag) == 0 && newIpsFlag == -1 || zoneFlag == 0 { + if len(networkIpPoolCreateCmdAddIpsFlag) == 0 && newIpsFlag == -1 || zoneFlag == -1 { lwCliInst.Die(fmt.Errorf("flags --new-ips --add-ips cannot both be empty. --zone cannot be empty")) } @@ -66,6 +66,6 @@ func init() { networkIpPoolCreateCmd.Flags().StringSliceVar(&networkIpPoolCreateCmdAddIpsFlag, "add-ips", []string{}, "ips separated by ',' to add to created IP Pool") networkIpPoolCreateCmd.Flags().Int64("new-ips", -1, "amount of IPs to assign to the created IP Pool") - networkIpPoolCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone")), + networkIpPoolCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone", -1)), "zone id to create the IP Pool in") } diff --git a/cmd/root.go b/cmd/root.go index e0fd80a..d69b5c7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -101,11 +101,14 @@ func osArgsForContext(re *regexp.Regexp) { } } -func defaultFlag(flag string) (value interface{}) { +func defaultFlag(flag string, defaultValueList ...interface{}) (value interface{}) { // calling config.InitConfig() here so default context gets set _, _ = config.InitConfig() setConfigArgs() value = defaults.GetOrNag(flag) + if len(defaultValueList) > 0 && value == nil { + value = defaultValueList[0] + } return } diff --git a/flags/defaults/types.go b/flags/defaults/types.go index cc0f7c1..61ae8e7 100644 --- a/flags/defaults/types.go +++ b/flags/defaults/types.go @@ -25,6 +25,7 @@ func (self AllFlags) String() string { } var permittedFlags = map[string]bool{ - "zone": true, - "template": true, + "zone": true, + "template": true, + "config-id": true, } From 86e45a7c66c9814b69bb35f1d9a2d726544873d6 Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Mon, 4 Jan 2021 11:51:45 -0500 Subject: [PATCH 07/13] hook private parent create to config-id default flag --- cmd/cloudPrivateParentCreate.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/cloudPrivateParentCreate.go b/cmd/cloudPrivateParentCreate.go index 72a52af..a5ec1c3 100644 --- a/cmd/cloudPrivateParentCreate.go +++ b/cmd/cloudPrivateParentCreate.go @@ -70,12 +70,12 @@ of configs, check 'cloud server options --configs'.`, func init() { cloudPrivateParentCmd.AddCommand(cloudPrivateParentCreateCmd) - cloudPrivateParentCreateCmd.Flags().Int64("config-id", -1, "config-id (category must be bare-metal or bare-metal-r)") + cloudPrivateParentCreateCmd.Flags().Int64("config-id", cast.ToInt64(defaultFlag("config-id", -1)), "config-id (category must be bare-metal or bare-metal-r)") cloudPrivateParentCreateCmd.Flags().String("name", "", "name for your Private Parent") cloudPrivateParentCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone", -1)), "id number of the zone to provision the Private Parent in ('cloud server options --zones')") - reqs := []string{"config-id", "name"} + reqs := []string{"name"} for _, req := range reqs { if err := cloudPrivateParentCreateCmd.MarkFlagRequired(req); err != nil { lwCliInst.Die(err) From ccb80152eaace282fdf5599ff922cc33a4ac2553 Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Mon, 4 Jan 2021 11:54:07 -0500 Subject: [PATCH 08/13] add config-id default flag to cloud server resize too --- cmd/cloudServerResize.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/cloudServerResize.go b/cmd/cloudServerResize.go index c0dc709..51b492f 100644 --- a/cmd/cloudServerResize.go +++ b/cmd/cloudServerResize.go @@ -18,6 +18,7 @@ package cmd import ( "fmt" + "github.com/spf13/cast" "github.com/spf13/cobra" "github.com/liquidweb/liquidweb-cli/instance" @@ -104,7 +105,7 @@ func init() { cloudServerResizeCmd.Flags().Int64("memory", -1, "desired memory (when private-parent)") cloudServerResizeCmd.Flags().Bool("skip-fs-resize", false, "whether or not to skip the fs resize") cloudServerResizeCmd.Flags().Int64("vcpu", -1, "desired vcpu count (when private-parent)") - cloudServerResizeCmd.Flags().Int64("config-id", -1, + cloudServerResizeCmd.Flags().Int64("config-id", cast.ToInt64(defaultFlag("config-id", -1)), "config-id of your desired config (when !private-parent) (see 'cloud server options --configs')") if err := cloudServerResizeCmd.MarkFlagRequired("uniq-id"); err != nil { From 5aec8cef48ef2b27e05ce7434d5c0bd61e187c88 Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Mon, 4 Jan 2021 11:57:18 -0500 Subject: [PATCH 09/13] catch blank template --- instance/cloudTemplateRestore.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/instance/cloudTemplateRestore.go b/instance/cloudTemplateRestore.go index 3ccb9e7..580f774 100644 --- a/instance/cloudTemplateRestore.go +++ b/instance/cloudTemplateRestore.go @@ -16,6 +16,8 @@ limitations under the License. package instance import ( + "errors" + "github.com/liquidweb/liquidweb-cli/types/api" "github.com/liquidweb/liquidweb-cli/validate" ) @@ -33,6 +35,10 @@ func (ci *Client) CloudTemplateRestore(params *CloudTemplateRestoreParams) (stri return "", err } + if params.Template == "" { + return "", errors.New("template cannot be blank") + } + apiArgs := map[string]interface{}{"template": params.Template, "uniq_id": params.UniqId} var details apiTypes.CloudTemplateRestoreResponse From 037489622816c9ff1bcaf7d2636c8599fab622c9 Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Mon, 4 Jan 2021 12:06:22 -0500 Subject: [PATCH 10/13] remove unneeded check, was causing issues with config-id default --- instance/cloudServerCreate.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/instance/cloudServerCreate.go b/instance/cloudServerCreate.go index ddfad04..779f629 100644 --- a/instance/cloudServerCreate.go +++ b/instance/cloudServerCreate.go @@ -98,9 +98,6 @@ func (ci *Client) CloudServerCreate(params *CloudServerCreateParams) (string, er // sanity check flags if params.PrivateParent != "" { - if params.ConfigId > 0 { - return "", fmt.Errorf("--config-id must be 0 or omitted when specifying --private-parent") - } // create on a private parent. diskspace, memory, vcpu are required. if params.Memory == -1 { return "", fmt.Errorf("--memory is required when specifying --private-parent") From 726e82a85c6fe59089789a2af9f9c36b8e7cea13 Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Mon, 4 Jan 2021 12:32:15 -0500 Subject: [PATCH 11/13] fix regression cloning onto private parents --- cmd/cloudServerClone.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cmd/cloudServerClone.go b/cmd/cloudServerClone.go index 9cada3b..ef2d2b8 100644 --- a/cmd/cloudServerClone.go +++ b/cmd/cloudServerClone.go @@ -65,9 +65,6 @@ Server is not on a Private Parent.`, hostnameFlag: map[string]string{"type": "NonEmptyString", "optional": "false"}, } - if privateParentFlag != "" && configIdFlag != -1 { - lwCliInst.Die(fmt.Errorf("cant pass both --config-id and --private-parent flags")) - } if privateParentFlag == "" && configIdFlag == -1 { lwCliInst.Die(fmt.Errorf("must pass --config-id or --private-parent")) } @@ -115,7 +112,7 @@ Server is not on a Private Parent.`, cloneArgs["vcpu"] = vcpuFlag validateFields[vcpuFlag] = "PositiveInt64" } - if configIdFlag != -1 { + if configIdFlag != -1 && privateParentFlag == "" { cloneArgs["config_id"] = configIdFlag validateFields[configIdFlag] = "PositiveInt64" } From 3dd181883a670e7507590b0c994565685e1d113e Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Tue, 12 Jan 2021 22:23:48 -0500 Subject: [PATCH 12/13] make default flags cmd specific --- cmd/cloudNetworkVipCreate.go | 2 +- cmd/cloudPrivateParentCreate.go | 4 ++-- cmd/cloudServerClone.go | 2 +- cmd/cloudServerCreate.go | 6 +++--- cmd/cloudServerResize.go | 2 +- cmd/cloudTemplateRestore.go | 2 +- cmd/defaultFlagsPermitted.go | 5 +---- cmd/networkIpPoolCreate.go | 2 +- flags/defaults/core.go | 13 +++++++++++-- flags/defaults/types.go | 21 ++++++++++++++++++--- 10 files changed, 40 insertions(+), 19 deletions(-) diff --git a/cmd/cloudNetworkVipCreate.go b/cmd/cloudNetworkVipCreate.go index c4b7cb3..3803ba2 100644 --- a/cmd/cloudNetworkVipCreate.go +++ b/cmd/cloudNetworkVipCreate.go @@ -94,6 +94,6 @@ func init() { cloudNetworkVipCmd.AddCommand(cloudNetworkVipCreateCmd) cloudNetworkVipCreateCmd.Flags().String("name", fmt.Sprintf("vip-%s", utils.RandomString(8)), "name for the new VIP") - cloudNetworkVipCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone", -1)), + cloudNetworkVipCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("cloud_network_vip_create_zone", -1)), "zone id to create VIP in (see: 'cloud server options --zones')") } diff --git a/cmd/cloudPrivateParentCreate.go b/cmd/cloudPrivateParentCreate.go index a5ec1c3..1b5a525 100644 --- a/cmd/cloudPrivateParentCreate.go +++ b/cmd/cloudPrivateParentCreate.go @@ -70,9 +70,9 @@ of configs, check 'cloud server options --configs'.`, func init() { cloudPrivateParentCmd.AddCommand(cloudPrivateParentCreateCmd) - cloudPrivateParentCreateCmd.Flags().Int64("config-id", cast.ToInt64(defaultFlag("config-id", -1)), "config-id (category must be bare-metal or bare-metal-r)") + cloudPrivateParentCreateCmd.Flags().Int64("config-id", cast.ToInt64(defaultFlag("cloud_private-parent_create_config-id", -1)), "config-id (category must be bare-metal or bare-metal-r)") cloudPrivateParentCreateCmd.Flags().String("name", "", "name for your Private Parent") - cloudPrivateParentCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone", -1)), + cloudPrivateParentCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("cloud_private-parent_create_zone", -1)), "id number of the zone to provision the Private Parent in ('cloud server options --zones')") reqs := []string{"name"} diff --git a/cmd/cloudServerClone.go b/cmd/cloudServerClone.go index ef2d2b8..211f472 100644 --- a/cmd/cloudServerClone.go +++ b/cmd/cloudServerClone.go @@ -160,7 +160,7 @@ func init() { cloudServerCloneCmd.Flags().Int64("vcpu", -1, "amount of vcpus for new Cloud Server (when private-parent)") // Non Private Parent - cloudServerCloneCmd.Flags().Int64("config-id", cast.ToInt64(defaultFlag("config-id", -1)), + cloudServerCloneCmd.Flags().Int64("config-id", cast.ToInt64(defaultFlag("cloud_server_clone_config-id", -1)), "config-id for new Cloud Server (when !private-parent) (see: 'cloud server options --configs')") if err := cloudServerCloneCmd.MarkFlagRequired("uniq-id"); err != nil { diff --git a/cmd/cloudServerCreate.go b/cmd/cloudServerCreate.go index 606f2b3..e8fc967 100644 --- a/cmd/cloudServerCreate.go +++ b/cmd/cloudServerCreate.go @@ -134,17 +134,17 @@ func init() { sshPubKeyFile = fmt.Sprintf("%s/.ssh/id_rsa.pub", home) } - cloudServerCreateCmd.Flags().String("template", cast.ToString(defaultFlag("template")), "template to use (see 'cloud server options --templates')") + cloudServerCreateCmd.Flags().String("template", cast.ToString(defaultFlag("cloud_server_create_template")), "template to use (see 'cloud server options --templates')") cloudServerCreateCmd.Flags().String("type", "SS.VPS", "some examples of types; SS.VPS, SS.VPS.WIN, SS.VM, SS.VM.WIN") cloudServerCreateCmd.Flags().String("hostname", "", "hostname to set") cloudServerCreateCmd.Flags().Int("ips", 1, "amount of IP addresses") cloudServerCreateCmd.Flags().String("public-ssh-key", sshPubKeyFile, "path to file containing the public ssh key you wish to be on the new Cloud Server") - cloudServerCreateCmd.Flags().Int("config-id", cast.ToInt(defaultFlag("config-id", -1)), "config-id to use") + cloudServerCreateCmd.Flags().Int("config-id", cast.ToInt(defaultFlag("cloud_server_create_config-id", -1)), "config-id to use") cloudServerCreateCmd.Flags().Int("backup-days", -1, "Enable daily backup plan. This is the amount of days to keep a backup") cloudServerCreateCmd.Flags().Int("backup-quota", -1, "Enable quota backup plan. This is the total amount of GB to keep.") cloudServerCreateCmd.Flags().String("bandwidth", "SS.10000", "bandwidth package to use") - cloudServerCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone", -1)), "zone (id) to create new Cloud Server in (see 'cloud server options --zones')") + cloudServerCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("cloud_server_create_zone", -1)), "zone (id) to create new Cloud Server in (see 'cloud server options --zones')") cloudServerCreateCmd.Flags().String("password", "", "root or administrator password to set") cloudServerCreateCmd.Flags().Int("backup-id", -1, "id of cloud backup to create from (see 'cloud backup list')") diff --git a/cmd/cloudServerResize.go b/cmd/cloudServerResize.go index 51b492f..ea424e7 100644 --- a/cmd/cloudServerResize.go +++ b/cmd/cloudServerResize.go @@ -105,7 +105,7 @@ func init() { cloudServerResizeCmd.Flags().Int64("memory", -1, "desired memory (when private-parent)") cloudServerResizeCmd.Flags().Bool("skip-fs-resize", false, "whether or not to skip the fs resize") cloudServerResizeCmd.Flags().Int64("vcpu", -1, "desired vcpu count (when private-parent)") - cloudServerResizeCmd.Flags().Int64("config-id", cast.ToInt64(defaultFlag("config-id", -1)), + cloudServerResizeCmd.Flags().Int64("config-id", cast.ToInt64(defaultFlag("cloud_server_resize_config-id", -1)), "config-id of your desired config (when !private-parent) (see 'cloud server options --configs')") if err := cloudServerResizeCmd.MarkFlagRequired("uniq-id"); err != nil { diff --git a/cmd/cloudTemplateRestore.go b/cmd/cloudTemplateRestore.go index 39f9554..abad64c 100644 --- a/cmd/cloudTemplateRestore.go +++ b/cmd/cloudTemplateRestore.go @@ -60,7 +60,7 @@ func init() { cloudTemplateCmd.AddCommand(cloudTemplateRestoreCmd) cloudTemplateRestoreCmd.Flags().String("uniq-id", "", "uniq-id of Cloud Server") - cloudTemplateRestoreCmd.Flags().String("template", cast.ToString(defaultFlag("template")), "name of template to restore") + cloudTemplateRestoreCmd.Flags().String("template", cast.ToString(defaultFlag("cloud_template_restore_template")), "name of template to restore") if err := cloudTemplateRestoreCmd.MarkFlagRequired("uniq-id"); err != nil { lwCliInst.Die(err) diff --git a/cmd/defaultFlagsPermitted.go b/cmd/defaultFlagsPermitted.go index ab70cba..74eceb3 100644 --- a/cmd/defaultFlagsPermitted.go +++ b/cmd/defaultFlagsPermitted.go @@ -36,10 +36,7 @@ on auth contexts, see 'help auth'.`, Run: func(cmd *cobra.Command, args []string) { permitted := defaults.GetPermitted() fmt.Println("Permitted flags:") - for flag, v := range permitted { - if !v { - continue - } + for _, flag := range permitted { fmt.Printf(" %s\n", flag) } }, diff --git a/cmd/networkIpPoolCreate.go b/cmd/networkIpPoolCreate.go index 0e72703..4c54ce8 100644 --- a/cmd/networkIpPoolCreate.go +++ b/cmd/networkIpPoolCreate.go @@ -66,6 +66,6 @@ func init() { networkIpPoolCreateCmd.Flags().StringSliceVar(&networkIpPoolCreateCmdAddIpsFlag, "add-ips", []string{}, "ips separated by ',' to add to created IP Pool") networkIpPoolCreateCmd.Flags().Int64("new-ips", -1, "amount of IPs to assign to the created IP Pool") - networkIpPoolCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("zone", -1)), + networkIpPoolCreateCmd.Flags().Int64("zone", cast.ToInt64(defaultFlag("network_ip-pool_create_zone", -1)), "zone id to create the IP Pool in") } diff --git a/flags/defaults/core.go b/flags/defaults/core.go index 87dbff2..d6df509 100644 --- a/flags/defaults/core.go +++ b/flags/defaults/core.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "sort" homedir "github.com/mitchellh/go-homedir" "github.com/spf13/viper" @@ -41,8 +42,16 @@ func NagsOn() (err error) { return } -func GetPermitted() (permitted map[string]bool) { - permitted = permittedFlags +func GetPermitted() (permitted []string) { + permitted = make([]string, 0, len(permittedFlags)) + for flag, val := range permittedFlags { + if !val { + continue + } + permitted = append(permitted, flag) + } + sort.Strings(permitted) + return } diff --git a/flags/defaults/types.go b/flags/defaults/types.go index 61ae8e7..0a800cd 100644 --- a/flags/defaults/types.go +++ b/flags/defaults/types.go @@ -25,7 +25,22 @@ func (self AllFlags) String() string { } var permittedFlags = map[string]bool{ - "zone": true, - "template": true, - "config-id": true, + // cloud network vip create + "cloud_network_vip_create_zone": true, + // cloud private-parent create + "cloud_private-parent_create_config-id": true, + "cloud_private-parent_create_zone": true, + // cloud server clone + "cloud_server_clone_config-id": true, + // cloud server create + "cloud_server_create_zone": true, + "cloud_server_create_template": true, + "cloud_server_create_config-id": true, + // cloud server resize + "cloud_server_resize_config-id": true, + // cloud template restore + "cloud_template_restore_template": true, + + // network ip-pool create + "network_ip-pool_create_zone": true, } From 6117fd19215c94af15e07bbaf98c91fa8f736e1d Mon Sep 17 00:00:00 2001 From: Scott Sullivan Date: Tue, 12 Jan 2021 23:17:18 -0500 Subject: [PATCH 13/13] add simple default flag validation using internal validator pkg --- flags/defaults/core.go | 53 +++++++++++++++++++++++++++++++++++------ flags/defaults/types.go | 53 +++++++++++++++++++++++++++++++---------- 2 files changed, 87 insertions(+), 19 deletions(-) diff --git a/flags/defaults/core.go b/flags/defaults/core.go index d6df509..a7d88f4 100644 --- a/flags/defaults/core.go +++ b/flags/defaults/core.go @@ -5,12 +5,15 @@ import ( "fmt" "os" "sort" + "strings" homedir "github.com/mitchellh/go-homedir" + "github.com/spf13/cast" "github.com/spf13/viper" "github.com/liquidweb/liquidweb-cli/config" "github.com/liquidweb/liquidweb-cli/utils" + "github.com/liquidweb/liquidweb-cli/validate" ) var ( @@ -44,8 +47,8 @@ func NagsOn() (err error) { func GetPermitted() (permitted []string) { permitted = make([]string, 0, len(permittedFlags)) - for flag, val := range permittedFlags { - if !val { + for flag, opts := range permittedFlags { + if enabled, ok := opts["enabled"].(bool); !enabled || !ok { continue } permitted = append(permitted, flag) @@ -78,7 +81,7 @@ func GetOrNag(flag string) (value interface{}) { } func Get(flag string) (value interface{}, err error) { - if err = permittedFlagOrError(flag); err != nil { + if _, err = permittedFlagOrError(flag); err != nil { return } @@ -104,7 +107,13 @@ func GetAll() (all AllFlags, err error) { } func Set(flag string, value interface{}) (err error) { - if err = permittedFlagOrError(flag); err != nil { + var validator string + validator, err = permittedFlagOrError(flag) + if err != nil { + return + } + + if err = validateFlagValue(validator, value); err != nil { return } @@ -126,7 +135,7 @@ func Set(flag string, value interface{}) (err error) { } func Delete(flag string) (err error) { - if err = permittedFlagOrError(flag); err != nil { + if _, err = permittedFlagOrError(flag); err != nil { return } @@ -146,16 +155,46 @@ func Delete(flag string) (err error) { return } -func permittedFlagOrError(flag string) (err error) { +func permittedFlagOrError(flag string) (validator string, err error) { if flag == "" { err = ErrorInvalidFlagName return } - if v, exists := permittedFlags[flag]; !exists || !v { + opts, exists := permittedFlags[flag] + if !exists { + err = fmt.Errorf("%s %w", flag, ErrorForbiddenFlag) + return + } + + if v, ok := opts["enabled"].(bool); !ok || !v { err = fmt.Errorf("%s %w", flag, ErrorForbiddenFlag) } + validator = cast.ToString(opts["type"]) + + return +} + +func validateFlagValue(validator string, value interface{}) (err error) { + var validateFields map[interface{}]interface{} + + if strings.HasSuffix(validator, "Int64") { + validateFields = map[interface{}]interface{}{ + cast.ToInt64(value): "PositiveInt64", + } + } else if strings.HasSuffix(validator, "Int") { + validateFields = map[interface{}]interface{}{ + cast.ToInt(value): "PositiveInt64", + } + } else if strings.HasSuffix(validator, "String") { + validateFields = map[interface{}]interface{}{ + cast.ToString(value): "NonEmptyString", + } + } + + err = validate.Validate(validateFields) + return } diff --git a/flags/defaults/types.go b/flags/defaults/types.go index 0a800cd..bdf8dd9 100644 --- a/flags/defaults/types.go +++ b/flags/defaults/types.go @@ -24,23 +24,52 @@ func (self AllFlags) String() string { return strings.Join(slice[:], "") } -var permittedFlags = map[string]bool{ +var permittedFlags = map[string]map[string]interface{}{ // cloud network vip create - "cloud_network_vip_create_zone": true, + "cloud_network_vip_create_zone": map[string]interface{}{ + "enabled": true, + "validator": "PositiveInt64", + }, // cloud private-parent create - "cloud_private-parent_create_config-id": true, - "cloud_private-parent_create_zone": true, + "cloud_private-parent_create_config-id": map[string]interface{}{ + "enabled": true, + "type": "PositiveInt64", + }, + "cloud_private-parent_create_zone": map[string]interface{}{ + "enabled": true, + "type": "PositiveInt64", + }, // cloud server clone - "cloud_server_clone_config-id": true, + "cloud_server_clone_config-id": map[string]interface{}{ + "enabled": true, + "type": "PositiveInt64", + }, // cloud server create - "cloud_server_create_zone": true, - "cloud_server_create_template": true, - "cloud_server_create_config-id": true, + "cloud_server_create_zone": map[string]interface{}{ + "enabled": true, + "type": "PositiveInt64", + }, + "cloud_server_create_template": map[string]interface{}{ + "enabled": true, + "type": "NonEmptyString", + }, + "cloud_server_create_config-id": map[string]interface{}{ + "enabled": true, + "type": "PositiveInt64", + }, // cloud server resize - "cloud_server_resize_config-id": true, + "cloud_server_resize_config-id": map[string]interface{}{ + "enabled": true, + "type": "PositiveInt64", + }, // cloud template restore - "cloud_template_restore_template": true, - + "cloud_template_restore_template": map[string]interface{}{ + "enabled": true, + "type": "NonEmptyString", + }, // network ip-pool create - "network_ip-pool_create_zone": true, + "network_ip-pool_create_zone": map[string]interface{}{ + "enabled": true, + "type": "PositiveInt64", + }, }