Merge "Remove cyclical dependency"

This commit is contained in:
Jenkins
2015-03-03 23:26:52 +00:00
committed by Gerrit Code Review
4 changed files with 138 additions and 126 deletions

View File

@@ -57,62 +57,13 @@ class HTTPAuthError(HTTPError):
pass
class Client(object):
class _HTTPClient(object):
def __init__(self, session=None, endpoint=None, project_id=None,
def __init__(self, session, endpoint=None, project_id=None,
verify=True, service_type=_DEFAULT_SERVICE_TYPE,
service_name=None, interface=_DEFAULT_SERVICE_INTERFACE,
region_name=None):
"""
Barbican client object used to interact with barbican service.
:param session: An instance of a keystoneclient Session that
can be either authenticated, or not authenticated. When using
a non-authenticated Session, you must provide some additional
parameters. When no session is provided it will default to a
non-authenticated Session.
:type session: keystoneclient.session.Session
:param str endpoint: Barbican endpoint url. Required when a session is
not given, or when using a non-authenticated session.
When using an authenticated session, the client will attempt
to get an endpoint from the session.
:param str project_id: The project ID used for context in Barbican.
Required when a session is not given, or when using a
non-authenticated session.
When using an authenticated session, the project ID will be
provided by the authentication mechanism.
:param bool verify: When a session is not given, the client will create
a non-authenticated session. This parameter is passed to the
session that is created. If set to False, it allows
barbicanclient to perform "insecure" TLS (https) requests.
The server's certificate will not be verified against any
certificate authorities.
WARNING: This option should be used with extreme caution.
:param str service_type: Used as an endpoint filter when using an
authenticated keystone session. Defaults to 'key-manager'.
:param str service_name: Used as an endpoint filter when using an
authenticated keystone session.
:param str interface: Used as an endpoint filter when using an
authenticated keystone session. Defaults to 'public'.
:param str region_name: Used as an endpoint filter when using an
authenticated keystone session.
"""
LOG.debug("Creating Client object")
self._session = session or ks_session.Session(verify=verify)
if self._session.auth is None:
self._validate_endpoint_and_project_id(endpoint, project_id)
if endpoint is not None:
self._barbican_endpoint = self._get_normalized_endpoint(endpoint)
else:
self._barbican_endpoint = self._get_normalized_endpoint(
self._session.get_endpoint(
service_type=service_type, service_name=service_name,
interface=interface, region_name=region_name
)
)
self._session = session
if project_id is None:
self._default_headers = dict()
@@ -120,25 +71,17 @@ class Client(object):
# If provided we'll include the project ID in all requests.
self._default_headers = {'X-Project-Id': project_id}
self._base_url = '{0}/{1}'.format(self._barbican_endpoint,
_DEFAULT_API_VERSION)
if not endpoint:
endpoint = session.get_endpoint(service_type=service_type,
service_name=service_name,
interface=interface,
region_name=region_name)
self.secrets = secrets.SecretManager(self)
self.orders = orders.OrderManager(self)
self.containers = containers.ContainerManager(self)
def _validate_endpoint_and_project_id(self, endpoint, project_id):
if endpoint is None:
raise ValueError('Barbican endpoint url must be provided when not '
'using auth in the Keystone Session.')
if project_id is None:
raise ValueError('Project ID must be provided when not using auth '
'in the Keystone Session')
def _get_normalized_endpoint(self, endpoint):
if endpoint.endswith('/'):
endpoint = endpoint[:-1]
return endpoint
self._barbican_endpoint = endpoint
self._base_url = '{0}/{1}'.format(endpoint, _DEFAULT_API_VERSION)
def _get(self, href, params=None):
headers = {'Accept': 'application/json'}
@@ -216,6 +159,63 @@ class Client(object):
return message
class Client(object):
def __init__(self, session=None, *args, **kwargs):
"""
Barbican client object used to interact with barbican service.
:param session: An instance of keystoneclient.session.Session that
can be either authenticated, or not authenticated. When using
a non-authenticated Session, you must provide some additional
parameters. When no session is provided it will default to a
non-authenticated Session.
:param endpoint: Barbican endpoint url. Required when a session is not
given, or when using a non-authentciated session.
When using an authenticated session, the client will attempt
to get an endpoint from the session.
:param project_id: The project ID used for context in Barbican.
Required when a session is not given, or when using a
non-authenticated session.
When using an authenticated session, the project ID will be
provided by the authentication mechanism.
:param verify: When a session is not given, the client will create
a non-authenticated session. This parameter is passed to the
session that is created. If set to False, it allows
barbicanclient to perform "insecure" TLS (https) requests.
The server's certificate will not be verified against any
certificate authorities.
WARNING: This option should be used with caution.
:param service_type: Used as an endpoint filter when using an
authenticated keystone session. Defaults to 'key-management'.
:param service_name: Used as an endpoint filter when using an
authenticated keystone session.
:param interface: Used as an endpoint filter when using an
authenticated keystone session. Defaults to 'public'.
:param region_name: Used as an endpoint filter when using an
authenticated keystone session.
"""
LOG.debug("Creating Client object")
if not session:
session = ks_session.Session(verify=kwargs.pop('verify', True))
if session.auth is None:
if kwargs.get('endpoint') is None:
raise ValueError('Barbican endpoint url must be provided when '
'not using auth in the Keystone Session.')
if kwargs.get('project_id') is None:
raise ValueError('Project ID must be provided when not using '
'auth in the Keystone Session')
httpclient = _HTTPClient(session=session, *args, **kwargs)
self.secrets = secrets.SecretManager(httpclient)
self.orders = orders.OrderManager(httpclient)
self.containers = containers.ContainerManager(httpclient)
def env(*vars, **kwargs):
"""Search for the first defined of possibly many env vars

View File

@@ -20,7 +20,7 @@ from oslo_utils.timeutils import parse_isotime
from barbicanclient import base
from barbicanclient import formatter
from barbicanclient import secrets
from barbicanclient import secrets as secret_manager
LOG = logging.getLogger(__name__)
@@ -76,6 +76,7 @@ class Container(ContainerFormatter):
container_ref=None, created=None, updated=None, status=None,
secret_refs=None):
self._api = api
self._secret_manager = secret_manager.SecretManager(api)
self._name = name
self._container_ref = container_ref
self._secret_refs = secret_refs
@@ -109,7 +110,7 @@ class Container(ContainerFormatter):
def _fill_secrets_from_secret_refs(self):
if self._secret_refs:
self._cached_secrets = dict(
(name.lower(), self._api.secrets.get(secret_ref=secret_ref))
(name.lower(), self._secret_manager.get(secret_ref=secret_ref))
for name, secret_ref in six.iteritems(self._secret_refs)
)
@@ -165,7 +166,7 @@ class Container(ContainerFormatter):
@_immutable_after_save
def add(self, name, secret):
if not isinstance(secret, secrets.Secret):
if not isinstance(secret, secret_manager.Secret):
raise ValueError("Must provide a valid Secret object")
if name.lower() in self.secrets:
raise KeyError("A secret with this name already exists!")

View File

@@ -26,8 +26,10 @@ class TestClient(testtools.TestCase):
self.responses = self.useFixture(fixture.Fixture())
self.endpoint = 'http://localhost:9311'
self.project_id = 'project_id'
self.client = client.Client(endpoint=self.endpoint,
project_id=self.project_id)
sess = mock.MagicMock()
self.httpclient = client._HTTPClient(session=sess,
endpoint=self.endpoint,
project_id=self.project_id)
class WhenTestingClientInit(TestClient):
@@ -38,23 +40,26 @@ class WhenTestingClientInit(TestClient):
return sess
def test_can_be_used_without_a_session(self):
c = client.Client(endpoint=self.endpoint,
project_id=self.project_id)
c = client._HTTPClient(session=self._get_fake_session(),
endpoint=self.endpoint,
project_id=self.project_id)
self.assertIsNotNone(c._session)
def test_api_version_is_appended_to_endpoint(self):
c = client.Client(endpoint=self.endpoint,
project_id=self.project_id)
c = client._HTTPClient(session=self._get_fake_session(),
endpoint=self.endpoint,
project_id=self.project_id)
self.assertEqual(c._base_url, 'http://localhost:9311/v1')
def test_default_headers_are_empty(self):
c = client.Client(session=self._get_fake_session())
c = client._HTTPClient(self._get_fake_session(), self.endpoint)
self.assertIsInstance(c._default_headers, dict)
self.assertFalse(bool(c._default_headers))
def test_project_id_is_added_to_default_headers(self):
c = client.Client(endpoint=self.endpoint,
project_id=self.project_id)
c = client._HTTPClient(session=self._get_fake_session(),
endpoint=self.endpoint,
project_id=self.project_id)
self.assertIn('X-Project-Id', c._default_headers.keys())
self.assertEqual(c._default_headers['X-Project-Id'], self.project_id)
@@ -67,20 +72,26 @@ class WhenTestingClientInit(TestClient):
**{"endpoint": self.endpoint})
def test_client_strips_trailing_slash_from_endpoint(self):
c = client.Client(endpoint=self.endpoint + '/',
project_id=self.project_id)
c = client._HTTPClient(session=self._get_fake_session(),
endpoint=self.endpoint + '/',
project_id=self.project_id)
self.assertEqual(c._barbican_endpoint, self.endpoint)
def test_base_url_starts_with_endpoint_url(self):
c = client.Client(endpoint=self.endpoint, project_id=self.project_id)
c = client._HTTPClient(session=self._get_fake_session(),
endpoint=self.endpoint,
project_id=self.project_id)
self.assertTrue(c._base_url.startswith(self.endpoint))
def test_base_url_ends_with_default_api_version(self):
c = client.Client(endpoint=self.endpoint, project_id=self.project_id)
c = client._HTTPClient(session=self._get_fake_session(),
endpoint=self.endpoint,
project_id=self.project_id)
self.assertTrue(c._base_url.endswith(client._DEFAULT_API_VERSION))
def test_gets_endpoint_from_keystone_session(self):
c = client.Client(session=self._get_fake_session())
c = client._HTTPClient(session=self._get_fake_session(),
endpoint=self.endpoint)
self.assertEqual(c._barbican_endpoint, self.endpoint)
@@ -106,33 +117,33 @@ class WhenTestingClientPost(TestClientWithSession):
def setUp(self):
super(WhenTestingClientPost, self).setUp()
self.session = self._get_fake_session_with_status_code(201)
self.client = client.Client(session=self.session)
self.httpclient = client._HTTPClient(self.session, self.endpoint)
def test_post_normalizes_url_with_traling_slash(self):
self.client._post(path='secrets', data={'test_data': 'test'})
self.httpclient._post(path='secrets', data={'test_data': 'test'})
args, kwargs = self.session.post.call_args
url = args[0]
self.assertTrue(url.endswith('/'))
def test_post_includes_content_type_header_of_application_json(self):
self.client._post(path='secrets', data={'test_data': 'test'})
self.httpclient._post(path='secrets', data={'test_data': 'test'})
args, kwargs = self.session.post.call_args
headers = kwargs.get('headers')
self.assertIn('Content-Type', headers.keys())
self.assertEqual(headers['Content-Type'], 'application/json')
def test_post_includes_default_headers(self):
self.client._default_headers = {'Test-Default-Header': 'test'}
self.client._post(path='secrets', data={'test_data': 'test'})
self.httpclient._default_headers = {'Test-Default-Header': 'test'}
self.httpclient._post(path='secrets', data={'test_data': 'test'})
args, kwargs = self.session.post.call_args
headers = kwargs.get('headers')
self.assertIn('Test-Default-Header', headers.keys())
def test_post_checks_status_code(self):
self.client._check_status_code = mock.MagicMock()
self.client._post(path='secrets', data={'test_data': 'test'})
self.httpclient._check_status_code = mock.MagicMock()
self.httpclient._post(path='secrets', data={'test_data': 'test'})
resp = self.session.post()
self.client._check_status_code.assert_called_with(resp)
self.httpclient._check_status_code.assert_called_with(resp)
class WhenTestingClientGet(TestClientWithSession):
@@ -140,65 +151,65 @@ class WhenTestingClientGet(TestClientWithSession):
def setUp(self):
super(WhenTestingClientGet, self).setUp()
self.session = self._get_fake_session_with_status_code(200)
self.client = client.Client(session=self.session)
self.httpclient = client._HTTPClient(self.session, self.endpoint)
self.headers = dict()
self.href = 'http://test_href'
def test_get_uses_href_as_is(self):
self.client._get(self.href)
self.httpclient._get(self.href)
args, kwargs = self.session.get.call_args
url = args[0]
self.assertEqual(url, self.href)
def test_get_passes_params(self):
params = object()
self.client._get(self.href, params=params)
self.httpclient._get(self.href, params=params)
args, kwargs = self.session.get.call_args
passed_params = kwargs.get('params')
self.assertIs(params, passed_params)
def test_get_includes_accept_header_of_application_json(self):
self.client._get(self.href)
self.httpclient._get(self.href)
args, kwargs = self.session.get.call_args
headers = kwargs.get('headers')
self.assertIn('Accept', headers.keys())
self.assertEqual(headers['Accept'], 'application/json')
def test_get_includes_default_headers(self):
self.client._default_headers = {'Test-Default-Header': 'test'}
self.client._get(self.href)
self.httpclient._default_headers = {'Test-Default-Header': 'test'}
self.httpclient._get(self.href)
args, kwargs = self.session.get.call_args
headers = kwargs.get('headers')
self.assertIn('Test-Default-Header', headers.keys())
def test_get_checks_status_code(self):
self.client._check_status_code = mock.MagicMock()
self.client._get(self.href)
self.httpclient._check_status_code = mock.MagicMock()
self.httpclient._get(self.href)
resp = self.session.get()
self.client._check_status_code.assert_called_with(resp)
self.httpclient._check_status_code.assert_called_with(resp)
def test_get_raw_uses_href_as_is(self):
self.client._get_raw(self.href, self.headers)
self.httpclient._get_raw(self.href, self.headers)
args, kwargs = self.session.get.call_args
url = args[0]
self.assertEqual(url, self.href)
def test_get_raw_passes_headers(self):
self.client._get_raw(self.href, self.headers)
self.httpclient._get_raw(self.href, self.headers)
args, kwargs = self.session.get.call_args
headers = kwargs.get('headers')
self.assertIs(headers, self.headers)
def test_get_raw_includes_default_headers(self):
self.client._default_headers = {'Test-Default-Header': 'test'}
self.client._get_raw(self.href, self.headers)
self.httpclient._default_headers = {'Test-Default-Header': 'test'}
self.httpclient._get_raw(self.href, self.headers)
self.assertIn('Test-Default-Header', self.headers.keys())
def test_get_raw_checks_status_code(self):
self.client._check_status_code = mock.MagicMock()
self.client._get_raw(self.href, self.headers)
self.httpclient._check_status_code = mock.MagicMock()
self.httpclient._get_raw(self.href, self.headers)
resp = self.session.get()
self.client._check_status_code.assert_called_with(resp)
self.httpclient._check_status_code.assert_called_with(resp)
class WhenTestingClientDelete(TestClientWithSession):
@@ -206,34 +217,35 @@ class WhenTestingClientDelete(TestClientWithSession):
def setUp(self):
super(WhenTestingClientDelete, self).setUp()
self.session = self._get_fake_session_with_status_code(200)
self.client = client.Client(session=self.session)
self.httpclient = client._HTTPClient(session=self.session,
endpoint=self.endpoint)
self.href = 'http://test_href'
def test_delete_uses_href_as_is(self):
self.client._delete(self.href)
self.httpclient._delete(self.href)
args, kwargs = self.session.delete.call_args
url = args[0]
self.assertEqual(url, self.href)
def test_delete_passes_json(self):
json = '{"test": "test"}'
self.client._delete(self.href, json=json)
self.httpclient._delete(self.href, json=json)
args, kwargs = self.session.delete.call_args
passed_json = kwargs.get('json')
self.assertEqual(passed_json, json)
def test_delete_includes_default_headers(self):
self.client._default_headers = {'Test-Default-Header': 'test'}
self.client._delete(self.href)
self.httpclient._default_headers = {'Test-Default-Header': 'test'}
self.httpclient._delete(self.href)
args, kwargs = self.session.delete.call_args
headers = kwargs.get('headers')
self.assertIn('Test-Default-Header', headers.keys())
def test_delete_checks_status_code(self):
self.client._check_status_code = mock.MagicMock()
self.client._delete(self.href)
self.httpclient._check_status_code = mock.MagicMock()
self.httpclient._delete(self.href)
resp = self.session.get()
self.client._check_status_code.assert_called_with(resp)
self.httpclient._check_status_code.assert_called_with(resp)
class WhenTestingCheckStatusCodes(TestClient):
@@ -241,20 +253,21 @@ class WhenTestingCheckStatusCodes(TestClient):
def test_raises_http_auth_error_for_401_response(self):
resp = mock.MagicMock()
resp.status_code = 401
self.assertRaises(client.HTTPAuthError, self.client._check_status_code,
self.assertRaises(client.HTTPAuthError,
self.httpclient._check_status_code,
resp)
def test_raises_http_server_error_for_500_response(self):
resp = mock.MagicMock()
resp.status_code = 500
self.assertRaises(client.HTTPServerError,
self.client._check_status_code, resp)
self.httpclient._check_status_code, resp)
def test_raises_http_client_error_for_400_response(self):
resp = mock.MagicMock()
resp.status_code = 400
self.assertRaises(client.HTTPClientError,
self.client._check_status_code, resp)
self.httpclient._check_status_code, resp)
class WhenTestingGetErrorMessage(TestClient):
@@ -262,14 +275,14 @@ class WhenTestingGetErrorMessage(TestClient):
def test_gets_error_message_from_title_in_json(self):
resp = mock.MagicMock()
resp.json.return_value = {'title': 'test_text'}
msg = self.client._get_error_message(resp)
msg = self.httpclient._get_error_message(resp)
self.assertEqual(msg, 'test_text')
def test_gets_error_message_from_content_when_no_json(self):
resp = mock.MagicMock()
resp.json.side_effect = ValueError()
resp.content = content = 'content'
msg = self.client._get_error_message(resp)
msg = self.httpclient._get_error_message(resp)
self.assertEqual(msg, content)

View File

@@ -28,7 +28,8 @@ class ContainerData(object):
self.type = 'generic'
self.secret = mock.Mock(spec=secrets.Secret)
self.secret.__bases__ = (secrets.Secret,)
self.secret.secret_ref = 'http://a/b/1'
self.secret.secret_ref = ('http://barbican/v1/secrets/'
'a73b62e4-eee2-4169-9a14-b8bb4da71d87')
self.secret.name = 'thing1'
self.generic_secret_refs = {self.secret.name: self.secret.secret_ref}
self.generic_secret_refs_json = [{'name': self.secret.name,
@@ -92,9 +93,6 @@ class WhenTestingContainers(test_client.BaseEntityResource):
self._setUp('containers')
self.container = ContainerData()
self.client.secrets = mock.MagicMock()
self.client.secrets.get.return_value = self.container.secret
self.client.secrets._api = self.client
self.manager = self.client.containers
self.consumers_post_resource = self.entity_href + '/consumers/'