@@ -25,60 +25,182 @@ import (
25
25
"github.com/spf13/viper"
26
26
)
27
27
28
+ const (
29
+ // ClientIDLen specifies the length of Arduino IoT Cloud client ids.
30
+ ClientIDLen = 32
31
+ // ClientSecretLen specifies the length of Arduino IoT Cloud client secrets.
32
+ ClientSecretLen = 64
33
+
34
+ // EnvPrefix is the prefix environment variables should have to be
35
+ // fetched as config parameters during the config retrieval.
36
+ EnvPrefix = "ARDUINO_CLOUD"
37
+ )
38
+
28
39
// Config contains all the configuration parameters
29
40
// known by arduino-cloud-cli.
30
41
type Config struct {
31
42
Client string `map-structure:"client"` // Client ID of the user
32
43
Secret string `map-structure:"secret"` // Secret ID of the user, unique for each Client ID
33
44
}
34
45
35
- // Retrieve returns the actual parameters contained in the
36
- // configuration file, if any. Returns error if no config file is found.
46
+ // Validate the config.
47
+ // If config is not valid, it returns an error explaining the reason.
48
+ func (c * Config ) Validate () error {
49
+ if len (c .Client ) != ClientIDLen {
50
+ return fmt .Errorf (
51
+ "client id not valid, expected len %d but got %d" ,
52
+ ClientIDLen ,
53
+ len (c .Client ),
54
+ )
55
+ }
56
+ if len (c .Secret ) != ClientSecretLen {
57
+ return fmt .Errorf (
58
+ "client secret not valid, expected len %d but got %d" ,
59
+ ClientSecretLen ,
60
+ len (c .Secret ),
61
+ )
62
+ }
63
+ return nil
64
+ }
65
+
66
+ // IsEmpty checks if config has no params set.
67
+ func (c * Config ) IsEmpty () bool {
68
+ return len (c .Client ) == 0 && len (c .Secret ) == 0
69
+ }
70
+
71
+ // Retrieve looks for configuration parameters in
72
+ // environment variables or in configuration file.
73
+ // Returns error if no config is found.
37
74
func Retrieve () (* Config , error ) {
75
+ // Config extracted from environment has highest priority
76
+ c , err := fromEnv ()
77
+ if err != nil {
78
+ return nil , fmt .Errorf ("reading config from environment variables: %w" , err )
79
+ }
80
+ // Return the config only if it has been found
81
+ if c != nil {
82
+ return c , nil
83
+ }
84
+
85
+ c , err = fromFile ()
86
+ if err != nil {
87
+ return nil , fmt .Errorf ("reading config from file: %w" , err )
88
+ }
89
+ if c != nil {
90
+ return c , nil
91
+ }
92
+
93
+ return nil , fmt .Errorf (
94
+ "config has not been found neither in environment variables " +
95
+ "nor in the current directory, its parents or in arduino15" ,
96
+ )
97
+ }
98
+
99
+ // fromFile looks for a configuration file.
100
+ // If a config file is not found, it returns a nil config without raising errors.
101
+ // If invalid config file is found, it returns an error.
102
+ func fromFile () (* Config , error ) {
103
+ // Looks for a configuration file
38
104
configDir , err := searchConfigDir ()
39
105
if err != nil {
40
106
return nil , fmt .Errorf ("can't get config directory: %w" , err )
41
107
}
108
+ // Return nil config if no config file is found
109
+ if configDir == nil {
110
+ return nil , nil
111
+ }
42
112
43
113
v := viper .New ()
44
114
v .SetConfigName (Filename )
45
- v .AddConfigPath (configDir )
115
+ v .AddConfigPath (* configDir )
46
116
err = v .ReadInConfig ()
47
117
if err != nil {
48
- err = fmt .Errorf ("%s: %w" , "retrieving config file" , err )
118
+ err = fmt .Errorf (
119
+ "config file found at %s but cannot read its content: %w" ,
120
+ * configDir ,
121
+ err ,
122
+ )
49
123
return nil , err
50
124
}
51
125
52
126
conf := & Config {}
53
- v .Unmarshal (conf )
127
+ err = v .Unmarshal (conf )
128
+ if err != nil {
129
+ return nil , fmt .Errorf (
130
+ "config file found at %s but cannot unmarshal it: %w" ,
131
+ * configDir ,
132
+ err ,
133
+ )
134
+ }
135
+ if err = conf .Validate (); err != nil {
136
+ return nil , fmt .Errorf (
137
+ "config file found at %s but is not valid: %w" ,
138
+ * configDir ,
139
+ err ,
140
+ )
141
+ }
54
142
return conf , nil
55
143
}
56
144
57
- func searchConfigDir () (string , error ) {
145
+ // fromEnv looks for configuration credentials in environment variables.
146
+ // If credentials are not found, it returns a nil config without raising errors.
147
+ // If invalid credentials are found, it returns an error.
148
+ func fromEnv () (* Config , error ) {
149
+ v := viper .New ()
150
+ SetDefaults (v )
151
+ v .SetEnvPrefix (EnvPrefix )
152
+ v .AutomaticEnv ()
153
+
154
+ conf := & Config {}
155
+ err := v .Unmarshal (conf )
156
+ if err != nil {
157
+ return nil , fmt .Errorf ("cannot unmarshal config from environment variables: %w" , err )
158
+ }
159
+
160
+ if conf .IsEmpty () {
161
+ return nil , nil
162
+ }
163
+
164
+ if err = conf .Validate (); err != nil {
165
+ return nil , fmt .Errorf (
166
+ "config retrieved from environment variables with prefix '%s' are not valid: %w" ,
167
+ EnvPrefix ,
168
+ err ,
169
+ )
170
+ }
171
+ return conf , nil
172
+ }
173
+
174
+ // searchConfigDir configuration file in different directories in the following order:
175
+ // current working directory, parents of the current working directory, arduino15 default directory.
176
+ // Returns a nil string if no config file has been found, without raising errors.
177
+ // Returns an error if any problem is encountered during the file research which prevents
178
+ // to understand whether a config file exists or not.
179
+ func searchConfigDir () (* string , error ) {
58
180
// Search in current directory and its parents.
59
181
cwd , err := paths .Getwd ()
60
182
if err != nil {
61
- return "" , err
183
+ return nil , err
62
184
}
63
185
// Don't let bad naming mislead you, cwd.Parents()[0] is cwd itself so
64
186
// we look in the current directory first and then on its parents.
65
187
for _ , path := range cwd .Parents () {
66
188
if path .Join (Filename + ".yaml" ).Exist () || path .Join (Filename + ".json" ).Exist () {
67
- return path .String (), nil
189
+ p := path .String ()
190
+ return & p , nil
68
191
}
69
192
}
70
193
71
194
// Search in arduino's default data directory.
72
195
arduino15 , err := arduino .DataDir ()
73
196
if err != nil {
74
- return "" , err
197
+ return nil , err
75
198
}
76
199
if arduino15 .Join (Filename + ".yaml" ).Exist () || arduino15 .Join (Filename + ".json" ).Exist () {
77
- return arduino15 .String (), nil
200
+ p := arduino15 .String ()
201
+ return & p , nil
78
202
}
79
203
80
- return "" , fmt .Errorf (
81
- "didn't find config file in the current directory, its parents or in %s" ,
82
- arduino15 .String (),
83
- )
204
+ // Didn't find config file in the current directory, its parents or in arduino15"
205
+ return nil , nil
84
206
}
0 commit comments