Fix shell operation with --os-auth-token

Currently the --os-auth-token/env[OS_AUTH_TOKEN] case doesn't
work, as we always expect a username/password to create the
connection to keystone.  This enables the client to be used
with only a token and tenant (which is required for keystoneclient
to retrieve the catalog) specified, e.g:

heat --os-auth-url http://127.0.0.1:35357/v2.0/ \
     --os-auth-token <a keystone token> \
     --os-tenant-id <tenant ID> stack-list

Change-Id: I478ce178f44e42c68153f1b347c6144f0a133f5b
Partial-Bug: #1252248
This commit is contained in:
Steven Hardy
2013-11-19 16:42:49 +00:00
parent a7ba3c323b
commit fe3629f1ba
3 changed files with 118 additions and 39 deletions

View File

@@ -219,15 +219,22 @@ class HeatShell(object):
:param tenant_id: unique identifier of tenant
:param tenant_name: name of tenant
:param auth_url: endpoint to authenticate against
:param token: token to use instead of username/password
"""
kc_args = {'auth_url': kwargs.get('auth_url'),
'insecure': kwargs.get('insecure'),
'username': kwargs.get('username'),
'password': kwargs.get('password')}
'insecure': kwargs.get('insecure')}
if kwargs.get('tenant_id'):
kc_args['tenant_id'] = kwargs.get('tenant_id')
else:
kc_args['tenant_name'] = kwargs.get('tenant_name')
if kwargs.get('token'):
kc_args['token'] = kwargs.get('token')
else:
kc_args['username'] = kwargs.get('username')
kc_args['password'] = kwargs.get('password')
return ksclient.Client(**kc_args)
def _get_endpoint(self, client, **kwargs):
@@ -280,18 +287,22 @@ class HeatShell(object):
self.do_help(args)
return 0
if not args.os_username:
if not args.os_username and not args.os_auth_token:
raise exc.CommandError("You must provide a username via"
" either --os-username or env[OS_USERNAME]")
" either --os-username or env[OS_USERNAME]"
" or a token via --os-auth-token or"
" env[OS_AUTH_TOKEN]")
if not args.os_password:
if not args.os_password and not args.os_auth_token:
raise exc.CommandError("You must provide a password via"
" either --os-password or env[OS_PASSWORD]")
" either --os-password or env[OS_PASSWORD]"
" or a token via --os-auth-token or"
" env[OS_AUTH_TOKEN]")
if not (args.os_tenant_id or args.os_tenant_name):
raise exc.CommandError("You must provide a tenant_id via"
" either --os-tenant-id or via "
"env[OS_TENANT_ID]")
" either --os-tenant-id or via"
" env[OS_TENANT_ID]")
if not args.os_auth_url:
raise exc.CommandError("You must provide an auth url via"
@@ -305,6 +316,7 @@ class HeatShell(object):
kwargs = {
'username': args.os_username,
'password': args.os_password,
'token': args.os_auth_token,
'tenant_id': args.os_tenant_id,
'tenant_name': args.os_tenant_name,
'auth_url': args.os_auth_url,

View File

@@ -18,12 +18,19 @@ from heatclient.v1 import client as v1client
from keystoneclient.v2_0 import client as ksclient
def script_keystone_client():
ksclient.Client(auth_url='http://no.where',
insecure=False,
password='password',
tenant_name='tenant_name',
username='username').AndReturn(FakeKeystone('abcd1234'))
def script_keystone_client(token=None):
if token:
ksclient.Client(auth_url='http://no.where',
insecure=False,
tenant_id='tenant_id',
token=token).AndReturn(FakeKeystone(token))
else:
ksclient.Client(auth_url='http://no.where',
insecure=False,
password='password',
tenant_name='tenant_name',
username='username').AndReturn(FakeKeystone(
'abcd1234'))
def script_heat_list():

View File

@@ -114,6 +114,29 @@ class EnvVarTest(TestCase):
self.shell_error('list', self.err)
class EnvVarTestToken(TestCase):
scenarios = [
('tenant_id', dict(
remove='OS_TENANT_ID',
err='You must provide a tenant_id')),
('auth_url', dict(
remove='OS_AUTH_URL',
err='You must provide an auth url')),
]
def test_missing_auth(self):
fake_env = {
'OS_AUTH_TOKEN': 'atoken',
'OS_TENANT_ID': 'tenant_id',
'OS_AUTH_URL': 'http://no.where',
}
fake_env[self.remove] = None
self.set_fake_env(fake_env)
self.shell_error('list', self.err)
class ShellParamValidationTest(TestCase):
scenarios = [
@@ -208,24 +231,16 @@ class ShellValidationTest(TestCase):
'Need to specify exactly one of')
class ShellTest(TestCase):
class ShellBase(TestCase):
# Patch os.environ to avoid required auth info.
def setUp(self):
super(ShellTest, self).setUp()
super(ShellBase, self).setUp()
self.m = mox.Mox()
self.m.StubOutWithMock(ksclient, 'Client')
self.m.StubOutWithMock(v1client.Client, 'json_request')
self.m.StubOutWithMock(v1client.Client, 'raw_request')
self.addCleanup(self.m.VerifyAll)
self.addCleanup(self.m.UnsetStubs)
fake_env = {
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': 'http://no.where',
}
self.set_fake_env(fake_env)
# Some tests set exc.verbose = 1, so reset on cleanup
def unset_exc_verbose():
@@ -249,6 +264,12 @@ class ShellTest(TestCase):
return out
class ShellTestCommon(ShellBase):
def setUp(self):
super(ShellTestCommon, self).setUp()
def test_help_unknown_command(self):
self.assertRaises(exc.CommandError, self.shell, 'help foofoo')
@@ -280,8 +301,28 @@ class ShellTest(TestCase):
for r in required:
self.assertRegexpMatches(help_text, r)
def test_list(self):
class ShellTestUserPass(ShellBase):
def setUp(self):
super(ShellTestUserPass, self).setUp()
self._set_fake_env()
# Patch os.environ to avoid required auth info.
def _set_fake_env(self):
fake_env = {
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': 'http://no.where',
}
self.set_fake_env(fake_env)
def _script_keystone_client(self):
fakes.script_keystone_client()
def test_list(self):
self._script_keystone_client()
fakes.script_heat_list()
self.m.ReplayAll()
@@ -313,7 +354,7 @@ class ShellTest(TestCase):
"title": "Not Found"
}
fakes.script_keystone_client()
self._script_keystone_client()
fakes.script_heat_error(json.dumps(resp_dict))
self.m.ReplayAll()
@@ -336,7 +377,7 @@ class ShellTest(TestCase):
"title": "Not Found"
}
fakes.script_keystone_client()
self._script_keystone_client()
fakes.script_heat_error(json.dumps(resp_dict))
self.m.ReplayAll()
@@ -351,7 +392,7 @@ class ShellTest(TestCase):
def test_parsable_malformed_error(self):
invalid_json = "ERROR: {Invalid JSON Error."
fakes.script_keystone_client()
self._script_keystone_client()
fakes.script_heat_error(invalid_json)
self.m.ReplayAll()
@@ -371,7 +412,7 @@ class ShellTest(TestCase):
"title": "Not Found"
}
fakes.script_keystone_client()
self._script_keystone_client()
fakes.script_heat_error(json.dumps(missing_message))
self.m.ReplayAll()
@@ -392,7 +433,7 @@ class ShellTest(TestCase):
"title": "Not Found"
}
fakes.script_keystone_client()
self._script_keystone_client()
fakes.script_heat_error(json.dumps(resp_dict))
self.m.ReplayAll()
@@ -405,7 +446,7 @@ class ShellTest(TestCase):
"ERROR: The Stack (bad) could not be found.\n")
def test_describe(self):
fakes.script_keystone_client()
self._script_keystone_client()
resp_dict = {"stack": {
"id": "1",
"stack_name": "teststack",
@@ -437,7 +478,7 @@ class ShellTest(TestCase):
self.assertRegexpMatches(list_text, r)
def test_template_show_cfn(self):
fakes.script_keystone_client()
self._script_keystone_client()
template_data = open(os.path.join(TEST_VAR_DIR,
'minimal.template')).read()
resp = fakes.FakeHTTPResponse(
@@ -464,7 +505,7 @@ class ShellTest(TestCase):
self.assertRegexpMatches(show_text, r)
def test_template_show_hot(self):
fakes.script_keystone_client()
self._script_keystone_client()
resp_dict = {"heat_template_version": "2013-05-23",
"parameters": {},
"resources": {},
@@ -490,7 +531,7 @@ class ShellTest(TestCase):
self.assertRegexpMatches(show_text, r)
def test_create(self):
fakes.script_keystone_client()
self._script_keystone_client()
resp = fakes.FakeHTTPResponse(
201,
'Created',
@@ -523,7 +564,7 @@ class ShellTest(TestCase):
def test_create_url(self):
fakes.script_keystone_client()
self._script_keystone_client()
resp = fakes.FakeHTTPResponse(
201,
'Created',
@@ -554,7 +595,7 @@ class ShellTest(TestCase):
def test_create_object(self):
fakes.script_keystone_client()
self._script_keystone_client()
template_file = os.path.join(TEST_VAR_DIR, 'minimal.template')
template_data = open(template_file).read()
v1client.Client.raw_request(
@@ -592,7 +633,7 @@ class ShellTest(TestCase):
self.assertRegexpMatches(create_text, r)
def test_update(self):
fakes.script_keystone_client()
self._script_keystone_client()
resp = fakes.FakeHTTPResponse(
202,
'Accepted',
@@ -624,7 +665,7 @@ class ShellTest(TestCase):
self.assertRegexpMatches(create_text, r)
def test_delete(self):
fakes.script_keystone_client()
self._script_keystone_client()
resp = fakes.FakeHTTPResponse(
204,
'No Content',
@@ -649,6 +690,25 @@ class ShellTest(TestCase):
self.assertRegexpMatches(create_text, r)
class ShellTestToken(ShellTestUserPass):
# Rerun all ShellTestUserPass test with token auth
def setUp(self):
self.token = 'a_token'
super(ShellTestToken, self).setUp()
def _set_fake_env(self):
fake_env = {
'OS_AUTH_TOKEN': self.token,
'OS_TENANT_ID': 'tenant_id',
'OS_AUTH_URL': 'http://no.where',
}
self.set_fake_env(fake_env)
def _script_keystone_client(self):
fakes.script_keystone_client(token=self.token)
class ShellEnvironmentTest(TestCase):
def setUp(self):