@@ -25,60 +25,182 @@ import (
2525 "github.com/spf13/viper"
2626)
2727
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+
2839// Config contains all the configuration parameters
2940// known by arduino-cloud-cli.
3041type Config struct {
3142 Client string `map-structure:"client"` // Client ID of the user
3243 Secret string `map-structure:"secret"` // Secret ID of the user, unique for each Client ID
3344}
3445
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.
3774func 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
38104 configDir , err := searchConfigDir ()
39105 if err != nil {
40106 return nil , fmt .Errorf ("can't get config directory: %w" , err )
41107 }
108+ // Return nil config if no config file is found
109+ if configDir == nil {
110+ return nil , nil
111+ }
42112
43113 v := viper .New ()
44114 v .SetConfigName (Filename )
45- v .AddConfigPath (configDir )
115+ v .AddConfigPath (* configDir )
46116 err = v .ReadInConfig ()
47117 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+ )
49123 return nil , err
50124 }
51125
52126 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+ }
54142 return conf , nil
55143}
56144
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 ) {
58180 // Search in current directory and its parents.
59181 cwd , err := paths .Getwd ()
60182 if err != nil {
61- return "" , err
183+ return nil , err
62184 }
63185 // Don't let bad naming mislead you, cwd.Parents()[0] is cwd itself so
64186 // we look in the current directory first and then on its parents.
65187 for _ , path := range cwd .Parents () {
66188 if path .Join (Filename + ".yaml" ).Exist () || path .Join (Filename + ".json" ).Exist () {
67- return path .String (), nil
189+ p := path .String ()
190+ return & p , nil
68191 }
69192 }
70193
71194 // Search in arduino's default data directory.
72195 arduino15 , err := arduino .DataDir ()
73196 if err != nil {
74- return "" , err
197+ return nil , err
75198 }
76199 if arduino15 .Join (Filename + ".yaml" ).Exist () || arduino15 .Join (Filename + ".json" ).Exist () {
77- return arduino15 .String (), nil
200+ p := arduino15 .String ()
201+ return & p , nil
78202 }
79203
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
84206}
0 commit comments