summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Hardy <shardy@redhat.com>2013-11-19 16:42:49 +0000
committerSteven Hardy <shardy@redhat.com>2013-11-27 19:13:06 +0000
commitfe3629f1bab78664498192efcc9d782d061459f1 (patch)
tree6436cf3356ea54b971b7263b5b4b7dbcf81e8765
parenta7ba3c323b16227e0ba2527f21bc89625f125234 (diff)
downloadpython-heatclient-fe3629f1bab78664498192efcc9d782d061459f1.tar.gz
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
-rw-r--r--heatclient/shell.py30
-rw-r--r--heatclient/tests/fakes.py19
-rw-r--r--heatclient/tests/test_shell.py108
3 files changed, 118 insertions, 39 deletions
diff --git a/heatclient/shell.py b/heatclient/shell.py
index 56ce087..b254964 100644
--- a/heatclient/shell.py
+++ b/heatclient/shell.py
@@ -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,
diff --git a/heatclient/tests/fakes.py b/heatclient/tests/fakes.py
index 51ef8fa..c6c3db3 100644
--- a/heatclient/tests/fakes.py
+++ b/heatclient/tests/fakes.py
@@ -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():
diff --git a/heatclient/tests/test_shell.py b/heatclient/tests/test_shell.py
index 26805b1..c902011 100644
--- a/heatclient/tests/test_shell.py
+++ b/heatclient/tests/test_shell.py
@@ -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):