Skip to content

Commit

Permalink
Merge pull request #109 from linode/feature/multi-user
Browse files Browse the repository at this point in the history
Added multi-user support
  • Loading branch information
patthiel committed Mar 12, 2019
2 parents 513c279 + 4aa2a24 commit 91d94b2
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 36 deletions.
57 changes: 48 additions & 9 deletions README.rst
Expand Up @@ -113,11 +113,11 @@ run the *configure* command::
Suppressing Defaults
""""""""""""""""""""

If you configured default values for `image`, `region`, and Linode `type`, they
If you configured default values for ``image``, ``region``, and Linode ``type``, they
will be sent for all requests that accept them if you do not specify a different
value. If you want to send a request *without* these arguments, you must invoke
the CLI with the `--no-defaults` option. For example, to create a Linode with
no `image` after a default Image has been configured, you would do this::
the CLI with the ``--no-defaults`` option. For example, to create a Linode with
no ``image`` after a default Image has been configured, you would do this::

linode-cli linodes create --region us-east --type g5-standard-2 --no-defaults

Expand All @@ -126,19 +126,58 @@ Suppressing Warnings

In some situations, like when the CLI is out of date, it will generate a warning
in addition to its normal output. If these warnings can interfere with your
scripts or you otherwise want them disabled, simply add the `--suppress-warnings`
scripts or you otherwise want them disabled, simply add the ``--suppress-warnings``
flag to prevent them from being emitted.

Environment Variables
"""""""""""""""""""""

If you prefer, you may store your token in an environment variable named
`LINODE_CLI_TOKEN` instead of using the configuration file. Doing so allows you
to bypass the initial configuration, and subsequent calls to `linode-cli configure`
``LINODE_CLI_TOKEN`` instead of using the configuration file. Doing so allows you
to bypass the initial configuration, and subsequent calls to ``linode-cli configure``
will allow you to set defaults without having to set a token. Be aware that if
the environment variable should be unset, the Linode CLI will stop working until
it is set again or the CLI is reconfigured with a token.

Multiple Users
^^^^^^^^^^^^^^

If you use the Linode CLI to manage multiple Linode accounts, you may configure
additional users using the ``linode-cli configure`` command. The CLI will automatically
detect that a new user is being configured based on the token given.

Displaying Configured Users
"""""""""""""""""""""""""""

To see what users are configured, simply run the following::

linode-cli show-users

The user who is currently active will be indicated by an asterisk.

Changing the Active User
""""""""""""""""""""""""

You may change the active user for all requests as follows::

linode-cli set-user USERNAME

Subsequent CLI commands will be executed as that user by default.

Should you wish to execute a single request as a different user, you can supply
the ``--as-user`` argument to specify the username you wish to act as for that
command. This *will not* change the active user.

Removing Configured Users
"""""""""""""""""""""""""

To remove a user from you previously configured, run::

linode-cli remove-user USERNAME

Once a user is removed, they will need to be reconfigured if you wish to use the
CLI for them again.

Kubernetes Deployment Plugin
----------------------------

Expand Down Expand Up @@ -167,10 +206,10 @@ The following is the help message for the command::
[--master-type TYPE] [--region REGION]
[--ssh-public-key KEYPATH]
NAME

positional arguments:
NAME A name for the cluster.

optional arguments:
-h, --help show this help message and exit
--node-type TYPE The Linode Type ID for cluster Nodes as retrieved with
Expand Down Expand Up @@ -297,7 +336,7 @@ When using ``install``, the ``PYTHON`` argument is optional - if provided, it
will install the CLI for that version of python. Valid values are ``2`` and
``3``, and it will default to ``3``.

Testing
Testing
-------

**WARNING!** Running the CLI tests will remove all linodes and data associated
Expand Down
69 changes: 67 additions & 2 deletions linodecli/__init__.py
Expand Up @@ -61,6 +61,9 @@ def main():
parser.add_argument('--no-defaults', action='store_true',
help="Suppress default values for arguments. Default values "
"are configured on initial setup or with linode-cli configure")
parser.add_argument('--as-user', metavar='USERNAME', type=str,
help="The username to execute this command as. This user must "
"be configured.")
parser.add_argument('--suppress-warnings', action='store_true',
help="Suppress warnings that are intended for human users. "
"This is useful for scripting the CLI's behavior.")
Expand Down Expand Up @@ -91,6 +94,10 @@ def main():
cli.suppress_warnings = parsed.suppress_warnings
cli.page = parsed.page

