Skip to content

Commit 2a1a174

Browse files
author
Dean Troyer
committed
Gate-unbreaking combo review
Fix argument precedence hack Working around issues in os-client-config <= 1.18.0 This is ugly because the issues in o-c-c 1.19.1 run even deeper than in 1.18.0, so we're going to use 1.19.0 get_one_cloud() that is known to work for OSC and fix o-c-c with an axe. Remove return values for set commands 'identity provider set' and 'service provider set' were still returning their show-like data, this is a fail for set commands now, don't know how this ever passed before... Constraints are ready to be used for tox.ini Per email[1] from Andreas, we don't need to hack at install_command any longer. [1] http://openstack.markmail.org/thread/a4l7tokbotwqvuoh Co-authorioed-by: Steve Martinelli <s.martinelli@gmail.com> Depends-On: I49313dc7d4f44ec897de7a375f25b7ed864226f1 Change-Id: I426548376fc7d3cdb36501310dafd8c44d22ae30
1 parent 0b91368 commit 2a1a174

7 files changed

Lines changed: 232 additions & 90 deletions

File tree

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
"""OpenStackConfig subclass for argument compatibility"""
15+
16+
import logging
17+
18+
from os_client_config.config import OpenStackConfig
19+
20+
21+
LOG = logging.getLogger(__name__)
22+
23+
24+
# Sublcass OpenStackConfig in order to munge config values
25+
# before auth plugins are loaded
26+
class OSC_Config(OpenStackConfig):
27+
28+
def _auth_select_default_plugin(self, config):
29+
"""Select a default plugin based on supplied arguments
30+
31+
Migrated from auth.select_auth_plugin()
32+
"""
33+
34+
identity_version = config.get('identity_api_version', '')
35+
36+
if config.get('username', None) and not config.get('auth_type', None):
37+
if identity_version == '3':
38+
config['auth_type'] = 'v3password'
39+
elif identity_version.startswith('2'):
40+
config['auth_type'] = 'v2password'
41+
else:
42+
# let keystoneauth figure it out itself
43+
config['auth_type'] = 'password'
44+
elif config.get('token', None) and not config.get('auth_type', None):
45+
if identity_version == '3':
46+
config['auth_type'] = 'v3token'
47+
elif identity_version.startswith('2'):
48+
config['auth_type'] = 'v2token'
49+
else:
50+
# let keystoneauth figure it out itself
51+
config['auth_type'] = 'token'
52+
else:
53+
# The ultimate default is similar to the original behaviour,
54+
# but this time with version discovery
55+
if not config.get('auth_type', None):
56+
config['auth_type'] = 'password'
57+
58+
LOG.debug("Auth plugin %s selected" % config['auth_type'])
59+
return config
60+
61+
def _auth_v2_arguments(self, config):
62+
"""Set up v2-required arguments from v3 info
63+
64+
Migrated from auth.build_auth_params()
65+
"""
66+
67+
if ('auth_type' in config and config['auth_type'].startswith("v2")):
68+
if 'project_id' in config['auth']:
69+
config['auth']['tenant_id'] = config['auth']['project_id']
70+
if 'project_name' in config['auth']:
71+
config['auth']['tenant_name'] = config['auth']['project_name']
72+
return config
73+
74+
def _auth_v2_ignore_v3(self, config):
75+
"""Remove v3 arguemnts if present for v2 plugin
76+
77+
Migrated from clientmanager.setup_auth()
78+
"""
79+
80+
# NOTE(hieulq): If USER_DOMAIN_NAME, USER_DOMAIN_ID, PROJECT_DOMAIN_ID
81+
# or PROJECT_DOMAIN_NAME is present and API_VERSION is 2.0, then
82+
# ignore all domain related configs.
83+
if (config.get('identity_api_version', '').startswith('2') and
84+
config.get('auth_type', None).endswith('password')):
85+
domain_props = [
86+
'project_domain_id',
87+
'project_domain_name',
88+
'user_domain_id',
89+
'user_domain_name',
90+
]
91+
for prop in domain_props:
92+
if config['auth'].pop(prop, None) is not None:
93+
LOG.warning("Ignoring domain related config " +
94+
prop + " because identity API version is 2.0")
95+
return config
96+
97+
def _auth_default_domain(self, config):
98+
"""Set a default domain from available arguments
99+
100+
Migrated from clientmanager.setup_auth()
101+
"""
102+
103+
identity_version = config.get('identity_api_version', '')
104+
auth_type = config.get('auth_type', None)
105+
106+
# TODO(mordred): This is a usability improvement that's broadly useful
107+
# We should port it back up into os-client-config.
108+
default_domain = config.get('default_domain', None)
109+
if (identity_version == '3' and
110+
not auth_type.startswith('v2') and
111+
default_domain):
112+
113+
# NOTE(stevemar): If PROJECT_DOMAIN_ID or PROJECT_DOMAIN_NAME is
114+
# present, then do not change the behaviour. Otherwise, set the
115+
# PROJECT_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability.
116+
if (
117+
not config['auth'].get('project_domain_id') and
118+
not config['auth'].get('project_domain_name')
119+
):
120+
config['auth']['project_domain_id'] = default_domain
121+
122+
# NOTE(stevemar): If USER_DOMAIN_ID or USER_DOMAIN_NAME is present,
123+
# then do not change the behaviour. Otherwise, set the
124+
# USER_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability.
125+
# NOTE(aloga): this should only be set if there is a username.
126+
# TODO(dtroyer): Move this to os-client-config after the plugin has
127+
# been loaded so we can check directly if the options are accepted.
128+
if (
129+
auth_type in ("password", "v3password", "v3totp") and
130+
not config['auth'].get('user_domain_id') and
131+
not config['auth'].get('user_domain_name')
132+
):
133+
config['auth']['user_domain_id'] = default_domain
134+
return config
135+
136+
def auth_config_hook(self, config):
137+
"""Allow examination of config values before loading auth plugin
138+
139+
OpenStackClient will override this to perform additional chacks
140+
on auth_type.
141+
"""
142+
143+
config = self._auth_select_default_plugin(config)
144+
config = self._auth_v2_arguments(config)
145+
config = self._auth_v2_ignore_v3(config)
146+
config = self._auth_default_domain(config)
147+
148+
LOG.debug("auth_config_hook(): %s" % config)
149+
return config
150+
151+
def _validate_auth_ksc(self, config, cloud):
152+
"""Old compatibility hack for OSC, no longer needed/wanted"""
153+
return config
154+
155+
def _validate_auth(self, config, loader):
156+
"""Validate auth plugin arguments"""
157+
# May throw a keystoneauth1.exceptions.NoMatchingPlugin
158+
159+
plugin_options = loader.get_options()
160+
161+
for p_opt in plugin_options:
162+
# if it's in config, win, move it and kill it from config dict
163+
# if it's in config.auth but not in config we're good
164+
# deprecated loses to current
165+
# provided beats default, deprecated or not
166+
winning_value = self._find_winning_auth_value(p_opt, config)
167+
if not winning_value:
168+
winning_value = self._find_winning_auth_value(
169+
p_opt, config['auth'])
170+
171+
# Clean up after ourselves
172+
for opt in [p_opt.name] + [o.name for o in p_opt.deprecated]:
173+
opt = opt.replace('-', '_')
174+
config.pop(opt, None)
175+
config['auth'].pop(opt, None)
176+
177+
if winning_value:
178+
# Prefer the plugin configuration dest value if the value's key
179+
# is marked as depreciated.
180+
if p_opt.dest is None:
181+
config['auth'][p_opt.name.replace('-', '_')] = (
182+
winning_value)
183+
else:
184+
config['auth'][p_opt.dest] = winning_value
185+
186+
return config

