Revert "Add keystone v3 auth support"

This reverts commit a98c1f3617.
The new usage of session doesn't work with resources or events.

Conflicts:
	heatclient/tests/test_shell.py
Closes-Bug: #1349467

Change-Id: Ib5b0db81454abe647aca6461dd7bd5f197267fb8
This commit is contained in:
Thomas Herve 2014-07-28 18:47:14 +02:00
parent 6eb45625e5
commit 42c5096770
7 changed files with 352 additions and 688 deletions

View File

@ -216,12 +216,6 @@ class HTTPClient(object):
def credentials_headers(self): def credentials_headers(self):
creds = {} creds = {}
# NOTE(dhu): (shardy) When deferred_auth_method=password, Heat
# encrypts and stores username/password. For Keystone v3, the
# intent is to use trusts since SHARDY is working towards
# deferred_auth_method=trusts as the default.
# TODO(dhu): Make Keystone v3 work in Heat standalone mode. Maye
# require X-Auth-User-Domain.
if self.username: if self.username:
creds['X-Auth-User'] = self.username creds['X-Auth-User'] = self.username
if self.password: if self.password:
@ -275,56 +269,3 @@ class HTTPClient(object):
def patch(self, url, **kwargs): def patch(self, url, **kwargs):
return self.client_request("PATCH", url, **kwargs) return self.client_request("PATCH", url, **kwargs)
class SessionClient(HTTPClient):
"""HTTP client based on Keystone client session."""
# NOTE(dhu): Will eventually move to a common session client.
# https://bugs.launchpad.net/python-keystoneclient/+bug/1332337
def __init__(self, session, auth, **kwargs):
self.session = session
self.auth = auth
self.auth_url = kwargs.get('auth_url')
self.region_name = kwargs.get('region_name')
self.interface = kwargs.get('interface',
kwargs.get('endpoint_type', 'public'))
self.service_type = kwargs.get('service_type')
self.include_pass = kwargs.get('include_pass')
self.username = kwargs.get('username')
self.password = kwargs.get('password')
# see if we can get the auth_url from auth plugin if one is not
# provided from kwargs
if not self.auth_url and hasattr(self.auth, 'auth_url'):
self.auth_url = self.auth.auth_url
def _http_request(self, url, method, **kwargs):
kwargs.setdefault('user_agent', USER_AGENT)
kwargs.setdefault('auth', self.auth)
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
endpoint_filter.setdefault('interface', self.interface)
endpoint_filter.setdefault('service_type', self.service_type)
endpoint_filter.setdefault('region_name', self.region_name)
# TODO(gyee): what are these headers for?
if self.auth_url:
kwargs['headers'].setdefault('X-Auth-Url', self.auth_url)
if self.region_name:
kwargs['headers'].setdefault('X-Region-Name', self.region_name)
if self.include_pass and 'X-Auth-Key' not in kwargs['headers']:
kwargs['headers'].update(self.credentials_headers())
return self.session.request(url, method, raise_exc=False, **kwargs)
def _construct_http_client(*args, **kwargs):
session = kwargs.pop('session', None)
auth = kwargs.pop('auth', None)
if session:
return SessionClient(session, auth, **kwargs)
else:
return HTTPClient(*args, **kwargs)

View File