if parsed.as_user:
# if they are acting as a non-default user, set it up early
cli.config.set_user(parsed.as_user)

if parsed.version:
# print version info and exit
print("linode-cli {}".format(VERSION))
Expand Down Expand Up @@ -137,6 +144,16 @@ def main():
# handle a help for the CLI
if parsed.command is None or (parsed.command is None and parsed.help):
parser.print_help()

# commands to manager CLI users (don't call out to API)
print()
print('CLI user management commands:')
um_commands = [['configure', 'set-user', 'show-users'],['remove-user']]
table = SingleTable(um_commands)
table.inner_heading_row_border = False
print(table.table)

# commands generated from the spec (call the API directly)
print()
print("Available commands:")

Expand All @@ -151,6 +168,7 @@ def main():
table.inner_heading_row_border = False
print(table.table)

# plugins registered to the CLI (do arbitrary things)
if plugins.available:
# only show this if there are any available plugins
print("Available plugins:")
Expand All @@ -176,8 +194,55 @@ def main():

# configure
if parsed.command == "configure":
cli.configure(username=parsed.action)
exit(0)
if parsed.help:
print('linode-cli configure')
print()
print('Configured the Linode CLI. This command can be used to change')
print('defaults selected for the current user, or to configure additional')
print('users.')
exit(0)
else:
cli.configure()
exit(0)

# block of commands for user-focused operations
if parsed.command == "set-user":
if parsed.help or not parsed.action:
print('linode-cli set-user [USER]')
print()
print('Sets the active user for the CLI out of users you have configured.')
print('To configure a new user, see `linode-cli configure`')
exit(0)
else:
cli.config.set_default_user(parsed.action)
exit(0)

if parsed.command == "show-users":
if parsed.help:
print('linode-cli show-users')
print()
print('Lists configured users. Configured users can be set as the')
print('active user (used for all commands going forward) with the')
print('`set-user` command, or used for a single command with the')
print('`--as-user` flag. New users can be added with `linode-cli configure`.')
print('The user that is currently active is indicated with a `*`')
exit(0)
else:
cli.config.print_users()
exit(0)

if parsed.command == "remove-user":
if parsed.help or not parsed.action:
print('linode-cli remove-user [USER]')
print()
print('Removes a user the CLI was configured with. This does not change')
print('your Linode account, only this CLI installation. Once removed,')
print('the user may not be set as active or used for commands unless')
print('configured again.')
exit(0)
else:
cli.config.remove_user(parsed.action)
exit(0)

# special command to bake shell completion script
if parsed.command == 'bake-bash':
Expand Down
11 changes: 4 additions & 7 deletions linodecli/cli.py
Expand Up @@ -25,7 +25,7 @@ class CLI:
"""
Responsible for loading or baking a spec and handling incoming commands
"""
def __init__(self, version, base_url, skip_config=False):
def __init__(self, version, base_url, skip_config=False, as_user=None):
self.ops = {}
self.spec = {}
self.defaults = True # whether to use default values for arguments
Expand All @@ -39,9 +39,6 @@ def __init__(self, version, base_url, skip_config=False):
self.config = CLIConfig(self.base_url, skip_config=skip_config)
self.load_baked()

if not skip_config:
self.token = self.config.get_token()

def _resolve_allOf(self, node):
"""
Given the contents of an "allOf" node, returns the entire dct having parsed
Expand Down Expand Up @@ -326,7 +323,7 @@ def do_request(self, operation, args, filter_header=None):
"""
method = getattr(requests, operation.method)
headers = {
'Authorization': "Bearer {}".format(self.token),
'Authorization': "Bearer {}".format(self.config.get_token()),
'Content-Type': 'application/json',
'User-Agent': "linode-cli:{}".format(self.version),
}
Expand Down Expand Up @@ -449,11 +446,11 @@ def handle_command(self, command, action, args):
print('Page {} of {}. Call with --page [PAGE] to load a different page.'.format(
result.json()['page'], result.json()['pages']))

def configure(self, username=None):
def configure(self):
"""
Reconfigure the application
"""
self.config.configure(username=username)
self.config.configure()

def call_operation(self, command, action, args=[], filters=None):
"""
Expand Down

0 comments on commit 91d94b2

Please sign in to comment.