openstackclient/identity/v3/identity_provider.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,10 @@ def take_action(self, parsed_args):
204204
if parsed_args.remote_id_file or parsed_args.remote_id:
205205
kwargs['remote_ids'] = remote_ids
206206

207-
identity_provider = federation_client.identity_providers.update(
208-
parsed_args.identity_provider, **kwargs)
209-
210-
identity_provider._info.pop('links', None)
211-
return zip(*sorted(six.iteritems(identity_provider._info)))
207+
federation_client.identity_providers.update(
208+
parsed_args.identity_provider,
209+
**kwargs
210+
)
212211

213212

214213
class ShowIdentityProvider(command.ShowOne):

openstackclient/identity/v3/service_provider.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,13 @@ def take_action(self, parsed_args):
182182
elif parsed_args.disable is True:
183183
enabled = False
184184

185-
service_provider = federation_client.service_providers.update(
186-
parsed_args.service_provider, enabled=enabled,
185+
federation_client.service_providers.update(
186+
parsed_args.service_provider,
187+
enabled=enabled,
187188
description=parsed_args.description,
188189
auth_url=parsed_args.auth_url,
189-
sp_url=parsed_args.service_provider_url)
190-
return zip(*sorted(six.iteritems(service_provider._info)))
190+
sp_url=parsed_args.service_provider_url,
191+
)
191192

192193

193194
class ShowServiceProvider(command.ShowOne):

openstackclient/shell.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import six
2626

2727
import openstackclient
28+
from openstackclient.common import client_config as cloud_config
2829
from openstackclient.common import clientmanager
2930
from openstackclient.common import commandmanager
3031

@@ -127,9 +128,33 @@ def _load_commands(self):
127128
def initialize_app(self, argv):
128129
super(OpenStackShell, self).initialize_app(argv)
129130

