Skip to content

Refactor config commands #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,29 @@ 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 <clientID> -s <secretID>`
`$ iot-cloud-cli config init`

A file named `config.yaml` will be created in the Current Working Directory containing the login credentials.
Example
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.

```yaml
client: 00112233445566778899aabbccddeeff
secret: 00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100
```

To create a configuration file in a different folder, use this command:

`$ iot-cloud-cli config init --dest-dir <destinationFolder>`

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

Expand Down
58 changes: 4 additions & 54 deletions cli/config/config.go
Original file line number Diff line number Diff line change
@@ -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
}
50 changes: 50 additions & 0 deletions cli/config/init.go
Original file line number Diff line number Diff line change
@@ -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, "config-format", "yaml", "Format 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")
}
20 changes: 0 additions & 20 deletions command/config/config.go

This file was deleted.

60 changes: 60 additions & 0 deletions command/config/init.go
Original file line number Diff line number Diff line change
@@ -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
}
7 changes: 3 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ 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
// configuration file, if any. Returns error if no config file is found.
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 {
Expand Down
15 changes: 15 additions & 0 deletions internal/config/default.go
Original file line number Diff line number Diff line change
@@ -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")
}