33used elsewhere.
44"""
55
6- import argparse
76import os
87import sys
9- import webbrowser
8+ import argparse
109
1110from .auth import (
11+ _get_token_web ,
1212 _check_full_access ,
1313 _do_get_request ,
1414 _get_token_terminal ,
15- _get_token_web ,
1615)
1716from .helpers import (
17+ _check_browsers ,
1818 _default_thing_input ,
1919 _get_config ,
2020 _get_config_path ,
2323
2424ENV_TOKEN_NAME = "LINODE_CLI_TOKEN"
2525
26- # this is a list of browser that _should_ work for web-based auth. This is mostly
27- # intended to exclude lynx and other terminal browsers which could be opened, but
28- # won't work.
29- KNOWN_GOOD_BROWSERS = {
30- "chrome" ,
31- "firefox" ,
32- "mozilla" ,
33- "netscape" ,
34- "opera" ,
35- "safari" ,
36- "chromium" ,
37- "chromium-browser" ,
38- "epiphany" ,
39- }
40-
41-
4226class CLIConfig :
4327 """
4428 Generates the necessary config for the Linode CLI
@@ -73,17 +57,6 @@ def __init__(self, base_url, username=None, skip_config=False):
7357 elif environ_token is not None :
7458 self .used_env_token = True
7559
76- def set_user (self , username ):
77- """
78- Sets the acting username. If this username is not in the config, this is
79- an error. This overrides the default username
80- """
81- if not self .config .has_section (username ):
82- print (f"User { username } is not configured!" )
83- sys .exit (1 )
84-
85- self .username = username
86-
8760 def default_username (self ):
8861 """
8962 Returns the default-user Username
@@ -92,83 +65,16 @@ def default_username(self):
9265 return self .config .get ("DEFAULT" , "default-user" )
9366 return ""
9467
95- def update_namespace (self , namespace , new_dict ):
96- """
97- In order to update the namespace, we need to turn it into a dict, modify it there,
98- then reconstruct it with the exploded dict.
99- """
100- ns_dict = vars (namespace )
101- warn_dict = {}
102- for k in new_dict :
103- if k .startswith ("plugin-" ):
104- # plugins set config options that start with 'plugin-' - these don't
105- # get included in the updated namespace
106- continue
107- if k in ns_dict and isinstance (k , list ):
108- ns_dict [k ].append (new_dict [k ])
109- if k in ns_dict and ns_dict [k ] is None :
110- warn_dict [k ] = new_dict [k ]
111- ns_dict [k ] = new_dict [k ]
112- if not any (
113- x in ["--suppress-warnings" , "--no-headers" ] for x in sys .argv
114- ):
115- print (
116- f"using default values: { warn_dict } , use --no-defaults flag to disable defaults"
117- )
118- return argparse .Namespace (** ns_dict )
119-
120- def update (self , namespace , allowed_defaults ):
68+ def set_user (self , username ):
12169 """
122- This updates a Namespace (as returned by ArgumentParser) with config values
123- if they aren't present in the Namespace already.
70+ Sets the acting username. If this username is not in the config, this is
71+ an error. This overrides the default username
12472 """
125- if self .used_env_token and self .config is None :
126- # the CLI is using a token defined in the environment; as such, we may
127- # not have actually loaded a config file. That's fine, there are just
128- # no defaults
129- return None
130-
131- username = self .username or self .default_username ()
132-
133- if not self .config .has_option (username , "token" ) and not os .environ .get (
134- ENV_TOKEN_NAME , None
135- ):
136- print (f"User { username } is not configured." )
73+ if not self .config .has_section (username ):
74+ print (f"User { username } is not configured!" )
13775 sys .exit (1 )
13876
139- if self .config .has_section (username ) and allowed_defaults :
140- # update_dicts = {
141- # default_key: self.config.get(username, default_key)
142- # for default_key in allowed_defaults
143- # if self.config.has_option(username, default_key)
144- # }
145- update_dicts = {}
146- for default_key in allowed_defaults :
147- if not self .config .has_option (username , default_key ):
148- continue
149- value = self .config .get (username , default_key )
150- if default_key == "authorized_users" :
151- update_dicts [default_key ] = [value ]
152- else :
153- update_dicts [default_key ] = value
154-
155- return self .update_namespace (namespace , update_dicts )
156- return namespace
157-
158- def get_token (self ):
159- """
160- Returns the token for a configured user
161- """
162- if self .used_env_token :
163- return os .environ .get (ENV_TOKEN_NAME , None )
164-
165- if self .config .has_option (
166- self .username or self .default_username (), "token"
167- ):
168- return self .config .get (
169- self .username or self .default_username (), "token"
170- )
171- return ""
77+ self .username = username
17278
17379 def remove_user (self , username ):
17480 """
@@ -177,7 +83,7 @@ def remove_user(self, username):
17783 """
17884 if self .default_username () == username :
17985 print (
180- f"Cannot remote { username } as they are the default user! You can "
86+ f"Cannot remove { username } as they are the default user! You can "
18187 "change the default user with: `linode-cli set-user USERNAME`"
18288 )
18389 sys .exit (1 )
@@ -210,8 +116,17 @@ def set_default_user(self, username):
210116 self .config .set ("DEFAULT" , "default-user" , username )
211117 self .write_config ()
212118
213- # plugin methods - these are intended for plugins to utilize to store their
214- # own persistent config information
119+ def get_token (self ):
120+ """
121+ Returns the token for a configured user
122+ """
123+ if self .used_env_token :
124+ return os .environ .get (ENV_TOKEN_NAME , None )
125+
126+ if self .config .has_option (self .username or self .default_username (), "token" ):
127+ return self .config .get (self .username or self .default_username (), "token" )
128+ return ""
129+
215130 def get_value (self , key ):
216131 """
217132 Retrieves and returns an existing config value for the current user. This
@@ -236,6 +151,8 @@ def get_value(self, key):
236151
237152 return self .config .get (username , key )
238153
154+ # plugin methods - these are intended for plugins to utilize to store their
155+ # own persistent config information
239156 def plugin_set_value (self , key , value ):
240157 """
241158 Sets a new config value for a plugin for the current user. Plugin config
@@ -286,31 +203,65 @@ def plugin_get_value(self, key):
286203
287204 return self .config .get (username , full_key )
288205
289- def write_config (self , silent = False ):
206+ # TODO: this is more of an argparsing function than it is a config function
207+ # might be better to move this to argparsing during refactor and just have
208+ # configuration return defaults or keys or something
209+ def update (self , namespace , allowed_defaults ):
210+ """
211+ This updates a Namespace (as returned by ArgumentParser) with config values
212+ if they aren't present in the Namespace already.
213+ """
214+ if self .used_env_token and self .config is None :
215+ return None
216+ username = self .username or self .default_username ()
217+ if (not self .config .has_option (username , "token" )
218+ and not os .environ .get (ENV_TOKEN_NAME , None )):
219+ print (f"User { username } is not configured." )
220+ sys .exit (1 )
221+ if (not self .config .has_section (username )
222+ or allowed_defaults is None ):
223+ return namespace
224+
225+ warn_dict = {}
226+ ns_dict = vars (namespace )
227+ for key in allowed_defaults :
228+ if key not in ns_dict :
229+ continue
230+ if ns_dict [key ] is not None :
231+ continue
232+ # plugins set config options that start with 'plugin-'
233+ # these don't get included in the updated namespace
234+ if key .startswith ("plugin-" ):
235+ continue
236+ if self .config .has_option (username , key ):
237+ value = self .config .get (username , key )
238+ else :
239+ value = allowed_defaults [key ]
240+ if key == "authorized_users" :
241+ ns_dict [key ] = [value ]
242+ warn_dict [key ] = [value ]
243+ else :
244+ ns_dict [key ] = value
245+ warn_dict [key ] = value
246+
247+ if not any (x in ["--suppress-warnings" , "--no-headers" ] for x in sys .argv ):
248+ print (f"using default values: { warn_dict } , "
249+ "use --no-defaults flag to disable defaults" )
250+ return argparse .Namespace (** ns_dict )
251+
252+ def write_config (self ):
290253 """
291254 Saves the config file as it is right now. This can be used by plugins
292255 to save values they've set, and is used internally to update the config
293256 on disk when a new user if configured.
294-
295- :param silent: If True, does not print a message noting the config file
296- has been updated. This is primarily intended for silently
297- updated the config file from one version to another.
298- :type silent: bool
299257 """
300-
301- # Create the ~/.config directory if it does not exist
302258 if not os .path .exists (f"{ os .path .expanduser ('~' )} /.config" ):
303259 os .makedirs (f"{ os .path .expanduser ('~' )} /.config" )
304-
305260 with open (_get_config_path (), "w" , encoding = "utf-8" ) as f :
306261 self .config .write (f )
307262
308- if not silent :
309- print (f"\n Config written to { _get_config_path ()} " )
310263
311- def configure (
312- self ,
313- ): # pylint: disable=too-many-branches,too-many-statements
264+ def configure (self ): # pylint: disable=too-many-branches,too-many-statements
314265 """
315266 This assumes we're running interactively, and prompts the user
316267 for a series of defaults in order to make future CLI calls
@@ -326,67 +277,36 @@ def configure(
326277 username = None
327278 token = None
328279
329- print (
330- """Welcome to the Linode CLI. This will walk you through some initial setup."""
331- )
280+ print ("Welcome to the Linode CLI. This will walk you through some initial setup." )
332281
333282 if ENV_TOKEN_NAME in os .environ :
334283 print (
335- """Using token from {env_token_name }.
284+ f """Using token from { ENV_TOKEN_NAME } .
336285Note that no token will be saved in your configuration file.
337- * If you lose or remove {env_token_name}.
338- * All profiles will use {env_token_name}.""" .format (
339- env_token_name = ENV_TOKEN_NAME
340- )
286+ * If you lose or remove { ENV_TOKEN_NAME } .
287+ * All profiles will use { ENV_TOKEN_NAME } ."""
341288 )
342289 username = "DEFAULT"
343290 token = os .getenv (ENV_TOKEN_NAME )
344291
345292 else :
346- # let's see if we _can_ use web
347- can_use_browser = True
348- try :
349- webbrowser .get ()
350- except webbrowser .Error :
351- # there are no browsers installed
352- can_use_browser = False
353-
354- if can_use_browser and not KNOWN_GOOD_BROWSERS .intersection (
355- webbrowser ._tryorder # pylint: disable=protected-access
356- ):
357- print ()
358- print (
359- "This tool defaults to web-based authentication, however "
360- "no known-working browsers were found."
361- )
362-
363- while True :
364- r = input ("Try it anyway? [y/N]: " )
365- if r .lower () in "yn " :
366- can_use_browser = r .lower () == "y"
367- break
368-
369- if self .configure_with_pat or not can_use_browser :
370- username , config ["token" ] = _get_token_terminal (self .base_url )
371- else :
372- # pylint: disable=line-too-long
373- print ()
374- print (
375- "The CLI will use its web-based authentication to log you in. "
376- "If you prefer to supply a Personal Access Token, use `linode-cli configure --token`. "
377- )
378- print ()
293+ if _check_browsers () and not self .configure_with_pat :
294+ print ("""
295+ The CLI will use its web-based authentication to log you in.
296+ If you prefer to supply a Personal Access Token, use `linode-cli configure --token`.
297+ """ )
379298 input (
380- "Press enter to continue. This will open a browser and proceed with authentication."
299+ "Press enter to continue. "
300+ "This will open a browser and proceed with authentication."
381301 )
382302 username , config ["token" ] = _get_token_web (self .base_url )
383- # pylint: enable=line-too-long
384-
303+ else :
304+ username , config [ "token" ] = _get_token_terminal ( self . base_url )
385305 token = config ["token" ]
386306
387- print ()
388- print ( f"Configuring { username } " )
389- print ()
307+ print (f" \n Configuring { username } \n " )
308+
309+ # Configuring Defaults
390310
391311 regions = [
392312 r ["id" ] for r in _do_get_request (self .base_url , "/regions" )["data" ]
@@ -452,20 +372,14 @@ def configure(
452372 if not is_default :
453373 if username != self .default_username ():
454374 while True :
455- value = input (
456- "Make this user the default when using the CLI? [y/N]: "
457- )
458-
375+ value = input ("Make this user the default when using the CLI? [y/N]: " )
459376 if value .lower () in "yn" :
460377 is_default = value .lower () == "y"
461378 break
462379 if not value .strip ():
463380 break
464-
465381 if not is_default : # they didn't change the default user
466- print (
467- f"Active user will remain { self .config .get ('DEFAULT' , 'default-user' )} "
468- )
382+ print (f"Active user will remain { self .config .get ('DEFAULT' , 'default-user' )} " )
469383
470384 if is_default :
471385 # if this is the default user, make it so
0 commit comments