From d73b98eb761cd7a836f1b3e2aaf6d78b20100f07 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Wed, 22 Dec 2021 14:07:26 +0100 Subject: [PATCH 1/4] Improve credentials --- internal/config/config.go | 16 +++--- internal/config/credentials.go | 91 ++++++++++++++-------------------- 2 files changed, 45 insertions(+), 62 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index e8c7c552..fc0f9ad1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -26,14 +26,14 @@ import ( // searchConfigDir looks for a configuration file in different directories in the following order: // current working directory, parents of the current working directory, arduino15 default directory. -// Returns a nil string if no config file has been found, without raising errors. +// Returns empty string and false if no config file has been found, without raising errors. // Returns an error if any problem is encountered during the file research which prevents // to understand whether a config file exists or not. -func searchConfigDir(confname string) (*string, error) { +func searchConfigDir(confname string) (dir string, found bool, err error) { // Search in current directory and its parents. cwd, err := paths.Getwd() if err != nil { - return nil, err + return "", false, err } // Don't let bad naming mislead you, cwd.Parents()[0] is cwd itself so // we look in the current directory first and then on its parents. @@ -41,25 +41,23 @@ func searchConfigDir(confname string) (*string, error) { logrus.Infof("Looking for %s in %s", confname, path) if file, found := configFileInDir(confname, path); found { logrus.Infof("Found %s at %s", confname, file) - p := path.String() - return &p, nil + return path.String(), true, nil } } // Search in arduino's default data directory. arduino15, err := arduino.DataDir() if err != nil { - return nil, err + return "", false, err } logrus.Infof("Looking for %s in %s", confname, arduino15) if file, found := configFileInDir(confname, arduino15); found { logrus.Infof("%s found at %s", confname, file) - p := arduino15.String() - return &p, nil + return arduino15.String(), true, nil } // Didn't find config file in the current directory, its parents or in arduino15" - return nil, nil + return "", false, nil } // configFileInDir looks for a configuration file in the passed directory. diff --git a/internal/config/credentials.go b/internal/config/credentials.go index 7d6a287c..2868a081 100644 --- a/internal/config/credentials.go +++ b/internal/config/credentials.go @@ -38,8 +38,8 @@ const ( CredentialsFilename = "arduino-cloud-credentials" ) -// SetDefaultCredentials sets the default credentials values. -func SetDefaultCredentials(settings *viper.Viper) { +// SetEmptyCredentials sets the default credentials values to empty strings. +func SetEmptyCredentials(settings *viper.Viper) { // Client ID settings.SetDefault("client", "") // Secret @@ -79,7 +79,8 @@ func (c *Credentials) IsEmpty() bool { // RetrieveCredentials looks for credentials in // environment variables or in credentials file. -// Returns error if no credentials are found. +// Returns error if no credentials are found or +// if found credentials are invalid. func RetrieveCredentials() (*Credentials, error) { // Credentials extracted from environment has highest priority logrus.Info("Looking for credentials in environment variables") @@ -87,49 +88,53 @@ func RetrieveCredentials() (*Credentials, error) { if err != nil { return nil, fmt.Errorf("reading credentials from environment variables: %w", err) } - // Return credentials only if found - if c != nil { - logrus.Info("Credentials found in environment variables") + // Return credentials if found in env + if !c.IsEmpty() { + // Return error if credentials are found but are not valid + if err := c.Validate(); err != nil { + return nil, fmt.Errorf( + "credentials retrieved from environment variables with prefix '%s' are not valid: %w", EnvPrefix, err, + ) + } + logrus.Infof("Credentials found in environment variables with prefix '%s'", EnvPrefix) return c, nil } logrus.Info("Looking for credentials in file system") - c, err = fromFile() + filepath, found, err := searchConfigDir(CredentialsFilename) if err != nil { - return nil, fmt.Errorf("reading credentials from file: %w", err) + return nil, fmt.Errorf("can't get credentials directory: %w", err) } - if c != nil { - return c, nil + if !found { + return nil, fmt.Errorf( + "credentials have not been found neither in environment variables " + + "nor in the current directory, its parents or in arduino15", + ) } - return nil, fmt.Errorf( - "credentials have not been found neither in environment variables " + - "nor in the current directory, its parents or in arduino15", - ) -} - -// fromFile looks for a credentials file. -// If a credentials file is not found, it returns nil credentials without raising errors. -// If invalid credentials file is found, it returns an error. -func fromFile() (*Credentials, error) { - // Looks for a credentials file - configDir, err := searchConfigDir(CredentialsFilename) + c, err = fromFile(filepath) if err != nil { - return nil, fmt.Errorf("can't get credentials directory: %w", err) + return nil, fmt.Errorf("reading credentials from file %s: %w", filepath, err) } - // Return nil credentials if no config file is found - if configDir == nil { - return nil, nil + // Return error if credentials are not valid + if err := c.Validate(); err != nil { + return nil, fmt.Errorf( + "credentials retrieved from file %s are not valid: %w", filepath, err, + ) } + return c, nil +} +// fromFile looks for a credentials file. +func fromFile(filepath string) (*Credentials, error) { v := viper.New() v.SetConfigName(CredentialsFilename) - v.AddConfigPath(*configDir) - err = v.ReadInConfig() + v.AddConfigPath(filepath) + err := v.ReadInConfig() if err != nil { err = fmt.Errorf( "credentials file found at %s but cannot read its content: %w", - *configDir, + filepath, err, ) return nil, err @@ -140,26 +145,18 @@ func fromFile() (*Credentials, error) { if err != nil { return nil, fmt.Errorf( "credentials file found at %s but cannot unmarshal it: %w", - *configDir, - err, - ) - } - if err = cred.Validate(); err != nil { - return nil, fmt.Errorf( - "credentials file found at %s but is not valid: %w", - *configDir, + filepath, err, ) } return cred, nil } -// fromEnv looks for credentials in environment variables. -// If credentials are not found, it returns nil credentials without raising errors. -// If invalid credentials are found, it returns an error. +// fromEnv retrieves credentials from environment variables. +// Returns empty credentials if not found. func fromEnv() (*Credentials, error) { v := viper.New() - SetDefaultCredentials(v) + SetEmptyCredentials(v) v.SetEnvPrefix(EnvPrefix) v.AutomaticEnv() @@ -168,17 +165,5 @@ func fromEnv() (*Credentials, error) { if err != nil { return nil, fmt.Errorf("cannot unmarshal credentials from environment variables: %w", err) } - - if cred.IsEmpty() { - return nil, nil - } - - if err = cred.Validate(); err != nil { - return nil, fmt.Errorf( - "credentials retrieved from environment variables with prefix '%s' are not valid: %w", - EnvPrefix, - err, - ) - } return cred, nil } From f4afd4d5eb798177aef0e3aa98697e4f29f33a9a Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Thu, 23 Dec 2021 12:06:08 +0100 Subject: [PATCH 2/4] Add credentials find command --- cli/credentials/credentials.go | 1 + cli/credentials/find.go | 51 ++++++++++++++++ internal/config/config.go | 8 +-- internal/config/credentials.go | 106 ++++++++++++++++++++------------- 4 files changed, 122 insertions(+), 44 deletions(-) create mode 100644 cli/credentials/find.go diff --git a/cli/credentials/credentials.go b/cli/credentials/credentials.go index 43e9baad..cd286d91 100644 --- a/cli/credentials/credentials.go +++ b/cli/credentials/credentials.go @@ -29,6 +29,7 @@ func NewCommand() *cobra.Command { } credentialsCommand.AddCommand(initInitCommand()) + credentialsCommand.AddCommand(initFindCommand()) return credentialsCommand } diff --git a/cli/credentials/find.go b/cli/credentials/find.go new file mode 100644 index 00000000..f633b403 --- /dev/null +++ b/cli/credentials/find.go @@ -0,0 +1,51 @@ +// This file is part of arduino-cloud-cli. +// +// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package credentials + +import ( + "os" + + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/arduino-cloud-cli/internal/config" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +func initFindCommand() *cobra.Command { + findCommand := &cobra.Command{ + Use: "find", + Short: "Find the credentials file being used in your current directory", + Long: "Find the Arduino Cloud CLI credentials file being used in your current directory", + Run: runFindCommand, + } + + return findCommand +} + +func runFindCommand(cmd *cobra.Command, args []string) { + logrus.Info("Looking for a credentials file") + + src, err := config.FindCredentials() + if err != nil { + feedback.Error("Error during credentials find: %v", err) + os.Exit(errorcodes.ErrGeneric) + } + + feedback.Printf("Using credentials in: %s", src) +} diff --git a/internal/config/config.go b/internal/config/config.go index fc0f9ad1..7a99e1a9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -24,12 +24,12 @@ import ( "github.com/spf13/viper" ) -// searchConfigDir looks for a configuration file in different directories in the following order: +// searchConfigFile looks for a configuration file in different directories in the following order: // current working directory, parents of the current working directory, arduino15 default directory. // Returns empty string and false if no config file has been found, without raising errors. // Returns an error if any problem is encountered during the file research which prevents // to understand whether a config file exists or not. -func searchConfigDir(confname string) (dir string, found bool, err error) { +func searchConfigFile(confname string) (dir string, found bool, err error) { // Search in current directory and its parents. cwd, err := paths.Getwd() if err != nil { @@ -41,7 +41,7 @@ func searchConfigDir(confname string) (dir string, found bool, err error) { logrus.Infof("Looking for %s in %s", confname, path) if file, found := configFileInDir(confname, path); found { logrus.Infof("Found %s at %s", confname, file) - return path.String(), true, nil + return file.String(), true, nil } } @@ -53,7 +53,7 @@ func searchConfigDir(confname string) (dir string, found bool, err error) { logrus.Infof("Looking for %s in %s", confname, arduino15) if file, found := configFileInDir(confname, arduino15); found { logrus.Infof("%s found at %s", confname, file) - return arduino15.String(), true, nil + return file.String(), true, nil } // Didn't find config file in the current directory, its parents or in arduino15" diff --git a/internal/config/credentials.go b/internal/config/credentials.go index 2868a081..7f2e571a 100644 --- a/internal/config/credentials.go +++ b/internal/config/credentials.go @@ -19,7 +19,9 @@ package config import ( "fmt" + "strings" + "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" "github.com/spf13/viper" ) @@ -77,77 +79,101 @@ func (c *Credentials) IsEmpty() bool { return len(c.Client) == 0 && len(c.Secret) == 0 } -// RetrieveCredentials looks for credentials in +// FindCredentials looks for credentials in // environment variables or in credentials file. -// Returns error if no credentials are found or -// if found credentials are invalid. -func RetrieveCredentials() (*Credentials, error) { +// Returns the source of found credentials (env or filepath). +// Returns an error if credentials are not found +// specifying paths where the credentials are searched. +func FindCredentials() (source string, err error) { // Credentials extracted from environment has highest priority logrus.Info("Looking for credentials in environment variables") c, err := fromEnv() if err != nil { - return nil, fmt.Errorf("reading credentials from environment variables: %w", err) + return "", fmt.Errorf("looking for credentials in environment variables: %w", err) } - // Return credentials if found in env if !c.IsEmpty() { - // Return error if credentials are found but are not valid - if err := c.Validate(); err != nil { + logrus.Infof("Credentials found in environment variables with prefix '%s'", EnvPrefix) + return "environment variables", nil + } + + logrus.Info("Looking for credentials in file system") + path, found, err := searchConfigFile(CredentialsFilename) + if err != nil { + return "", fmt.Errorf("looking for credentials files: %w", err) + } + if found { + return path, nil + } + + return "", fmt.Errorf( + "credentials have not been found neither in environment variables " + + "nor in the current directory, its parents or in arduino15", + ) +} + +// RetrieveCredentials retrieves credentials from +// environment variables or credentials file. +// Returns error if credentials are not found or +// if found credentials are invalid. +func RetrieveCredentials() (cred *Credentials, err error) { + // Credentials extracted from environment has highest priority + logrus.Info("Looking for credentials in environment variables") + cred, err = fromEnv() + if err != nil { + return nil, fmt.Errorf("reading credentials from environment variables: %w", err) + } + // Returns credentials if found in env + if !cred.IsEmpty() { + // Returns error if credentials are found but are not valid + if err := cred.Validate(); err != nil { return nil, fmt.Errorf( "credentials retrieved from environment variables with prefix '%s' are not valid: %w", EnvPrefix, err, ) } logrus.Infof("Credentials found in environment variables with prefix '%s'", EnvPrefix) - return c, nil + return cred, nil } logrus.Info("Looking for credentials in file system") - filepath, found, err := searchConfigDir(CredentialsFilename) + filepath, found, err := searchConfigFile(CredentialsFilename) if err != nil { return nil, fmt.Errorf("can't get credentials directory: %w", err) } - if !found { - return nil, fmt.Errorf( - "credentials have not been found neither in environment variables " + - "nor in the current directory, its parents or in arduino15", - ) + // Returns credentials if found in a file + if found { + if cred, err = fromFile(filepath); err != nil { + return nil, fmt.Errorf("reading credentials from file %s: %w", filepath, err) + } + // Returns error if credentials are found but are not valid + if err := cred.Validate(); err != nil { + return nil, fmt.Errorf( + "credentials retrieved from file %s are not valid: %w", filepath, err, + ) + } + return cred, nil } - c, err = fromFile(filepath) - if err != nil { - return nil, fmt.Errorf("reading credentials from file %s: %w", filepath, err) - } - // Return error if credentials are not valid - if err := c.Validate(); err != nil { - return nil, fmt.Errorf( - "credentials retrieved from file %s are not valid: %w", filepath, err, - ) - } - return c, nil + return nil, fmt.Errorf( + "credentials have not been found neither in environment variables " + + "nor in the current directory, its parents or in arduino15", + ) } -// fromFile looks for a credentials file. +// fromFile retrieves credentials from a credentials file. +// Returns error if credentials are not found or cannot be fetched. func fromFile(filepath string) (*Credentials, error) { v := viper.New() - v.SetConfigName(CredentialsFilename) - v.AddConfigPath(filepath) + v.SetConfigFile(filepath) + v.SetConfigType(strings.TrimLeft(paths.New(filepath).Ext(), ".")) err := v.ReadInConfig() if err != nil { - err = fmt.Errorf( - "credentials file found at %s but cannot read its content: %w", - filepath, - err, - ) - return nil, err + return nil, fmt.Errorf("cannot read credentials file: %w", err) } cred := &Credentials{} err = v.Unmarshal(cred) if err != nil { - return nil, fmt.Errorf( - "credentials file found at %s but cannot unmarshal it: %w", - filepath, - err, - ) + return nil, fmt.Errorf("cannot unmarshal credentials file: %w", err) } return cred, nil } From eb101fceaf64aff72c6fe00bcb4a1b8bc8746879 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Thu, 23 Dec 2021 13:18:48 +0100 Subject: [PATCH 3/4] Improve --- cli/credentials/find.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/credentials/find.go b/cli/credentials/find.go index f633b403..a060577c 100644 --- a/cli/credentials/find.go +++ b/cli/credentials/find.go @@ -30,8 +30,8 @@ import ( func initFindCommand() *cobra.Command { findCommand := &cobra.Command{ Use: "find", - Short: "Find the credentials file being used in your current directory", - Long: "Find the Arduino Cloud CLI credentials file being used in your current directory", + Short: "Find the credentials that would be used in your current directory", + Long: "Find the credentials to access Arduino IoT Cloud that would be used in your current directory", Run: runFindCommand, } @@ -39,11 +39,11 @@ func initFindCommand() *cobra.Command { } func runFindCommand(cmd *cobra.Command, args []string) { - logrus.Info("Looking for a credentials file") + logrus.Info("Looking for credentials") src, err := config.FindCredentials() if err != nil { - feedback.Error("Error during credentials find: %v", err) + feedback.Errorf("Error during credentials find: %v", err) os.Exit(errorcodes.ErrGeneric) } From 6c6858588fb7bd5b231769d05d8251b34c93abf0 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Thu, 23 Dec 2021 13:24:03 +0100 Subject: [PATCH 4/4] Update readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 37492a71..e0ccebcf 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,12 @@ Credentials file is supported in two different format: json and yaml. Use the `- It is also possible to specify credentials directly in `ARDUINO_CLOUD_CLIENT` and `ARDUINO_CLOUD_SECRET` environment variables. Credentials specified in environment variables have higher priority than the ones specified in credentials files. +#### Find credentials + +To have information about which credentials would be used in the current folder you can use the following command: + +`$ arduino-cloud-cli credentials find` + ## 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.