Skip to content

Commit

Permalink
Add defaults for configure options of the SshTransport plugin
Browse files Browse the repository at this point in the history
The options for the `verdi computer configure` command are created
dynamically based on the `_valid_connect_options` and the
`_valid_auth_options` class attributes of the transport plugin class.
These are interactive options whose defaults are context based, meaning
they can be defined by a previously existing transport configuration.
However, the "default" defaults, if you will, i.e. the defaults when the
computer has never been configured before, were not defined, so they
were also not printed in the help message string of the command. We know
explicitly define these base defaults.
  • Loading branch information
sphuber committed Jul 6, 2020
1 parent 5e5d5e0 commit 66bc488
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 22 deletions.
38 changes: 23 additions & 15 deletions aiida/transports/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,25 +71,33 @@ def transport_option_default(name, computer):
return default


def interactive_default(transport_type, key, also_noninteractive=False):
"""Create a contextual_default value callback for an auth_param key."""
def interactive_default(key, also_non_interactive=False):
"""Create a contextual_default value callback for an auth_param key.
:param key: the name of the option.
:param also_non_interactive: indicates whether this option should provide a default also in non-interactive mode. If
False, the option will raise `MissingParameter` if no explicit value is specified when the command is called in
non-interactive mode.
"""

@with_dbenv()
def get_default(ctx):
"""Determine the default value from the context."""
from aiida import orm

if not also_non_interactive and ctx.params['non_interactive']:
raise click.MissingParameter()

user = ctx.params['user'] or orm.User.objects.get_default()
computer = ctx.params['computer']

try:
authinfo = orm.AuthInfo.objects.get(dbcomputer_id=computer.id, aiidauser_id=user.id)
except NotExistent:
authinfo = orm.AuthInfo(computer=computer, user=user)
non_interactive = ctx.params['non_interactive']
old_authparams = authinfo.get_auth_params()
if not also_noninteractive and non_interactive:
raise click.MissingParameter()
suggestion = old_authparams.get(key)

auth_params = authinfo.get_auth_params()
suggestion = auth_params.get(key)
suggestion = suggestion or transport_option_default(key, computer)
return suggestion

Expand All @@ -99,25 +107,25 @@ def get_default(ctx):
def create_option(name, spec):
"""Create a click option from a name and partial specs as used in transport auth_options."""
from copy import deepcopy

spec = deepcopy(spec)
name_dashed = name.replace('_', '-')
option_name = '--{}'.format(name_dashed)
existing_option = spec.pop('option', None)

if spec.pop('switch', False):
option_name = '--{name}/--no-{name}'.format(name=name_dashed)
kwargs = {}

if 'default' in spec:
kwargs['show_default'] = True
else:
kwargs['contextual_default'] = interactive_default(
'ssh', name, also_noninteractive=spec.pop('non_interactive_default', False)
)
kwargs = {'cls': InteractiveOption, 'show_default': True}

non_interactive_default = spec.pop('non_interactive_default', False)
kwargs['contextual_default'] = interactive_default(name, also_non_interactive=non_interactive_default)

kwargs['cls'] = InteractiveOption
kwargs.update(spec)

if existing_option:
return existing_option(**kwargs)

return click.option(option_name, **kwargs)


Expand Down
23 changes: 17 additions & 6 deletions aiida/transports/plugins/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,17 @@ class SshTransport(Transport): # pylint: disable=too-many-public-methods
'non_interactive_default': True
}
),
('port', {
'option': options.PORT,
'prompt': 'Port number',
'non_interactive_default': True
}),
(
'port',
{
'option': options.PORT,
'prompt': 'Port number',
'non_interactive_default': True,
},
),
(
'look_for_keys', {
'default': True,
'switch': True,
'prompt': 'Look for keys',
'help': 'Automatically look for private keys in the ~/.ssh folder.',
Expand All @@ -106,6 +110,7 @@ class SshTransport(Transport): # pylint: disable=too-many-public-methods
),
(
'allow_agent', {
'default': False,
'switch': True,
'prompt': 'Allow ssh agent',
'help': 'Switch to allow or disallow using an SSH agent.',
Expand All @@ -115,13 +120,14 @@ class SshTransport(Transport): # pylint: disable=too-many-public-methods
(
'proxy_command', {
'prompt': 'SSH proxy command',
'help': 'SSH proxy command for tunneling through a proxy server.' +
'help': 'SSH proxy command for tunneling through a proxy server.'
' Leave empty to parse the proxy command from the SSH config file.',
'non_interactive_default': True
}
), # Managed 'manually' in connect
(
'compress', {
'default': True,
'switch': True,
'prompt': 'Compress file transfers',
'help': 'Turn file transfer compression on or off.',
Expand All @@ -130,6 +136,7 @@ class SshTransport(Transport): # pylint: disable=too-many-public-methods
),
(
'gss_auth', {
'default': False,
'type': bool,
'prompt': 'GSS auth',
'help': 'Enable when using GSS kerberos token to connect.',
Expand All @@ -138,6 +145,7 @@ class SshTransport(Transport): # pylint: disable=too-many-public-methods
),
(
'gss_kex', {
'default': False,
'type': bool,
'prompt': 'GSS kex',
'help': 'GSS kex for kerberos, if not configured in SSH config file.',
Expand All @@ -146,6 +154,7 @@ class SshTransport(Transport): # pylint: disable=too-many-public-methods
),
(
'gss_deleg_creds', {
'default': False,
'type': bool,
'prompt': 'GSS deleg_creds',
'help': 'GSS deleg_creds for kerberos, if not configured in SSH config file.',
Expand Down Expand Up @@ -177,6 +186,7 @@ class SshTransport(Transport): # pylint: disable=too-many-public-methods
_valid_auth_options = _valid_connect_options + [
(
'load_system_host_keys', {
'default': True,
'switch': True,
'prompt': 'Load system host keys',
'help': 'Load system host keys from default SSH location.',
Expand All @@ -185,6 +195,7 @@ class SshTransport(Transport): # pylint: disable=too-many-public-methods
),
(
'key_policy', {
'default': 'RejectPolicy',
'type': click.Choice(['RejectPolicy', 'WarningPolicy', 'AutoAddPolicy']),
'prompt': 'Key policy',
'help': 'SSH key policy if host is not known.',
Expand Down
2 changes: 1 addition & 1 deletion tests/cmdline/params/options/test_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ def test_non_interactive(self):
def test_non_interactive_default(self):
"""
scenario: InteractiveOption, invoked with only --non-interactive
behaviour: fail
behaviour: success
"""
cmd = self.simple_command(default='default')
runner = CliRunner()
Expand Down

0 comments on commit 66bc488

Please sign in to comment.