Skip to content

Commit 085bc52

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Defer auth prompting until it is actually needed"
2 parents 5537895 + 14dbfe4 commit 085bc52

3 files changed

Lines changed: 81 additions & 3 deletions

File tree

openstackclient/common/client_config.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import logging
1717

1818
from os_client_config import config
19+
from os_client_config import exceptions as occ_exceptions
1920

2021

2122
LOG = logging.getLogger(__name__)
@@ -182,6 +183,14 @@ def auth_config_hook(self, config):
182183
LOG.debug("auth_config_hook(): %s" % config)
183184
return config
184185

186+
def load_auth_plugin(self, config):
187+
"""Get auth plugin and validate args"""
188+
189+
loader = self._get_auth_loader(config)
190+
config = self._validate_auth(config, loader)
191+
auth_plugin = loader.load_from_options(**config['auth'])
192+
return auth_plugin
193+
185194
def _validate_auth_ksc(self, config, cloud, fixed_argparse=None):
186195
"""Old compatibility hack for OSC, no longer needed/wanted"""
187196
return config
@@ -192,6 +201,8 @@ def _validate_auth(self, config, loader, fixed_argparse=None):
192201

193202
plugin_options = loader.get_options()
194203

204+
msgs = []
205+
prompt_options = []
195206
for p_opt in plugin_options:
196207
# if it's in config, win, move it and kill it from config dict
197208
# if it's in config.auth but not in config we're good
@@ -202,6 +213,16 @@ def _validate_auth(self, config, loader, fixed_argparse=None):
202213
winning_value = self._find_winning_auth_value(
203214
p_opt, config['auth'])
204215

216+
# if the plugin tells us that this value is required
217+
# then error if it's doesn't exist now
218+
if not winning_value and p_opt.required:
219+
msgs.append(
220+
'Missing value {auth_key}'
221+
' required for auth plugin {plugin}'.format(
222+
auth_key=p_opt.name, plugin=config.get('auth_type'),
223+
)
224+
)
225+
205226
# Clean up after ourselves
206227
for opt in [p_opt.name] + [o.name for o in p_opt.deprecated]:
207228
opt = opt.replace('-', '_')
@@ -224,6 +245,13 @@ def _validate_auth(self, config, loader, fixed_argparse=None):
224245
p_opt.dest not in config['auth'] and
225246
self._pw_callback is not None
226247
):
248+
# Defer these until we know all required opts are present
249+
prompt_options.append(p_opt)
250+
251+
if msgs:
252+
raise occ_exceptions.OpenStackConfigException('\n'.join(msgs))
253+
else:
254+
for p_opt in prompt_options:
227255
config['auth'][p_opt.dest] = self._pw_callback(p_opt.prompt)
228256

229257
return config

openstackclient/common/clientmanager.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,26 @@ def __init__(
6060
self._cacert = self.cacert
6161
self._insecure = not self.verify
6262

63+
def setup_auth(self):
64+
"""Set up authentication"""
65+
66+
if self._auth_setup_completed:
67+
return
68+
69+
# NOTE(dtroyer): Validate the auth args; this is protected with 'if'
70+
# because openstack_config is an optional argument to
71+
# CloudConfig.__init__() and we'll die if it was not
72+
# passed.
73+
if self._cli_options._openstack_config is not None:
74+
self._cli_options._openstack_config._pw_callback = \
75+
shell.prompt_for_password
76+
self._cli_options._auth = \
77+
self._cli_options._openstack_config.load_auth_plugin(
78+
self._cli_options.config,
79+
)
80+
81+
return super(ClientManager, self).setup_auth()
82+
6383
def is_network_endpoint_enabled(self):
6484
"""Check if the network endpoint is enabled"""
6585

openstackclient/shell.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,11 @@ def initialize_app(self, argv):
140140
# First, throw away what has already been done with o-c-c and
141141
# use our own.
142142
try:
143-
cc = cloud_config.OSC_Config(
143+
self.cloud_config = cloud_config.OSC_Config(
144144
override_defaults={
145145
'interface': None,
146146
'auth_type': self._auth_type,
147147
},
148-
pw_func=shell.prompt_for_password,
149148
)
150149
except (IOError, OSError) as e:
151150
self.log.critical("Could not read clouds.yaml configuration file")
@@ -154,9 +153,13 @@ def initialize_app(self, argv):
154153

155154
if not self.options.debug:
156155
self.options.debug = None
157-
self.cloud = cc.get_one_cloud(
156+
157+
# NOTE(dtroyer): Need to do this with validate=False to defer the
158+
# auth plugin handling to ClientManager.setup_auth()
159+
self.cloud = self.cloud_config.get_one_cloud(
158160
cloud=self.options.cloud,
159161
argparse=self.options,
162+
validate=False,
160163
)
161164

162165
# Then, re-create the client_manager with the correct arguments
@@ -165,6 +168,33 @@ def initialize_app(self, argv):
165168
api_version=self.api_version,
166169
)
167170

171+
def prepare_to_run_command(self, cmd):
172+
"""Set up auth and API versions"""
173+
174+
# TODO(dtroyer): Move this to osc-lib
175+
# NOTE(dtroyer): If auth is not required for a command, force fake
176+
# token auth so KSA plugins are happy
177+
178+
kwargs = {}
179+
if not cmd.auth_required:
180+
# Build fake token creds to keep ksa and o-c-c hushed
181+
kwargs['auth_type'] = 'token_endpoint'
182+
kwargs['auth'] = {}
183+
kwargs['auth']['token'] = 'x'
184+
kwargs['auth']['url'] = 'x'
185+
186+
# Validate auth options
187+
self.cloud = self.cloud_config.get_one_cloud(
188+
cloud=self.options.cloud,
189+
argparse=self.options,
190+
validate=True,
191+
**kwargs
192+
)
193+
# Push the updated args into ClientManager
194+
self.client_manager._cli_options = self.cloud
195+
196+
return super(OpenStackShell, self).prepare_to_run_command(cmd)
197+
168198

169199
def main(argv=None):
170200
if argv is None:

0 commit comments

Comments
 (0)