Skip to content

Commit a396b73

Browse files
Paolo Calaopolldo
Paolo Calao
authored andcommitted
Add credentials find command (#92)
Implement a command to give users information about which credentials would be used by the cloud-cli in the current folder
1 parent b4ef674 commit a396b73

File tree

5 files changed

+138
-71
lines changed

5 files changed

+138
-71
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ Credentials file is supported in two different format: json and yaml. Use the `-
5555

5656
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.
5757

58+
#### Find credentials
59+
60+
To have information about which credentials would be used in the current folder you can use the following command:
61+
62+
`$ arduino-cloud-cli credentials find`
63+
5864
## Device provisioning
5965

6066
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.

cli/credentials/credentials.go

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func NewCommand() *cobra.Command {
2929
}
3030

3131
credentialsCommand.AddCommand(initInitCommand())
32+
credentialsCommand.AddCommand(initFindCommand())
3233

3334
return credentialsCommand
3435
}

cli/credentials/find.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// This file is part of arduino-cloud-cli.
2+
//
3+
// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package credentials
19+
20+
import (
21+
"os"
22+
23+
"github.com/arduino/arduino-cli/cli/errorcodes"
24+
"github.com/arduino/arduino-cli/cli/feedback"
25+
"github.com/arduino/arduino-cloud-cli/internal/config"
26+
"github.com/sirupsen/logrus"
27+
"github.com/spf13/cobra"
28+
)
29+
30+
func initFindCommand() *cobra.Command {
31+
findCommand := &cobra.Command{
32+
Use: "find",
33+
Short: "Find the credentials that would be used in your current directory",
34+
Long: "Find the credentials to access Arduino IoT Cloud that would be used in your current directory",
35+
Run: runFindCommand,
36+
}
37+
38+
return findCommand
39+
}
40+
41+
func runFindCommand(cmd *cobra.Command, args []string) {
42+
logrus.Info("Looking for credentials")
43+
44+
src, err := config.FindCredentials()
45+
if err != nil {
46+
feedback.Errorf("Error during credentials find: %v", err)
47+
os.Exit(errorcodes.ErrGeneric)
48+
}
49+
50+
feedback.Printf("Using credentials in: %s", src)
51+
}

internal/config/config.go

+8-10
Original file line numberDiff line numberDiff line change
@@ -24,42 +24,40 @@ import (
2424
"github.com/spf13/viper"
2525
)
2626

27-
// searchConfigDir looks for a configuration file in different directories in the following order:
27+
// searchConfigFile looks for a configuration file in different directories in the following order:
2828
// current working directory, parents of the current working directory, arduino15 default directory.
29-
// Returns a nil string if no config file has been found, without raising errors.
29+
// Returns empty string and false if no config file has been found, without raising errors.
3030
// Returns an error if any problem is encountered during the file research which prevents
3131
// to understand whether a config file exists or not.
32-
func searchConfigDir(confname string) (*string, error) {
32+
func searchConfigFile(confname string) (dir string, found bool, err error) {
3333
// Search in current directory and its parents.
3434
cwd, err := paths.Getwd()
3535
if err != nil {
36-
return nil, err
36+
return "", false, err
3737
}
3838
// Don't let bad naming mislead you, cwd.Parents()[0] is cwd itself so
3939
// we look in the current directory first and then on its parents.
4040
for _, path := range cwd.Parents() {
4141
logrus.Infof("Looking for %s in %s", confname, path)
4242
if file, found := configFileInDir(confname, path); found {
4343
logrus.Infof("Found %s at %s", confname, file)
44-
p := path.String()
45-
return &p, nil
44+
return file.String(), true, nil
4645
}
4746
}
4847

4948
// Search in arduino's default data directory.
5049
arduino15, err := arduino.DataDir()
5150
if err != nil {
52-
return nil, err
51+
return "", false, err
5352
}
5453
logrus.Infof("Looking for %s in %s", confname, arduino15)
5554
if file, found := configFileInDir(confname, arduino15); found {
5655
logrus.Infof("%s found at %s", confname, file)
57-
p := arduino15.String()
58-
return &p, nil
56+
return file.String(), true, nil
5957
}
6058

6159
// Didn't find config file in the current directory, its parents or in arduino15"
62-
return nil, nil
60+
return "", false, nil
6361
}
6462

6563
// configFileInDir looks for a configuration file in the passed directory.

internal/config/credentials.go

+72-61
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ package config
1919

2020
import (
2121
"fmt"
22+
"strings"
2223

24+
"github.com/arduino/go-paths-helper"
2325
"github.com/sirupsen/logrus"
2426
"github.com/spf13/viper"
2527
)
@@ -38,8 +40,8 @@ const (
3840
CredentialsFilename = "arduino-cloud-credentials"
3941
)
4042

41-
// SetDefaultCredentials sets the default credentials values.
42-
func SetDefaultCredentials(settings *viper.Viper) {
43+
// SetEmptyCredentials sets the default credentials values to empty strings.
44+
func SetEmptyCredentials(settings *viper.Viper) {
4345
// Client ID
4446
settings.SetDefault("client", "")
4547
// Secret
@@ -77,89 +79,110 @@ func (c *Credentials) IsEmpty() bool {
7779
return len(c.Client) == 0 && len(c.Secret) == 0
7880
}
7981

80-
// RetrieveCredentials looks for credentials in
82+
// FindCredentials looks for credentials in
8183
// environment variables or in credentials file.
82-
// Returns error if no credentials are found.
83-
func RetrieveCredentials() (*Credentials, error) {
84+
// Returns the source of found credentials (env or filepath).
85+
// Returns an error if credentials are not found
86+
// specifying paths where the credentials are searched.
87+
func FindCredentials() (source string, err error) {
8488
// Credentials extracted from environment has highest priority
8589
logrus.Info("Looking for credentials in environment variables")
8690
c, err := fromEnv()
8791
if err != nil {
88-
return nil, fmt.Errorf("reading credentials from environment variables: %w", err)
92+
return "", fmt.Errorf("looking for credentials in environment variables: %w", err)
8993
}
90-
// Return credentials only if found
91-
if c != nil {
92-
logrus.Info("Credentials found in environment variables")
93-
return c, nil
94+
if !c.IsEmpty() {
95+
logrus.Infof("Credentials found in environment variables with prefix '%s'", EnvPrefix)
96+
return "environment variables", nil
9497
}
9598

9699
logrus.Info("Looking for credentials in file system")
97-
c, err = fromFile()
100+
path, found, err := searchConfigFile(CredentialsFilename)
98101
if err != nil {
99-
return nil, fmt.Errorf("reading credentials from file: %w", err)
102+
return "", fmt.Errorf("looking for credentials files: %w", err)
100103
}
101-
if c != nil {
102-
return c, nil
104+
if found {
105+
return path, nil
103106
}
104107

105-
return nil, fmt.Errorf(
108+
return "", fmt.Errorf(
106109
"credentials have not been found neither in environment variables " +
107110
"nor in the current directory, its parents or in arduino15",
108111
)
109112
}
110113

111-
// fromFile looks for a credentials file.
112-
// If a credentials file is not found, it returns nil credentials without raising errors.
113-
// If invalid credentials file is found, it returns an error.
114-
func fromFile() (*Credentials, error) {
115-
// Looks for a credentials file
116-
configDir, err := searchConfigDir(CredentialsFilename)
114+
// RetrieveCredentials retrieves credentials from
115+
// environment variables or credentials file.
116+
// Returns error if credentials are not found or
117+
// if found credentials are invalid.
118+
func RetrieveCredentials() (cred *Credentials, err error) {
119+
// Credentials extracted from environment has highest priority
120+
logrus.Info("Looking for credentials in environment variables")
121+
cred, err = fromEnv()
122+
if err != nil {
123+
return nil, fmt.Errorf("reading credentials from environment variables: %w", err)
124+
}
125+
// Returns credentials if found in env
126+
if !cred.IsEmpty() {
127+
// Returns error if credentials are found but are not valid
128+
if err := cred.Validate(); err != nil {
129+
return nil, fmt.Errorf(
130+
"credentials retrieved from environment variables with prefix '%s' are not valid: %w", EnvPrefix, err,
131+
)
132+
}
133+
logrus.Infof("Credentials found in environment variables with prefix '%s'", EnvPrefix)
134+
return cred, nil
135+
}
136+
137+
logrus.Info("Looking for credentials in file system")
138+
filepath, found, err := searchConfigFile(CredentialsFilename)
117139
if err != nil {
118140
return nil, fmt.Errorf("can't get credentials directory: %w", err)
119141
}
120-
// Return nil credentials if no config file is found
121-
if configDir == nil {
122-
return nil, nil
142+
// Returns credentials if found in a file
143+
if found {
144+
if cred, err = fromFile(filepath); err != nil {
145+
return nil, fmt.Errorf("reading credentials from file %s: %w", filepath, err)
146+
}
147+
// Returns error if credentials are found but are not valid
148+
if err := cred.Validate(); err != nil {
149+
return nil, fmt.Errorf(
150+
"credentials retrieved from file %s are not valid: %w", filepath, err,
151+
)
152+
}
153+
return cred, nil
123154
}
124155

156+
return nil, fmt.Errorf(
157+
"credentials have not been found neither in environment variables " +
158+
"nor in the current directory, its parents or in arduino15",
159+
)
160+
}
161+
162+
// fromFile retrieves credentials from a credentials file.
163+
// Returns error if credentials are not found or cannot be fetched.
164+
func fromFile(filepath string) (*Credentials, error) {
125165
v := viper.New()
126-
v.SetConfigName(CredentialsFilename)
127-
v.AddConfigPath(*configDir)
128-
err = v.ReadInConfig()
166+
v.SetConfigFile(filepath)
167+
v.SetConfigType(strings.TrimLeft(paths.New(filepath).Ext(), "."))
168+
err := v.ReadInConfig()
129169
if err != nil {
130-
err = fmt.Errorf(
131-
"credentials file found at %s but cannot read its content: %w",
132-
*configDir,
133-
err,
134-
)
135-
return nil, err
170+
return nil, fmt.Errorf("cannot read credentials file: %w", err)
136171
}
137172

138173
cred := &Credentials{}
139174
err = v.Unmarshal(cred)
140175
if err != nil {
141-
return nil, fmt.Errorf(
142-
"credentials file found at %s but cannot unmarshal it: %w",
143-
*configDir,
144-
err,
145-
)
146-
}
147-
if err = cred.Validate(); err != nil {
148-
return nil, fmt.Errorf(
149-
"credentials file found at %s but is not valid: %w",
150-
*configDir,
151-
err,
152-
)
176+
return nil, fmt.Errorf("cannot unmarshal credentials file: %w", err)
153177
}
154178
return cred, nil
155179
}
156180

157-
// fromEnv looks for credentials in environment variables.
158-
// If credentials are not found, it returns nil credentials without raising errors.
159-
// If invalid credentials are found, it returns an error.
181+
// fromEnv retrieves credentials from environment variables.
182+
// Returns empty credentials if not found.
160183
func fromEnv() (*Credentials, error) {
161184
v := viper.New()
162-
SetDefaultCredentials(v)
185+
SetEmptyCredentials(v)
163186
v.SetEnvPrefix(EnvPrefix)
164187
v.AutomaticEnv()
165188

@@ -168,17 +191,5 @@ func fromEnv() (*Credentials, error) {
168191
if err != nil {
169192
return nil, fmt.Errorf("cannot unmarshal credentials from environment variables: %w", err)
170193
}
171-
172-
if cred.IsEmpty() {
173-
return nil, nil
174-
}
175-
176-
if err = cred.Validate(); err != nil {
177-
return nil, fmt.Errorf(
178-
"credentials retrieved from environment variables with prefix '%s' are not valid: %w",
179-
EnvPrefix,
180-
err,
181-
)
182-
}
183194
return cred, nil
184195
}

0 commit comments

Comments
 (0)