@ -18,23 +18,15 @@ from __future__ import print_function
import argparse import argparse
import logging import logging
import six
import sys import sys
import six from keystoneclient.v2_0 import client as ksclient
import six.moves.urllib.parse as urlparse
from keystoneclient.auth.identity import v2 as v2_auth
from keystoneclient.auth.identity import v3 as v3_auth
from keystoneclient.auth import token_endpoint
from keystoneclient import discover
from keystoneclient.openstack.common.apiclient import exceptions as ks_exc
from keystoneclient import session as kssession
import heatclient import heatclient
from heatclient import client as heat_client from heatclient import client as heat_client
from heatclient.common import utils from heatclient.common import utils
from heatclient import exc from heatclient import exc
from heatclient.openstack.common.gettextutils import _
from heatclient.openstack.common import strutils from heatclient.openstack.common import strutils
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -42,173 +34,6 @@ logger = logging.getLogger(__name__)
class HeatShell(object): class HeatShell(object):
def _append_global_identity_args(self, parser):
# FIXME(gyee): these are global identity (Keystone) arguments which
# should be consistent and shared by all service clients. Therefore,
# they should be provided by python-keystoneclient. We will need to
# refactor this code once this functionality is avaible in
# python-keystoneclient.
parser.add_argument('-k', '--insecure',
default=False,
action='store_true',
help='Explicitly allow heatclient to perform '
'\"insecure SSL\" (https) requests. The server\'s '
'certificate will not be verified against any '
'certificate authorities. This option should '
'be used with caution.')
parser.add_argument('--os-cert',
help='Path of certificate file to use in SSL '
'connection. This file can optionally be '
'prepended with the private key.')
# for backward compatibility only
parser.add_argument('--cert-file',
dest='os_cert',
help='DEPRECATED! Use --os-cert.')
parser.add_argument('--os-key',
help='Path of client key to use in SSL '
'connection. This option is not necessary '
'if your key is prepended to your cert file.')
parser.add_argument('--key-file',
dest='os_key',
help='DEPRECATED! Use --os-key.')
parser.add_argument('--os-cacert',
metavar='<ca-certificate-file>',
dest='os_cacert',
default=utils.env('OS_CACERT'),
help='Path of CA TLS certificate(s) used to '
'verify the remote server\'s certificate. '
'Without this option glance looks for the '
'default system CA certificates.')
parser.add_argument('--ca-file',
dest='os_cacert',
help='DEPRECATED! Use --os-cacert.')
parser.add_argument('--os-username',
default=utils.env('OS_USERNAME'),
help='Defaults to env[OS_USERNAME].')
parser.add_argument('--os_username',
help=argparse.SUPPRESS)
parser.add_argument('--os-user-id',
default=utils.env('OS_USER_ID'),
help='Defaults to env[OS_USER_ID].')
parser.add_argument('--os_user_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-user-domain-id',
default=utils.env('OS_USER_DOMAIN_ID'),
help='Defaults to env[OS_USER_DOMAIN_ID].')
parser.add_argument('--os_user_domain_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-user-domain-name',
default=utils.env('OS_USER_DOMAIN_NAME'),
help='Defaults to env[OS_USER_DOMAIN_NAME].')
parser.add_argument('--os_user_domain_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-id',
default=utils.env('OS_PROJECT_ID'),
help='Another way to specify tenant ID. '
'This option is mutually exclusive with '
' --os-tenant-id. '
'Defaults to env[OS_PROJECT_ID].')
parser.add_argument('--os_project_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-name',
default=utils.env('OS_PROJECT_NAME'),
help='Another way to specify tenant name. '
'This option is mutually exclusive with '
' --os-tenant-name. '
'Defaults to env[OS_PROJECT_NAME].')
parser.add_argument('--os_project_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-domain-id',
default=utils.env('OS_PROJECT_DOMAIN_ID'),
help='Defaults to env[OS_PROJECT_DOMAIN_ID].')
parser.add_argument('--os_project_domain_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-domain-name',
default=utils.env('OS_PROJECT_DOMAIN_NAME'),
help='Defaults to env[OS_PROJECT_DOMAIN_NAME].')
parser.add_argument('--os_project_domain_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-password',
default=utils.env('OS_PASSWORD'),
help='Defaults to env[OS_PASSWORD].')
parser.add_argument('--os_password',
help=argparse.SUPPRESS)
parser.add_argument('--os-tenant-id',
default=utils.env('OS_TENANT_ID'),
help='Defaults to env[OS_TENANT_ID].')
parser.add_argument('--os_tenant_id',
default=utils.env('OS_TENANT_ID'),
help=argparse.SUPPRESS)
parser.add_argument('--os-tenant-name',
default=utils.env('OS_TENANT_NAME'),
help='Defaults to env[OS_TENANT_NAME].')
parser.add_argument('--os_tenant_name',
default=utils.env('OS_TENANT_NAME'),
help=argparse.SUPPRESS)
parser.add_argument('--os-auth-url',
default=utils.env('OS_AUTH_URL'),
help='Defaults to env[OS_AUTH_URL].')
parser.add_argument('--os_auth_url',
help=argparse.SUPPRESS)
parser.add_argument('--os-region-name',
default=utils.env('OS_REGION_NAME'),
help='Defaults to env[OS_REGION_NAME].')
parser.add_argument('--os_region_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-auth-token',
default=utils.env('OS_AUTH_TOKEN'),
help='Defaults to env[OS_AUTH_TOKEN].')
parser.add_argument('--os_auth_token',
help=argparse.SUPPRESS)
parser.add_argument('--os-service-type',
default=utils.env('OS_SERVICE_TYPE'),
help='Defaults to env[OS_SERVICE_TYPE].')
parser.add_argument('--os_service_type',
help=argparse.SUPPRESS)
parser.add_argument('--os-endpoint-type',
default=utils.env('OS_ENDPOINT_TYPE'),
help='Defaults to env[OS_ENDPOINT_TYPE].')
parser.add_argument('--os_endpoint_type',
help=argparse.SUPPRESS)
def get_base_parser(self): def get_base_parser(self):
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
prog='heat', prog='heat',
@ -238,13 +63,92 @@ class HeatShell(object):
default=False, action="store_true", default=False, action="store_true",
help="Print more verbose output.") help="Print more verbose output.")
parser.add_argument('-k', '--insecure',
default=False,
action='store_true',
help="Explicitly allow the client to perform "
"\"insecure\" SSL (https) requests. The server's "
"certificate will not be verified against any "
"certificate authorities. "
"This option should be used with caution.")
parser.add_argument('--os-cacert',
metavar='<ca-certificate>',
default=utils.env('OS_CACERT', default=None),
help='Specify a CA bundle file to use in '
'verifying a TLS (https) server certificate. '
'Defaults to env[OS_CACERT]')
parser.add_argument('--cert-file',
help='Path of certificate file to use in SSL '
'connection. This file can optionally be '
'prepended with the private key.')
parser.add_argument('--key-file',
help='Path of client key to use in SSL connection.'
'This option is not necessary if your key is'
' prepended to your cert file.')
parser.add_argument('--ca-file',
help='Path of CA SSL certificate(s) used to verify'
' the remote server\'s certificate. Without this'
' option the client looks'
' for the default system CA certificates.')
parser.add_argument('--api-timeout', parser.add_argument('--api-timeout',
help='Number of seconds to wait for an ' help='Number of seconds to wait for an '
'API response, ' 'API response, '
'defaults to system socket timeout') 'defaults to system socket timeout')
# os-no-client-auth tells heatclient to use token, instead of parser.add_argument('--os-username',
# env[OS_AUTH_URL] default=utils.env('OS_USERNAME'),
help='Defaults to env[OS_USERNAME].')
parser.add_argument('--os_username',
help=argparse.SUPPRESS)
parser.add_argument('--os-password',
default=utils.env('OS_PASSWORD'),
help='Defaults to env[OS_PASSWORD].')
parser.add_argument('--os_password',
help=argparse.SUPPRESS)
parser.add_argument('--os-tenant-id',
default=utils.env('OS_TENANT_ID'),
help='Defaults to env[OS_TENANT_ID].')
parser.add_argument('--os_tenant_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-tenant-name',
default=utils.env('OS_TENANT_NAME'),
help='Defaults to env[OS_TENANT_NAME].')
parser.add_argument('--os_tenant_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-auth-url',
default=utils.env('OS_AUTH_URL'),
help='Defaults to env[OS_AUTH_URL].')
parser.add_argument('--os_auth_url',
help=argparse.SUPPRESS)
parser.add_argument('--os-region-name',
default=utils.env('OS_REGION_NAME'),
help='Defaults to env[OS_REGION_NAME].')
parser.add_argument('--os_region_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-auth-token',
default=utils.env('OS_AUTH_TOKEN'),
help='Defaults to env[OS_AUTH_TOKEN].')
parser.add_argument('--os_auth_token',
help=argparse.SUPPRESS)
parser.add_argument('--os-no-client-auth', parser.add_argument('--os-no-client-auth',
default=utils.env('OS_NO_CLIENT_AUTH'), default=utils.env('OS_NO_CLIENT_AUTH'),
action='store_true', action='store_true',
@ -265,6 +169,20 @@ class HeatShell(object):
parser.add_argument('--heat_api_version', parser.add_argument('--heat_api_version',
help=argparse.SUPPRESS) help=argparse.SUPPRESS)
parser.add_argument('--os-service-type',
default=utils.env('OS_SERVICE_TYPE'),
help='Defaults to env[OS_SERVICE_TYPE].')
parser.add_argument('--os_service_type',
help=argparse.SUPPRESS)
parser.add_argument('--os-endpoint-type',
default=utils.env('OS_ENDPOINT_TYPE'),
help='Defaults to env[OS_ENDPOINT_TYPE].')
parser.add_argument('--os_endpoint_type',
help=argparse.SUPPRESS)
# This unused option should remain so that scripts that # This unused option should remain so that scripts that
# use it do not break. It is suppressed so it will not # use it do not break. It is suppressed so it will not
# appear in the help. # appear in the help.
@ -278,12 +196,6 @@ class HeatShell(object):
action='store_true', action='store_true',
help='Send os-username and os-password to heat.') help='Send os-username and os-password to heat.')
# FIXME(gyee): this method should come from python-keystoneclient.
# Will refactor this code once it is available.
# https://bugs.launchpad.net/python-keystoneclient/+bug/1332337
self._append_global_identity_args(parser)
return parser return parser
def get_subcommand_parser(self, version): def get_subcommand_parser(self, version):
@ -329,6 +241,45 @@ class HeatShell(object):
subparser.add_argument(*args, **kwargs) subparser.add_argument(*args, **kwargs)
subparser.set_defaults(func=callback) subparser.set_defaults(func=callback)
def _get_ksclient(self, **kwargs):
"""Get an endpoint and auth token from Keystone.
:param username: name of user
:param password: user's password
: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'),
'cacert': kwargs.get('cacert')}
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):
"""Get an endpoint using the provided keystone client."""
if kwargs.get('region_name'):
return client.service_catalog.url_for(
service_type=kwargs.get('service_type') or 'orchestration',
attr='region',
filter_value=kwargs.get('region_name'),
endpoint_type=kwargs.get('endpoint_type') or 'publicURL')
return client.service_catalog.url_for(
service_type=kwargs.get('service_type') or 'orchestration',
endpoint_type=kwargs.get('endpoint_type') or 'publicURL')
def _setup_logging(self, debug): def _setup_logging(self, debug):
log_lvl = logging.DEBUG if debug else logging.WARNING log_lvl = logging.DEBUG if debug else logging.WARNING
logging.basicConfig( logging.basicConfig(
@ -341,132 +292,6 @@ class HeatShell(object):
if verbose: if verbose:
exc.verbose = 1 exc.verbose = 1
def _discover_auth_versions(self, session, auth_url):
# discover the API versions the server is supporting base on the
# given URL
v2_auth_url = None
v3_auth_url = None
try:
ks_discover = discover.Discover(session=session, auth_url=auth_url)
v2_auth_url = ks_discover.url_for('2.0')
v3_auth_url = ks_discover.url_for('3.0')
except ks_exc.ClientException:
# Identity service may not support discover API version.
# Lets trying to figure out the API version from the original URL.
url_parts = urlparse.urlparse(auth_url)
(scheme, netloc, path, params, query, fragment) = url_parts
path = path.lower()
if path.startswith('/v3'):
v3_auth_url = auth_url
elif path.startswith('/v2'):
v2_auth_url = auth_url
else:
# not enough information to determine the auth version
msg = _('Unable to determine the Keystone version '
'to authenticate with using the given '
'auth_url. Identity service may not support API '
'version discovery. Please provide a versioned '
'auth_url instead.')
raise exc.CommandError(msg)
return (v2_auth_url, v3_auth_url)
def _get_keystone_session(self, **kwargs):
# first create a Keystone session
cacert = kwargs.pop('cacert', None)
cert = kwargs.pop('cert', None)
key = kwargs.pop('key', None)
insecure = kwargs.pop('insecure', False)
timeout = kwargs.pop('timeout', None)
verify = kwargs.pop('verify', None)
# FIXME(gyee): this code should come from keystoneclient
if verify is None:
if insecure:
verify = False
else:
# TODO(gyee): should we do
# heatclient.common.http.get_system_ca_fle()?
verify = cacert or True
if cert and key:
# passing cert and key together is deprecated in favour of the
# requests lib form of having the cert and key as a tuple
cert = (cert, key)
return kssession.Session(verify=verify, cert=cert, timeout=timeout)
def _get_keystone_auth(self, session, auth_url, **kwargs):
# FIXME(gyee): this code should come from keystoneclient
# https://bugs.launchpad.net/python-keystoneclient/+bug/1332337
auth_token = kwargs.pop('auth_token', None)
# static token auth only
if auth_token:
endpoint = kwargs.pop('endpoint', None)
return token_endpoint.Token(endpoint, auth_token)
# discover the supported keystone versions using the given url
(v2_auth_url, v3_auth_url) = self._discover_auth_versions(
session=session,
auth_url=auth_url)
# Determine which authentication plugin to use. First inspect the
# auth_url to see the supported version. If both v3 and v2 are
# supported, then use the highest version if possible.
username = kwargs.pop('username', None)
user_id = kwargs.pop('user_id', None)
user_domain_name = kwargs.pop('user_domain_name', None)
user_domain_id = kwargs.pop('user_domain_id', None)
project_domain_name = kwargs.pop('project_domain_name', None)
project_domain_id = kwargs.pop('project_domain_id', None)
auth = None
if v3_auth_url and v2_auth_url:
# support both v2 and v3 auth. Use v3 if domain information is
# provided.
if (user_domain_name or user_domain_id or project_domain_name or
project_domain_id):
auth = v3_auth.Password(
v3_auth_url,
username=username,
user_id=user_id,
user_domain_name=user_domain_name,
user_domain_id=user_domain_id,
project_domain_name=project_domain_name,
project_domain_id=project_domain_id,
**kwargs)
else:
auth = v2_auth.Password(
v2_auth_url,
username,
kwargs.pop('password', None),
tenant_id=kwargs.pop('project_id', None),
tenant_name=kwargs.pop('project_name', None))
elif v3_auth_url:
# support only v3
auth = v3_auth.Password(
v3_auth_url,
username=username,
user_id=user_id,
user_domain_name=user_domain_name,
user_domain_id=user_domain_id,
project_domain_name=project_domain_name,
project_domain_id=project_domain_id,
**kwargs)
elif v2_auth_url:
# support only v2
auth = v2_auth.Password(
v2_auth_url,
username,
kwargs.pop('password', None),
tenant_id=kwargs.pop('project_id', None),
tenant_name=kwargs.pop('project_name', None))
else:
raise exc.CommandError('Unable to determine the Keystone version '
'to authenticate with using the given '
'auth_url.')
return auth
def main(self, argv): def main(self, argv):
# Parse args once to find version # Parse args once to find version
parser = self.get_base_parser() parser = self.get_base_parser()
@ -515,21 +340,13 @@ class HeatShell(object):
" via either --heat-url or" " via either --heat-url or"
" env[HEAT_URL]") " env[HEAT_URL]")
else: else:
# Tenant/project name or ID is needed to make keystoneclient # Tenant name or ID is needed to make keystoneclient retrieve a
# retrieve a service catalog, it's not required if # service catalog, it's not required if os_no_client_auth is
# os_no_client_auth is specified, neither is the auth URL # specified, neither is the auth URL
if not (args.os_tenant_id or args.os_tenant_name):
if not (args.os_tenant_id or args.os_tenant_name or raise exc.CommandError("You must provide a tenant_id via"
args.os_project_id or args.os_project_name): " either --os-tenant-id or via"
raise exc.CommandError("You must provide a tenant id via" " env[OS_TENANT_ID]")
" either --os-tenant-id or"
" env[OS_TENANT_ID] or a tenant name"
" via either --os-tenant-name or"
" env[OS_TENANT_NAME] or a project id"
" via either --os-project-id or"
" env[OS_PROJECT_ID] or a project"
" name via either --os-project-name or"
" env[OS_PROJECT_NAME]")
if not args.os_auth_url: if not args.os_auth_url:
raise exc.CommandError("You must provide an auth url via" raise exc.CommandError("You must provide an auth url via"
@ -537,55 +354,46 @@ class HeatShell(object):
" env[OS_AUTH_URL]") " env[OS_AUTH_URL]")
kwargs = { 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,
'service_type': args.os_service_type,
'endpoint_type': args.os_endpoint_type,
'insecure': args.insecure, 'insecure': args.insecure,
'cacert': args.os_cacert, 'cacert': args.os_cacert,
'cert': args.os_cert,
'key': args.os_key,
'timeout': args.api_timeout
}
keystone_session = self._get_keystone_session(**kwargs)
endpoint = args.heat_url
if args.os_no_client_auth:
kwargs = {
'endpoint': endpoint,
'auth_token': args.os_auth_token}
keystone_auth = self._get_keystone_auth(keystone_session,
args.os_auth_url,
**kwargs)
else:
project_id = args.os_project_id or args.os_tenant_id
project_name = args.os_project_name or args.os_tenant_name
kwargs = {
'username': args.os_username,
'user_id': args.os_user_id,
'user_domain_id': args.os_user_domain_id,
'user_domain_name': args.os_user_domain_name,
'password': args.os_password,
'auth_token': args.os_auth_token,
'project_id': project_id,
'project_name': project_name,
'project_domain_id': args.os_project_domain_id,
'project_domain_name': args.os_project_domain_name,
}
keystone_auth = self._get_keystone_auth(keystone_session,
args.os_auth_url,
**kwargs)
service_type = args.os_service_type or 'orchestration'
endpoint_type = args.os_endpoint_type or 'publicURL'
kwargs = {
'auth_url': args.os_auth_url,
'session': keystone_session,
'auth': keystone_auth,
'service_type': service_type,
'endpoint_type': endpoint_type,
'region_name': args.os_region_name,
'username': args.os_username,
'password': args.os_password,
'include_pass': args.include_password 'include_pass': args.include_password
} }
endpoint = args.heat_url
if not args.os_no_client_auth:
_ksclient = self._get_ksclient(**kwargs)
token = args.os_auth_token or _ksclient.auth_token
kwargs = {
'token': token,
'insecure': args.insecure,
'ca_file': args.ca_file,
'cert_file': args.cert_file,
'key_file': args.key_file,
'username': args.os_username,
'password': args.os_password,
'endpoint_type': args.os_endpoint_type,
'include_pass': args.include_password
}
if args.os_region_name:
kwargs['region_name'] = args.os_region_name
if not endpoint:
endpoint = self._get_endpoint(_ksclient, **kwargs)
if args.api_timeout:
kwargs['timeout'] = args.api_timeout
client = heat_client.Client(api_version, endpoint, **kwargs) client = heat_client.Client(api_version, endpoint, **kwargs)
args.func(client, args) args.func(client, args)

View File

@ -14,6 +14,24 @@
from heatclient.common import http from heatclient.common import http
from heatclient import exc from heatclient import exc
from heatclient.openstack.common import jsonutils from heatclient.openstack.common import jsonutils
from keystoneclient.v2_0 import client as ksclient
def script_keystone_client(token=None):
if token:
ksclient.Client(auth_url='http://no.where',
insecure=False,
cacert=None,
tenant_id='tenant_id',
token=token).AndReturn(FakeKeystone(token))
else:
ksclient.Client(auth_url='http://no.where',
insecure=False,
cacert=None,
password='password',
tenant_name='tenant_name',
username='username').AndReturn(FakeKeystone(
'abcd1234'))
def script_heat_list(url=None): def script_heat_list(url=None):

View File

@ -1,83 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
from heatclient.openstack.common import jsonutils
from keystoneclient.fixture import v2 as ks_v2_fixture
from keystoneclient.fixture import v3 as ks_v3_fixture
# these are copied from python-keystoneclient tests
BASE_HOST = 'http://keystone.example.com'
BASE_URL = "%s:5000/" % BASE_HOST
UPDATED = '2013-03-06T00:00:00Z'
V2_URL = "%sv2.0" % BASE_URL
V2_DESCRIBED_BY_HTML = {'href': 'http://docs.openstack.org/api/'
'openstack-identity-service/2.0/content/',
'rel': 'describedby',
'type': 'text/html'}
V2_DESCRIBED_BY_PDF = {'href': 'http://docs.openstack.org/api/openstack-ident'
'ity-service/2.0/identity-dev-guide-2.0.pdf',
'rel': 'describedby',
'type': 'application/pdf'}
V2_VERSION = {'id': 'v2.0',
'links': [{'href': V2_URL, 'rel': 'self'},
V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF],
'status': 'stable',
'updated': UPDATED}
V3_URL = "%sv3" % BASE_URL
V3_MEDIA_TYPES = [{'base': 'application/json',
'type': 'application/vnd.openstack.identity-v3+json'},
{'base': 'application/xml',
'type': 'application/vnd.openstack.identity-v3+xml'}]
V3_VERSION = {'id': 'v3.0',
'links': [{'href': V3_URL, 'rel': 'self'}],
'media-types': V3_MEDIA_TYPES,
'status': 'stable',
'updated': UPDATED}
TOKENID = uuid.uuid4().hex
def _create_version_list(versions):
return jsonutils.dumps({'versions': {'values': versions}})
def _create_single_version(version):
return jsonutils.dumps({'version': version})
V3_VERSION_LIST = _create_version_list([V3_VERSION, V2_VERSION])
V2_VERSION_LIST = _create_version_list([V2_VERSION])
V3_VERSION_ENTRY = _create_single_version(V3_VERSION)
V2_VERSION_ENTRY = _create_single_version(V2_VERSION)
HEAT_ENDPOINT = 'http://www.heat.com/v1'
def keystone_request_callback(request, uri, headers):
response_headers = {"content-type": "application/json"}
token_id = TOKENID
if uri == BASE_URL:
return (200, headers, V3_VERSION_LIST)
elif uri == BASE_URL + "/v2.0":
v2_token = ks_v2_fixture.Token(token_id)
return (200, response_headers, jsonutils.dumps(v2_token))
elif uri == BASE_URL + "/v3":
v3_token = ks_v3_fixture.Token()
response_headers["X-Subject-Token"] = token_id
return (201, response_headers, jsonutils.dumps(v3_token))

View File

@ -19,7 +19,6 @@ from six.moves.urllib import request
import sys import sys
import fixtures import fixtures
import httpretty
import tempfile import tempfile
import testscenarios import testscenarios
import testtools import testtools
@ -28,32 +27,18 @@ from heatclient.openstack.common import jsonutils
from heatclient.openstack.common import strutils from heatclient.openstack.common import strutils
from mox3 import mox from mox3 import mox
from keystoneclient.v2_0 import client as ksclient
from heatclient.common import http from heatclient.common import http
from heatclient import exc from heatclient import exc
import heatclient.shell import heatclient.shell
from heatclient.tests import fakes from heatclient.tests import fakes
from heatclient.tests import keystone_client_fixtures
load_tests = testscenarios.load_tests_apply_scenarios load_tests = testscenarios.load_tests_apply_scenarios
TEST_VAR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), TEST_VAR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
'var')) 'var'))
FAKE_ENV_KEYSTONE_V2 = {
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': keystone_client_fixtures.BASE_URL,
}
FAKE_ENV_KEYSTONE_V3 = {
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': keystone_client_fixtures.BASE_URL,
'OS_USER_DOMAIN_ID': 'default',
'OS_PROJECT_DOMAIN_ID': 'default',
}
class TestCase(testtools.TestCase): class TestCase(testtools.TestCase):
@ -89,12 +74,6 @@ class TestCase(testtools.TestCase):
sys.stderr = orig sys.stderr = orig
return err return err
def register_keystone_auth_fixture(self):
httpretty.register_uri(
httpretty.GET,
keystone_client_fixtures.BASE_URL,
body=keystone_client_fixtures.keystone_request_callback)
class EnvVarTest(TestCase): class EnvVarTest(TestCase):
@ -107,7 +86,7 @@ class EnvVarTest(TestCase):
err='You must provide a password')), err='You must provide a password')),
('tenant_name', dict( ('tenant_name', dict(
remove='OS_TENANT_NAME', remove='OS_TENANT_NAME',
err='You must provide a tenant id')), err='You must provide a tenant_id')),
('auth_url', dict( ('auth_url', dict(
remove='OS_AUTH_URL', remove='OS_AUTH_URL',
err='You must provide an auth url')), err='You must provide an auth url')),
@ -131,7 +110,7 @@ class EnvVarTestToken(TestCase):
scenarios = [ scenarios = [
('tenant_id', dict( ('tenant_id', dict(
remove='OS_TENANT_ID', remove='OS_TENANT_ID',
err='You must provide a tenant id')), err='You must provide a tenant_id')),
('auth_url', dict( ('auth_url', dict(
remove='OS_AUTH_URL', remove='OS_AUTH_URL',
err='You must provide an auth url')), err='You must provide an auth url')),
@ -172,14 +151,17 @@ class ShellParamValidationTest(TestCase):
self.addCleanup(self.m.VerifyAll) self.addCleanup(self.m.VerifyAll)
self.addCleanup(self.m.UnsetStubs) self.addCleanup(self.m.UnsetStubs)
@httpretty.activate
def test_bad_parameters(self): def test_bad_parameters(self):
self.register_keystone_auth_fixture() self.m.StubOutWithMock(ksclient, 'Client')
self.m.StubOutWithMock(http.HTTPClient, 'json_request')
fakes.script_keystone_client()
self.m.ReplayAll()
fake_env = { fake_env = {
'OS_USERNAME': 'username', 'OS_USERNAME': 'username',
'OS_PASSWORD': 'password', 'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name', 'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': keystone_client_fixtures.BASE_URL, 'OS_AUTH_URL': 'http://no.where',
} }
self.set_fake_env(fake_env) self.set_fake_env(fake_env)
template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') template_file = os.path.join(TEST_VAR_DIR, 'minimal.template')
@ -195,63 +177,35 @@ class ShellValidationTest(TestCase):
self.addCleanup(self.m.VerifyAll) self.addCleanup(self.m.VerifyAll)
self.addCleanup(self.m.UnsetStubs) self.addCleanup(self.m.UnsetStubs)
@httpretty.activate
def test_failed_auth(self): def test_failed_auth(self):
self.register_keystone_auth_fixture() self.m.StubOutWithMock(ksclient, 'Client')
self.m.StubOutWithMock(http.HTTPClient, 'json_request') self.m.StubOutWithMock(http.HTTPClient, 'json_request')
fakes.script_keystone_client()
failed_msg = 'Unable to authenticate user with credentials provided' failed_msg = 'Unable to authenticate user with credentials provided'
http.HTTPClient.json_request( http.HTTPClient.json_request(
'GET', '/stacks?').AndRaise(exc.Unauthorized(failed_msg)) 'GET', '/stacks?').AndRaise(exc.Unauthorized(failed_msg))
self.m.ReplayAll() self.m.ReplayAll()
self.set_fake_env(FAKE_ENV_KEYSTONE_V2)
self.shell_error('stack-list', failed_msg)
@httpretty.activate
def test_stack_create_validation(self):
# emulate Keystone version discovery
httpretty.register_uri(
httpretty.GET,
keystone_client_fixtures.V2_URL,
body=keystone_client_fixtures.V2_VERSION_ENTRY)
# emulate Keystone v2 token request
httpretty.register_uri(
httpretty.POST,
'%s/tokens' % (keystone_client_fixtures.V2_URL),
body=keystone_client_fixtures.keystone_request_callback)
fake_env = { fake_env = {
'OS_USERNAME': 'username', 'OS_USERNAME': 'username',
'OS_PASSWORD': 'password', 'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name', 'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': keystone_client_fixtures.V2_URL, 'OS_AUTH_URL': 'http://no.where',
} }
self.set_fake_env(fake_env) self.set_fake_env(fake_env)
self.shell_error( self.shell_error('stack-list', failed_msg)
'stack-create teststack '
'--parameters="InstanceType=m1.large;DBUsername=wp;'
'DBPassword=verybadpassword;KeyName=heat_key;'
'LinuxDistribution=F17"',
'Need to specify exactly one of')
@httpretty.activate def test_stack_create_validation(self):
def test_stack_create_validation_keystone_v3(self): self.m.StubOutWithMock(ksclient, 'Client')
# emulate Keystone version discovery self.m.StubOutWithMock(http.HTTPClient, 'json_request')
httpretty.register_uri( fakes.script_keystone_client()
httpretty.GET,
keystone_client_fixtures.V3_URL,
body=keystone_client_fixtures.V3_VERSION_ENTRY)
# emulate Keystone v2 token request
httpretty.register_uri(
httpretty.POST,
'%s/tokens' % (keystone_client_fixtures.V3_URL),
body=keystone_client_fixtures.keystone_request_callback)
self.m.ReplayAll()
fake_env = { fake_env = {
'OS_USERNAME': 'username', 'OS_USERNAME': 'username',
'OS_PASSWORD': 'password', 'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name', 'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': keystone_client_fixtures.V3_URL, 'OS_AUTH_URL': 'http://no.where',
} }
self.set_fake_env(fake_env) self.set_fake_env(fake_env)
self.shell_error( self.shell_error(
@ -267,6 +221,7 @@ class ShellBase(TestCase):
def setUp(self): def setUp(self):
super(ShellBase, self).setUp() super(ShellBase, self).setUp()
self.m = mox.Mox() self.m = mox.Mox()
self.m.StubOutWithMock(ksclient, 'Client')
self.m.StubOutWithMock(http.HTTPClient, 'json_request') self.m.StubOutWithMock(http.HTTPClient, 'json_request')
self.m.StubOutWithMock(http.HTTPClient, 'raw_request') self.m.StubOutWithMock(http.HTTPClient, 'raw_request')
self.addCleanup(self.m.VerifyAll) self.addCleanup(self.m.VerifyAll)
@ -300,7 +255,6 @@ class ShellTestCommon(ShellBase):
def setUp(self): def setUp(self):
super(ShellTestCommon, self).setUp() super(ShellTestCommon, self).setUp()
self.set_fake_env(FAKE_ENV_KEYSTONE_V2)
def test_help_unknown_command(self): def test_help_unknown_command(self):
self.assertRaises(exc.CommandError, self.shell, 'help foofoo') self.assertRaises(exc.CommandError, self.shell, 'help foofoo')
@ -327,36 +281,54 @@ class ShellTestCommon(ShellBase):
self.assertEqual(output1, output2) self.assertEqual(output1, output2)
self.assertRegexpMatches(output1, '^usage: heat %s' % command) self.assertRegexpMatches(output1, '^usage: heat %s' % command)
@httpretty.activate
def test_debug_switch_raises_error(self): def test_debug_switch_raises_error(self):
self.register_keystone_auth_fixture() fakes.script_keystone_client()
http.HTTPClient.json_request( http.HTTPClient.json_request(
'GET', '/stacks?').AndRaise(exc.Unauthorized("FAIL")) 'GET', '/stacks?').AndRaise(exc.Unauthorized("FAIL"))
self.m.ReplayAll() self.m.ReplayAll()
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)
args = ['--debug', 'stack-list'] args = ['--debug', 'stack-list']
self.assertRaises(exc.Unauthorized, heatclient.shell.main, args) self.assertRaises(exc.Unauthorized, heatclient.shell.main, args)
@httpretty.activate
def test_dash_d_switch_raises_error(self): def test_dash_d_switch_raises_error(self):
self.register_keystone_auth_fixture() fakes.script_keystone_client()
http.HTTPClient.json_request( http.HTTPClient.json_request(
'GET', '/stacks?').AndRaise(exc.CommandError("FAIL")) 'GET', '/stacks?').AndRaise(exc.CommandError("FAIL"))
self.m.ReplayAll() self.m.ReplayAll()
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)
args = ['-d', 'stack-list'] args = ['-d', 'stack-list']
self.assertRaises(exc.CommandError, heatclient.shell.main, args) self.assertRaises(exc.CommandError, heatclient.shell.main, args)
@httpretty.activate
def test_no_debug_switch_no_raises_errors(self): def test_no_debug_switch_no_raises_errors(self):
self.register_keystone_auth_fixture() fakes.script_keystone_client()
http.HTTPClient.json_request( http.HTTPClient.json_request(
'GET', '/stacks?').AndRaise(exc.Unauthorized("FAIL")) 'GET', '/stacks?').AndRaise(exc.Unauthorized("FAIL"))
self.m.ReplayAll() self.m.ReplayAll()
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)
args = ['stack-list'] args = ['stack-list']
self.assertRaises(SystemExit, heatclient.shell.main, args) self.assertRaises(SystemExit, heatclient.shell.main, args)
@ -380,12 +352,21 @@ class ShellTestUserPass(ShellBase):
super(ShellTestUserPass, self).setUp() super(ShellTestUserPass, self).setUp()
self._set_fake_env() self._set_fake_env()
# Patch os.environ to avoid required auth info.
def _set_fake_env(self): def _set_fake_env(self):
self.set_fake_env(FAKE_ENV_KEYSTONE_V2) 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()
@httpretty.activate
def test_stack_list(self): def test_stack_list(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
fakes.script_heat_list() fakes.script_heat_list()
self.m.ReplayAll() self.m.ReplayAll()
@ -404,9 +385,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(list_text, r) self.assertRegexpMatches(list_text, r)
@httpretty.activate
def test_stack_list_with_args(self): def test_stack_list_with_args(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
expected_url = '/stacks?%s' % parse.urlencode({ expected_url = '/stacks?%s' % parse.urlencode({
'limit': 2, 'limit': 2,
'status': ['COMPLETE', 'FAILED'], 'status': ['COMPLETE', 'FAILED'],
@ -433,9 +413,7 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(list_text, r) self.assertRegexpMatches(list_text, r)
@httpretty.activate
def test_parsable_error(self): def test_parsable_error(self):
self.register_keystone_auth_fixture()
message = "The Stack (bad) could not be found." message = "The Stack (bad) could not be found."
resp_dict = { resp_dict = {
"explanation": "The resource could not be found.", "explanation": "The resource could not be found.",
@ -448,6 +426,7 @@ class ShellTestUserPass(ShellBase):
"title": "Not Found" "title": "Not Found"
} }
self._script_keystone_client()
fakes.script_heat_error(jsonutils.dumps(resp_dict)) fakes.script_heat_error(jsonutils.dumps(resp_dict))
self.m.ReplayAll() self.m.ReplayAll()
@ -455,9 +434,7 @@ class ShellTestUserPass(ShellBase):
e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad") e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad")
self.assertEqual("ERROR: " + message, str(e)) self.assertEqual("ERROR: " + message, str(e))
@httpretty.activate
def test_parsable_verbose(self): def test_parsable_verbose(self):
self.register_keystone_auth_fixture()
message = "The Stack (bad) could not be found." message = "The Stack (bad) could not be found."
resp_dict = { resp_dict = {
"explanation": "The resource could not be found.", "explanation": "The resource could not be found.",
@ -470,6 +447,7 @@ class ShellTestUserPass(ShellBase):
"title": "Not Found" "title": "Not Found"
} }
self._script_keystone_client()
fakes.script_heat_error(jsonutils.dumps(resp_dict)) fakes.script_heat_error(jsonutils.dumps(resp_dict))
self.m.ReplayAll() self.m.ReplayAll()
@ -479,18 +457,15 @@ class ShellTestUserPass(ShellBase):
e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad") e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad")
self.assertIn(message, str(e)) self.assertIn(message, str(e))
@httpretty.activate
def test_parsable_malformed_error(self): def test_parsable_malformed_error(self):
self.register_keystone_auth_fixture()
invalid_json = "ERROR: {Invalid JSON Error." invalid_json = "ERROR: {Invalid JSON Error."
self._script_keystone_client()
fakes.script_heat_error(invalid_json) fakes.script_heat_error(invalid_json)
self.m.ReplayAll() self.m.ReplayAll()
e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad") e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad")
self.assertEqual("ERROR: " + invalid_json, str(e)) self.assertEqual("ERROR: " + invalid_json, str(e))
@httpretty.activate
def test_parsable_malformed_error_missing_message(self): def test_parsable_malformed_error_missing_message(self):
self.register_keystone_auth_fixture()
missing_message = { missing_message = {
"explanation": "The resource could not be found.", "explanation": "The resource could not be found.",
"code": 404, "code": 404,
@ -501,15 +476,14 @@ class ShellTestUserPass(ShellBase):
"title": "Not Found" "title": "Not Found"
} }
self._script_keystone_client()
fakes.script_heat_error(jsonutils.dumps(missing_message)) fakes.script_heat_error(jsonutils.dumps(missing_message))
self.m.ReplayAll() self.m.ReplayAll()
e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad") e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad")
self.assertEqual("ERROR: Internal Error", str(e)) self.assertEqual("ERROR: Internal Error", str(e))
@httpretty.activate
def test_parsable_malformed_error_missing_traceback(self): def test_parsable_malformed_error_missing_traceback(self):
self.register_keystone_auth_fixture()
message = "The Stack (bad) could not be found." message = "The Stack (bad) could not be found."
resp_dict = { resp_dict = {
"explanation": "The resource could not be found.", "explanation": "The resource could not be found.",
@ -521,6 +495,7 @@ class ShellTestUserPass(ShellBase):
"title": "Not Found" "title": "Not Found"
} }
self._script_keystone_client()
fakes.script_heat_error(jsonutils.dumps(resp_dict)) fakes.script_heat_error(jsonutils.dumps(resp_dict))
self.m.ReplayAll() self.m.ReplayAll()
@ -530,9 +505,8 @@ class ShellTestUserPass(ShellBase):
self.assertEqual("ERROR: The Stack (bad) could not be found.\n", self.assertEqual("ERROR: The Stack (bad) could not be found.\n",
str(e)) str(e))
@httpretty.activate
def test_stack_show(self): def test_stack_show(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"stack": { resp_dict = {"stack": {
"id": "1", "id": "1",
"stack_name": "teststack", "stack_name": "teststack",
@ -563,9 +537,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(list_text, r) self.assertRegexpMatches(list_text, r)
@httpretty.activate
def test_stack_abandon(self): def test_stack_abandon(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"stack": { resp_dict = {"stack": {
"id": "1", "id": "1",
@ -607,6 +580,7 @@ class ShellTestUserPass(ShellBase):
self.assertEqual(abandoned_stack, jsonutils.loads(abandon_resp)) self.assertEqual(abandoned_stack, jsonutils.loads(abandon_resp))
def _output_fake_response(self): def _output_fake_response(self):
self._script_keystone_client()
resp_dict = {"stack": { resp_dict = {"stack": {
"id": "1", "id": "1",
@ -644,31 +618,24 @@ class ShellTestUserPass(ShellBase):
self.m.ReplayAll() self.m.ReplayAll()
@httpretty.activate
def test_output_list(self): def test_output_list(self):
self.register_keystone_auth_fixture()
self._output_fake_response() self._output_fake_response()
list_text = self.shell('output-list teststack/1') list_text = self.shell('output-list teststack/1')
for r in ['output1', 'output2', 'output_uni']: for r in ['output1', 'output2', 'output_uni']:
self.assertRegexpMatches(list_text, r) self.assertRegexpMatches(list_text, r)
@httpretty.activate
def test_output_show(self): def test_output_show(self):
self.register_keystone_auth_fixture()
self._output_fake_response() self._output_fake_response()
list_text = self.shell('output-show teststack/1 output1') list_text = self.shell('output-show teststack/1 output1')
self.assertRegexpMatches(list_text, 'value1') self.assertRegexpMatches(list_text, 'value1')
@httpretty.activate
def test_output_show_unicode(self): def test_output_show_unicode(self):
self.register_keystone_auth_fixture()
self._output_fake_response() self._output_fake_response()
list_text = self.shell('output-show teststack/1 output_uni') list_text = self.shell('output-show teststack/1 output_uni')
self.assertRegexpMatches(list_text, u'test\u2665') self.assertRegexpMatches(list_text, u'test\u2665')
@httpretty.activate
def test_template_show_cfn(self): def test_template_show_cfn(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
template_data = open(os.path.join(TEST_VAR_DIR, template_data = open(os.path.join(TEST_VAR_DIR,
'minimal.template')).read() 'minimal.template')).read()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
@ -694,9 +661,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(show_text, r) self.assertRegexpMatches(show_text, r)
@httpretty.activate
def test_template_show_cfn_unicode(self): def test_template_show_cfn_unicode(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"AWSTemplateFormatVersion": "2010-09-09", resp_dict = {"AWSTemplateFormatVersion": "2010-09-09",
"Description": u"test\u2665", "Description": u"test\u2665",
"Outputs": {}, "Outputs": {},
@ -725,9 +691,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(show_text, r) self.assertRegexpMatches(show_text, r)
@httpretty.activate
def test_template_show_hot(self): def test_template_show_hot(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"heat_template_version": "2013-05-23", resp_dict = {"heat_template_version": "2013-05-23",
"parameters": {}, "parameters": {},
"resources": {}, "resources": {},
@ -752,9 +717,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(show_text, r) self.assertRegexpMatches(show_text, r)
@httpretty.activate
def test_stack_preview(self): def test_stack_preview(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"stack": { resp_dict = {"stack": {
"id": "1", "id": "1",
"stack_name": "teststack", "stack_name": "teststack",
@ -793,9 +757,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(preview_text, r) self.assertRegexpMatches(preview_text, r)
@httpretty.activate
def test_stack_create(self): def test_stack_create(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
201, 201,
'Created', 'Created',
@ -827,9 +790,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(create_text, r) self.assertRegexpMatches(create_text, r)
@httpretty.activate
def test_stack_create_timeout(self): def test_stack_create_timeout(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') template_file = os.path.join(TEST_VAR_DIR, 'minimal.template')
template_data = open(template_file).read() template_data = open(template_file).read()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
@ -875,9 +837,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(create_text, r) self.assertRegexpMatches(create_text, r)
@httpretty.activate
def test_stack_update_timeout(self): def test_stack_update_timeout(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') template_file = os.path.join(TEST_VAR_DIR, 'minimal.template')
template_data = open(template_file).read() template_data = open(template_file).read()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
@ -923,9 +884,9 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(update_text, r) self.assertRegexpMatches(update_text, r)
@httpretty.activate
def test_stack_create_url(self): def test_stack_create_url(self):
self.register_keystone_auth_fixture()
self._script_keystone_client()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
201, 201,
'Created', 'Created',
@ -971,9 +932,9 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(create_text, r) self.assertRegexpMatches(create_text, r)
@httpretty.activate
def test_stack_create_object(self): def test_stack_create_object(self):
self.register_keystone_auth_fixture()
self._script_keystone_client()
template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') template_file = os.path.join(TEST_VAR_DIR, 'minimal.template')
template_data = open(template_file).read() template_data = open(template_file).read()
http.HTTPClient.raw_request( http.HTTPClient.raw_request(
@ -1011,9 +972,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(create_text, r) self.assertRegexpMatches(create_text, r)
@httpretty.activate
def test_stack_adopt(self): def test_stack_adopt(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
201, 201,
'Created', 'Created',
@ -1047,19 +1007,17 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(adopt_text, r) self.assertRegexpMatches(adopt_text, r)
@httpretty.activate
def test_stack_adopt_without_data(self): def test_stack_adopt_without_data(self):
self.register_keystone_auth_fixture()
failed_msg = 'Need to specify --adopt-file' failed_msg = 'Need to specify --adopt-file'
self._script_keystone_client()
self.m.ReplayAll() self.m.ReplayAll()
template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') template_file = os.path.join(TEST_VAR_DIR, 'minimal.template')
self.shell_error( self.shell_error(
'stack-adopt teststack ' 'stack-adopt teststack '
'--template-file=%s ' % template_file, failed_msg) '--template-file=%s ' % template_file, failed_msg)
@httpretty.activate
def test_stack_update(self): def test_stack_update(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
202, 202,
'Accepted', 'Accepted',
@ -1092,9 +1050,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(update_text, r) self.assertRegexpMatches(update_text, r)
@httpretty.activate
def test_stack_delete(self): def test_stack_delete(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
204, 204,
'No Content', 'No Content',
@ -1118,9 +1075,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(delete_text, r) self.assertRegexpMatches(delete_text, r)
@httpretty.activate
def test_stack_delete_multiple(self): def test_stack_delete_multiple(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
204, 204,
'No Content', 'No Content',
@ -1147,9 +1103,8 @@ class ShellTestUserPass(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(delete_text, r) self.assertRegexpMatches(delete_text, r)
@httpretty.activate
def test_build_info(self): def test_build_info(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = { resp_dict = {
'build_info': { 'build_info': {
'api': {'revision': 'api_revision'}, 'api': {'revision': 'api_revision'},
@ -1178,10 +1133,22 @@ class ShellTestUserPass(ShellBase):
class ShellTestEvents(ShellBase): class ShellTestEvents(ShellBase):
def setUp(self): def setUp(self):
super(ShellTestEvents, self).setUp() super(ShellTestEvents, self).setUp()
self.set_fake_env(FAKE_ENV_KEYSTONE_V2) 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()
scenarios = [ scenarios = [
('integer_id', dict( ('integer_id', dict(
@ -1191,9 +1158,8 @@ class ShellTestEvents(ShellBase):
event_id_one='3d68809e-c4aa-4dc9-a008-933823d2e44f', event_id_one='3d68809e-c4aa-4dc9-a008-933823d2e44f',
event_id_two='43b68bae-ed5d-4aed-a99f-0b3d39c2418a'))] event_id_two='43b68bae-ed5d-4aed-a99f-0b3d39c2418a'))]
@httpretty.activate
def test_event_list(self): def test_event_list(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"events": [ resp_dict = {"events": [
{"event_time": "2013-12-05T14:14:30Z", {"event_time": "2013-12-05T14:14:30Z",
"id": self.event_id_one, "id": self.event_id_one,
@ -1258,9 +1224,8 @@ class ShellTestEvents(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(event_list_text, r) self.assertRegexpMatches(event_list_text, r)
@httpretty.activate
def test_event_list_pagination(self): def test_event_list_pagination(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
# test for pagination # test for pagination
resp_dict = {"events": [ resp_dict = {"events": [
{"event_time": "2013-12-05T14:14:30Z", {"event_time": "2013-12-05T14:14:30Z",
@ -1312,9 +1277,8 @@ class ShellTestEvents(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(event_list_text, r) self.assertRegexpMatches(event_list_text, r)
@httpretty.activate
def test_event_show(self): def test_event_show(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"event": resp_dict = {"event":
{"event_time": "2013-12-05T14:14:30Z", {"event_time": "2013-12-05T14:14:30Z",
"id": self.event_id_one, "id": self.event_id_one,
@ -1383,13 +1347,25 @@ class ShellTestEvents(ShellBase):
class ShellTestResources(ShellBase): class ShellTestResources(ShellBase):
def setUp(self): def setUp(self):
super(ShellTestResources, self).setUp() super(ShellTestResources, self).setUp()
self.set_fake_env(FAKE_ENV_KEYSTONE_V2) 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_resource_list(self, with_resource_name): def _test_resource_list(self, with_resource_name):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"resources": [ resp_dict = {"resources": [
{"links": [{"href": "http://heat.example.com:8004/foo", {"links": [{"href": "http://heat.example.com:8004/foo",
"rel": "self"}, "rel": "self"},
@ -1438,17 +1414,14 @@ class ShellTestResources(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(resource_list_text, r) self.assertRegexpMatches(resource_list_text, r)
@httpretty.activate
def test_resource_list(self): def test_resource_list(self):
self._test_resource_list(True) self._test_resource_list(True)
@httpretty.activate
def test_resource_list_no_resource_name(self): def test_resource_list_no_resource_name(self):
self._test_resource_list(False) self._test_resource_list(False)
@httpretty.activate
def test_resource_list_empty(self): def test_resource_list_empty(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"resources": []} resp_dict = {"resources": []}
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
200, 200,
@ -1475,9 +1448,8 @@ class ShellTestResources(ShellBase):
--------------+ --------------+
''', resource_list_text) ''', resource_list_text)
@httpretty.activate
def test_resource_show(self): def test_resource_show(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"resource": resp_dict = {"resource":
{"description": "", {"description": "",
"links": [{"href": "http://heat.example.com:8004/foo", "links": [{"href": "http://heat.example.com:8004/foo",
@ -1536,9 +1508,8 @@ class ShellTestResources(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(resource_show_text, r) self.assertRegexpMatches(resource_show_text, r)
@httpretty.activate
def test_resource_signal(self): def test_resource_signal(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
200, 200,
'OK', 'OK',
@ -1562,9 +1533,8 @@ class ShellTestResources(ShellBase):
stack_id, resource_name)) stack_id, resource_name))
self.assertEqual("", text) self.assertEqual("", text)
@httpretty.activate
def test_resource_signal_no_data(self): def test_resource_signal_no_data(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
200, 200,
'OK', 'OK',
@ -1586,9 +1556,8 @@ class ShellTestResources(ShellBase):
'resource-signal {0} {1}'.format(stack_id, resource_name)) 'resource-signal {0} {1}'.format(stack_id, resource_name))
self.assertEqual("", text) self.assertEqual("", text)
@httpretty.activate
def test_resource_signal_no_json(self): def test_resource_signal_no_json(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
stack_id = 'teststack/1' stack_id = 'teststack/1'
resource_name = 'aResource' resource_name = 'aResource'
@ -1600,9 +1569,8 @@ class ShellTestResources(ShellBase):
stack_id, resource_name)) stack_id, resource_name))
self.assertIn('Data should be in JSON format', str(error)) self.assertIn('Data should be in JSON format', str(error))
@httpretty.activate
def test_resource_signal_no_dict(self): def test_resource_signal_no_dict(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
stack_id = 'teststack/1' stack_id = 'teststack/1'
resource_name = 'aResource' resource_name = 'aResource'
@ -1614,9 +1582,8 @@ class ShellTestResources(ShellBase):
stack_id, resource_name)) stack_id, resource_name))
self.assertEqual('Data should be a JSON dict', str(error)) self.assertEqual('Data should be a JSON dict', str(error))
@httpretty.activate
def test_resource_signal_both_data(self): def test_resource_signal_both_data(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
stack_id = 'teststack/1' stack_id = 'teststack/1'
resource_name = 'aResource' resource_name = 'aResource'
@ -1629,9 +1596,8 @@ class ShellTestResources(ShellBase):
self.assertEqual('Can only specify one of data and data-file', self.assertEqual('Can only specify one of data and data-file',
str(error)) str(error))
@httpretty.activate
def test_resource_signal_data_file(self): def test_resource_signal_data_file(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp = fakes.FakeHTTPResponse( resp = fakes.FakeHTTPResponse(
200, 200,
'OK', 'OK',
@ -1662,11 +1628,23 @@ class ShellTestResources(ShellBase):
class ShellTestResourceTypes(ShellBase): class ShellTestResourceTypes(ShellBase):
def setUp(self): def setUp(self):
super(ShellTestResourceTypes, self).setUp() super(ShellTestResourceTypes, self).setUp()
self.set_fake_env(FAKE_ENV_KEYSTONE_V3) 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()
@httpretty.activate
def test_resource_type_template_yaml(self): def test_resource_type_template_yaml(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"heat_template_version": "2013-05-23", resp_dict = {"heat_template_version": "2013-05-23",
"parameters": {}, "parameters": {},
"resources": {}, "resources": {},
@ -1694,9 +1672,8 @@ class ShellTestResourceTypes(ShellBase):
for r in required: for r in required:
self.assertRegexpMatches(show_text, r) self.assertRegexpMatches(show_text, r)
@httpretty.activate
def test_resource_type_template_json(self): def test_resource_type_template_json(self):
self.register_keystone_auth_fixture() self._script_keystone_client()
resp_dict = {"AWSTemplateFormatVersion": "2013-05-23", resp_dict = {"AWSTemplateFormatVersion": "2013-05-23",
"Parameters": {}, "Parameters": {},
"Resources": {}, "Resources": {},
@ -1728,18 +1705,23 @@ class ShellTestResourceTypes(ShellBase):
class ShellTestBuildInfo(ShellBase): class ShellTestBuildInfo(ShellBase):
def setUp(self): def setUp(self):
super(ShellTestBuildInfo, self).setUp() super(ShellTestBuildInfo, self).setUp()
self._set_fake_env() self._set_fake_env()
def _set_fake_env(self): def _set_fake_env(self):
'''Patch os.environ to avoid required auth info.''' '''Patch os.environ to avoid required auth info.'''
self.set_fake_env(FAKE_ENV_KEYSTONE_V2)
@httpretty.activate 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 test_build_info(self): def test_build_info(self):
self.register_keystone_auth_fixture() fakes.script_keystone_client()
resp_dict = { resp_dict = {
'build_info': { 'build_info': {
'api': {'revision': 'api_revision'}, 'api': {'revision': 'api_revision'},
@ -1788,11 +1770,8 @@ class ShellTestToken(ShellTestUserPass):
} }
self.set_fake_env(fake_env) self.set_fake_env(fake_env)
def _script_keystone_client(self):
class ShellTestUserPassKeystoneV3(ShellTestUserPass): fakes.script_keystone_client(token=self.token)
def _set_fake_env(self):
self.set_fake_env(FAKE_ENV_KEYSTONE_V3)
class ShellTestStandaloneToken(ShellTestUserPass): class ShellTestStandaloneToken(ShellTestUserPass):
@ -1817,9 +1796,11 @@ class ShellTestStandaloneToken(ShellTestUserPass):
} }
self.set_fake_env(fake_env) self.set_fake_env(fake_env)
@httpretty.activate def _script_keystone_client(self):
# The StanaloneMode shouldn't need any keystoneclient stubbing
pass
def test_bad_template_file(self): def test_bad_template_file(self):
self.register_keystone_auth_fixture()
failed_msg = 'Error parsing template ' failed_msg = 'Error parsing template '
with tempfile.NamedTemporaryFile() as bad_json_file: with tempfile.NamedTemporaryFile() as bad_json_file:

View File

@ -36,7 +36,7 @@ class Client(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Initialize a new client for the Heat v1 API.""" """Initialize a new client for the Heat v1 API."""
self.http_client = http._construct_http_client(*args, **kwargs) self.http_client = http.HTTPClient(*args, **kwargs)
self.stacks = stacks.StackManager(self.http_client) self.stacks = stacks.StackManager(self.http_client)
self.resources = resources.ResourceManager(self.http_client) self.resources = resources.ResourceManager(self.http_client)
self.resource_types = resource_types.ResourceTypeManager( self.resource_types = resource_types.ResourceTypeManager(

View File

@ -3,7 +3,6 @@ discover
fixtures>=0.3.14 fixtures>=0.3.14
# Hacking already pins down pep8, pyflakes and flake8 # Hacking already pins down pep8, pyflakes and flake8
hacking>=0.8.0,<0.9 hacking>=0.8.0,<0.9
httpretty>=0.8.0,!=0.8.1,!=0.8.2
mock>=1.0 mock>=1.0
mox3>=0.7.0 mox3>=0.7.0
sphinx>=1.1.2,!=1.2.0,<1.3 sphinx>=1.1.2,!=1.2.0,<1.3