130-
# For now we need to build our own ClientManager so re-do what
131-
# has already been done :(
132-
# TODO(dtroyer): remove when osc-lib is fixed
131+
# Argument precedence is really broken in multiple places
132+
# so we're just going to fix it here until o-c-c and osc-lib
133+
# get sorted out.
134+
# TODO(dtroyer): remove when os-client-config and osc-lib are fixed
135+
136+
# First, throw away what has already been done with o-c-c and
137+
# use our own.
138+
try:
139+
cc = cloud_config.OSC_Config(
140+
override_defaults={
141+
'interface': None,
142+
'auth_type': self._auth_type,
143+
},
144+
)
145+
except (IOError, OSError) as e:
146+
self.log.critical("Could not read clouds.yaml configuration file")
147+
self.print_help_if_requested()
148+
raise e
149+
150+
if not self.options.debug:
151+
self.options.debug = None
152+
self.cloud = cc.get_one_cloud(
153+
cloud=self.options.cloud,
154+
argparse=self.options,
155+
)
156+
157+
# Then, re-create the client_manager with the correct arguments
133158
self.client_manager = clientmanager.ClientManager(
134159
cli_options=self.cloud,
135160
api_version=self.api_version,

openstackclient/tests/identity/v3/test_identity_provider.py

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -356,19 +356,11 @@ def prepare(self):
356356
('remote_id', None)
357357
]
358358
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
359-
columns, data = self.cmd.take_action(parsed_args)
359+
self.cmd.take_action(parsed_args)
360360
self.identity_providers_mock.update.assert_called_with(
361361
identity_fakes.idp_id,
362362
description=new_description,
363363
)
364-
self.assertEqual(self.columns, columns)
365-
datalist = (
366-
identity_fakes.idp_description,
367-
False,
368-
identity_fakes.idp_id,
369-
identity_fakes.idp_remote_ids
370-
)
371-
self.assertEqual(datalist, data)
372364

373365
def test_identity_provider_disable(self):
374366
"""Disable Identity Provider
@@ -402,22 +394,13 @@ def prepare(self):
402394
]
403395
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
404396

405-
columns, data = self.cmd.take_action(parsed_args)
397+
self.cmd.take_action(parsed_args)
406398
self.identity_providers_mock.update.assert_called_with(
407399
identity_fakes.idp_id,
408400
enabled=False,
409401
remote_ids=identity_fakes.idp_remote_ids
410402
)
411403

412-
self.assertEqual(self.columns, columns)
413-
datalist = (
414-
identity_fakes.idp_description,
415-
False,
416-
identity_fakes.idp_id,
417-
identity_fakes.idp_remote_ids
418-
)
419-
self.assertEqual(datalist, data)
420-
421404
def test_identity_provider_enable(self):
422405
"""Enable Identity Provider.
423406
@@ -448,12 +431,10 @@ def prepare(self):
448431
]
449432
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
450433

451-
columns, data = self.cmd.take_action(parsed_args)
434+
self.cmd.take_action(parsed_args)
452435
self.identity_providers_mock.update.assert_called_with(
453436
identity_fakes.idp_id, enabled=True,
454437
remote_ids=identity_fakes.idp_remote_ids)
455-
self.assertEqual(self.columns, columns)
456-
self.assertEqual(self.datalist, data)
457438

458439
def test_identity_provider_replace_remote_ids(self):
459440
"""Enable Identity Provider.
@@ -488,18 +469,10 @@ def prepare(self):
488469
]
489470
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
490471

491-
columns, data = self.cmd.take_action(parsed_args)
472+
self.cmd.take_action(parsed_args)
492473
self.identity_providers_mock.update.assert_called_with(
493474
identity_fakes.idp_id, enabled=True,
494475
remote_ids=[self.new_remote_id])
495-
self.assertEqual(self.columns, columns)
496-
datalist = (
497-
identity_fakes.idp_description,
498-
True,
499-
identity_fakes.idp_id,
500-
[self.new_remote_id]
501-
)
502-
self.assertEqual(datalist, data)
503476

504477
def test_identity_provider_replace_remote_ids_file(self):
505478
"""Enable Identity Provider.
@@ -538,18 +511,10 @@ def prepare(self):
538511
mocker.return_value = self.new_remote_id
539512
with mock.patch("openstackclient.identity.v3.identity_provider."
540513
"utils.read_blob_file_contents", mocker):
541-
columns, data = self.cmd.take_action(parsed_args)
514+
self.cmd.take_action(parsed_args)
542515
self.identity_providers_mock.update.assert_called_with(
543516
identity_fakes.idp_id, enabled=True,
544517
remote_ids=[self.new_remote_id])
545-
self.assertEqual(self.columns, columns)
546-
datalist = (
547-
identity_fakes.idp_description,
548-
True,
549-
identity_fakes.idp_id,
550-
[self.new_remote_id]
551-
)
552-
self.assertEqual(datalist, data)
553518

554519
def test_identity_provider_no_options(self):
555520
def prepare(self):
@@ -580,12 +545,7 @@ def prepare(self):
580545
]
581546
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
582547

583-
columns, data = self.cmd.take_action(parsed_args)
584-
585-
# expect take_action() to return (None, None) as
586-
# neither --enable nor --disable was specified
587-
self.assertEqual(self.columns, columns)
588-
self.assertEqual(self.datalist, data)
548+
self.cmd.take_action(parsed_args)
589549

590550

591551
class TestIdentityProviderShow(TestIdentityProvider):

0 commit comments

Comments
 (0)