diff -Nru python-keystoneclient-3.1.0/debian/changelog python-keystoneclient-3.2.0/debian/changelog --- python-keystoneclient-3.1.0/debian/changelog 2016-05-31 20:10:27.000000000 +0000 +++ python-keystoneclient-3.2.0/debian/changelog 2016-07-13 17:36:09.000000000 +0000 @@ -1,4 +1,18 @@ -python-keystoneclient (1:3.1.0-0ubuntu1) yakkety; urgency=medium +python-keystoneclient (1:3.2.0-1~ubuntu16.10.1~ppa201607131336) yakkety; urgency=medium + + * No-change backport to yakkety + + -- Corey Bryant Wed, 13 Jul 2016 13:36:09 -0400 + +python-keystoneclient (1:3.2.0-1) experimental; urgency=medium + + * New upstream release. + * Fixed (build-)depends for this release. + * Removed import-warnings.patch applied upstream. + + -- Thomas Goirand Tue, 12 Jul 2016 14:57:18 +0000 + +python-keystoneclient (1:3.1.0-1) experimental; urgency=medium [ Ondřej Nový ] * Standards-Version is 3.9.8 now (no change) @@ -12,10 +26,11 @@ from upstream in favor of the openstack CLI. * d/python(3)-keystoneclient.*, d/rules, d/tests: Drop keystone CLI and man page. They've been removed from upstream in favor of the openstack CLI. - * d/rules: Set html_last_updated_fmt for doc/source/conf.py when building docs. + * d/rules: Set html_last_updated_fmt for doc/source/conf.py when building + docs. * d/p/import-warnings.patch: import warnings in doc/source/conf.py. - -- Corey Bryant Tue, 31 May 2016 14:21:18 -0400 + -- Thomas Goirand Sun, 05 Jun 2016 16:23:00 +0000 python-keystoneclient (1:2.3.1-2) unstable; urgency=medium diff -Nru python-keystoneclient-3.1.0/debian/control python-keystoneclient-3.2.0/debian/control --- python-keystoneclient-3.1.0/debian/control 2016-05-31 20:10:14.000000000 +0000 +++ python-keystoneclient-3.2.0/debian/control 2016-07-13 16:27:30.000000000 +0000 @@ -1,80 +1,79 @@ Source: python-keystoneclient Section: python Priority: extra -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: PKG OpenStack +Maintainer: PKG OpenStack Uploaders: Thomas Goirand , - Corey Bryant + Corey Bryant , Build-Depends: debhelper (>= 9), dh-python, openstack-pkg-tools, python-all, python-pbr (>= 1.8), python-setuptools, + python-sphinx, python3-all, python3-pbr (>= 1.8), - python-sphinx, Build-Depends-Indep: bandit, memcached, python-bandit, python-coverage, python-debtcollector, - python-fixtures (>= 1.3.1), + python-fixtures (>= 3.0.0), python-hacking (>= 0.10.0), - python-iso8601 (>= 0.1.11), python-keyring (>= 5.5.1), - python-keystoneauth1 (>= 2.1.0), + python-keystoneauth1 (>= 2.7.0), python-lxml, python-memcache (>= 1.56), - python-mock (>= 1.3), + python-mock (>= 2.0), python-mox3, python-oauthlib, - python-oslo.config (>= 1:3.9.0), + python-oslo.config (>= 1:3.10.0), python-oslo.i18n (>= 2.1.0), python-oslo.serialization (>= 1.10.0), - python-oslo.utils (>= 3.5.0), + python-oslo.utils (>= 3.14.0), python-oslosphinx (>= 2.5.0), python-oslotest (>= 1.10.0), python-positional, python-pysaml2 (>= 3.0.0), python-requests (>= 2.10.0), - python-requests-mock (>= 0.7.0), + python-requests-mock (>= 1.0.0), python-six (>= 1.9.0), python-stevedore (>= 1.10.0), + python-tempest (>= 1:12.1.0), python-testresources, python-testscenarios, python-testtools (>= 1.4.0), python3-bandit, python3-coverage, python3-debtcollector, - python3-fixtures (>= 1.3.1), - python3-iso8601 (>= 0.1.11), + python3-fixtures (>= 3.0.0), python3-hacking (>= 0.10.0), python3-keyring (>= 5.5.1), - python3-keystoneauth1 (>= 2.1.0), + python3-keystoneauth1 (>= 2.7.0), python3-lxml, python3-memcache (>= 1.56), - python3-mock (>= 1.3), + python3-mock (>= 2.0), python3-mox3, python3-oauthlib, - python3-oslo.config (>= 1:3.9.0), + python3-oslo.config (>= 1:3.10.0), python3-oslo.i18n (>= 2.1.0), python3-oslo.serialization (>= 1.10.0), - python3-oslo.utils (>= 3.5.0), + python3-oslo.utils (>= 3.14.0), python3-oslotest (>= 1.10.0), python3-pep8, python3-positional, python3-pysaml2 (>= 3.0.0), python3-requests (>= 2.10.0), - python3-requests-mock (>= 0.7.0), + python3-requests-mock (>= 1.0.0), python3-six (>= 1.9.0), python3-stevedore (>= 1.10.0), python3-subunit, + python3-tempest (>= 1:12.1.0), python3-testresources, python3-testscenarios, python3-testtools (>= 1.4.0), subunit, - tempest (>= 11.0.0), + tempest (>= 1:12.1.0), testrepository, Standards-Version: 3.9.8 Homepage: https://github.com/openstack/python-keystoneclient @@ -85,14 +84,13 @@ Package: python-keystoneclient Architecture: all Depends: python-debtcollector, - python-iso8601 (>= 0.1.11), python-keyring (>= 5.5.1), - python-keystoneauth1 (>= 2.1.0), + python-keystoneauth1 (>= 2.7.0), python-lxml, - python-oslo.config (>= 1:3.9.0), + python-oslo.config (>= 1:3.10.0), python-oslo.i18n (>= 2.1.0), python-oslo.serialization (>= 1.10.0), - python-oslo.utils (>= 3.5.0), + python-oslo.utils (>= 3.14.0), python-pbr (>= 1.8), python-positional, python-requests (>= 2.10.0), @@ -120,14 +118,13 @@ Package: python3-keystoneclient Architecture: all Depends: python3-debtcollector, - python3-iso8601 (>= 0.1.11), python3-keyring (>= 5.5.1), - python3-keystoneauth1 (>= 2.1.0), + python3-keystoneauth1 (>= 2.7.0), python3-lxml, - python3-oslo.config (>= 1:3.9.0), + python3-oslo.config (>= 1:3.10.0), python3-oslo.i18n (>= 2.1.0), python3-oslo.serialization (>= 1.10.0), - python3-oslo.utils (>= 3.5.0), + python3-oslo.utils (>= 3.14.0), python3-pbr (>= 1.8), python3-positional, python3-requests (>= 2.10.0), diff -Nru python-keystoneclient-3.1.0/debian/patches/import-warnings.patch python-keystoneclient-3.2.0/debian/patches/import-warnings.patch --- python-keystoneclient-3.1.0/debian/patches/import-warnings.patch 2016-05-31 19:49:08.000000000 +0000 +++ python-keystoneclient-3.2.0/debian/patches/import-warnings.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -From 9bc94cc0482f0c4034dc8deaa5f6d5e6f1c611f6 Mon Sep 17 00:00:00 2001 -From: Corey Bryant -Date: Tue, 31 May 2016 15:45:17 -0400 -Subject: [PATCH] import warnings in doc/source/conf.py - -Change-Id: If14c02e156f7fe6884ad8de4b80869b2d01de4d7 -Closes-Bug: 1587625 ---- - doc/source/conf.py | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/doc/source/conf.py b/doc/source/conf.py -index 6699b82..1d5cb10 100644 ---- a/doc/source/conf.py -+++ b/doc/source/conf.py -@@ -17,6 +17,7 @@ from __future__ import unicode_literals - import os - import subprocess - import sys -+import warnings - - import pbr.version - --- -2.8.1 - diff -Nru python-keystoneclient-3.1.0/debian/patches/series python-keystoneclient-3.2.0/debian/patches/series --- python-keystoneclient-3.1.0/debian/patches/series 2016-05-31 19:51:22.000000000 +0000 +++ python-keystoneclient-3.2.0/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -import-warnings.patch diff -Nru python-keystoneclient-3.1.0/doc/source/conf.py python-keystoneclient-3.2.0/doc/source/conf.py --- python-keystoneclient-3.1.0/doc/source/conf.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/doc/source/conf.py 2016-07-01 04:24:16.000000000 +0000 @@ -17,6 +17,7 @@ import os import subprocess import sys +import warnings import pbr.version diff -Nru python-keystoneclient-3.1.0/keystoneclient/fixture/discovery.py python-keystoneclient-3.2.0/keystoneclient/fixture/discovery.py --- python-keystoneclient-3.1.0/keystoneclient/fixture/discovery.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/fixture/discovery.py 2016-07-01 04:24:16.000000000 +0000 @@ -10,252 +10,29 @@ # License for the specific language governing permissions and limitations # under the License. -import datetime +from keystoneauth1.fixture import discovery -from oslo_utils import timeutils -from positional import positional - -from keystoneclient import utils __all__ = ('DiscoveryList', 'V2Discovery', 'V3Discovery', ) -_DEFAULT_DAYS_AGO = 30 +V2Discovery = discovery.V2Discovery +"""A Version element for a V2 identity service endpoint. + +An alias of :py:exc:`keystoneauth1.fixture.discovery.V2Discovery` +""" + +V3Discovery = discovery.V3Discovery +"""A Version element for a V3 identity service endpoint. -class DiscoveryBase(dict): - """The basic version discovery structure. +An alias of :py:exc:`keystoneauth1.fixture.discovery.V3Discovery` +""" - All version discovery elements should have access to these values. +DiscoveryList = discovery.DiscoveryList +"""A List of version elements. - :param string id: The version id for this version entry. - :param string status: The status of this entry. - :param DateTime updated: When the API was last updated. - """ - - @positional() - def __init__(self, id, status=None, updated=None): - super(DiscoveryBase, self).__init__() - - self.id = id - self.status = status or 'stable' - self.updated = updated or (timeutils.utcnow() - - datetime.timedelta(days=_DEFAULT_DAYS_AGO)) - - @property - def id(self): - return self.get('id') - - @id.setter - def id(self, value): - self['id'] = value - - @property - def status(self): - return self.get('status') - - @status.setter - def status(self, value): - self['status'] = value - - @property - def links(self): - return self.setdefault('links', []) - - @property - def updated_str(self): - return self.get('updated') - - @updated_str.setter - def updated_str(self, value): - self['updated'] = value - - @property - def updated(self): - return timeutils.parse_isotime(self.updated_str) - - @updated.setter - def updated(self, value): - self.updated_str = utils.isotime(value) - - @positional() - def add_link(self, href, rel='self', type=None): - link = {'href': href, 'rel': rel} - if type: - link['type'] = type - self.links.append(link) - return link - - @property - def media_types(self): - return self.setdefault('media-types', []) - - @positional(1) - def add_media_type(self, base, type): - mt = {'base': base, 'type': type} - self.media_types.append(mt) - return mt - - -class V2Discovery(DiscoveryBase): - """A Version element for a V2 identity service endpoint. - - Provides some default values and helper methods for creating a v2.0 - endpoint version structure. Clients should use this instead of creating - their own structures. - - :param string href: The url that this entry should point to. - :param string id: The version id that should be reported. (optional) - Defaults to 'v2.0'. - :param bool html: Add HTML describedby links to the structure. - :param bool pdf: Add PDF describedby links to the structure. - - """ - - _DESC_URL = 'http://docs.openstack.org/api/openstack-identity-service/2.0/' - - @positional() - def __init__(self, href, id=None, html=True, pdf=True, **kwargs): - super(V2Discovery, self).__init__(id or 'v2.0', **kwargs) - - self.add_link(href) - - if html: - self.add_html_description() - if pdf: - self.add_pdf_description() - - def add_html_description(self): - """Add the HTML described by links. - - The standard structure includes a link to a HTML document with the - API specification. Add it to this entry. - """ - self.add_link(href=self._DESC_URL + 'content', - rel='describedby', - type='text/html') - - def add_pdf_description(self): - """Add the PDF described by links. - - The standard structure includes a link to a PDF document with the - API specification. Add it to this entry. - """ - self.add_link(href=self._DESC_URL + 'identity-dev-guide-2.0.pdf', - rel='describedby', - type='application/pdf') - - -class V3Discovery(DiscoveryBase): - """A Version element for a V3 identity service endpoint. - - Provides some default values and helper methods for creating a v3 - endpoint version structure. Clients should use this instead of creating - their own structures. - - :param href: The url that this entry should point to. - :param string id: The version id that should be reported. (optional) - Defaults to 'v3.0'. - :param bool json: Add JSON media-type elements to the structure. - :param bool xml: Add XML media-type elements to the structure. - """ - - @positional() - def __init__(self, href, id=None, json=True, xml=True, **kwargs): - super(V3Discovery, self).__init__(id or 'v3.0', **kwargs) - - self.add_link(href) - - if json: - self.add_json_media_type() - if xml: - self.add_xml_media_type() - - def add_json_media_type(self): - """Add the JSON media-type links. - - The standard structure includes a list of media-types that the endpoint - supports. Add JSON to the list. - """ - self.add_media_type(base='application/json', - type='application/vnd.openstack.identity-v3+json') - - def add_xml_media_type(self): - """Add the XML media-type links. - - The standard structure includes a list of media-types that the endpoint - supports. Add XML to the list. - """ - self.add_media_type(base='application/xml', - type='application/vnd.openstack.identity-v3+xml') - - -class DiscoveryList(dict): - """A List of version elements. - - Creates a correctly structured list of identity service endpoints for - use in testing with discovery. - - :param string href: The url that this should be based at. - :param bool v2: Add a v2 element. - :param bool v3: Add a v3 element. - :param string v2_status: The status to use for the v2 element. - :param DateTime v2_updated: The update time to use for the v2 element. - :param bool v2_html: True to add a html link to the v2 element. - :param bool v2_pdf: True to add a pdf link to the v2 element. - :param string v3_status: The status to use for the v3 element. - :param DateTime v3_updated: The update time to use for the v3 element. - :param bool v3_json: True to add a html link to the v2 element. - :param bool v3_xml: True to add a pdf link to the v2 element. - """ - - TEST_URL = 'http://keystone.host:5000/' - - @positional(2) - def __init__(self, href=None, v2=True, v3=True, v2_id=None, v3_id=None, - v2_status=None, v2_updated=None, v2_html=True, v2_pdf=True, - v3_status=None, v3_updated=None, v3_json=True, v3_xml=True): - super(DiscoveryList, self).__init__(versions={'values': []}) - - href = href or self.TEST_URL - - if v2: - v2_href = href.rstrip('/') + '/v2.0' - self.add_v2(v2_href, id=v2_id, status=v2_status, - updated=v2_updated, html=v2_html, pdf=v2_pdf) - - if v3: - v3_href = href.rstrip('/') + '/v3' - self.add_v3(v3_href, id=v3_id, status=v3_status, - updated=v3_updated, json=v3_json, xml=v3_xml) - - @property - def versions(self): - return self['versions']['values'] - - def add_version(self, version): - """Add a new version structure to the list. - - :param dict version: A new version structure to add to the list. - """ - self.versions.append(version) - - def add_v2(self, href, **kwargs): - """Add a v2 version to the list. - - The parameters are the same as V2Discovery. - """ - obj = V2Discovery(href, **kwargs) - self.add_version(obj) - return obj - - def add_v3(self, href, **kwargs): - """Add a v3 version to the list. - - The parameters are the same as V3Discovery. - """ - obj = V3Discovery(href, **kwargs) - self.add_version(obj) - return obj +An alias of :py:exc:`keystoneauth1.fixture.discovery.DiscoveryList` +""" diff -Nru python-keystoneclient-3.1.0/keystoneclient/fixture/exception.py python-keystoneclient-3.2.0/keystoneclient/fixture/exception.py --- python-keystoneclient-3.1.0/keystoneclient/fixture/exception.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/fixture/exception.py 2016-07-01 04:24:16.000000000 +0000 @@ -10,11 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. +from keystoneauth1.fixture import exception -class FixtureValidationError(Exception): - """The token you created is not legitimate. - The data contained in the token that was generated is not valid and would - not have been returned from a keystone server. You should not do testing - with this token. - """ +FixtureValidationError = exception.FixtureValidationError +"""The token you created is not legitimate. + +An alias of :py:exc:`keystoneauth1.fixture.exception.FixtureValidationError`` +""" diff -Nru python-keystoneclient-3.1.0/keystoneclient/fixture/__init__.py python-keystoneclient-3.2.0/keystoneclient/fixture/__init__.py --- python-keystoneclient-3.1.0/keystoneclient/fixture/__init__.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/fixture/__init__.py 2016-07-01 04:24:16.000000000 +0000 @@ -20,14 +20,27 @@ imported into the main client (keystoneclient or other). Because of this there may be dependencies from this module on libraries that are only available in testing. + +.. warning:: + + The keystoneclient.fixture package is deprecated in favor of + keystoneauth1.fixture and will not be supported. + """ +import warnings + from keystoneclient.fixture.discovery import * # noqa from keystoneclient.fixture import exception from keystoneclient.fixture import v2 from keystoneclient.fixture import v3 +warnings.warn( + "The keystoneclient.fixture package is deprecated in favor of " + "keystoneauth1.fixture and will not be supported.", DeprecationWarning) + + FixtureValidationError = exception.FixtureValidationError V2Token = v2.Token V3Token = v3.Token diff -Nru python-keystoneclient-3.1.0/keystoneclient/fixture/v2.py python-keystoneclient-3.2.0/keystoneclient/fixture/v2.py --- python-keystoneclient-3.1.0/keystoneclient/fixture/v2.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/fixture/v2.py 2016-07-01 04:24:16.000000000 +0000 @@ -10,237 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. -import datetime -import uuid +from keystoneauth1.fixture import v2 -from oslo_utils import timeutils -from keystoneclient.fixture import exception -from keystoneclient import utils +Token = v2.Token +"""A V2 Keystone token that can be used for testing. - -class _Service(dict): - - def add_endpoint(self, public, admin=None, internal=None, - tenant_id=None, region=None, id=None): - data = {'tenantId': tenant_id or uuid.uuid4().hex, - 'publicURL': public, - 'adminURL': admin or public, - 'internalURL': internal or public, - 'region': region, - 'id': id or uuid.uuid4().hex} - - self.setdefault('endpoints', []).append(data) - return data - - -class Token(dict): - """A V2 Keystone token that can be used for testing. - - This object is designed to allow clients to generate a correct V2 token for - use in there test code. It should prevent clients from having to know the - correct token format and allow them to test the portions of token handling - that matter to them and not copy and paste sample. - """ - - def __init__(self, token_id=None, expires=None, issued=None, - tenant_id=None, tenant_name=None, user_id=None, - user_name=None, trust_id=None, trustee_user_id=None, - audit_id=None, audit_chain_id=None): - super(Token, self).__init__() - - self.token_id = token_id or uuid.uuid4().hex - self.user_id = user_id or uuid.uuid4().hex - self.user_name = user_name or uuid.uuid4().hex - self.audit_id = audit_id or uuid.uuid4().hex - - if not issued: - issued = timeutils.utcnow() - datetime.timedelta(minutes=2) - if not expires: - expires = issued + datetime.timedelta(hours=1) - - try: - self.issued = issued - except (TypeError, AttributeError): - # issued should be able to be passed as a string so ignore - self.issued_str = issued - - try: - self.expires = expires - except (TypeError, AttributeError): - # expires should be able to be passed as a string so ignore - self.expires_str = expires - - if tenant_id or tenant_name: - self.set_scope(tenant_id, tenant_name) - - if trust_id or trustee_user_id: - # the trustee_user_id will generally be the same as the user_id as - # the token is being issued to the trustee - self.set_trust(id=trust_id, - trustee_user_id=trustee_user_id or user_id) - - if audit_chain_id: - self.audit_chain_id = audit_chain_id - - @property - def root(self): - return self.setdefault('access', {}) - - @property - def _token(self): - return self.root.setdefault('token', {}) - - @property - def token_id(self): - return self._token['id'] - - @token_id.setter - def token_id(self, value): - self._token['id'] = value - - @property - def expires_str(self): - return self._token['expires'] - - @expires_str.setter - def expires_str(self, value): - self._token['expires'] = value - - @property - def expires(self): - return timeutils.parse_isotime(self.expires_str) - - @expires.setter - def expires(self, value): - self.expires_str = utils.isotime(value) - - @property - def issued_str(self): - return self._token['issued_at'] - - @issued_str.setter - def issued_str(self, value): - self._token['issued_at'] = value - - @property - def issued(self): - return timeutils.parse_isotime(self.issued_str) - - @issued.setter - def issued(self, value): - self.issued_str = utils.isotime(value) - - @property - def _user(self): - return self.root.setdefault('user', {}) - - @property - def user_id(self): - return self._user['id'] - - @user_id.setter - def user_id(self, value): - self._user['id'] = value - - @property - def user_name(self): - return self._user['name'] - - @user_name.setter - def user_name(self, value): - self._user['name'] = value - - @property - def tenant_id(self): - return self._token.get('tenant', {}).get('id') - - @tenant_id.setter - def tenant_id(self, value): - self._token.setdefault('tenant', {})['id'] = value - - @property - def tenant_name(self): - return self._token.get('tenant', {}).get('name') - - @tenant_name.setter - def tenant_name(self, value): - self._token.setdefault('tenant', {})['name'] = value - - @property - def _metadata(self): - return self.root.setdefault('metadata', {}) - - @property - def trust_id(self): - return self.root.setdefault('trust', {}).get('id') - - @trust_id.setter - def trust_id(self, value): - self.root.setdefault('trust', {})['id'] = value - - @property - def trustee_user_id(self): - return self.root.setdefault('trust', {}).get('trustee_user_id') - - @trustee_user_id.setter - def trustee_user_id(self, value): - self.root.setdefault('trust', {})['trustee_user_id'] = value - - @property - def audit_id(self): - try: - return self._token.get('audit_ids', [])[0] - except IndexError: - return None - - @audit_id.setter - def audit_id(self, value): - audit_chain_id = self.audit_chain_id - lval = [value] if audit_chain_id else [value, audit_chain_id] - self._token['audit_ids'] = lval - - @property - def audit_chain_id(self): - try: - return self._token.get('audit_ids', [])[1] - except IndexError: - return None - - @audit_chain_id.setter - def audit_chain_id(self, value): - self._token['audit_ids'] = [self.audit_id, value] - - def validate(self): - scoped = 'tenant' in self.token - catalog = self.root.get('serviceCatalog') - - if catalog and not scoped: - msg = 'You cannot have a service catalog on an unscoped token' - raise exception.FixtureValidationError(msg) - - if scoped and not self.user.get('roles'): - msg = 'You must have roles on a token to scope it' - raise exception.FixtureValidationError(msg) - - def add_role(self, name=None, id=None): - id = id or uuid.uuid4().hex - name = name or uuid.uuid4().hex - roles = self._user.setdefault('roles', []) - roles.append({'name': name}) - self._metadata.setdefault('roles', []).append(id) - return {'id': id, 'name': name} - - def add_service(self, type, name=None): - name = name or uuid.uuid4().hex - service = _Service(name=name, type=type) - self.root.setdefault('serviceCatalog', []).append(service) - return service - - def set_scope(self, id=None, name=None): - self.tenant_id = id or uuid.uuid4().hex - self.tenant_name = name or uuid.uuid4().hex - - def set_trust(self, id=None, trustee_user_id=None): - self.trust_id = id or uuid.uuid4().hex - self.trustee_user_id = trustee_user_id or uuid.uuid4().hex +An alias of :py:exc:`keystoneauth1.fixture.v2.Token` +""" diff -Nru python-keystoneclient-3.1.0/keystoneclient/fixture/v3.py python-keystoneclient-3.2.0/keystoneclient/fixture/v3.py --- python-keystoneclient-3.1.0/keystoneclient/fixture/v3.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/fixture/v3.py 2016-07-01 04:24:16.000000000 +0000 @@ -10,413 +10,17 @@ # License for the specific language governing permissions and limitations # under the License. -import datetime -import uuid +from keystoneauth1.fixture import v3 -from oslo_utils import timeutils -from keystoneclient.fixture import exception -from keystoneclient import utils +Token = v3.Token +"""A V3 Keystone token that can be used for testing. +An alias of :py:exc:`keystoneauth1.fixture.v3.Token` +""" -class _Service(dict): - """One of the services that exist in the catalog. +V3FederationToken = v3.V3FederationToken +"""A V3 Keystone Federation token that can be used for testing. - You use this by adding a service to a token which returns an instance of - this object and then you can add_endpoints to the service. - """ - - def add_endpoint(self, interface, url, region=None, id=None): - data = {'id': id or uuid.uuid4().hex, - 'interface': interface, - 'url': url, - 'region': region, - 'region_id': region} - self.setdefault('endpoints', []).append(data) - return data - - def add_standard_endpoints(self, public=None, admin=None, internal=None, - region=None): - ret = [] - - if public: - ret.append(self.add_endpoint('public', public, region=region)) - if admin: - ret.append(self.add_endpoint('admin', admin, region=region)) - if internal: - ret.append(self.add_endpoint('internal', internal, region=region)) - - return ret - - -class Token(dict): - """A V3 Keystone token that can be used for testing. - - This object is designed to allow clients to generate a correct V3 token for - use in there test code. It should prevent clients from having to know the - correct token format and allow them to test the portions of token handling - that matter to them and not copy and paste sample. - """ - - def __init__(self, expires=None, issued=None, user_id=None, user_name=None, - user_domain_id=None, user_domain_name=None, methods=None, - project_id=None, project_name=None, project_domain_id=None, - project_domain_name=None, domain_id=None, domain_name=None, - trust_id=None, trust_impersonation=None, trustee_user_id=None, - trustor_user_id=None, oauth_access_token_id=None, - oauth_consumer_id=None, audit_id=None, audit_chain_id=None): - super(Token, self).__init__() - - self.user_id = user_id or uuid.uuid4().hex - self.user_name = user_name or uuid.uuid4().hex - self.user_domain_id = user_domain_id or uuid.uuid4().hex - self.user_domain_name = user_domain_name or uuid.uuid4().hex - self.audit_id = audit_id or uuid.uuid4().hex - - if not methods: - methods = ['password'] - self.methods.extend(methods) - - if not issued: - issued = timeutils.utcnow() - datetime.timedelta(minutes=2) - - try: - self.issued = issued - except (TypeError, AttributeError): - # issued should be able to be passed as a string so ignore - self.issued_str = issued - - if not expires: - expires = self.issued + datetime.timedelta(hours=1) - - try: - self.expires = expires - except (TypeError, AttributeError): - # expires should be able to be passed as a string so ignore - self.expires_str = expires - - if (project_id or project_name or - project_domain_id or project_domain_name): - self.set_project_scope(id=project_id, - name=project_name, - domain_id=project_domain_id, - domain_name=project_domain_name) - - if domain_id or domain_name: - self.set_domain_scope(id=domain_id, name=domain_name) - - if (trust_id or (trust_impersonation is not None) or - trustee_user_id or trustor_user_id): - self.set_trust_scope(id=trust_id, - impersonation=trust_impersonation, - trustee_user_id=trustee_user_id, - trustor_user_id=trustor_user_id) - - if oauth_access_token_id or oauth_consumer_id: - self.set_oauth(access_token_id=oauth_access_token_id, - consumer_id=oauth_consumer_id) - - if audit_chain_id: - self.audit_chain_id = audit_chain_id - - @property - def root(self): - return self.setdefault('token', {}) - - @property - def expires_str(self): - return self.root.get('expires_at') - - @expires_str.setter - def expires_str(self, value): - self.root['expires_at'] = value - - @property - def expires(self): - return timeutils.parse_isotime(self.expires_str) - - @expires.setter - def expires(self, value): - self.expires_str = utils.isotime(value, subsecond=True) - - @property - def issued_str(self): - return self.root.get('issued_at') - - @issued_str.setter - def issued_str(self, value): - self.root['issued_at'] = value - - @property - def issued(self): - return timeutils.parse_isotime(self.issued_str) - - @issued.setter - def issued(self, value): - self.issued_str = utils.isotime(value, subsecond=True) - - @property - def _user(self): - return self.root.setdefault('user', {}) - - @property - def user_id(self): - return self._user.get('id') - - @user_id.setter - def user_id(self, value): - self._user['id'] = value - - @property - def user_name(self): - return self._user.get('name') - - @user_name.setter - def user_name(self, value): - self._user['name'] = value - - @property - def _user_domain(self): - return self._user.setdefault('domain', {}) - - @property - def user_domain_id(self): - return self._user_domain.get('id') - - @user_domain_id.setter - def user_domain_id(self, value): - self._user_domain['id'] = value - - @property - def user_domain_name(self): - return self._user_domain.get('name') - - @user_domain_name.setter - def user_domain_name(self, value): - self._user_domain['name'] = value - - @property - def methods(self): - return self.root.setdefault('methods', []) - - @property - def project_id(self): - return self.root.get('project', {}).get('id') - - @project_id.setter - def project_id(self, value): - self.root.setdefault('project', {})['id'] = value - - @property - def project_name(self): - return self.root.get('project', {}).get('name') - - @project_name.setter - def project_name(self, value): - self.root.setdefault('project', {})['name'] = value - - @property - def project_domain_id(self): - return self.root.get('project', {}).get('domain', {}).get('id') - - @project_domain_id.setter - def project_domain_id(self, value): - project = self.root.setdefault('project', {}) - project.setdefault('domain', {})['id'] = value - - @property - def project_domain_name(self): - return self.root.get('project', {}).get('domain', {}).get('name') - - @project_domain_name.setter - def project_domain_name(self, value): - project = self.root.setdefault('project', {}) - project.setdefault('domain', {})['name'] = value - - @property - def domain_id(self): - return self.root.get('domain', {}).get('id') - - @domain_id.setter - def domain_id(self, value): - self.root.setdefault('domain', {})['id'] = value - - @property - def domain_name(self): - return self.root.get('domain', {}).get('name') - - @domain_name.setter - def domain_name(self, value): - self.root.setdefault('domain', {})['name'] = value - - @property - def trust_id(self): - return self.root.get('OS-TRUST:trust', {}).get('id') - - @trust_id.setter - def trust_id(self, value): - self.root.setdefault('OS-TRUST:trust', {})['id'] = value - - @property - def trust_impersonation(self): - return self.root.get('OS-TRUST:trust', {}).get('impersonation') - - @trust_impersonation.setter - def trust_impersonation(self, value): - self.root.setdefault('OS-TRUST:trust', {})['impersonation'] = value - - @property - def trustee_user_id(self): - trust = self.root.get('OS-TRUST:trust', {}) - return trust.get('trustee_user', {}).get('id') - - @trustee_user_id.setter - def trustee_user_id(self, value): - trust = self.root.setdefault('OS-TRUST:trust', {}) - trust.setdefault('trustee_user', {})['id'] = value - - @property - def trustor_user_id(self): - trust = self.root.get('OS-TRUST:trust', {}) - return trust.get('trustor_user', {}).get('id') - - @trustor_user_id.setter - def trustor_user_id(self, value): - trust = self.root.setdefault('OS-TRUST:trust', {}) - trust.setdefault('trustor_user', {})['id'] = value - - @property - def oauth_access_token_id(self): - return self.root.get('OS-OAUTH1', {}).get('access_token_id') - - @oauth_access_token_id.setter - def oauth_access_token_id(self, value): - self.root.setdefault('OS-OAUTH1', {})['access_token_id'] = value - - @property - def oauth_consumer_id(self): - return self.root.get('OS-OAUTH1', {}).get('consumer_id') - - @oauth_consumer_id.setter - def oauth_consumer_id(self, value): - self.root.setdefault('OS-OAUTH1', {})['consumer_id'] = value - - @property - def audit_id(self): - try: - return self.root.get('audit_ids', [])[0] - except IndexError: - return None - - @audit_id.setter - def audit_id(self, value): - audit_chain_id = self.audit_chain_id - lval = [value] if audit_chain_id else [value, audit_chain_id] - self.root['audit_ids'] = lval - - @property - def audit_chain_id(self): - try: - return self.root.get('audit_ids', [])[1] - except IndexError: - return None - - @audit_chain_id.setter - def audit_chain_id(self, value): - self.root['audit_ids'] = [self.audit_id, value] - - @property - def role_ids(self): - return [r['id'] for r in self.root.get('roles', [])] - - @property - def role_names(self): - return [r['name'] for r in self.root.get('roles', [])] - - def validate(self): - project = self.root.get('project') - domain = self.root.get('domain') - trust = self.root.get('OS-TRUST:trust') - catalog = self.root.get('catalog') - roles = self.root.get('roles') - scoped = project or domain or trust - - if sum((bool(project), bool(domain), bool(trust))) > 1: - msg = 'You cannot scope to multiple targets' - raise exception.FixtureValidationError(msg) - - if catalog and not scoped: - msg = 'You cannot have a service catalog on an unscoped token' - raise exception.FixtureValidationError(msg) - - if scoped and not self.user.get('roles'): - msg = 'You must have roles on a token to scope it' - raise exception.FixtureValidationError(msg) - - if bool(scoped) != bool(roles): - msg = 'You must be scoped to have roles and vice-versa' - raise exception.FixtureValidationError(msg) - - def add_role(self, name=None, id=None): - roles = self.root.setdefault('roles', []) - data = {'id': id or uuid.uuid4().hex, - 'name': name or uuid.uuid4().hex} - roles.append(data) - return data - - def add_service(self, type, name=None, id=None): - service = _Service(type=type, id=id or uuid.uuid4().hex) - if name: - service['name'] = name - self.root.setdefault('catalog', []).append(service) - return service - - def set_project_scope(self, id=None, name=None, domain_id=None, - domain_name=None): - self.project_id = id or uuid.uuid4().hex - self.project_name = name or uuid.uuid4().hex - self.project_domain_id = domain_id or uuid.uuid4().hex - self.project_domain_name = domain_name or uuid.uuid4().hex - - def set_domain_scope(self, id=None, name=None): - self.domain_id = id or uuid.uuid4().hex - self.domain_name = name or uuid.uuid4().hex - - def set_trust_scope(self, id=None, impersonation=False, - trustee_user_id=None, trustor_user_id=None): - self.trust_id = id or uuid.uuid4().hex - self.trust_impersonation = impersonation - self.trustee_user_id = trustee_user_id or uuid.uuid4().hex - self.trustor_user_id = trustor_user_id or uuid.uuid4().hex - - def set_oauth(self, access_token_id=None, consumer_id=None): - self.oauth_access_token_id = access_token_id or uuid.uuid4().hex - self.oauth_consumer_id = consumer_id or uuid.uuid4().hex - - -class V3FederationToken(Token): - """A V3 Keystone Federation token that can be used for testing. - - Similar to V3Token, this object is designed to allow clients to generate - a correct V3 federation token for use in test code. - """ - - def __init__(self, methods=None, identity_provider=None, protocol=None, - groups=None): - methods = methods or ['saml2'] - super(V3FederationToken, self).__init__(methods=methods) - # NOTE(stevemar): Federated tokens do not have a domain for the user - del self._user['domain'] - self.add_federation_info_to_user(identity_provider, protocol, groups) - - def add_federation_info_to_user(self, identity_provider=None, - protocol=None, groups=None): - data = { - "OS-FEDERATION": { - "identity_provider": identity_provider or uuid.uuid4().hex, - "protocol": protocol or uuid.uuid4().hex, - "groups": groups or [{"id": uuid.uuid4().hex}] - } - } - self._user.update(data) - return data +An alias of :py:exc:`keystoneauth1.fixture.v3.V3FederationToken` +""" diff -Nru python-keystoneclient-3.1.0/keystoneclient/tests/functional/v3/client_fixtures.py python-keystoneclient-3.2.0/keystoneclient/tests/functional/v3/client_fixtures.py --- python-keystoneclient-3.1.0/keystoneclient/tests/functional/v3/client_fixtures.py 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/tests/functional/v3/client_fixtures.py 2016-07-01 04:24:16.000000000 +0000 @@ -0,0 +1,69 @@ +# 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 fixtures +import uuid + + +RESOURCE_NAME_PREFIX = 'keystoneclient-functional-' + + +class Base(fixtures.Fixture): + + def __init__(self, client, domain_id=None): + super(Base, self).__init__() + + self.client = client + self.domain_id = domain_id + self.ref = None + self.entity = None + + def __getattr__(self, name): + """Return the attribute from the represented entity.""" + return getattr(self.entity, name) + + +class User(Base): + + def setUp(self): + super(User, self).setUp() + + self.ref = {'name': RESOURCE_NAME_PREFIX + uuid.uuid4().hex, + 'domain': self.domain_id} + self.entity = self.client.users.create(**self.ref) + self.addCleanup(self.client.users.delete, self.entity) + + +class Group(Base): + + def setUp(self): + super(Group, self).setUp() + + self.ref = {'name': RESOURCE_NAME_PREFIX + uuid.uuid4().hex, + 'domain': self.domain_id} + self.entity = self.client.groups.create(**self.ref) + self.addCleanup(self.client.groups.delete, self.entity) + + +class Domain(Base): + + def setUp(self): + super(Domain, self).setUp() + + self.ref = {'name': RESOURCE_NAME_PREFIX + uuid.uuid4().hex, + 'description': uuid.uuid4().hex, + 'enabled': True} + self.entity = self.client.domains.create(**self.ref) + + # Only disabled domains can be deleted + self.addCleanup(self.client.domains.delete, self.entity) + self.addCleanup(self.client.domains.update, self.entity, enabled=False) diff -Nru python-keystoneclient-3.1.0/keystoneclient/tests/functional/v3/test_domains.py python-keystoneclient-3.2.0/keystoneclient/tests/functional/v3/test_domains.py --- python-keystoneclient-3.1.0/keystoneclient/tests/functional/v3/test_domains.py 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/tests/functional/v3/test_domains.py 2016-07-01 04:24:16.000000000 +0000 @@ -0,0 +1,97 @@ +# 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 keystoneauth1.exceptions import http +from keystoneclient.tests.functional import base +from keystoneclient.tests.functional.v3 import client_fixtures as fixtures + + +class DomainsTestCase(base.V3ClientTestCase): + + def check_domain(self, domain, domain_ref=None): + self.assertIsNotNone(domain.id) + self.assertIn('self', domain.links) + self.assertIn('/domains/' + domain.id, domain.links['self']) + + if domain_ref: + self.assertEqual(domain_ref['name'], domain.name) + self.assertEqual(domain_ref['enabled'], domain.enabled) + + # There is no guarantee description is present in domain + if hasattr(domain_ref, 'description'): + self.assertEqual(domain_ref['description'], domain.description) + else: + # Only check remaining mandatory attributes + self.assertIsNotNone(domain.name) + self.assertIsNotNone(domain.enabled) + + def test_create_domain(self): + domain_ref = { + 'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex, + 'description': uuid.uuid4().hex, + 'enabled': True} + domain = self.client.domains.create(**domain_ref) + self.check_domain(domain, domain_ref) + + # Only disabled domains can be deleted + self.addCleanup(self.client.domains.delete, domain) + self.addCleanup(self.client.domains.update, domain, enabled=False) + + def test_get_domain(self): + domain_id = self.project_domain_id + domain_ret = self.client.domains.get(domain_id) + self.check_domain(domain_ret) + + def test_list_domains(self): + domain_one = fixtures.Domain(self.client) + self.useFixture(domain_one) + + domain_two = fixtures.Domain(self.client) + self.useFixture(domain_two) + + domains = self.client.domains.list() + + # All domains are valid + for domain in domains: + self.check_domain(domain) + + self.assertIn(domain_one.entity, domains) + self.assertIn(domain_two.entity, domains) + + def test_update_domain(self): + domain = fixtures.Domain(self.client) + self.useFixture(domain) + + new_description = uuid.uuid4().hex + domain_ret = self.client.domains.update(domain.id, + description=new_description) + + domain.ref.update({'description': new_description}) + self.check_domain(domain_ret, domain.ref) + + def test_delete_domain(self): + domain = self.client.domains.create(name=uuid.uuid4().hex, + description=uuid.uuid4().hex, + enabled=True) + + # Only disabled domains can be deleted + self.assertRaises(http.Forbidden, + self.client.domains.delete, + domain.id) + + self.client.domains.update(domain, enabled=False) + self.client.domains.delete(domain.id) + self.assertRaises(http.NotFound, + self.client.domains.get, + domain.id) diff -Nru python-keystoneclient-3.1.0/keystoneclient/tests/functional/v3/test_groups.py python-keystoneclient-3.2.0/keystoneclient/tests/functional/v3/test_groups.py --- python-keystoneclient-3.1.0/keystoneclient/tests/functional/v3/test_groups.py 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/tests/functional/v3/test_groups.py 2016-07-01 04:24:16.000000000 +0000 @@ -0,0 +1,94 @@ +# 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 keystoneauth1.exceptions import http +from keystoneclient.tests.functional import base +from keystoneclient.tests.functional.v3 import client_fixtures as fixtures + + +class GroupsTestCase(base.V3ClientTestCase): + + def check_group(self, group, group_ref=None): + self.assertIsNotNone(group.id) + self.assertIn('self', group.links) + self.assertIn('/groups/' + group.id, group.links['self']) + + if group_ref: + self.assertEqual(group_ref['name'], group.name) + self.assertEqual(group_ref['domain'], group.domain_id) + + # There is no guarantee description is present in group + if hasattr(group_ref, 'description'): + self.assertEqual(group_ref['description'], group.description) + + else: + # Only check remaining mandatory attributes + self.assertIsNotNone(group.name) + self.assertIsNotNone(group.domain_id) + + def test_create_group(self): + group_ref = { + 'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex, + 'domain': self.project_domain_id, + 'description': uuid.uuid4().hex} + + group = self.client.groups.create(**group_ref) + self.addCleanup(self.client.groups.delete, group) + self.check_group(group, group_ref) + + def test_get_group(self): + group = fixtures.Group(self.client, self.project_domain_id) + self.useFixture(group) + + group_ret = self.client.groups.get(group.id) + self.check_group(group_ret, group.ref) + + def test_list_groups(self): + group_one = fixtures.Group(self.client, self.project_domain_id) + self.useFixture(group_one) + + group_two = fixtures.Group(self.client, self.project_domain_id) + self.useFixture(group_two) + + groups = self.client.groups.list() + + # All groups are valid + for group in groups: + self.check_group(group) + + self.assertIn(group_one.entity, groups) + self.assertIn(group_two.entity, groups) + + def test_update_group(self): + group = fixtures.Group(self.client, self.project_domain_id) + self.useFixture(group) + + new_name = fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex + new_description = uuid.uuid4().hex + + group_ret = self.client.groups.update(group.id, + name=new_name, + description=new_description) + + group.ref.update({'name': new_name, 'description': new_description}) + self.check_group(group_ret, group.ref) + + def test_delete_group(self): + group = self.client.groups.create(name=uuid.uuid4().hex, + domain=self.project_domain_id) + + self.client.groups.delete(group.id) + self.assertRaises(http.NotFound, + self.client.groups.get, + group.id) diff -Nru python-keystoneclient-3.1.0/keystoneclient/tests/functional/v3/test_users.py python-keystoneclient-3.2.0/keystoneclient/tests/functional/v3/test_users.py --- python-keystoneclient-3.1.0/keystoneclient/tests/functional/v3/test_users.py 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/tests/functional/v3/test_users.py 2016-07-01 04:24:16.000000000 +0000 @@ -0,0 +1,117 @@ +# 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 keystoneauth1.exceptions import http +from keystoneclient.tests.functional import base +from keystoneclient.tests.functional.v3 import client_fixtures as fixtures + + +class UsersTestCase(base.V3ClientTestCase): + + def check_user(self, user, user_ref=None): + self.assertIsNotNone(user.id) + self.assertIsNotNone(user.enabled) + self.assertIn('self', user.links) + self.assertIn('/users/' + user.id, user.links['self']) + + if user_ref: + self.assertEqual(user_ref['name'], user.name) + self.assertEqual(user_ref['domain'], user.domain_id) + # There is no guarantee the attributes below are present in user + if hasattr(user_ref, 'description'): + self.assertEqual(user_ref['description'], user.description) + if hasattr(user_ref, 'email'): + self.assertEqual(user_ref['email'], user.email) + if hasattr(user_ref, 'default_project'): + self.assertEqual(user_ref['default_project'], + user.default_project_id) + else: + # Only check remaining mandatory attributes + self.assertIsNotNone(user.name) + self.assertIsNotNone(user.domain_id) + + def test_create_user(self): + user_ref = { + 'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex, + 'domain': self.project_domain_id, + 'default_project': self.project_id, + 'password': uuid.uuid4().hex, + 'description': uuid.uuid4().hex} + + user = self.client.users.create(**user_ref) + self.addCleanup(self.client.users.delete, user) + self.check_user(user, user_ref) + + def test_get_user(self): + user = fixtures.User(self.client, self.project_domain_id) + self.useFixture(user) + + user_ret = self.client.users.get(user.id) + self.check_user(user_ret, user.ref) + + def test_list_users(self): + user_one = fixtures.User(self.client, self.project_domain_id) + self.useFixture(user_one) + + user_two = fixtures.User(self.client, self.project_domain_id) + self.useFixture(user_two) + + users = self.client.users.list() + + # All users are valid + for user in users: + self.check_user(user) + + self.assertIn(user_one.entity, users) + self.assertIn(user_two.entity, users) + + def test_update_user(self): + user = fixtures.User(self.client, self.project_domain_id) + self.useFixture(user) + + new_description = uuid.uuid4().hex + user_ret = self.client.users.update(user.id, + description=new_description) + + user.ref.update({'description': new_description}) + self.check_user(user_ret, user.ref) + + def test_user_grouping(self): + # keystoneclient.v3.users owns user grouping operations, this is why + # this test case belongs to this class + user = fixtures.User(self.client, self.project_domain_id) + group = fixtures.Group(self.client, self.project_domain_id) + self.useFixture(user) + self.useFixture(group) + + self.assertRaises(http.NotFound, + self.client.users.check_in_group, + user.id, group.id) + + self.client.users.add_to_group(user.id, group.id) + self.client.users.check_in_group(user.id, group.id) + self.client.users.remove_from_group(user.id, group.id) + + self.assertRaises(http.NotFound, + self.client.users.check_in_group, + user.id, group.id) + + def test_delete_user(self): + user = self.client.users.create(name=uuid.uuid4().hex, + domain=self.project_domain_id) + + self.client.users.delete(user.id) + self.assertRaises(http.NotFound, + self.client.users.get, + user.id) diff -Nru python-keystoneclient-3.1.0/keystoneclient/tests/unit/v3/saml2_fixtures.py python-keystoneclient-3.2.0/keystoneclient/tests/unit/v3/saml2_fixtures.py --- python-keystoneclient-3.1.0/keystoneclient/tests/unit/v3/saml2_fixtures.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/tests/unit/v3/saml2_fixtures.py 2016-07-01 04:24:16.000000000 +0000 @@ -145,7 +145,7 @@ } ], "links": { - "self": "http://identity:35357/v3/OS-FEDERATION/projects", + "self": "http://identity:35357/v3/auth/projects", "previous": 'null', "next": 'null' } @@ -164,7 +164,7 @@ } ], "links": { - "self": "http://identity:35357/v3/OS-FEDERATION/domains", + "self": "http://identity:35357/v3/auth/domains", "previous": 'null', "next": 'null' } diff -Nru python-keystoneclient-3.1.0/keystoneclient/tests/unit/v3/test_federation.py python-keystoneclient-3.2.0/keystoneclient/tests/unit/v3/test_federation.py --- python-keystoneclient-3.1.0/keystoneclient/tests/unit/v3/test_federation.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/tests/unit/v3/test_federation.py 2016-07-01 04:24:16.000000000 +0000 @@ -13,10 +13,18 @@ import copy import uuid +from keystoneauth1 import fixture as auth_fixture +from keystoneauth1.identity import v3 +from keystoneauth1 import session +from keystoneauth1.tests.unit import k2k_fixtures +import six +from testtools import matchers + from keystoneclient import access from keystoneclient import exceptions from keystoneclient import fixture from keystoneclient.tests.unit.v3 import utils +from keystoneclient.v3 import client from keystoneclient.v3.contrib.federation import base from keystoneclient.v3.contrib.federation import identity_providers from keystoneclient.v3.contrib.federation import mappings @@ -339,7 +347,7 @@ self.collection_key = 'projects' self.model = projects.Project self.manager = self.client.federation.projects - self.URL = "%s%s" % (self.TEST_URL, '/OS-FEDERATION/projects') + self.URL = "%s%s" % (self.TEST_URL, '/auth/projects') def new_ref(self, **kwargs): kwargs.setdefault('id', uuid.uuid4().hex) @@ -361,6 +369,101 @@ self.assertIsInstance(project, self.model) +class K2KFederatedProjectTests(utils.TestCase): + + TEST_ROOT_URL = 'http://127.0.0.1:5000/' + TEST_URL = '%s%s' % (TEST_ROOT_URL, 'v3') + TEST_PASS = 'password' + REQUEST_ECP_URL = TEST_URL + '/auth/OS-FEDERATION/saml2/ecp' + + SP_ID = 'sp1' + SP_ROOT_URL = 'https://example.com/v3' + SP_URL = 'https://example.com/Shibboleth.sso/SAML2/ECP' + SP_AUTH_URL = (SP_ROOT_URL + + '/OS-FEDERATION/identity_providers' + '/testidp/protocols/saml2/auth') + + def setUp(self): + super(K2KFederatedProjectTests, self).setUp() + self.token_v3 = auth_fixture.V3Token() + self.token_v3.add_service_provider( + self.SP_ID, self.SP_AUTH_URL, self.SP_URL) + self.session = session.Session() + self.collection_key = 'projects' + self.model = projects.Project + self.URL = '%s%s' % (self.SP_ROOT_URL, '/auth/projects') + self.k2kplugin = self.get_plugin() + self._mock_k2k_flow_urls() + + def new_ref(self, **kwargs): + kwargs.setdefault('id', uuid.uuid4().hex) + kwargs.setdefault('domain_id', uuid.uuid4().hex) + kwargs.setdefault('enabled', True) + kwargs.setdefault('name', uuid.uuid4().hex) + return kwargs + + def _get_base_plugin(self): + self.stub_url('POST', ['auth', 'tokens'], + headers={'X-Subject-Token': uuid.uuid4().hex}, + json=self.token_v3) + return v3.Password(self.TEST_URL, + username=self.TEST_USER, + password=self.TEST_PASS) + + def _mock_k2k_flow_urls(self): + # We need to check the auth versions available + self.requests_mock.get( + self.TEST_URL, + json={'version': auth_fixture.V3Discovery(self.TEST_URL)}, + headers={'Content-Type': 'application/json'}) + + # The identity provider receives a request for an ECP wrapped + # assertion. This assertion contains the user authentication info + # and will be presented to the service provider + self.requests_mock.register_uri( + 'POST', + self.REQUEST_ECP_URL, + content=six.b(k2k_fixtures.ECP_ENVELOPE), + headers={'Content-Type': 'application/vnd.paos+xml'}, + status_code=200) + + # The service provider is presented with the ECP wrapped assertion + # generated by the identity provider and should return a redirect + # (302 or 303) upon successful authentication + self.requests_mock.register_uri( + 'POST', + self.SP_URL, + content=six.b(k2k_fixtures.TOKEN_BASED_ECP), + headers={'Content-Type': 'application/vnd.paos+xml'}, + status_code=302) + + # Should not follow the redirect URL, but use the auth_url attribute + self.requests_mock.register_uri( + 'GET', + self.SP_AUTH_URL, + json=k2k_fixtures.UNSCOPED_TOKEN, + headers={'X-Subject-Token': k2k_fixtures.UNSCOPED_TOKEN_HEADER}) + + def get_plugin(self, **kwargs): + kwargs.setdefault('base_plugin', self._get_base_plugin()) + kwargs.setdefault('service_provider', self.SP_ID) + return v3.Keystone2Keystone(**kwargs) + + def test_list_projects(self): + k2k_client = client.Client(session=self.session, auth=self.k2kplugin) + self.requests_mock.get(self.URL, json={ + self.collection_key: [self.new_ref(), self.new_ref()] + }) + self.requests_mock.get(self.SP_ROOT_URL, json={ + 'version': auth_fixture.discovery.V3Discovery(self.SP_ROOT_URL) + }) + returned_list = k2k_client.federation.projects.list() + + self.assertThat(returned_list, matchers.HasLength(2)) + for project in returned_list: + self.assertIsInstance(project, self.model) + + class FederationDomainTests(utils.ClientTestCase): def setUp(self): @@ -370,7 +473,7 @@ self.model = domains.Domain self.manager = self.client.federation.domains - self.URL = "%s%s" % (self.TEST_URL, '/OS-FEDERATION/domains') + self.URL = "%s%s" % (self.TEST_URL, '/auth/domains') def new_ref(self, **kwargs): kwargs.setdefault('id', uuid.uuid4().hex) @@ -410,7 +513,7 @@ def test_get_user_domain_id(self): """Ensure a federated user's domain ID does not exist.""" - self.assertIsNone(self.federated_token.user_domain_id) + self.assertEqual('Federated', self.federated_token.user_domain_id) class ServiceProviderTests(utils.ClientTestCase, utils.CrudTests): diff -Nru python-keystoneclient-3.1.0/keystoneclient/v3/contrib/federation/base.py python-keystoneclient-3.2.0/keystoneclient/v3/contrib/federation/base.py --- python-keystoneclient-3.1.0/keystoneclient/v3/contrib/federation/base.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/v3/contrib/federation/base.py 2016-07-01 04:24:16.000000000 +0000 @@ -30,10 +30,10 @@ raise exceptions.MethodNotImplemented def list(self): - url = '/OS-FEDERATION/%s' % self.object_type + url = '/auth/%s' % self.object_type try: tenant_list = self._list(url, self.object_type) - except exceptions.EndpointNotFound: + except exceptions.EndpointException: endpoint_filter = {'interface': base_auth.AUTH_INTERFACE} tenant_list = self._list(url, self.object_type, endpoint_filter=endpoint_filter) diff -Nru python-keystoneclient-3.1.0/keystoneclient/v3/domains.py python-keystoneclient-3.2.0/keystoneclient/v3/domains.py --- python-keystoneclient-3.1.0/keystoneclient/v3/domains.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/v3/domains.py 2016-07-01 04:24:16.000000000 +0000 @@ -39,6 +39,18 @@ @positional(1, enforcement=positional.WARN) def create(self, name, description=None, enabled=True, **kwargs): + """Create a domain. + + :param str name: the name of the domain. + :param str description: a description of the domain. + :param bool enabled: whether the domain is enabled. + :param kwargs: any other attribute provided will be passed to the + server. + + :returns: the created domain returned from server. + :rtype: :class:`keystoneclient.v3.domains.Domain` + + """ return super(DomainManager, self).create( name=name, description=description, @@ -46,14 +58,27 @@ **kwargs) def get(self, domain): + """Retrieve a domain. + + :param domain: the domain to be retrieved from the server. + :type domain: str or :class:`keystoneclient.v3.domains.Domain` + + :returns: the specified domain returned from server. + :rtype: :class:`keystoneclient.v3.domains.Domain` + + """ return super(DomainManager, self).get( domain_id=base.getid(domain)) def list(self, **kwargs): """List domains. - ``**kwargs`` allows filter criteria to be passed where + :param kwargs: allows filter criteria to be passed where supported by the server. + + :returns: a list of domains. + :rtype: list of :class:`keystoneclient.v3.domains.Domain`. + """ # Ref bug #1267530 we have to pass 0 for False to get the expected # results on all keystone versions @@ -64,6 +89,20 @@ @positional(enforcement=positional.WARN) def update(self, domain, name=None, description=None, enabled=None, **kwargs): + """Update a domain. + + :param domain: the domain to be updated on the server. + :type domain: str or :class:`keystoneclient.v3.domains.Domain` + :param str name: the new name of the domain. + :param str description: the new description of the domain. + :param bool enabled: whether the domain is enabled. + :param kwargs: any other attribute provided will be passed to the + server. + + :returns: the updated domain returned from server. + :rtype: :class:`keystoneclient.v3.domains.Domain` + + """ return super(DomainManager, self).update( domain_id=base.getid(domain), name=name, @@ -72,5 +111,13 @@ **kwargs) def delete(self, domain): + """"Delete a domain. + + :param domain: the domain to be deleted on the server. + :type domain: str or :class:`keystoneclient.v3.domains.Domain` + + :returns: 204 No Content. + + """ return super(DomainManager, self).delete( domain_id=base.getid(domain)) diff -Nru python-keystoneclient-3.1.0/keystoneclient/v3/groups.py python-keystoneclient-3.2.0/keystoneclient/v3/groups.py --- python-keystoneclient-3.1.0/keystoneclient/v3/groups.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/v3/groups.py 2016-07-01 04:24:16.000000000 +0000 @@ -56,6 +56,19 @@ @positional(1, enforcement=positional.WARN) def create(self, name, domain=None, description=None, **kwargs): + """Create a group. + + :param str name: the name of the group. + :param domain: the domain of the group. + :type domain: str or :class:`keystoneclient.v3.domains.Domain` + :param str description: a description of the group. + :param kwargs: any other attribute provided will be passed to the + server. + + :returns: the created group returned from server. + :rtype: :class:`keystoneclient.v3.groups.Group` + + """ return super(GroupManager, self).create( name=name, domain_id=base.getid(domain), @@ -66,11 +79,15 @@ def list(self, user=None, domain=None, **kwargs): """List groups. - If domain or user is provided, then filter groups with - that attribute. + :param user: the user of the groups to be filtered on. + :type user: str or :class:`keystoneclient.v3.users.User` + :param domain: the domain of the groups to be filtered on. + :type domain: str or :class:`keystoneclient.v3.domains.Domain` + :param kwargs: any other attribute provided will filter groups on. + + :returns: a list of groups. + :rtype: list of :class:`keystoneclient.v3.groups.Group`. - If ``**kwargs`` are provided, then filter groups with - attributes matching ``**kwargs``. """ if user: base_url = '/users/%s' % base.getid(user) @@ -82,11 +99,32 @@ **kwargs) def get(self, group): + """Retrieve a group. + + :param group: the group to be retrieved from the server. + :type group: str or :class:`keystoneclient.v3.groups.Group` + + :returns: the specified group returned from server. + :rtype: :class:`keystoneclient.v3.groups.Group` + + """ return super(GroupManager, self).get( group_id=base.getid(group)) @positional(enforcement=positional.WARN) def update(self, group, name=None, description=None, **kwargs): + """Update a group. + + :param group: the group to be updated on the server. + :type group: str or :class:`keystoneclient.v3.groups.Group` + :param str name: the new name of the group. + :param str description: the new description of the group. + :param kwargs: any other attribute provided will be passed to server. + + :returns: the updated group returned from server. + :rtype: :class:`keystoneclient.v3.groups.Group` + + """ return super(GroupManager, self).update( group_id=base.getid(group), name=name, @@ -94,5 +132,13 @@ **kwargs) def delete(self, group): + """Delete a group. + + :param group: the group to be deleted on the server. + :type group: str or :class:`keystoneclient.v3.groups.Group` + + :returns: 204 No Content. + + """ return super(GroupManager, self).delete( group_id=base.getid(group)) diff -Nru python-keystoneclient-3.1.0/keystoneclient/v3/projects.py python-keystoneclient-3.2.0/keystoneclient/v3/projects.py --- python-keystoneclient-3.1.0/keystoneclient/v3/projects.py 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/keystoneclient/v3/projects.py 2016-07-01 04:24:16.000000000 +0000 @@ -68,13 +68,19 @@ enabled=True, parent=None, **kwargs): """Create a project. - :param str name: project name. - :param domain: the project domain. - :type domain: :py:class:`keystoneclient.v3.domains.Domain` or str - :param str description: the project description. (optional) - :param boolean enabled: if the project is enabled. (optional) - :param parent: the project's parent in the hierarchy. (optional) - :type parent: :py:class:`keystoneclient.v3.projects.Project` or str + :param str name: the name of the project. + :param domain: the domain of the project. + :type domain: str or :class:`keystoneclient.v3.domains.Domain` + :param str description: the description of the project. + :param bool enabled: whether the project is enabled. + :param parent: the parent of the project in the hierarchy. + :type parent: str or :class:`keystoneclient.v3.projects.Project` + :param kwargs: any other attribute provided will be passed to the + server. + + :returns: the created project returned from server. + :rtype: :class:`keystoneclient.v3.projects.Project` + """ # NOTE(rodrigods): the API must be backwards compatible, so if an # application was passing a 'parent_id' before as kwargs, the call @@ -94,11 +100,16 @@ def list(self, domain=None, user=None, **kwargs): """List projects. - If domain or user are provided, then filter projects with - those attributes. + :param domain: the domain of the projects to be filtered on. + :type domain: str or :class:`keystoneclient.v3.domains.Domain` + :param user: filter in projects the specified user has role + assignments on. + :type user: str or :class:`keystoneclient.v3.users.User` + :param kwargs: any other attribute provided will filter projects on. + + :returns: a list of projects. + :rtype: list of :class:`keystoneclient.v3.projects.Project` - If ``**kwargs`` are provided, then filter projects with - attributes matching ``**kwargs``. """ base_url = '/users/%s' % base.getid(user) if user else None return super(ProjectManager, self).list( @@ -124,26 +135,31 @@ @positional() def get(self, project, subtree_as_list=False, parents_as_list=False, subtree_as_ids=False, parents_as_ids=False): - """Get a project. + """Retrieve a project. - :param project: project to be retrieved. - :type project: :py:class:`keystoneclient.v3.projects.Project` or str - :param boolean subtree_as_list: retrieve projects below this project - in the hierarchy as a flat list. - (optional) - :param boolean parents_as_list: retrieve projects above this project - in the hierarchy as a flat list. - (optional) - :param boolean subtree_as_ids: retrieve the IDs from the projects below - this project in the hierarchy as a - structured dictionary. (optional) - :param boolean parents_as_ids: retrieve the IDs from the projects above - this project in the hierarchy as a - structured dictionary. (optional) + :param project: the project to be retrieved from the server. + :type project: str or :class:`keystoneclient.v3.projects.Project` + :param bool subtree_as_list: retrieve projects below this project in + the hierarchy as a flat list. It only + includes the projects in which the current + user has role assignments on. + :param bool parents_as_list: retrieve projects above this project in + the hierarchy as a flat list. It only + includes the projects in which the current + user has role assignments on. + :param bool subtree_as_ids: retrieve the IDs from the projects below + this project in the hierarchy as a + structured dictionary. + :param bool parents_as_ids: retrieve the IDs from the projects above + this project in the hierarchy as a + structured dictionary. + :returns: the specified project returned from server. + :rtype: :class:`keystoneclient.v3.projects.Project` :raises keystoneclient.exceptions.ValidationError: if subtree_as_list and subtree_as_ids or parents_as_list and parents_as_ids are included at the same time in the call. + """ self._check_not_parents_as_ids_and_parents_as_list( parents_as_ids, parents_as_list) @@ -169,6 +185,21 @@ @positional(enforcement=positional.WARN) def update(self, project, name=None, domain=None, description=None, enabled=None, **kwargs): + """Update a project. + + :param project: the project to be updated on the server. + :type project: str or :class:`keystoneclient.v3.projects.Project` + :param str name: the new name of the project. + :param domain: the new domain of the project. + :type domain: str or :class:`keystoneclient.v3.domains.Domain` + :param str description: the new description of the project. + :param bool enabled: whether the project is enabled. + :param kwargs: any other attribute provided will be passed to server. + + :returns: the updated project returned from server. + :rtype: :class:`keystoneclient.v3.projects.Project` + + """ return super(ProjectManager, self).update( project_id=base.getid(project), domain_id=base.getid(domain), @@ -178,5 +209,13 @@ **kwargs) def delete(self, project): + """Delete a project. + + :param project: the project to be deleted on the server. + :type project: str or :class:`keystoneclient.v3.projects.Project` + + :returns: 204 No Content. + + """ return super(ProjectManager, self).delete( project_id=base.getid(project)) diff -Nru python-keystoneclient-3.1.0/other-requirements.txt python-keystoneclient-3.2.0/other-requirements.txt --- python-keystoneclient-3.1.0/other-requirements.txt 1970-01-01 00:00:00.000000000 +0000 +++ python-keystoneclient-3.2.0/other-requirements.txt 2016-07-01 04:24:16.000000000 +0000 @@ -0,0 +1,27 @@ +# This is a cross-platform list tracking distribution packages needed by tests; +# see http://docs.openstack.org/infra/bindep/ for additional information. + +gettext +libssl-dev + +dbus-devel [platform:rpm] +dbus-glib-devel [platform:rpm] +libdbus-1-dev [platform:dpkg] +libdbus-glib-1-dev [platform:dpkg] +libffi-dev [platform:dpkg] +libffi-devel [platform:rpm] +libldap2-dev [platform:dpkg] +libsasl2-dev [platform:dpkg] +libxml2-dev [platform:dpkg] +libxslt1-dev [platform:dpkg] +python-dev [platform:dpkg] +python3-all-dev [platform:ubuntu !platform:ubuntu-precise] +python3-dev [platform:dpkg] +python3.4 [platform:ubuntu-trusty] +python3.5 [platform:ubuntu-xenial] + +cyrus-sasl-devel [platform:rpm] +libxml2-devel [platform:rpm] +python-devel [platform:rpm] +python3-devel [platform:fedora] +python34-devel [platform:centos] diff -Nru python-keystoneclient-3.1.0/README.rst python-keystoneclient-3.2.0/README.rst --- python-keystoneclient-3.1.0/README.rst 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/README.rst 2016-07-01 04:24:16.000000000 +0000 @@ -41,13 +41,15 @@ By way of a quick-start:: - # use v2.0 auth with http://example.com:5000/v2.0 - >>> from keystoneauth1.identity import v2 + >>> from keystoneauth1.identity import v3 >>> from keystoneauth1 import session - >>> from keystoneclient.v2_0 import client - >>> auth = v2.Password(username=USERNAME, password=PASSWORD, tenant_name=TENANT, auth_url=AUTH_URL) + >>> from keystoneclient.v3 import client + >>> auth = v3.Password(auth_url="http://example.com:5000/v3", username="admin", + ... password="password", project_name="admin", + ... user_domain_id="default", project_domain_id="default") >>> sess = session.Session(auth=auth) >>> keystone = client.Client(session=sess) - >>> keystone.tenants.list() - >>> tenant = keystone.tenants.create(tenant_name="test", description="My new tenant!", enabled=True) - >>> tenant.delete() + >>> keystone.projects.list() + [...] + >>> project = keystone.projects.create(name="test", description="My new Project!", domain="default", enabled=True) + >>> project.delete() diff -Nru python-keystoneclient-3.1.0/requirements.txt python-keystoneclient-3.2.0/requirements.txt --- python-keystoneclient-3.1.0/requirements.txt 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/requirements.txt 2016-07-01 04:24:16.000000000 +0000 @@ -4,13 +4,12 @@ pbr>=1.6 # Apache-2.0 -iso8601>=0.1.11 # MIT debtcollector>=1.2.0 # Apache-2.0 -keystoneauth1>=2.1.0 # Apache-2.0 -oslo.config>=3.9.0 # Apache-2.0 +keystoneauth1>=2.7.0 # Apache-2.0 +oslo.config>=3.10.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.5.0 # Apache-2.0 +oslo.utils>=3.14.0 # Apache-2.0 positional>=1.0.1 # Apache-2.0 requests>=2.10.0 # Apache-2.0 six>=1.9.0 # MIT diff -Nru python-keystoneclient-3.1.0/test-requirements.txt python-keystoneclient-3.2.0/test-requirements.txt --- python-keystoneclient-3.1.0/test-requirements.txt 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/test-requirements.txt 2016-07-01 04:24:16.000000000 +0000 @@ -6,17 +6,17 @@ flake8-docstrings==0.2.1.post1 # MIT coverage>=3.6 # Apache-2.0 -fixtures<2.0,>=1.3.1 # Apache-2.0/BSD +fixtures>=3.0.0 # Apache-2.0/BSD keyring>=5.5.1 # MIT/PSF lxml>=2.3 # BSD -mock>=1.2 # BSD +mock>=2.0 # BSD oauthlib>=0.6 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 -reno>=1.6.2 # Apache2 -requests-mock>=0.7.0 # Apache-2.0 -sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD -tempest>=11.0.0 # Apache-2.0 +reno>=1.8.0 # Apache2 +requests-mock>=1.0 # Apache-2.0 +sphinx!=1.3b1,<1.3,>=1.2.1 # BSD +tempest>=12.1.0 # Apache-2.0 testrepository>=0.0.18 # Apache-2.0/BSD testresources>=0.2.4 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD diff -Nru python-keystoneclient-3.1.0/tox.ini python-keystoneclient-3.2.0/tox.ini --- python-keystoneclient-3.1.0/tox.ini 2016-05-17 14:09:45.000000000 +0000 +++ python-keystoneclient-3.2.0/tox.ini 2016-07-01 04:24:16.000000000 +0000 @@ -45,7 +45,8 @@ # D102: Missing docstring in public method # D103: Missing docstring in public function # D104: Missing docstring in public package -ignore = D100,D101,D102,D103,D104 +# D203: 1 blank line required before class docstring (deprecated in pep257) +ignore = D100,D101,D102,D103,D104,D203 show-source = True exclude = .venv,.tox,dist,doc,*egg,build @@ -60,3 +61,11 @@ import_exceptions = keystoneclient.i18n local-check-factory = keystoneclient.tests.hacking.checks.factory + +[testenv:bindep] +# Do not install any requirements. We want this to be fast and work even if +# system dependencies are missing, since it's used to tell you what system +# dependencies are missing! This also means that bindep must be installed +# separately, outside of the requirements files. +deps = bindep +commands = bindep test