Skip to content

Commit 97e5ca8

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 c794b60 commit 97e5ca8

File tree

5 files changed

+138
-71
lines changed

5 files changed

+138
-71
lines changed

Diff for: 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.

Diff for: 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
}

Diff for: 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+
}

Diff for: 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.

Diff for: 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)