From cf8f5a7d52ae6399a175be4127119225a070b4c4 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Tue, 14 Sep 2021 12:34:38 +0200 Subject: [PATCH 1/5] Refactor config command --- cli/config/config.go | 58 +++--------------------------------- cli/config/init.go | 50 +++++++++++++++++++++++++++++++ command/config/config.go | 20 ------------- command/config/init.go | 60 ++++++++++++++++++++++++++++++++++++++ internal/config/default.go | 15 ++++++++++ 5 files changed, 129 insertions(+), 74 deletions(-) create mode 100644 cli/config/init.go delete mode 100644 command/config/config.go create mode 100644 command/config/init.go create mode 100644 internal/config/default.go diff --git a/cli/config/config.go b/cli/config/config.go index f2de8b18..2319dfb7 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -1,67 +1,17 @@ package config import ( - "os" - "strings" - - "github.com/arduino/arduino-cli/cli/errorcodes" - "github.com/arduino/arduino-cli/cli/feedback" - paths "github.com/arduino/go-paths-helper" - "github.com/arduino/iot-cloud-cli/command/config" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -var configFlags struct { - file string - client string - secret string -} - func NewCommand() *cobra.Command { configCommand := &cobra.Command{ Use: "config", - Short: "Set the configuration file", - Long: "Set the configuration file to access Arduino IoT Cloud", - Run: runConfigCommand, - } - configCommand.Flags().StringVarP(&configFlags.file, "file", "f", "", "Existing configuration yaml file") - configCommand.Flags().StringVarP(&configFlags.client, "client", "c", "", "Client ID") - configCommand.Flags().StringVarP(&configFlags.secret, "secret", "s", "", "Secret ID") - return configCommand -} - -func runConfigCommand(cmd *cobra.Command, args []string) { - if configFlags.file == "" && (configFlags.client == "" || configFlags.secret == "") { - feedback.Error("Error during config: provide either a yaml file or credentials") - os.Exit(errorcodes.ErrGeneric) + Short: "Configuration commands.", + Long: "Configuration commands.", } - conf := viper.New() + configCommand.AddCommand(initInitCommand()) - if configFlags.file != "" { - file := paths.New(configFlags.file) - filename := strings.TrimSuffix(file.String(), file.Ext()) - conf.SetConfigName(filename) - conf.SetConfigType(strings.Trim(file.Ext(), ".")) - conf.AddConfigPath(".") - err := conf.ReadInConfig() - if err != nil { - feedback.Errorf("Error during config: fatal error config file: %v", err) - os.Exit(errorcodes.ErrGeneric) - } - - } else { - conf.BindPFlag("client", cmd.Flag("client")) - conf.BindPFlag("secret", cmd.Flag("secret")) - } - - err := config.Config(conf) - if err != nil { - feedback.Errorf("Error during config: storing config file: %v", err) - os.Exit(errorcodes.ErrGeneric) - } - - logrus.Info("Configuration file updated") + return configCommand } diff --git a/cli/config/init.go b/cli/config/init.go new file mode 100644 index 00000000..32c649d0 --- /dev/null +++ b/cli/config/init.go @@ -0,0 +1,50 @@ +package config + +import ( + "os" + + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/iot-cloud-cli/command/config" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var initFlags struct { + destDir string + overwrite bool + format string +} + +func initInitCommand() *cobra.Command { + initCommand := &cobra.Command{ + Use: "init", + Short: "Initialize a configuration file with default values", + Long: "Initialize an Arduino IoT Cloud CLI configuration file with default values", + Run: runInitCommand, + } + + initCommand.Flags().StringVar(&initFlags.destDir, "dest-dir", ".", "Sets where to save the configuration file.") + initCommand.Flags().BoolVar(&initFlags.overwrite, "overwrite", false, "Overwrite existing config file.") + initCommand.Flags().StringVar(&initFlags.format, "ext", "yaml", "Extension of the configuration file, can be {yaml|json}") + + return initCommand +} + +func runInitCommand(cmd *cobra.Command, args []string) { + logrus.Infof("Initializing a config file in folder: %s", initFlags.destDir) + + params := &config.InitParams{ + DestDir: initFlags.destDir, + Overwrite: initFlags.overwrite, + Format: initFlags.format, + } + + err := config.Init(params) + if err != nil { + feedback.Errorf("Error during config init: %v", err) + os.Exit(errorcodes.ErrGeneric) + } + + logrus.Info("Config file successfully initialized") +} diff --git a/command/config/config.go b/command/config/config.go deleted file mode 100644 index d04744b6..00000000 --- a/command/config/config.go +++ /dev/null @@ -1,20 +0,0 @@ -package config - -import ( - "github.com/arduino/arduino-cli/cli/feedback" - "github.com/spf13/viper" -) - -// Config accepts a configured viper instance and -// saves it into a specific config file. -func Config(conf *viper.Viper) error { - - // TODO: check if conf has the correct parameters - // e.g. 'client' and 'secret' - - if err := conf.WriteConfigAs("config.yaml"); err != nil { - feedback.Errorf("Cannot create config file: %v", err) - return err - } - return nil -} diff --git a/command/config/init.go b/command/config/init.go new file mode 100644 index 00000000..46123cfd --- /dev/null +++ b/command/config/init.go @@ -0,0 +1,60 @@ +package config + +import ( + "errors" + "fmt" + "strings" + + "github.com/arduino/go-paths-helper" + "github.com/arduino/iot-cloud-cli/internal/config" + "github.com/spf13/viper" +) + +// InitParams contains the parameters needed to initialize a configuration file. +// DestDir - destination directory in which the configuration file will be saved. +// Overwrite - specify if existing config file should be overwritten. +// Format - the config file format, can be 'json' or 'yaml'. +type InitParams struct { + DestDir string + Overwrite bool + Format string +} + +func validateFormatString(arg string) error { + if arg != "json" && arg != "yaml" { + return errors.New("passed format is not valid, select between 'json' and 'yaml'") + } + return nil +} + +// Init initializes a configuration file with default values. +// If the file doesn't exist, it is created. +// If it exists, it is written to only if overwrite param is true. +func Init(params *InitParams) error { + configPath, err := paths.New(params.DestDir).Abs() + if err != nil { + return fmt.Errorf("%s: %w", "cannot retrieve absolute path of passed dest-dir", err) + } + if !configPath.IsDir() { + return fmt.Errorf("%s: %w", "passed dest-dir is not a valid directory", err) + } + + params.Format = strings.ToLower(params.Format) + if err := validateFormatString(params.Format); err != nil { + return err + } + + configFile := configPath.Join(config.Filename + "." + params.Format) + + if !params.Overwrite && configFile.Exist() { + return errors.New("config file already exists, use --overwrite to discard the existing one") + } + + newSettings := viper.New() + config.SetDefaults(newSettings) + if err := newSettings.WriteConfigAs(configFile.String()); err != nil { + return fmt.Errorf("cannot create config file: %v", err) + } + + return nil +} diff --git a/internal/config/default.go b/internal/config/default.go new file mode 100644 index 00000000..b7f359c5 --- /dev/null +++ b/internal/config/default.go @@ -0,0 +1,15 @@ +package config + +import "github.com/spf13/viper" + +var ( + Filename = "arduino-cloud" +) + +// SetDefaults sets the default values for configuration keys +func SetDefaults(settings *viper.Viper) { + // Client ID + settings.SetDefault("client", "xxxxxxxxxxxxxx") + // Secret + settings.SetDefault("secret", "xxxxxxxxxxxxxx") +} From 1e023d3170110397c7d1daea742a11f3616cc098 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Tue, 14 Sep 2021 12:38:32 +0200 Subject: [PATCH 2/5] Support multiple config formats --- internal/config/config.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 7805decd..ed3ee5d1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -10,9 +10,9 @@ import ( // known by iot-cloud-cli type Config struct { // Client ID of the user - Client string `yaml:"client"` + Client string `map-structure:"client"` // Secret ID of the user, unique for each Client ID - Secret string `yaml:"secret"` + Secret string `map-structure:"secret"` } // Retrieve returns the actual parameters contained in the @@ -20,8 +20,7 @@ type Config struct { func Retrieve() (*Config, error) { conf := &Config{} v := viper.New() - v.SetConfigName("config") - v.SetConfigType("yaml") + v.SetConfigName(Filename) v.AddConfigPath(".") err := v.ReadInConfig() if err != nil { From 5dfeedaa59322614407de21c9d88f1a2faf756d1 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Tue, 14 Sep 2021 12:56:25 +0200 Subject: [PATCH 3/5] Rename config format flag --- cli/config/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/config/init.go b/cli/config/init.go index 32c649d0..04446a90 100644 --- a/cli/config/init.go +++ b/cli/config/init.go @@ -26,7 +26,7 @@ func initInitCommand() *cobra.Command { initCommand.Flags().StringVar(&initFlags.destDir, "dest-dir", ".", "Sets where to save the configuration file.") initCommand.Flags().BoolVar(&initFlags.overwrite, "overwrite", false, "Overwrite existing config file.") - initCommand.Flags().StringVar(&initFlags.format, "ext", "yaml", "Extension of the configuration file, can be {yaml|json}") + initCommand.Flags().StringVar(&initFlags.format, "config-format", "yaml", "Format of the configuration file, can be {yaml|json}") return initCommand } From 89aff91c7cf24f1934905098dcb5c1b207a22a26 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Tue, 14 Sep 2021 15:23:22 +0200 Subject: [PATCH 4/5] Update readme --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b12cd434..799ad05f 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,16 @@ This is all you need to use iot-cloud-cli for device **provisioning**: ## Set a configuration -iot-cloud-cli needs to be configured before being used. In particular a client ID and the corresponding secret ID should be set. +iot-cloud-cli needs a configuration file to be used. At the moment, the configuration file should be contained in the same directory where the cli commands are executed. +The configuration file contains the Arduino IoT Cloud client ID and its corresponding secret. You can retrieve them from the [cloud](https://create.arduino.cc/iot/integrations) by creating a new API key. -Once you have the IDs, call this command with your parameters: +Once you have the IDs, call this command to init a new configuration file: -`$ iot-cloud-cli config -c -s ` +`$ iot-cloud-cli config init` -A file named `config.yaml` will be created in the Current Working Directory containing the login credentials. -Example - -```yaml -client: 00112233445566778899aabbccddeeff -secret: 00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100 -``` +A file named `arduino-cloud.yaml` will be created in the current working directory. +Then you should open such file and replace the client and secret placeholders with the value you previously retrieved. ## Device provisioning From dbcca491451f08f6f301c5d81434ceccd4379d92 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Tue, 14 Sep 2021 16:31:48 +0200 Subject: [PATCH 5/5] Update readme --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 799ad05f..ee563e65 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,19 @@ Once you have the IDs, call this command to init a new configuration file: A file named `arduino-cloud.yaml` will be created in the current working directory. Then you should open such file and replace the client and secret placeholders with the value you previously retrieved. + +To create a configuration file in a different folder, use this command: + +`$ iot-cloud-cli config init --dest-dir ` + +To reset an old configuration file, just overwrite it using this command: + +`$ iot-cloud-cli config init --overwrite` + +Configuration file is supported in two different format: json and yaml. Use the `--config-format` to choose it. Default is yaml. + +`$ iot-cloud-cli config init --config-format json` + ## Device provisioning When provisioning a device, you can optionally specify the port to which the device is connected to and its fqbn. If they are not given, then the first device found will be provisioned.