summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.rst4
-rw-r--r--HACKING.rst4
-rw-r--r--README.rst10
-rw-r--r--bindep.txt2
-rw-r--r--doc/source/conf.py8
-rw-r--r--doc/source/index.rst6
-rw-r--r--keystoneclient/auth/base.py2
-rw-r--r--keystoneclient/auth/cli.py2
-rw-r--r--keystoneclient/auth/identity/v3/base.py6
-rw-r--r--keystoneclient/base.py5
-rw-r--r--keystoneclient/contrib/ec2/utils.py2
-rw-r--r--keystoneclient/discover.py6
-rw-r--r--keystoneclient/generic/client.py6
-rw-r--r--keystoneclient/i18n.py2
-rw-r--r--keystoneclient/releasenotes/notes/removed-generic-client-ff505b2b01bc9302.yaml6
-rw-r--r--keystoneclient/session.py35
-rw-r--r--keystoneclient/tests/functional/v3/client_fixtures.py16
-rw-r--r--keystoneclient/tests/functional/v3/test_credentials.py31
-rw-r--r--keystoneclient/tests/functional/v3/test_endpoint_groups.py120
-rw-r--r--keystoneclient/tests/functional/v3/test_implied_roles.py5
-rw-r--r--keystoneclient/tests/functional/v3/test_projects.py73
-rw-r--r--keystoneclient/tests/functional/v3/test_users.py23
-rw-r--r--keystoneclient/tests/hacking/__init__.py0
-rw-r--r--keystoneclient/tests/hacking/checks.py37
-rw-r--r--keystoneclient/tests/unit/apiclient/test_exceptions.py2
-rw-r--r--keystoneclient/tests/unit/client_fixtures.py61
-rw-r--r--keystoneclient/tests/unit/generic/test_client.py4
-rw-r--r--keystoneclient/tests/unit/test_base.py16
-rw-r--r--keystoneclient/tests/unit/test_ec2utils.py39
-rw-r--r--keystoneclient/tests/unit/test_hacking_checks.py50
-rw-r--r--keystoneclient/tests/unit/test_http.py15
-rw-r--r--keystoneclient/tests/unit/test_session.py101
-rw-r--r--keystoneclient/tests/unit/v2_0/test_client.py2
-rw-r--r--keystoneclient/tests/unit/v2_0/test_discovery.py4
-rw-r--r--keystoneclient/tests/unit/v2_0/test_extensions.py4
-rw-r--r--keystoneclient/tests/unit/v3/test_client.py2
-rw-r--r--keystoneclient/tests/unit/v3/test_discover.py8
-rw-r--r--keystoneclient/tests/unit/v3/test_endpoint_groups.py34
-rw-r--r--keystoneclient/tests/unit/v3/test_roles.py236
-rw-r--r--keystoneclient/utils.py4
-rw-r--r--keystoneclient/v3/client.py9
-rw-r--r--keystoneclient/v3/contrib/federation/identity_providers.py2
-rw-r--r--keystoneclient/v3/endpoint_groups.py136
-rw-r--r--keystoneclient/v3/roles.py268
-rw-r--r--releasenotes/notes/bp-pci-dss-query-password-expired-users-b0c4b1bbdcf33f16.yaml6
-rw-r--r--releasenotes/notes/bug-1616105-cc8b85eb056e99e2.yaml8
-rw-r--r--releasenotes/notes/bug-1654847-d2e9df994c7b617f.yaml5
-rw-r--r--releasenotes/notes/deprecated_auth-d2a2bf537bdb88d3.yaml4
-rw-r--r--releasenotes/source/conf.py1
-rw-r--r--releasenotes/source/index.rst1
-rw-r--r--releasenotes/source/ocata.rst6
-rw-r--r--requirements.txt12
-rw-r--r--setup.cfg3
-rw-r--r--setup.py2
-rw-r--r--test-requirements.txt8
-rwxr-xr-xtools/tox_install.sh30
-rw-r--r--tox.ini12
57 files changed, 1063 insertions, 443 deletions
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index b7139ec..604d3ac 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -1,7 +1,7 @@
If you would like to contribute to the development of OpenStack,
you must follow the steps documented at:
- http://docs.openstack.org/infra/manual/developers.html
+ https://docs.openstack.org/infra/manual/developers.html
If you already have a good understanding of how the system works
and your OpenStack accounts are set up, you can skip to the
@@ -9,7 +9,7 @@ development workflow section of this documentation to learn how
changes to OpenStack should be submitted for review via the
Gerrit tool:
- http://docs.openstack.org/infra/manual/developers.html#development-workflow
+ https://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
diff --git a/HACKING.rst b/HACKING.rst
index 0dfef99..1ab4a87 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -2,7 +2,7 @@ Keystone Style Commandments
===========================
- Step 1: Read the OpenStack Style Commandments
- http://docs.openstack.org/developer/hacking/
+ https://docs.openstack.org/developer/hacking/
- Step 2: Read on
Exceptions
@@ -17,7 +17,7 @@ Testing
python-keystoneclient uses testtools and testr for its unittest suite
and its test runner. Basic workflow around our use of tox and testr can
-be found at http://wiki.openstack.org/testr. If you'd like to learn more
+be found at https://wiki.openstack.org/testr. If you'd like to learn more
in depth:
https://testtools.readthedocs.org/
diff --git a/README.rst b/README.rst
index 02eed1c..7421e5e 100644
--- a/README.rst
+++ b/README.rst
@@ -2,8 +2,8 @@
Team and repository tags
========================
-.. image:: http://governance.openstack.org/badges/python-keystoneclient.svg
- :target: http://governance.openstack.org/reference/tags/index.html
+.. image:: https://governance.openstack.org/badges/python-keystoneclient.svg
+ :target: https://governance.openstack.org/reference/tags/index.html
.. Change things from this point on
@@ -33,14 +33,14 @@ OpenStack's Identity Service. For command line interface support, use
* `How to Contribute`_
.. _PyPi: https://pypi.python.org/pypi/python-keystoneclient
-.. _Online Documentation: http://docs.openstack.org/developer/python-keystoneclient
+.. _Online Documentation: https://docs.openstack.org/developer/python-keystoneclient
.. _Launchpad project: https://launchpad.net/python-keystoneclient
.. _Blueprints: https://blueprints.launchpad.net/python-keystoneclient
.. _Bugs: https://bugs.launchpad.net/python-keystoneclient
.. _Source: https://git.openstack.org/cgit/openstack/python-keystoneclient
.. _OpenStackClient: https://pypi.python.org/pypi/python-openstackclient
-.. _How to Contribute: http://docs.openstack.org/infra/manual/developers.html
-.. _Specs: http://specs.openstack.org/openstack/keystone-specs/
+.. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html
+.. _Specs: https://specs.openstack.org/openstack/keystone-specs/
.. contents:: Contents:
:local:
diff --git a/bindep.txt b/bindep.txt
index bcd43fb..d179369 100644
--- a/bindep.txt
+++ b/bindep.txt
@@ -1,5 +1,5 @@
# This is a cross-platform list tracking distribution packages needed by tests;
-# see http://docs.openstack.org/infra/bindep/ for additional information.
+# see https://docs.openstack.org/infra/bindep/ for additional information.
gettext
libssl-dev
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 1d5cb10..cda0c77 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
# python-keystoneclient documentation build configuration file, created by
# sphinx-quickstart on Sun Dec 6 14:19:25 2009.
#
@@ -229,9 +227,9 @@ latex_documents = [
# If false, no module index is generated.
#latex_use_modindex = True
-keystoneauth_url = 'http://docs.openstack.org/developer/keystoneauth/'
+keystoneauth_url = 'https://docs.openstack.org/developer/keystoneauth/'
intersphinx_mapping = {
- 'python': ('http://docs.python.org/', None),
- 'osloconfig': ('http://docs.openstack.org/developer/oslo.config/', None),
+ 'python': ('https://docs.python.org/', None),
+ 'osloconfig': ('https://docs.openstack.org/developer/oslo.config/', None),
'keystoneauth1': (keystoneauth_url, None),
}
diff --git a/doc/source/index.rst b/doc/source/index.rst
index c5f5260..74613cd 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -21,8 +21,8 @@ Related Identity Projects
In addition to creating the Python client library, the Keystone team also
provides `Identity Service`_, as well as `WSGI Middleware`_.
-.. _`Identity Service`: http://docs.openstack.org/developer/keystone/
-.. _`WSGI Middleware`: http://docs.openstack.org/developer/keystonemiddleware/
+.. _`Identity Service`: https://docs.openstack.org/developer/keystone/
+.. _`WSGI Middleware`: https://docs.openstack.org/developer/keystonemiddleware/
Release Notes
=============
@@ -41,7 +41,7 @@ using `Gerrit`_.
.. _on GitHub: https://github.com/openstack/python-keystoneclient
.. _Launchpad: https://launchpad.net/python-keystoneclient
-.. _Gerrit: http://docs.openstack.org/infra/manual/developers.html#development-workflow
+.. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow
Run tests with ``tox``.
diff --git a/keystoneclient/auth/base.py b/keystoneclient/auth/base.py
index df7521c..0036b8c 100644
--- a/keystoneclient/auth/base.py
+++ b/keystoneclient/auth/base.py
@@ -252,7 +252,7 @@ class BaseAuthPlugin(object):
:returns: A list of Param objects describing available plugin
parameters.
- :rtype: list
+ :rtype: List
"""
return []
diff --git a/keystoneclient/auth/cli.py b/keystoneclient/auth/cli.py
index d8cc820..1b8e054 100644
--- a/keystoneclient/auth/cli.py
+++ b/keystoneclient/auth/cli.py
@@ -32,7 +32,7 @@ def register_argparse_arguments(parser, argv, default=None):
the options required for that specific plugin if available.
:param argparse.ArgumentParser: the parser to attach argparse options to.
- :param list argv: the arguments provided to the application.
+ :param List argv: the arguments provided to the application.
:param str/class default: a default plugin name or a plugin object to use
if one isn't specified by the CLI. default: None.
diff --git a/keystoneclient/auth/identity/v3/base.py b/keystoneclient/auth/identity/v3/base.py
index d82c289..3576045 100644
--- a/keystoneclient/auth/identity/v3/base.py
+++ b/keystoneclient/auth/identity/v3/base.py
@@ -33,7 +33,7 @@ class BaseAuth(base.BaseIdentityPlugin):
"""Identity V3 Authentication Plugin.
:param string auth_url: Identity service endpoint for authentication.
- :param list auth_methods: A collection of methods to authenticate with.
+ :param List auth_methods: A collection of methods to authenticate with.
:param string trust_id: Trust ID for trust scoping.
:param string domain_id: Domain ID for domain scoping.
:param string domain_name: Domain name for domain scoping.
@@ -111,7 +111,7 @@ class Auth(BaseAuth):
"""Identity V3 Authentication Plugin.
:param string auth_url: Identity service endpoint for authentication.
- :param list auth_methods: A collection of methods to authenticate with.
+ :param List auth_methods: A collection of methods to authenticate with.
:param string trust_id: Trust ID for trust scoping.
:param string domain_id: Domain ID for domain scoping.
:param string domain_name: Domain name for domain scoping.
@@ -235,7 +235,7 @@ class AuthMethod(object):
:param session: The communication session.
:type session: keystoneclient.session.Session
- :param Auth auth: The auth plugin calling the method.
+ :param base.Auth auth: The auth plugin calling the method.
:param dict headers: The headers that will be sent with the auth
request if a plugin needs to add to them.
:return: The identifier of this plugin and a dict of authentication
diff --git a/keystoneclient/base.py b/keystoneclient/base.py
index 4e393c6..dc449f7 100644
--- a/keystoneclient/base.py
+++ b/keystoneclient/base.py
@@ -353,7 +353,10 @@ class CrudManager(Manager):
return self._head(self.build_url(dict_args_in_out=kwargs))
def _build_query(self, params):
- return '?%s' % urllib.parse.urlencode(params) if params else ''
+ if params is None:
+ return ''
+ else:
+ return '?%s' % urllib.parse.urlencode(params, doseq=True)
def build_key_only_query(self, params_list):
"""Build a query that does not include values, just keys.
diff --git a/keystoneclient/contrib/ec2/utils.py b/keystoneclient/contrib/ec2/utils.py
index f7fb8a1..dcd3ff5 100644
--- a/keystoneclient/contrib/ec2/utils.py
+++ b/keystoneclient/contrib/ec2/utils.py
@@ -225,7 +225,7 @@ class Ec2Signer(object):
# port if we detect an old boto version. FIXME: remove when all
# distros package boto >= 2.9.3, this is a transitional workaround
user_agent = headers_lower.get('user-agent', '')
- strip_port = re.match('Boto/2.[0-9].[0-2]', user_agent)
+ strip_port = re.match('Boto/2\.[0-9]\.[0-2]', user_agent)
header_list = []
sh_str = auth_param('SignedHeaders')
diff --git a/keystoneclient/discover.py b/keystoneclient/discover.py
index 85b0875..a70b409 100644
--- a/keystoneclient/discover.py
+++ b/keystoneclient/discover.py
@@ -10,7 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
import warnings
from debtcollector import removals
@@ -26,9 +25,6 @@ from keystoneclient.v2_0 import client as v2_client
from keystoneclient.v3 import client as v3_client
-_logger = logging.getLogger(__name__)
-
-
_CLIENT_VERSIONS = {2: v2_client.Client,
3: v3_client.Client}
@@ -231,7 +227,7 @@ class Discover(_discover.Discover):
:returns: The endpoints returned from the server that match the
criteria.
- :rtype: list
+ :rtype: List
Example::
diff --git a/keystoneclient/generic/client.py b/keystoneclient/generic/client.py
index aee7f67..e6b5833 100644
--- a/keystoneclient/generic/client.py
+++ b/keystoneclient/generic/client.py
@@ -15,6 +15,7 @@
import logging
+from debtcollector import removals
from six.moves.urllib import parse as urlparse
from keystoneclient import exceptions
@@ -25,6 +26,11 @@ from keystoneclient.i18n import _
_logger = logging.getLogger(__name__)
+# NOTE(jamielennox): To be removed after Pike.
+@removals.removed_class('keystoneclient.generic.client.Client',
+ message='Use keystoneauth discovery',
+ version='3.9.0',
+ removal_version='4.0.0')
class Client(httpclient.HTTPClient):
"""Client for the OpenStack Keystone pre-version calls API.
diff --git a/keystoneclient/i18n.py b/keystoneclient/i18n.py
index 21879ab..fdbf183 100644
--- a/keystoneclient/i18n.py
+++ b/keystoneclient/i18n.py
@@ -14,7 +14,7 @@
"""oslo.i18n integration module.
-See http://docs.openstack.org/developer/oslo.i18n/usage.html .
+See https://docs.openstack.org/developer/oslo.i18n/usage.html .
"""
diff --git a/keystoneclient/releasenotes/notes/removed-generic-client-ff505b2b01bc9302.yaml b/keystoneclient/releasenotes/notes/removed-generic-client-ff505b2b01bc9302.yaml
new file mode 100644
index 0000000..61b9d17
--- /dev/null
+++ b/keystoneclient/releasenotes/notes/removed-generic-client-ff505b2b01bc9302.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+ - Deprecate the `keystoneclient.generic` client. This client used to be able
+ to determine available API versions and some basics around installed
+ extensions however the APIs were never upgraded for the v3 API. It doesn't
+ seem to be used in the openstack ecosystem.
diff --git a/keystoneclient/session.py b/keystoneclient/session.py
index 1c3f30b..6d7faaa 100644
--- a/keystoneclient/session.py
+++ b/keystoneclient/session.py
@@ -37,6 +37,11 @@ osprofiler_web = importutils.try_import("osprofiler.web")
USER_AGENT = 'python-keystoneclient'
+# NOTE(jamielennox): Clients will likely want to print more than json. Please
+# propose a patch if you have a content type you think is reasonable to print
+# here and we'll add it to the list as required.
+_LOG_CONTENT_TYPES = set(['application/json'])
+
_logger = logging.getLogger(__name__)
@@ -164,7 +169,7 @@ class Session(object):
def _process_header(header):
"""Redact the secure headers to be logged."""
secure_headers = ('authorization', 'x-auth-token',
- 'x-subject-token',)
+ 'x-subject-token', 'x-service-token')
if header[0].lower() in secure_headers:
token_hasher = hashlib.sha1()
token_hasher.update(header[1].encode('utf-8'))
@@ -201,6 +206,11 @@ class Session(object):
% self._process_header(header))
if data:
+ if isinstance(data, six.binary_type):
+ try:
+ data = data.decode("ascii")
+ except UnicodeDecodeError:
+ data = "<binary_data>"
string_parts.append("-d '%s'" % data)
try:
logger.debug(' '.join(string_parts))
@@ -216,7 +226,24 @@ class Session(object):
if not logger.isEnabledFor(logging.DEBUG):
return
- text = _remove_service_catalog(response.text)
+ # NOTE(samueldmq): If the response does not provide enough info about
+ # the content type to decide whether it is useful and safe to log it
+ # or not, just do not log the body. Trying to# read the response body
+ # anyways may result on reading a long stream of bytes and getting an
+ # unexpected MemoryError. See bug 1616105 for further details.
+ content_type = response.headers.get('content-type', None)
+
+ # NOTE(lamt): Per [1], the Content-Type header can be of the form
+ # Content-Type := type "/" subtype *[";" parameter]
+ # [1] https://www.w3.org/Protocols/rfc1341/4_Content-Type.html
+ for log_type in _LOG_CONTENT_TYPES:
+ if content_type is not None and content_type.startswith(log_type):
+ text = _remove_service_catalog(response.text)
+ break
+ else:
+ text = ('Omitted, Content-Type is set to %s. Only '
+ '%s responses have their bodies logged.')
+ text = text % (content_type, ', '.join(_LOG_CONTENT_TYPES))
string_parts = [
'RESP:',
@@ -224,9 +251,7 @@ class Session(object):
]
for header in six.iteritems(response.headers):
string_parts.append('%s: %s' % self._process_header(header))
- if text:
- string_parts.append('\nRESP BODY: %s\n' %
- strutils.mask_password(text))
+ string_parts.append('\nRESP BODY: %s\n' % strutils.mask_password(text))
logger.debug(' '.join(string_parts))
diff --git a/keystoneclient/tests/functional/v3/client_fixtures.py b/keystoneclient/tests/functional/v3/client_fixtures.py
index 37da4a4..dd7209a 100644
--- a/keystoneclient/tests/functional/v3/client_fixtures.py
+++ b/keystoneclient/tests/functional/v3/client_fixtures.py
@@ -114,8 +114,8 @@ class InferenceRule(Base):
self.ref = {'prior_role': self.prior_role,
'implied_role': self.implied_role}
- self.entity = self.client.roles.create_implied(**self.ref)
- self.addCleanup(self.client.roles.delete_implied, self.prior_role,
+ self.entity = self.client.inference_rules.create(**self.ref)
+ self.addCleanup(self.client.inference_rules.delete, self.prior_role,
self.implied_role)
@@ -178,6 +178,18 @@ class Endpoint(Base):
self.addCleanup(self.client.endpoints.delete, self.entity)
+class EndpointGroup(Base):
+
+ def setUp(self):
+ super(EndpointGroup, self).setUp()
+
+ self.ref = {'name': RESOURCE_NAME_PREFIX + uuid.uuid4().hex,
+ 'filters': {'interface': 'public'},
+ 'description': uuid.uuid4().hex}
+ self.entity = self.client.endpoint_groups.create(**self.ref)
+ self.addCleanup(self.client.endpoint_groups.delete, self.entity)
+
+
class Credential(Base):
def __init__(self, client, user, type, project=None):
diff --git a/keystoneclient/tests/functional/v3/test_credentials.py b/keystoneclient/tests/functional/v3/test_credentials.py
index d428f10..a5d00b1 100644
--- a/keystoneclient/tests/functional/v3/test_credentials.py
+++ b/keystoneclient/tests/functional/v3/test_credentials.py
@@ -20,6 +20,11 @@ from keystoneclient.tests.functional.v3 import client_fixtures as fixtures
class CredentialsTestCase(base.V3ClientTestCase):
+ def setUp(self):
+ super(CredentialsTestCase, self).setUp()
+ self.test_domain = fixtures.Domain(self.client)
+ self.useFixture(self.test_domain)
+
def check_credential(self, credential, credential_ref=None):
self.assertIsNotNone(credential.id)
self.assertIn('self', credential.links)
@@ -46,7 +51,7 @@ class CredentialsTestCase(base.V3ClientTestCase):
self.assertIsNotNone(credential.project_id)
def test_create_credential_of_cert_type(self):
- user = fixtures.User(self.client, self.project_domain_id)
+ user = fixtures.User(self.client, self.test_domain.id)
self.useFixture(user)
credential_ref = {'user': user.id,
@@ -58,7 +63,7 @@ class CredentialsTestCase(base.V3ClientTestCase):
self.check_credential(credential, credential_ref)
def test_create_credential_of_ec2_type(self):
- user = fixtures.User(self.client, self.project_domain_id)
+ user = fixtures.User(self.client, self.test_domain.id)
self.useFixture(user)
# project is mandatory attribute if the credential type is ec2
@@ -70,7 +75,7 @@ class CredentialsTestCase(base.V3ClientTestCase):
self.client.credentials.create,
**credential_ref)
- project = fixtures.Project(self.client, self.project_domain_id)
+ project = fixtures.Project(self.client, self.test_domain.id)
self.useFixture(project)
credential_ref = {'user': user.id,
@@ -84,7 +89,7 @@ class CredentialsTestCase(base.V3ClientTestCase):
self.check_credential(credential, credential_ref)
def test_create_credential_of_totp_type(self):
- user = fixtures.User(self.client, self.project_domain_id)
+ user = fixtures.User(self.client, self.test_domain.id)
self.useFixture(user)
credential_ref = {'user': user.id,
@@ -96,9 +101,9 @@ class CredentialsTestCase(base.V3ClientTestCase):
self.check_credential(credential, credential_ref)
def test_get_credential(self):
- user = fixtures.User(self.client, self.project_domain_id)
+ user = fixtures.User(self.client, self.test_domain.id)
self.useFixture(user)
- project = fixtures.Project(self.client, self.project_domain_id)
+ project = fixtures.Project(self.client, self.test_domain.id)
self.useFixture(project)
for credential_type in ['cert', 'ec2', 'totp']:
@@ -111,14 +116,14 @@ class CredentialsTestCase(base.V3ClientTestCase):
self.check_credential(credential_ret, credential.ref)
def test_list_credentials(self):
- user = fixtures.User(self.client, self.project_domain_id)
+ user = fixtures.User(self.client, self.test_domain.id)
self.useFixture(user)
cert_credential = fixtures.Credential(self.client, user=user.id,
type='cert')
self.useFixture(cert_credential)
- project = fixtures.Project(self.client, self.project_domain_id)
+ project = fixtures.Project(self.client, self.test_domain.id)
self.useFixture(project)
ec2_credential = fixtures.Credential(self.client, user=user.id,
type='ec2', project=project.id)
@@ -139,12 +144,12 @@ class CredentialsTestCase(base.V3ClientTestCase):
self.assertIn(totp_credential.entity, credentials)
def test_update_credential(self):
- user = fixtures.User(self.client, self.project_domain_id)
+ user = fixtures.User(self.client, self.test_domain.id)
self.useFixture(user)
- new_user = fixtures.User(self.client, self.project_domain_id)
+ new_user = fixtures.User(self.client, self.test_domain.id)
self.useFixture(new_user)
- new_project = fixtures.Project(self.client, self.project_domain_id)
+ new_project = fixtures.Project(self.client, self.test_domain.id)
self.useFixture(new_project)
credential = fixtures.Credential(self.client, user=user.id,
@@ -166,9 +171,9 @@ class CredentialsTestCase(base.V3ClientTestCase):
self.check_credential(credential_ret, credential.ref)
def test_delete_credential(self):
- user = fixtures.User(self.client, self.project_domain_id)
+ user = fixtures.User(self.client, self.test_domain.id)
self.useFixture(user)
- project = fixtures.Project(self.client, self.project_domain_id)
+ project = fixtures.Project(self.client, self.test_domain.id)
self.useFixture(project)
for credential_type in ['cert', 'ec2', 'totp']:
diff --git a/keystoneclient/tests/functional/v3/test_endpoint_groups.py b/keystoneclient/tests/functional/v3/test_endpoint_groups.py
new file mode 100644
index 0000000..10eccfb
--- /dev/null
+++ b/keystoneclient/tests/functional/v3/test_endpoint_groups.py
@@ -0,0 +1,120 @@
+# 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 EndpointGroupsTestCase(base.V3ClientTestCase):
+
+ def check_endpoint_group(self, endpoint_group, endpoint_group_ref=None):
+ self.assertIsNotNone(endpoint_group.id)
+ self.assertIn('self', endpoint_group.links)
+ self.assertIn('/endpoint_groups/' + endpoint_group.id,
+ endpoint_group.links['self'])
+
+ if endpoint_group_ref:
+ self.assertEqual(endpoint_group_ref['name'], endpoint_group.name)
+ self.assertEqual(endpoint_group_ref['filters'],
+ endpoint_group.filters)
+
+ # There is no guarantee description is present in endpoint groups
+ if hasattr(endpoint_group_ref, 'description'):
+ self.assertEqual(endpoint_group_ref['description'],
+ endpoint_group.description)
+ else:
+ # Only check remaining mandatory attributes
+ self.assertIsNotNone(endpoint_group.name)
+ self.assertIsNotNone(endpoint_group.filters)
+
+ def test_create_endpoint_group(self):
+ endpoint_group_ref = {
+ 'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex,
+ 'filters': {'interface': 'internal'},
+ 'description': uuid.uuid4().hex}
+ endpoint_group = self.client.endpoint_groups.create(
+ **endpoint_group_ref)
+
+ self.addCleanup(self.client.endpoint_groups.delete, endpoint_group)
+ self.check_endpoint_group(endpoint_group, endpoint_group_ref)
+
+ def test_get_endpoint_group(self):
+ endpoint_group = fixtures.EndpointGroup(self.client)
+ self.useFixture(endpoint_group)
+
+ endpoint_ret = self.client.endpoint_groups.get(endpoint_group.id)
+ self.check_endpoint_group(endpoint_ret, endpoint_group.ref)
+
+ self.assertRaises(http.NotFound,
+ self.client.endpoint_groups.get,
+ uuid.uuid4().hex)
+
+ def test_check_endpoint_group(self):
+ endpoint_group = fixtures.EndpointGroup(self.client)
+ self.useFixture(endpoint_group)
+
+ self.client.endpoint_groups.check(endpoint_group.id)
+ self.assertRaises(http.NotFound,
+ self.client.endpoint_groups.check,
+ uuid.uuid4().hex)
+
+ def test_list_endpoint_groups(self):
+ endpoint_group_one = fixtures.EndpointGroup(self.client)
+ self.useFixture(endpoint_group_one)
+
+ endpoint_group_two = fixtures.EndpointGroup(self.client)
+ self.useFixture(endpoint_group_two)
+
+ endpoint_groups = self.client.endpoint_groups.list()
+
+ # All endpoints are valid
+ for endpoint_group in endpoint_groups:
+ self.check_endpoint_group(endpoint_group)
+
+ self.assertIn(endpoint_group_one.entity, endpoint_groups)
+ self.assertIn(endpoint_group_two.entity, endpoint_groups)
+
+ def test_update_endpoint_group(self):
+ endpoint_group = fixtures.EndpointGroup(self.client)
+ self.useFixture(endpoint_group)
+
+ new_name = fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex
+ new_filters = {'interface': 'public'}
+ new_description = uuid.uuid4().hex
+
+ endpoint_group_ret = self.client.endpoint_groups.update(
+ endpoint_group,
+ name=new_name,
+ filters=new_filters,
+ description=new_description)
+
+ endpoint_group.ref.update({'name': new_name, 'filters': new_filters,
+ 'description': new_description})
+ self.check_endpoint_group(endpoint_group_ret, endpoint_group.ref)
+
+ def test_delete_endpoint_group(self):
+ endpoint_group = self.client.endpoint_groups.create(
+ name=fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex,
+ filters={'interface': 'admin'},
+ description=uuid.uuid4().hex)
+
+ self.client.endpoint_groups.delete(endpoint_group.id)
+ self.assertRaises(http.NotFound,
+ self.client.endpoint_groups.check,
+ endpoint_group.id)
+ self.assertRaises(http.NotFound,
+ self.client.endpoint_groups.get,
+ endpoint_group.id)
diff --git a/keystoneclient/tests/functional/v3/test_implied_roles.py b/keystoneclient/tests/functional/v3/test_implied_roles.py
index b2f743c..0d5dbc5 100644
--- a/keystoneclient/tests/functional/v3/test_implied_roles.py
+++ b/keystoneclient/tests/functional/v3/test_implied_roles.py
@@ -48,11 +48,12 @@ class TestImpliedRoles(base.V3ClientTestCase):
super(TestImpliedRoles, self).setUp()
def test_implied_roles(self):
- initial_rule_count = len(self.client.roles.list_role_inferences())
+ initial_rule_count = (
+ len(self.client.inference_rules.list_inference_roles()))
self.create_roles()
self.create_rules()
- rule_count = len(self.client.roles.list_role_inferences())
+ rule_count = len(self.client.inference_rules.list_inference_roles())
self.assertEqual(initial_rule_count + len(inference_rules),
rule_count)
diff --git a/keystoneclient/tests/functional/v3/test_projects.py b/keystoneclient/tests/functional/v3/test_projects.py
index d06aaa8..0fa631d 100644
--- a/keystoneclient/tests/functional/v3/test_projects.py
+++ b/keystoneclient/tests/functional/v3/test_projects.py
@@ -20,6 +20,14 @@ from keystoneclient.tests.functional.v3 import client_fixtures as fixtures
class ProjectsTestCase(base.V3ClientTestCase):
+ def setUp(self):
+ super(ProjectsTestCase, self).setUp()
+ self.test_domain = fixtures.Domain(self.client)
+ self.useFixture(self.test_domain)
+
+ self.test_project = fixtures.Project(self.client, self.test_domain.id)
+ self.useFixture(self.test_project)
+
def check_project(self, project, project_ref=None):
self.assertIsNotNone(project.id)
self.assertIn('self', project.links)
@@ -46,10 +54,10 @@ class ProjectsTestCase(base.V3ClientTestCase):
def test_create_subproject(self):
project_ref = {
'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex,
- 'domain': self.project_domain_id,
+ 'domain': self.test_domain.id,
'enabled': True,
'description': uuid.uuid4().hex,
- 'parent': self.project_id}
+ 'parent': self.test_project.id}
project = self.client.projects.create(**project_ref)
self.addCleanup(self.client.projects.delete, project)
@@ -58,7 +66,7 @@ class ProjectsTestCase(base.V3ClientTestCase):
def test_create_project(self):
project_ref = {
'name': fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex,
- 'domain': self.project_domain_id,
+ 'domain': self.test_domain.id,
'enabled': True,
'description': uuid.uuid4().hex}
@@ -67,31 +75,25 @@ class ProjectsTestCase(base.V3ClientTestCase):
self.check_project(project, project_ref)
def test_get_project(self):
- project = fixtures.Project(self.client, self.project_domain_id)
- self.useFixture(project)
-
- project_ret = self.client.projects.get(project.id)
- self.check_project(project_ret, project.ref)
+ project_ret = self.client.projects.get(self.test_project.id)
+ self.check_project(project_ret, self.test_project.ref)
def test_get_project_invalid_params(self):
self.assertRaises(exceptions.ValidationError,
self.client.projects.get,
- self.project_id,
+ self.test_project.id,
subtree_as_list=True, subtree_as_ids=True)
self.assertRaises(exceptions.ValidationError,
self.client.projects.get,
- self.project_id,
+ self.test_project.id,
parents_as_list=True, parents_as_ids=True)
def test_get_hierarchy_as_list(self):
- parent_project = fixtures.Project(self.client, self.project_domain_id)
- self.useFixture(parent_project)
-
- project = fixtures.Project(self.client, self.project_domain_id,
- parent=parent_project.id)
+ project = fixtures.Project(self.client, self.test_domain.id,
+ parent=self.test_project.id)
self.useFixture(project)
- child_project = fixtures.Project(self.client, self.project_domain_id,
+ child_project = fixtures.Project(self.client, self.test_domain.id,
parent=project.id)
self.useFixture(child_project)
@@ -101,8 +103,9 @@ class ProjectsTestCase(base.V3ClientTestCase):
role = fixtures.Role(self.client)
self.useFixture(role)
self.client.roles.grant(role.id, user=self.user_id,
- project=parent_project.id)
- self.client.roles.grant(role.id, user=self.user_id, project=project.id)
+ project=self.test_project.id)
+ self.client.roles.grant(role.id, user=self.user_id,
+ project=project.id)
self.client.roles.grant(role.id, user=self.user_id,
project=child_project.id)
@@ -111,20 +114,19 @@ class ProjectsTestCase(base.V3ClientTestCase):
parents_as_list=True)
self.check_project(project_ret, project.ref)
- self.assertItemsEqual([{'project': parent_project.entity.to_dict()}],
- project_ret.parents)
- self.assertItemsEqual([{'project': child_project.entity.to_dict()}],
- project_ret.subtree)
+ self.assertItemsEqual(
+ [{'project': self.test_project.entity.to_dict()}],
+ project_ret.parents)
+ self.assertItemsEqual(
+ [{'project': child_project.entity.to_dict()}],
+ project_ret.subtree)
def test_get_hierarchy_as_ids(self):
- parent_project = fixtures.Project(self.client, self.project_domain_id)
- self.useFixture(parent_project)
-
- project = fixtures.Project(self.client, self.project_domain_id,
- parent=parent_project.id)
+ project = fixtures.Project(self.client, self.test_domain.id,
+ parent=self.test_project.id)
self.useFixture(project)
- child_project = fixtures.Project(self.client, self.project_domain_id,
+ child_project = fixtures.Project(self.client, self.test_domain.id,
parent=project.id)
self.useFixture(child_project)
@@ -132,14 +134,14 @@ class ProjectsTestCase(base.V3ClientTestCase):
subtree_as_ids=True,
parents_as_ids=True)
- self.assertItemsEqual([parent_project.id], project_ret.parents)
+ self.assertItemsEqual([self.test_project.id], project_ret.parents)
self.assertItemsEqual([child_project.id], project_ret.subtree)
def test_list_projects(self):
- project_one = fixtures.Project(self.client, self.project_domain_id)
+ project_one = fixtures.Project(self.client, self.test_domain.id)
self.useFixture(project_one)
- project_two = fixtures.Project(self.client, self.project_domain_id)
+ project_two = fixtures.Project(self.client, self.test_domain.id)
self.useFixture(project_two)
projects = self.client.projects.list()
@@ -152,7 +154,7 @@ class ProjectsTestCase(base.V3ClientTestCase):
self.assertIn(project_two.entity, projects)
def test_update_project(self):
- project = fixtures.Project(self.client, self.project_domain_id)
+ project = fixtures.Project(self.client, self.test_domain.id)
self.useFixture(project)
new_name = fixtures.RESOURCE_NAME_PREFIX + uuid.uuid4().hex
@@ -167,19 +169,16 @@ class ProjectsTestCase(base.V3ClientTestCase):
self.check_project(project_ret, project.ref)
def test_update_project_domain_not_allowed(self):
- project = fixtures.Project(self.client)
- self.useFixture(project)
-
domain = fixtures.Domain(self.client)
self.useFixture(domain)
# Cannot update domain after project is created.
self.assertRaises(http.BadRequest,
self.client.projects.update,
- project.id, domain=domain.id)
+ self.test_project.id, domain=domain.id)
def test_delete_project(self):
project = self.client.projects.create(name=uuid.uuid4().hex,
- domain=self.project_domain_id,
+ domain=self.test_domain.id,
enabled=True)
self.client.projects.delete(project.id)
diff --git a/keystoneclient/tests/functional/v3/test_users.py b/keystoneclient/tests/functional/v3/test_users.py
index b39c7f9..780ddba 100644
--- a/keystoneclient/tests/functional/v3/test_users.py
+++ b/keystoneclient/tests/functional/v3/test_users.py
@@ -76,6 +76,29 @@ class UsersTestCase(base.V3ClientTestCase):
self.assertIn(user_one.entity, users)
self.assertIn(user_two.entity, users)
+ def test_list_users_with_filters(self):
+ suffix = uuid.uuid4().hex
+ user1_ref = {
+ 'name': 'test_user' + suffix,
+ 'domain': self.project_domain_id,
+ 'default_project': self.project_id,
+ 'password': uuid.uuid4().hex,
+ 'description': uuid.uuid4().hex}
+
+ user2_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}
+
+ user1 = self.client.users.create(**user1_ref)
+ self.client.users.create(**user2_ref)
+
+ users = self.client.users.list(name__contains=['test_user', suffix])
+ self.assertEqual(1, len(users))
+ self.assertIn(user1, users)
+
def test_update_user(self):
user = fixtures.User(self.client, self.project_domain_id)
self.useFixture(user)
diff --git a/keystoneclient/tests/hacking/__init__.py b/keystoneclient/tests/hacking/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/keystoneclient/tests/hacking/__init__.py
+++ /dev/null
diff --git a/keystoneclient/tests/hacking/checks.py b/keystoneclient/tests/hacking/checks.py
deleted file mode 100644
index 4282698..0000000
--- a/keystoneclient/tests/hacking/checks.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""python-keystoneclient's pep8 extensions.
-
-In order to make the review process faster and easier for core devs we are
-adding some python-keystoneclient specific pep8 checks. This will catch common
-errors so that core devs don't have to.
-
-"""
-
-
-import re
-
-
-def check_oslo_namespace_imports(logical_line, blank_before, filename):
- oslo_namespace_imports = re.compile(
- r"(((from)|(import))\s+oslo\.)|(from\s+oslo\s+import\s+)")
-
- if re.match(oslo_namespace_imports, logical_line):
- msg = ("K333: '%s' must be used instead of '%s'.") % (
- logical_line.replace('oslo.', 'oslo_'),
- logical_line)
- yield(0, msg)
-
-
-def factory(register):
- register(check_oslo_namespace_imports)
diff --git a/keystoneclient/tests/unit/apiclient/test_exceptions.py b/keystoneclient/tests/unit/apiclient/test_exceptions.py
index 4a803c7..ddf8d98 100644
--- a/keystoneclient/tests/unit/apiclient/test_exceptions.py
+++ b/keystoneclient/tests/unit/apiclient/test_exceptions.py
@@ -40,7 +40,7 @@ class ExceptionsArgsTest(utils.TestCase):
method,
url)
self.assertIsInstance(ex, ex_cls)
- self.assertEqual(ex.message, json_data["error"]["message"])
+ self.assertIn(json_data["error"]["message"], ex.message)
self.assertEqual(ex.details, json_data["error"]["details"])
self.assertEqual(ex.method, method)
self.assertEqual(ex.url, url)
diff --git a/keystoneclient/tests/unit/client_fixtures.py b/keystoneclient/tests/unit/client_fixtures.py
index b03f428..0e00545 100644
--- a/keystoneclient/tests/unit/client_fixtures.py
+++ b/keystoneclient/tests/unit/client_fixtures.py
@@ -709,67 +709,6 @@ class Examples(fixtures.Fixture):
EXAMPLES_RESOURCE = testresources.FixtureResource(Examples())
-class HackingCode(fixtures.Fixture):
- """A fixture to house the various code examples.
-
- Examples contains various keystoneclient hacking style checks.
- """
-
- oslo_namespace_imports = {
- 'code': """
- import oslo.utils
- import oslo_utils
- import oslo.utils.encodeutils
- import oslo_utils.encodeutils
- from oslo import utils
- from oslo.utils import encodeutils
- from oslo_utils import encodeutils
-
- import oslo.serialization
- import oslo_serialization
- import oslo.serialization.jsonutils
- import oslo_serialization.jsonutils
- from oslo import serialization
- from oslo.serialization import jsonutils
- from oslo_serialization import jsonutils
-
- import oslo.config
- import oslo_config
- import oslo.config.cfg
- import oslo_config.cfg
- from oslo import config
- from oslo.config import cfg
- from oslo_config import cfg
-
- import oslo.i18n
- import oslo_i18n
- import oslo.i18n.log
- import oslo_i18n.log
- from oslo import i18n
- from oslo.i18n import log
- from oslo_i18n import log
- """,
- 'expected_errors': [
- (1, 0, 'K333'),
- (3, 0, 'K333'),
- (5, 0, 'K333'),
- (6, 0, 'K333'),
- (9, 0, 'K333'),
- (11, 0, 'K333'),
- (13, 0, 'K333'),
- (14, 0, 'K333'),
- (17, 0, 'K333'),
- (19, 0, 'K333'),
- (21, 0, 'K333'),
- (22, 0, 'K333'),
- (25, 0, 'K333'),
- (27, 0, 'K333'),
- (29, 0, 'K333'),
- (30, 0, 'K333'),
- ],
- }
-
-
class Deprecations(fixtures.Fixture):
def setUp(self):
super(Deprecations, self).setUp()
diff --git a/keystoneclient/tests/unit/generic/test_client.py b/keystoneclient/tests/unit/generic/test_client.py
index a3690fb..5c27b6e 100644
--- a/keystoneclient/tests/unit/generic/test_client.py
+++ b/keystoneclient/tests/unit/generic/test_client.py
@@ -22,7 +22,8 @@ BASE_HOST = 'http://keystone.example.com'
BASE_URL = "%s:5000/" % BASE_HOST
V2_URL = "%sv2.0" % BASE_URL
-EXTENSION_NAMESPACE = "http://docs.openstack.org/identity/api/ext/OS-FAKE/v1.0"
+EXTENSION_NAMESPACE = ("https://docs.openstack.org/identity/api/ext/OS-FAKE/"
+ "v1.0")
EXTENSION_DESCRIBED = {"href": "https://github.com/openstack/identity-api",
"rel": "describedby",
"type": "text/html"}
@@ -58,6 +59,7 @@ class ClientDiscoveryTests(utils.TestCase):
def test_discover_extensions_v2(self):
self.requests_mock.get("%s/extensions" % V2_URL, text=EXTENSION_LIST)
# Creating a HTTPClient not using session is deprecated.
+ # creating a generic client at all is deprecated.
with self.deprecations.expect_deprecations_here():
extensions = client.Client().discover_extensions(url=V2_URL)
self.assertIn(EXTENSION_ALIAS_FOO, extensions)
diff --git a/keystoneclient/tests/unit/test_base.py b/keystoneclient/tests/unit/test_base.py
index f6ca651..0a0fde1 100644
--- a/keystoneclient/tests/unit/test_base.py
+++ b/keystoneclient/tests/unit/test_base.py
@@ -11,9 +11,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+import fixtures
from keystoneauth1.identity import v2
from keystoneauth1 import session
-from oslotest import mockpatch
from keystoneclient import base
from keystoneclient.tests.unit import utils
@@ -44,7 +44,7 @@ class BaseTest(utils.TestCase):
session_ = session.Session(auth=auth)
self.client = client.Client(session=session_)
- self.useFixture(mockpatch.PatchObject(
+ self.useFixture(fixtures.MockPatchObject(
self.client._adapter, 'get', side_effect=AttributeError,
autospec=True))
@@ -127,7 +127,7 @@ class ManagerTest(utils.TestCase):
self.assertEqual(self.mgr.api, self.client)
def test_get(self):
- get_mock = self.useFixture(mockpatch.PatchObject(
+ get_mock = self.useFixture(fixtures.MockPatchObject(
self.client, 'get', autospec=True, return_value=(None, self.body))
).mock
rsrc = self.mgr._get(self.url, "hello")
@@ -135,7 +135,7 @@ class ManagerTest(utils.TestCase):
self.assertEqual(rsrc.hi, 1)
def test_post(self):
- post_mock = self.useFixture(mockpatch.PatchObject(
+ post_mock = self.useFixture(fixtures.MockPatchObject(
self.client, 'post', autospec=True, return_value=(None, self.body))
).mock
@@ -150,7 +150,7 @@ class ManagerTest(utils.TestCase):
self.assertEqual(rsrc["hi"], 1)
def test_put(self):
- put_mock = self.useFixture(mockpatch.PatchObject(
+ put_mock = self.useFixture(fixtures.MockPatchObject(
self.client, 'put', autospec=True, return_value=(None, self.body))
).mock
@@ -165,7 +165,7 @@ class ManagerTest(utils.TestCase):
self.assertEqual(rsrc.hello["hi"], 1)
def test_patch(self):
- patch_mock = self.useFixture(mockpatch.PatchObject(
+ patch_mock = self.useFixture(fixtures.MockPatchObject(
self.client, 'patch', autospec=True,
return_value=(None, self.body))
).mock
@@ -181,12 +181,12 @@ class ManagerTest(utils.TestCase):
self.assertEqual(rsrc.hello["hi"], 1)
def test_update(self):
- patch_mock = self.useFixture(mockpatch.PatchObject(
+ patch_mock = self.useFixture(fixtures.MockPatchObject(
self.client, 'patch', autospec=True,
return_value=(None, self.body))
).mock
- put_mock = self.useFixture(mockpatch.PatchObject(
+ put_mock = self.useFixture(fixtures.MockPatchObject(
self.client, 'put', autospec=True, return_value=(None, self.body))
).mock
diff --git a/keystoneclient/tests/unit/test_ec2utils.py b/keystoneclient/tests/unit/test_ec2utils.py
index 5102f6b..1d8809b 100644
--- a/keystoneclient/tests/unit/test_ec2utils.py
+++ b/keystoneclient/tests/unit/test_ec2utils.py
@@ -265,3 +265,42 @@ class Ec2SignerTest(testtools.TestCase):
expected = ('26dd92ea79aaa49f533d13b1055acdc'
'd7d7321460d64621f96cc79c4f4d4ab2b')
self.assertEqual(expected, signature)
+
+ def test_generate_v4_port_malformed_version(self):
+ """Test v4 generator with host:port format for malformed boto version.
+
+ Validate for malformed version of boto, where the port should
+ not be stripped.
+ """
+ # Create a new signer object with the AWS example key
+ secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
+ signer = utils.Ec2Signer(secret)
+
+ body_hash = ('b6359072c78d70ebee1e81adcbab4f0'
+ '1bf2c23245fa365ef83fe8f1f955085e2')
+ auth_str = ('AWS4-HMAC-SHA256 '
+ 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/'
+ 'us-east-1/iam/aws4_request,'
+ 'SignedHeaders=content-type;host;x-amz-date,')
+ headers = {'Content-type':
+ 'application/x-www-form-urlencoded; charset=utf-8',
+ 'X-Amz-Date': '20110909T233600Z',
+ 'Host': 'foo:8000',
+ 'Authorization': auth_str,
+ 'User-Agent': 'Boto/2.922 (linux2)'}
+ # Note the example in the AWS docs is inconsistent, previous
+ # examples specify no query string, but the final POST example
+ # does, apparently incorrectly since an empty parameter list
+ # aligns all steps and the final signature with the examples
+ params = {}
+ credentials = {'host': 'foo:8000',
+ 'verb': 'POST',
+ 'path': '/',
+ 'params': params,
+ 'headers': headers,
+ 'body_hash': body_hash}
+ signature = signer.generate(credentials)
+
+ expected = ('26dd92ea79aaa49f533d13b1055acdc'
+ 'd7d7321460d64621f96cc79c4f4d4ab2b')
+ self.assertEqual(expected, signature)
diff --git a/keystoneclient/tests/unit/test_hacking_checks.py b/keystoneclient/tests/unit/test_hacking_checks.py
deleted file mode 100644
index f1e81c9..0000000
--- a/keystoneclient/tests/unit/test_hacking_checks.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import textwrap
-
-import mock
-import pep8
-import testtools
-
-from keystoneclient.tests.hacking import checks
-from keystoneclient.tests.unit import client_fixtures
-
-
-class TestCheckOsloNamespaceImports(testtools.TestCase):
- def setUp(self):
- super(TestCheckOsloNamespaceImports, self).setUp()
- self.useFixture(client_fixtures.Deprecations())
-
- # We are patching pep8 so that only the check under test is actually
- # installed.
- @mock.patch('pep8._checks',
- {'physical_line': {}, 'logical_line': {}, 'tree': {}})
- def run_check(self, code):
- pep8.register_check(checks.check_oslo_namespace_imports)
-
- lines = textwrap.dedent(code).strip().splitlines(True)
-
- checker = pep8.Checker(lines=lines)
- checker.check_all()
- checker.report._deferred_print.sort()
- return checker.report._deferred_print
-
- def assert_has_errors(self, code, expected_errors=None):
- actual_errors = [e[:3] for e in self.run_check(code)]
- self.assertEqual(expected_errors or [], actual_errors)
-
- def test(self):
- code_ex = self.useFixture(client_fixtures.HackingCode())
- code = code_ex.oslo_namespace_imports['code']
- errors = code_ex.oslo_namespace_imports['expected_errors']
- self.assert_has_errors(code, expected_errors=errors)
diff --git a/keystoneclient/tests/unit/test_http.py b/keystoneclient/tests/unit/test_http.py
index 56f116c..0282f1a 100644
--- a/keystoneclient/tests/unit/test_http.py
+++ b/keystoneclient/tests/unit/test_http.py
@@ -97,7 +97,7 @@ class ClientTest(utils.TestCase):
cl.get('/hi')
except exceptions.BadRequest as exc:
exc_raised = True
- self.assertEqual(exc.message, "Error message string")
+ self.assertEqual(exc.message, "Error message string (HTTP 400)")
self.assertTrue(exc_raised, 'Exception not raised.')
def test_post(self):
@@ -166,22 +166,24 @@ class BasicRequestTests(utils.TestCase):
self.addCleanup(self.logger.setLevel, level)
def request(self, method='GET', response='Test Response', status_code=200,
- url=None, **kwargs):
+ url=None, headers={}, **kwargs):
if not url:
url = self.url
self.requests_mock.register_uri(method, url, text=response,
- status_code=status_code)
+ status_code=status_code,
+ headers=headers)
with self.deprecations.expect_deprecations_here():
- return httpclient.request(url, method, **kwargs)
+ return httpclient.request(url, method, headers=headers, **kwargs)
def test_basic_params(self):
method = 'GET'
response = 'Test Response'
status = 200
- self.request(method=method, status_code=status, response=response)
+ self.request(method=method, status_code=status, response=response,
+ headers={'Content-Type': 'application/json'})
self.assertEqual(self.requests_mock.last_request.method, method)
@@ -209,7 +211,8 @@ class BasicRequestTests(utils.TestCase):
def test_body(self):
data = "BODY DATA"
- self.request(response=data)
+ self.request(response=data,
+ headers={'Content-Type': 'application/json'})
logger_message = self.logger_message.getvalue()
self.assertThat(logger_message, matchers.Contains('BODY:'))
self.assertThat(logger_message, matchers.Contains(data))
diff --git a/keystoneclient/tests/unit/test_session.py b/keystoneclient/tests/unit/test_session.py
index 8fb364a..7a3c57d 100644
--- a/keystoneclient/tests/unit/test_session.py
+++ b/keystoneclient/tests/unit/test_session.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+#
# 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
@@ -149,12 +151,14 @@ class SessionTests(utils.TestCase):
in order to redact secure headers while debug is true.
"""
session = client_session.Session(verify=False)
- headers = {'HEADERA': 'HEADERVALB'}
+ headers = {'HEADERA': 'HEADERVALB',
+ 'Content-Type': 'application/json'}
security_headers = {'Authorization': uuid.uuid4().hex,
'X-Auth-Token': uuid.uuid4().hex,
- 'X-Subject-Token': uuid.uuid4().hex, }
- body = 'BODYRESPONSE'
- data = 'BODYDATA'
+ 'X-Subject-Token': uuid.uuid4().hex,
+ 'X-Service-Token': uuid.uuid4().hex}
+ body = '{"a": "b"}'
+ data = '{"c": "d"}'
all_headers = dict(
itertools.chain(headers.items(), security_headers.items()))
self.stub_url('POST', text=body, headers=all_headers)
@@ -181,20 +185,65 @@ class SessionTests(utils.TestCase):
def test_logs_failed_output(self):
"""Test that output is logged even for failed requests."""
session = client_session.Session()
- body = uuid.uuid4().hex
+ body = {uuid.uuid4().hex: uuid.uuid4().hex}
- self.stub_url('GET', text=body, status_code=400)
+ self.stub_url('GET', json=body, status_code=400,
+ headers={'Content-Type': 'application/json'})
resp = session.get(self.TEST_URL, raise_exc=False)
self.assertEqual(resp.status_code, 400)
+ self.assertIn(list(body.keys())[0], self.logger.output)
+ self.assertIn(list(body.values())[0], self.logger.output)
+
+ def test_logging_body_only_for_specified_content_types(self):
+ """Verify response body is only logged in specific content types.
+
+ Response bodies are logged only when the response's Content-Type header
+ is set to application/json. This prevents us to get an unexpected
+ MemoryError when reading arbitrary responses, such as streams.
+ """
+ OMITTED_BODY = ('Omitted, Content-Type is set to %s. Only '
+ 'application/json responses have their bodies logged.')
+ session = client_session.Session(verify=False)
+
+ # Content-Type is not set
+ body = jsonutils.dumps({'token': {'id': '...'}})
+ self.stub_url('POST', text=body)
+ session.post(self.TEST_URL)
+ self.assertNotIn(body, self.logger.output)
+ self.assertIn(OMITTED_BODY % None, self.logger.output)
+
+ # Content-Type is set to text/xml
+ body = '<token><id>...</id></token>'
+ self.stub_url('POST', text=body, headers={'Content-Type': 'text/xml'})
+ session.post(self.TEST_URL)
+ self.assertNotIn(body, self.logger.output)
+ self.assertIn(OMITTED_BODY % 'text/xml', self.logger.output)
+
+ # Content-Type is set to application/json
+ body = jsonutils.dumps({'token': {'id': '...'}})
+ self.stub_url('POST', text=body,
+ headers={'Content-Type': 'application/json'})
+ session.post(self.TEST_URL)
+ self.assertIn(body, self.logger.output)
+ self.assertNotIn(OMITTED_BODY % 'application/json', self.logger.output)
+
+ # Content-Type is set to application/json; charset=UTF-8
+ body = jsonutils.dumps({'token': {'id': '...'}})
+ self.stub_url(
+ 'POST', text=body,
+ headers={'Content-Type': 'application/json; charset=UTF-8'})
+ session.post(self.TEST_URL)
self.assertIn(body, self.logger.output)
+ self.assertNotIn(OMITTED_BODY % 'application/json; charset=UTF-8',
+ self.logger.output)
def test_unicode_data_in_debug_output(self):
"""Verify that ascii-encodable data is logged without modification."""
session = client_session.Session(verify=False)
body = 'RESP'
- data = u'unicode_data'
+ data = u'αβγδ'
self.stub_url('POST', text=body)
session.post(self.TEST_URL, data=data)
@@ -219,12 +268,7 @@ class SessionTests(utils.TestCase):
# raise a UnicodeDecodeError)
session.post(unicode(self.TEST_URL), data=data)
- self.assertIn("Replaced characters that could not be decoded"
- " in log output", self.logger.output)
-
- # Our data payload should have changed to
- # include the replacement char
- self.assertIn(u"-d 'my data\ufffd'", self.logger.output)
+ self.assertNotIn('my data', self.logger.output)
def test_logging_cacerts(self):
path_to_certs = '/path/to/certs'
@@ -315,7 +359,8 @@ class SessionTests(utils.TestCase):
"auth_username": "verybadusername",
"auth_method": "CHAP"}}}
body_json = jsonutils.dumps(body)
- response = mock.Mock(text=body_json, status_code=200, headers={})
+ response = mock.Mock(text=body_json, status_code=200,
+ headers={'content-type': 'application/json'})
session._http_log_response(response, logger)
self.assertEqual(1, logger.debug.call_count)
@@ -768,22 +813,24 @@ class SessionAuthTests(utils.TestCase):
auth = AuthPlugin()
sess = client_session.Session(auth=auth)
- response = uuid.uuid4().hex
+ response = {uuid.uuid4().hex: uuid.uuid4().hex}
self.stub_url('GET',
- text=response,
- headers={'Content-Type': 'text/html'})
+ json=response,
+ headers={'Content-Type': 'application/json'})
resp = sess.get(self.TEST_URL, logger=logger)
- self.assertEqual(response, resp.text)
+ self.assertEqual(response, resp.json())
output = io.getvalue()
self.assertIn(self.TEST_URL, output)
- self.assertIn(response, output)
+ self.assertIn(list(response.keys())[0], output)
+ self.assertIn(list(response.values())[0], output)
self.assertNotIn(self.TEST_URL, self.logger.output)
- self.assertNotIn(response, self.logger.output)
+ self.assertNotIn(list(response.keys())[0], self.logger.output)
+ self.assertNotIn(list(response.values())[0], self.logger.output)
class AdapterTest(utils.TestCase):
@@ -965,21 +1012,23 @@ class AdapterTest(utils.TestCase):
sess = client_session.Session(auth=auth)
adpt = adapter.Adapter(sess, auth=auth, logger=logger)
- response = uuid.uuid4().hex
+ response = {uuid.uuid4().hex: uuid.uuid4().hex}
- self.stub_url('GET', text=response,
- headers={'Content-Type': 'text/html'})
+ self.stub_url('GET', json=response,
+ headers={'Content-Type': 'application/json'})
resp = adpt.get(self.TEST_URL, logger=logger)
- self.assertEqual(response, resp.text)
+ self.assertEqual(response, resp.json())
output = io.getvalue()
self.assertIn(self.TEST_URL, output)
- self.assertIn(response, output)
+ self.assertIn(list(response.keys())[0], output)
+ self.assertIn(list(response.values())[0], output)
self.assertNotIn(self.TEST_URL, self.logger.output)
- self.assertNotIn(response, self.logger.output)
+ self.assertNotIn(list(response.keys())[0], self.logger.output)
+ self.assertNotIn(list(response.values())[0], self.logger.output)
class ConfLoadingTests(utils.TestCase):
diff --git a/keystoneclient/tests/unit/v2_0/test_client.py b/keystoneclient/tests/unit/v2_0/test_client.py
index 90b6450..fc9bf14 100644
--- a/keystoneclient/tests/unit/v2_0/test_client.py
+++ b/keystoneclient/tests/unit/v2_0/test_client.py
@@ -218,4 +218,4 @@ class KeystoneClientTest(utils.TestCase):
# authenticated
sess = auth_session.Session()
cl = client.Client(session=sess)
- self.assertEqual(None, cl.service_catalog)
+ self.assertIsNone(cl.service_catalog)
diff --git a/keystoneclient/tests/unit/v2_0/test_discovery.py b/keystoneclient/tests/unit/v2_0/test_discovery.py
index 5afe59a..a3700e0 100644
--- a/keystoneclient/tests/unit/v2_0/test_discovery.py
+++ b/keystoneclient/tests/unit/v2_0/test_discovery.py
@@ -29,11 +29,11 @@ class DiscoverKeystoneTests(utils.UnauthenticatedTestCase):
"href": "http://127.0.0.1:5000/v2.0/", },
{"rel": "describedby",
"type": "text/html",
- "href": "http://docs.openstack.org/api/"
+ "href": "https://docs.openstack.org/api/"
"openstack-identity-service/2.0/content/", },
{"rel": "describedby",
"type": "application/pdf",
- "href": "http://docs.openstack.org/api/"
+ "href": "https://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"identity-dev-guide-2.0.pdf", },
{"rel": "describedby",
diff --git a/keystoneclient/tests/unit/v2_0/test_extensions.py b/keystoneclient/tests/unit/v2_0/test_extensions.py
index 662d380..3927bc0 100644
--- a/keystoneclient/tests/unit/v2_0/test_extensions.py
+++ b/keystoneclient/tests/unit/v2_0/test_extensions.py
@@ -22,7 +22,7 @@ class ExtensionTests(utils.ClientTestCase):
"values": [
{
'name': 'OpenStack Keystone User CRUD',
- 'namespace': 'http://docs.openstack.org/'
+ 'namespace': 'https://docs.openstack.org/'
'identity/api/ext/OS-KSCRUD/v1.0',
'updated': '2013-07-07T12:00:0-00:00',
'alias': 'OS-KSCRUD',
@@ -36,7 +36,7 @@ class ExtensionTests(utils.ClientTestCase):
},
{
'name': 'OpenStack EC2 API',
- 'namespace': 'http://docs.openstack.org/'
+ 'namespace': 'https://docs.openstack.org/'
'identity/api/ext/OS-EC2/v1.0',
'updated': '2013-09-07T12:00:0-00:00',
'alias': 'OS-EC2',
diff --git a/keystoneclient/tests/unit/v3/test_client.py b/keystoneclient/tests/unit/v3/test_client.py
index 42004ff..29a2818 100644
--- a/keystoneclient/tests/unit/v3/test_client.py
+++ b/keystoneclient/tests/unit/v3/test_client.py
@@ -268,4 +268,4 @@ class KeystoneClientTest(utils.TestCase):
# authenticated
sess = auth_session.Session()
cl = client.Client(session=sess)
- self.assertEqual(None, cl.service_catalog)
+ self.assertIsNone(cl.service_catalog)
diff --git a/keystoneclient/tests/unit/v3/test_discover.py b/keystoneclient/tests/unit/v3/test_discover.py
index 898d46b..f54b2f9 100644
--- a/keystoneclient/tests/unit/v3/test_discover.py
+++ b/keystoneclient/tests/unit/v3/test_discover.py
@@ -27,12 +27,12 @@ class DiscoverKeystoneTests(utils.UnauthenticatedTestCase):
"href": "http://127.0.0.1:5000/v3.0/", },
{"rel": "describedby",
"type": "text/html",
- "href": "http://docs.openstack.org/api/"
+ "href": "https://docs.openstack.org/api/"
"openstack-identity-service/3/"
"content/", },
{"rel": "describedby",
"type": "application/pdf",
- "href": "http://docs.openstack.org/api/"
+ "href": "https://docs.openstack.org/api/"
"openstack-identity-service/3/"
"identity-dev-guide-3.pdf", },
]},
@@ -44,12 +44,12 @@ class DiscoverKeystoneTests(utils.UnauthenticatedTestCase):
"href": "http://127.0.0.1:5000/v2.0/", },
{"rel": "describedby",
"type": "text/html",
- "href": "http://docs.openstack.org/api/"
+ "href": "https://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"content/", },
{"rel": "describedby",
"type": "application/pdf",
- "href": "http://docs.openstack.org/api/"
+ "href": "https://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"identity-dev-guide-2.0.pdf", }
]}],
diff --git a/keystoneclient/tests/unit/v3/test_endpoint_groups.py b/keystoneclient/tests/unit/v3/test_endpoint_groups.py
new file mode 100644
index 0000000..364fd53
--- /dev/null
+++ b/keystoneclient/tests/unit/v3/test_endpoint_groups.py
@@ -0,0 +1,34 @@
+# 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 keystoneclient.tests.unit.v3 import utils
+from keystoneclient.v3 import endpoint_groups
+
+
+class EndpointGroupTests(utils.ClientTestCase, utils.CrudTests):
+
+ def setUp(self):
+ super(EndpointGroupTests, self).setUp()
+ self.key = 'endpoint_group'
+ self.collection_key = 'endpoint_groups'
+ self.model = endpoint_groups.EndpointGroup
+ self.manager = self.client.endpoint_groups
+ self.path_prefix = 'OS-EP-FILTER'
+
+ def new_ref(self, **kwargs):
+ kwargs.setdefault('id', uuid.uuid4().hex)
+ kwargs.setdefault('name', uuid.uuid4().hex)
+ kwargs.setdefault('filters', '{"interface": "public"}')
+ kwargs.setdefault('description', uuid.uuid4().hex)
+ return kwargs
diff --git a/keystoneclient/tests/unit/v3/test_roles.py b/keystoneclient/tests/unit/v3/test_roles.py
index 7dfd7f2..51d9fc7 100644
--- a/keystoneclient/tests/unit/v3/test_roles.py
+++ b/keystoneclient/tests/unit/v3/test_roles.py
@@ -599,70 +599,226 @@ class RoleTests(utils.ClientTestCase, utils.CrudTests):
group=group_id,
user=user_id)
- def test_implied_role_check(self):
+
+class DeprecatedImpliedRoleTests(utils.ClientTestCase):
+ def setUp(self):
+ super(DeprecatedImpliedRoleTests, self).setUp()
+ self.key = 'role'
+ self.collection_key = 'roles'
+ self.model = roles.Role
+ self.manager = self.client.roles
+
+ def test_implied_create(self):
+ prior_id = uuid.uuid4().hex
+ prior_name = uuid.uuid4().hex
+ implied_id = uuid.uuid4().hex
+ implied_name = uuid.uuid4().hex
+
+ mock_response = {
+ "role_inference": {
+ "implies": {
+ "id": implied_id,
+ "links": {"self": "http://host/v3/roles/%s" % implied_id},
+ "name": implied_name
+ },
+ "prior_role": {
+ "id": prior_id,
+ "links": {"self": "http://host/v3/roles/%s" % prior_id},
+ "name": prior_name
+ }
+ }
+ }
+
+ self.stub_url('PUT',
+ ['roles', prior_id, 'implies', implied_id],
+ json=mock_response,
+ status_code=201)
+
+ with self.deprecations.expect_deprecations_here():
+ manager_result = self.manager.create_implied(prior_id, implied_id)
+ self.assertIsInstance(manager_result, roles.InferenceRule)
+ self.assertEqual(mock_response['role_inference']['implies'],
+ manager_result.implies)
+ self.assertEqual(mock_response['role_inference']['prior_role'],
+ manager_result.prior_role)
+
+
+class ImpliedRoleTests(utils.ClientTestCase, utils.CrudTests):
+ def setUp(self):
+ super(ImpliedRoleTests, self).setUp()
+ self.key = 'role_inference'
+ self.collection_key = 'role_inferences'
+ self.model = roles.InferenceRule
+ self.manager = self.client.inference_rules
+
+ def test_check(self):
prior_role_id = uuid.uuid4().hex
implied_role_id = uuid.uuid4().hex
self.stub_url('HEAD',
['roles', prior_role_id, 'implies', implied_role_id],
status_code=200)
- self.manager.check_implied(prior_role_id, implied_role_id)
+ result = self.manager.check(prior_role_id, implied_role_id)
+ self.assertTrue(result)
+
+ def test_get(self):
+ prior_id = uuid.uuid4().hex
+ prior_name = uuid.uuid4().hex
+ implied_id = uuid.uuid4().hex
+ implied_name = uuid.uuid4().hex
+
+ mock_response = {
+ "role_inference": {
+ "implies": {
+ "id": implied_id,
+ "links": {"self": "http://host/v3/roles/%s" % implied_id},
+ "name": implied_name
+ },
+ "prior_role": {
+ "id": prior_id,
+ "links": {"self": "http://host/v3/roles/%s" % prior_id},
+ "name": prior_name
+ }
+ }
+ }
- def test_implied_role_get(self):
- prior_role_id = uuid.uuid4().hex
- implied_role_id = uuid.uuid4().hex
self.stub_url('GET',
- ['roles', prior_role_id, 'implies', implied_role_id],
- json={'role': {}},
- status_code=204)
+ ['roles', prior_id, 'implies', implied_id],
+ json=mock_response,
+ status_code=200)
- self.manager.get_implied(prior_role_id, implied_role_id)
+ manager_result = self.manager.get(prior_id, implied_id)
+ self.assertIsInstance(manager_result, roles.InferenceRule)
+ self.assertEqual(mock_response['role_inference']['implies'],
+ manager_result.implies)
+ self.assertEqual(mock_response['role_inference']['prior_role'],
+ manager_result.prior_role)
- def test_implied_role_create(self):
- prior_role_id = uuid.uuid4().hex
- implied_role_id = uuid.uuid4().hex
- test_json = {
+ def test_create(self):
+ prior_id = uuid.uuid4().hex
+ prior_name = uuid.uuid4().hex
+ implied_id = uuid.uuid4().hex
+ implied_name = uuid.uuid4().hex
+
+ mock_response = {
"role_inference": {
- "prior_role": {
- "id": prior_role_id,
- "links": {},
- "name": "prior role name"
- },
"implies": {
- "id": implied_role_id,
- "links": {},
- "name": "implied role name"
+ "id": implied_id,
+ "links": {"self": "http://host/v3/roles/%s" % implied_id},
+ "name": implied_name
+ },
+ "prior_role": {
+ "id": prior_id,
+ "links": {"self": "http://host/v3/roles/%s" % prior_id},
+ "name": prior_name
}
- },
- "links": {}
+ }
}
self.stub_url('PUT',
- ['roles', prior_role_id, 'implies', implied_role_id],
- json=test_json,
- status_code=200)
+ ['roles', prior_id, 'implies', implied_id],
+ json=mock_response,
+ status_code=201)
- returned_rule = self.manager.create_implied(
- prior_role_id, implied_role_id)
+ manager_result = self.manager.create(prior_id, implied_id)
- self.assertEqual(test_json['role_inference']['implies'],
- returned_rule.implies)
- self.assertEqual(test_json['role_inference']['prior_role'],
- returned_rule.prior_role)
+ self.assertIsInstance(manager_result, roles.InferenceRule)
+ self.assertEqual(mock_response['role_inference']['implies'],
+ manager_result.implies)
+ self.assertEqual(mock_response['role_inference']['prior_role'],
+ manager_result.prior_role)
- def test_implied_role_delete(self):
+ def test_delete(self):
prior_role_id = uuid.uuid4().hex
implied_role_id = uuid.uuid4().hex
self.stub_url('DELETE',
['roles', prior_role_id, 'implies', implied_role_id],
- status_code=200)
+ status_code=204)
- self.manager.delete_implied(prior_role_id, implied_role_id)
+ status, body = self.manager.delete(prior_role_id, implied_role_id)
+ self.assertEqual(204, status.status_code)
+ self.assertIsNone(body)
+
+ def test_list_role_inferences(self):
+ prior_id = uuid.uuid4().hex
+ prior_name = uuid.uuid4().hex
+ implied_id = uuid.uuid4().hex
+ implied_name = uuid.uuid4().hex
+
+ mock_response = {
+ "role_inferences": [{
+ "implies": [{
+ "id": implied_id,
+ "links": {"self": "http://host/v3/roles/%s" % implied_id},
+ "name": implied_name
+ }],
+ "prior_role": {
+ "id": prior_id,
+ "links": {"self": "http://host/v3/roles/%s" % prior_id},
+ "name": prior_name
+ }
+ }]
+ }
- def test_list_role_inferences(self, **kwargs):
self.stub_url('GET',
- ['role_inferences', ''],
- json={'role_inferences': {}},
- status_code=204)
+ ['role_inferences'],
+ json=mock_response,
+ status_code=200)
+ manager_result = self.manager.list_inference_roles()
+ self.assertEqual(1, len(manager_result))
+ self.assertIsInstance(manager_result[0], roles.InferenceRule)
+ self.assertEqual(mock_response['role_inferences'][0]['implies'],
+ manager_result[0].implies)
+ self.assertEqual(mock_response['role_inferences'][0]['prior_role'],
+ manager_result[0].prior_role)
+
+ def test_list(self):
+ prior_id = uuid.uuid4().hex
+ prior_name = uuid.uuid4().hex
+ implied_id = uuid.uuid4().hex
+ implied_name = uuid.uuid4().hex
+
+ mock_response = {
+ "role_inference": {
+ "implies": [{
+ "id": implied_id,
+ "links": {"self": "http://host/v3/roles/%s" % implied_id},
+ "name": implied_name
+ }],
+ "prior_role": {
+ "id": prior_id,
+ "links": {"self": "http://host/v3/roles/%s" % prior_id},
+ "name": prior_name
+ }
+ },
+ "links": {"self": "http://host/v3/roles/%s/implies" % prior_id}
+ }
+
+ self.stub_url('GET',
+ ['roles', prior_id, 'implies'],
+ json=mock_response,
+ status_code=200)
- self.manager.list_role_inferences()
+ manager_result = self.manager.list(prior_id)
+ self.assertIsInstance(manager_result, roles.InferenceRule)
+ self.assertEqual(1, len(manager_result.implies))
+ self.assertEqual(mock_response['role_inference']['implies'],
+ manager_result.implies)
+ self.assertEqual(mock_response['role_inference']['prior_role'],
+ manager_result.prior_role)
+
+ def test_update(self):
+ # Update not supported for rule inferences
+ self.assertRaises(exceptions.MethodNotImplemented, self.manager.update)
+
+ def test_find(self):
+ # Find not supported for rule inferences
+ self.assertRaises(exceptions.MethodNotImplemented, self.manager.find)
+
+ def test_put(self):
+ # Put not supported for rule inferences
+ self.assertRaises(exceptions.MethodNotImplemented, self.manager.put)
+
+ def test_list_params(self):
+ # Put not supported for rule inferences
+ self.skipTest("list params not supported by rule inferences")
diff --git a/keystoneclient/utils.py b/keystoneclient/utils.py
index cf77bd4..8e8dd7c 100644
--- a/keystoneclient/utils.py
+++ b/keystoneclient/utils.py
@@ -12,7 +12,6 @@
import getpass
import hashlib
-import logging
import sys
from keystoneauth1 import exceptions as ksa_exceptions
@@ -25,9 +24,6 @@ import six
from keystoneclient import exceptions as ksc_exceptions
-logger = logging.getLogger(__name__)
-
-
def find_resource(manager, name_or_id):
"""Helper for the _find_* methods."""
# first try the entity as a string
diff --git a/keystoneclient/v3/client.py b/keystoneclient/v3/client.py
index 62c0c8d..2ca180a 100644
--- a/keystoneclient/v3/client.py
+++ b/keystoneclient/v3/client.py
@@ -33,6 +33,7 @@ from keystoneclient.v3 import credentials
from keystoneclient.v3 import domain_configs
from keystoneclient.v3 import domains
from keystoneclient.v3 import ec2
+from keystoneclient.v3 import endpoint_groups
from keystoneclient.v3 import endpoints
from keystoneclient.v3 import groups
from keystoneclient.v3 import policies
@@ -130,6 +131,11 @@ class Client(httpclient.HTTPClient):
:py:class:`keystoneclient.v3.contrib.endpoint_filter.\
EndpointFilterManager`
+ .. py:attribute:: endpoint_groups
+
+ :py:class:`keystoneclient.v3.endpoint_groups.\
+ EndpointGroupManager`
+
.. py:attribute:: endpoint_policy
:py:class:`keystoneclient.v3.contrib.endpoint_policy.\
@@ -211,6 +217,8 @@ class Client(httpclient.HTTPClient):
self.ec2 = ec2.EC2Manager(self._adapter)
self.endpoint_filter = endpoint_filter.EndpointFilterManager(
self._adapter)
+ self.endpoint_groups = endpoint_groups.EndpointGroupManager(
+ self._adapter)
self.endpoint_policy = endpoint_policy.EndpointPolicyManager(
self._adapter)
self.endpoints = endpoints.EndpointManager(self._adapter)
@@ -225,6 +233,7 @@ class Client(httpclient.HTTPClient):
self.role_assignments = (
role_assignments.RoleAssignmentManager(self._adapter))
self.roles = roles.RoleManager(self._adapter)
+ self.inference_rules = roles.InferenceRuleManager(self._adapter)
self.services = services.ServiceManager(self._adapter)
self.simple_cert = simple_cert.SimpleCertManager(self._adapter)
self.tokens = tokens.TokenManager(self._adapter)
diff --git a/keystoneclient/v3/contrib/federation/identity_providers.py b/keystoneclient/v3/contrib/federation/identity_providers.py
index 85c4a73..4675ca3 100644
--- a/keystoneclient/v3/contrib/federation/identity_providers.py
+++ b/keystoneclient/v3/contrib/federation/identity_providers.py
@@ -79,7 +79,7 @@ class IdentityProviderManager(base.CrudManager):
GET /OS-FEDERATION/identity_providers
:returns: a list of IdentityProvider resource objects.
- :rtype: list
+ :rtype: List
"""
return super(IdentityProviderManager, self).list(**kwargs)
diff --git a/keystoneclient/v3/endpoint_groups.py b/keystoneclient/v3/endpoint_groups.py
new file mode 100644
index 0000000..f8b47c4
--- /dev/null
+++ b/keystoneclient/v3/endpoint_groups.py
@@ -0,0 +1,136 @@
+# 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.
+
+from keystoneclient import base
+
+
+class EndpointGroup(base.Resource):
+ """Represents an identity endpoint group.
+
+ Attributes:
+ * id: a UUID that identifies the endpoint group
+ * name: the endpoint group name
+ * description: the endpoint group description
+ * filters: representation of filters in the format of JSON that define
+ what endpoint entities are part of the group
+
+ """
+
+ pass
+
+
+class EndpointGroupManager(base.CrudManager):
+ """Manager class for Endpoint Groups."""
+
+ resource_class = EndpointGroup
+ collection_key = 'endpoint_groups'
+ key = 'endpoint_group'
+ base_url = 'OS-EP-FILTER'
+
+ def create(self, name, filters, description=None, **kwargs):
+ """Create an endpoint group.
+
+ :param str name: the name of the endpoint group.
+ :param str filters: representation of filters in the format of JSON
+ that define what endpoint entities are part of the
+ group.
+ :param str description: a description of the endpoint group.
+ :param kwargs: any other attribute provided will be passed to the
+ server.
+
+ :returns: the created endpoint group returned from server.
+ :rtype: :class:`keystoneclient.v3.endpoint_groups.EndpointGroup`
+
+ """
+ return super(EndpointGroupManager, self).create(
+ name=name,
+ filters=filters,
+ description=description,
+ **kwargs)
+
+ def get(self, endpoint_group):
+ """Retrieve an endpoint group.
+
+ :param endpoint_group: the endpoint group to be retrieved from the
+ server.
+ :type endpoint_group:
+ str or :class:`keystoneclient.v3.endpoint_groups.EndpointGroup`
+
+ :returns: the specified endpoint group returned from server.
+ :rtype: :class:`keystoneclient.v3.endpoint_groups.EndpointGroup`
+
+ """
+ return super(EndpointGroupManager, self).get(
+ endpoint_group_id=base.getid(endpoint_group))
+
+ def check(self, endpoint_group):
+ """Check if an endpoint group exists.
+
+ :param endpoint_group: the endpoint group to be checked against the
+ server.
+ :type endpoint_group:
+ str or :class:`keystoneclient.v3.endpoint_groups.EndpointGroup`
+
+ :returns: none if the specified endpoint group exists.
+
+ """
+ return super(EndpointGroupManager, self).head(
+ endpoint_group_id=base.getid(endpoint_group))
+
+ def list(self, **kwargs):
+ """List endpoint groups.
+
+ Any parameter provided will be passed to the server.
+
+ :returns: a list of endpoint groups.
+ :rtype: list of
+ :class:`keystoneclient.v3.endpoint_groups.EndpointGroup`.
+
+ """
+ return super(EndpointGroupManager, self).list(**kwargs)
+
+ def update(self, endpoint_group, name=None, filters=None,
+ description=None, **kwargs):
+ """Update an endpoint group.
+
+ :param str name: the new name of the endpoint group.
+ :param str filters: the new representation of filters in the format of
+ JSON that define what endpoint entities are part of
+ the group.
+ :param str description: the new description of the endpoint group.
+ :param kwargs: any other attribute provided will be passed to the
+ server.
+
+ :returns: the updated endpoint group returned from server.
+ :rtype: :class:`keystoneclient.v3.endpoint_groups.EndpointGroup`
+
+ """
+ return super(EndpointGroupManager, self).update(
+ endpoint_group_id=base.getid(endpoint_group),
+ name=name,
+ filters=filters,
+ description=description,
+ **kwargs)
+
+ def delete(self, endpoint_group):
+ """Delete an endpoint group.
+
+ :param endpoint_group: the endpoint group to be deleted on the server.
+ :type endpoint_group:
+ str or :class:`keystoneclient.v3.endpoint_groups.EndpointGroup`
+
+ :returns: Response object with 204 status.
+ :rtype: :class:`requests.models.Response`
+
+ """
+ return super(EndpointGroupManager, self).delete(
+ endpoint_group_id=base.getid(endpoint_group))
diff --git a/keystoneclient/v3/roles.py b/keystoneclient/v3/roles.py
index c083856..d5439ff 100644
--- a/keystoneclient/v3/roles.py
+++ b/keystoneclient/v3/roles.py
@@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from debtcollector import removals
from positional import positional
from keystoneclient import base
@@ -35,7 +36,7 @@ class Role(base.Resource):
class InferenceRule(base.Resource):
- """Represents an Rule that states one ROle implies another.
+ """Represents a rule that states one role implies another.
Attributes:
* prior_role: this role implies the other
@@ -52,6 +53,7 @@ class RoleManager(base.CrudManager):
resource_class = Role
collection_key = 'roles'
key = 'role'
+ deprecation_msg = 'keystoneclient.v3.roles.InferenceRuleManager'
def _role_grants_base_url(self, user, group, domain, project,
use_inherit_extension):
@@ -118,92 +120,6 @@ class RoleManager(base.CrudManager):
domain_id=domain_id,
**kwargs)
- def _implied_role_url_tail(self, prior_role, implied_role):
- base_url = ('/%(prior_role_id)s/implies/%(implied_role_id)s' %
- {'prior_role_id': base.getid(prior_role),
- 'implied_role_id': base.getid(implied_role)})
- return base_url
-
- def create_implied(self, prior_role, implied_role, **kwargs):
- """Create an inference rule.
-
- :param prior_role: the role which implies ``implied_role``.
- :type role: str or :class:`keystoneclient.v3.roles.Role`
- :param implied_role: the role which is implied by ``prior_role``.
- :type role: str or :class:`keystoneclient.v3.roles.Role`
- :param kwargs: any other attribute provided will be passed to the
- server.
-
- """
- url_tail = self._implied_role_url_tail(prior_role, implied_role)
- resp, body = self.client.put("/roles" + url_tail, **kwargs)
- return self.resource_class(self, body['role_inference'])
-
- def delete_implied(self, prior_role, implied_role, **kwargs):
- """Delete an inference rule.
-
- :param prior_role: the role which implies ``implied_role``.
- :type role: str or :class:`keystoneclient.v3.roles.Role`
- :param implied_role: the role which is implied by ``prior_role``.
- :type role: str or :class:`keystoneclient.v3.roles.Role`
- :param kwargs: any other attribute provided will be passed to the
- server.
-
- :returns: Response object with 204 status.
- :rtype: :class:`requests.models.Response`
-
- """
- url_tail = self._implied_role_url_tail(prior_role, implied_role)
- return super(RoleManager, self).delete(tail=url_tail, **kwargs)
-
- def get_implied(self, prior_role, implied_role, **kwargs):
- """Retrieve an inference rule.
-
- :param prior_role: the role which implies ``implied_role``.
- :type role: str or :class:`keystoneclient.v3.roles.Role`
- :param implied_role: the role which is implied by ``prior_role``.
- :type role: str or :class:`keystoneclient.v3.roles.Role`
- :param kwargs: any other attribute provided will be passed to the
- server.
-
- :returns: the specified role inference returned from server.
- :rtype: :class:`keystoneclient.v3.roles.InferenceRule`
-
- """
- url_tail = self._implied_role_url_tail(prior_role, implied_role)
- return super(RoleManager, self).get(tail=url_tail, **kwargs)
-
- def check_implied(self, prior_role, implied_role, **kwargs):
- """Check if an inference rule exists.
-
- :param prior_role: the role which implies ``implied_role``.
- :type role: str or :class:`keystoneclient.v3.roles.Role`
- :param implied_role: the role which is implied by ``prior_role``.
- :type role: str or :class:`keystoneclient.v3.roles.Role`
- :param kwargs: any other attribute provided will be passed to the
- server.
-
- :returns: response object with 200 status returned from server.
- :rtype: :class:`requests.models.Response`
-
- """
- url_tail = self._implied_role_url_tail(prior_role, implied_role)
- return super(RoleManager, self).head(tail=url_tail, **kwargs)
-
- def list_role_inferences(self, **kwargs):
- """List role inferences.
-
- :param kwargs: attributes provided will be passed to the server.
-
- :returns: a list of roles inferences.
- :rtype: list of :class:`keystoneclient.v3.roles.InferenceRule`
-
- """
- resp, body = self.client.get('/role_inferences/', **kwargs)
- obj_class = InferenceRule
- return [obj_class(self, res, loaded=True)
- for res in body['role_inferences']]
-
def get(self, role):
"""Retrieve a role.
@@ -440,3 +356,181 @@ class RoleManager(base.CrudManager):
role_id=base.getid(role),
os_inherit_extension_inherited=os_inherit_extension_inherited,
**kwargs)
+
+ @removals.remove(message='Use %s.create instead.' % deprecation_msg,
+ version='3.9.0', removal_version='4.0.0')
+ def create_implied(self, prior_role, implied_role, **kwargs):
+ return InferenceRuleManager(self.client).create(prior_role,
+ implied_role)
+
+ @removals.remove(message='Use %s.delete instead.' % deprecation_msg,
+ version='3.9.0', removal_version='4.0.0')
+ def delete_implied(self, prior_role, implied_role, **kwargs):
+ return InferenceRuleManager(self.client).delete(prior_role,
+ implied_role)
+
+ @removals.remove(message='Use %s.get instead.' % deprecation_msg,
+ version='3.9.0', removal_version='4.0.0')
+ def get_implied(self, prior_role, implied_role, **kwargs):
+ return InferenceRuleManager(self.client).get(prior_role,
+ implied_role)
+
+ @removals.remove(message='Use %s.check instead.' % deprecation_msg,
+ version='3.9.0', removal_version='4.0.0')
+ def check_implied(self, prior_role, implied_role, **kwargs):
+ return InferenceRuleManager(self.client).check(prior_role,
+ implied_role)
+
+ @removals.remove(message='Use %s.list_inference_roles' % deprecation_msg,
+ version='3.9.0', removal_version='4.0.0')
+ def list_role_inferences(self, **kwargs):
+ return InferenceRuleManager(self.client).list_inference_roles()
+
+
+class InferenceRuleManager(base.CrudManager):
+ """Manager class for manipulating Identity inference rules."""
+
+ resource_class = InferenceRule
+ collection_key = 'role_inferences'
+ key = 'role_inference'
+
+ def _implied_role_url_tail(self, prior_role, implied_role):
+ base_url = ('/%(prior_role_id)s/implies/%(implied_role_id)s' %
+ {'prior_role_id': base.getid(prior_role),
+ 'implied_role_id': base.getid(implied_role)})
+ return base_url
+
+ def create(self, prior_role, implied_role):
+ """Create an inference rule.
+
+ An inference rule is comprised of two roles, a prior role and an
+ implied role. The prior role will imply the implied role.
+
+ Valid HTTP return codes:
+
+ * 201: Resource is created successfully
+ * 404: A role cannot be found
+ * 409: The inference rule already exists
+
+ :param prior_role: the role which implies ``implied_role``.
+ :type role: str or :class:`keystoneclient.v3.roles.Role`
+ :param implied_role: the role which is implied by ``prior_role``.
+ :type role: str or :class:`keystoneclient.v3.roles.Role`
+
+ :returns: a newly created role inference returned from server.
+ :rtype: :class:`keystoneclient.v3.roles.InferenceRule`
+
+ """
+ url_tail = self._implied_role_url_tail(prior_role, implied_role)
+ _resp, body = self.client.put("/roles" + url_tail)
+ return self.resource_class(self, body['role_inference'])
+
+ def delete(self, prior_role, implied_role):
+ """Delete an inference rule.
+
+ When deleting an inference rule, both roles are required. Note that
+ neither role is deleted, only the inference relationship is dissolved.
+
+ Valid HTTP return codes:
+
+ * 204: Delete request is accepted
+ * 404: A role cannot be found
+
+ :param prior_role: the role which implies ``implied_role``.
+ :type role: str or :class:`keystoneclient.v3.roles.Role`
+ :param implied_role: the role which is implied by ``prior_role``.
+ :type role: str or :class:`keystoneclient.v3.roles.Role`
+
+ :returns: Response object with 204 status.
+ :rtype: :class:`requests.models.Response`
+
+ """
+ url_tail = self._implied_role_url_tail(prior_role, implied_role)
+ return self.client.delete("/roles" + url_tail)
+
+ def get(self, prior_role, implied_role):
+ """Retrieve an inference rule.
+
+ Valid HTTP return codes:
+
+ * 200: Inference rule is returned
+ * 404: A role cannot be found
+
+ :param prior_role: the role which implies ``implied_role``.
+ :type role: str or :class:`keystoneclient.v3.roles.Role`
+ :param implied_role: the role which is implied by ``prior_role``.
+ :type role: str or :class:`keystoneclient.v3.roles.Role`
+
+ :returns: the specified role inference returned from server.
+ :rtype: :class:`keystoneclient.v3.roles.InferenceRule`
+
+ """
+ url_tail = self._implied_role_url_tail(prior_role, implied_role)
+ _resp, body = self.client.get("/roles" + url_tail)
+ return self.resource_class(self, body['role_inference'])
+
+ def list(self, prior_role):
+ """List all roles that a role may imply.
+
+ Valid HTTP return codes:
+
+ * 200: List of inference rules are returned
+ * 404: A role cannot be found
+
+ :param prior_role: the role which implies ``implied_role``.
+ :type role: str or :class:`keystoneclient.v3.roles.Role`
+
+ :returns: the specified role inference returned from server.
+ :rtype: :class:`keystoneclient.v3.roles.InferenceRule`
+
+ """
+ url_tail = ('/%s/implies' % base.getid(prior_role))
+ _resp, body = self.client.get("/roles" + url_tail)
+ return self.resource_class(self, body['role_inference'])
+
+ def check(self, prior_role, implied_role):
+ """Check if an inference rule exists.
+
+ Valid HTTP return codes:
+
+ * 204: The rule inference exists
+ * 404: A role cannot be found
+
+ :param prior_role: the role which implies ``implied_role``.
+ :type role: str or :class:`keystoneclient.v3.roles.Role`
+ :param implied_role: the role which is implied by ``prior_role``.
+ :type role: str or :class:`keystoneclient.v3.roles.Role`
+
+ :returns: response object with 204 status returned from server.
+ :rtype: :class:`requests.models.Response`
+
+ """
+ url_tail = self._implied_role_url_tail(prior_role, implied_role)
+ return self.client.head("/roles" + url_tail)
+
+ def list_inference_roles(self):
+ """List all rule inferences.
+
+ Valid HTTP return codes:
+
+ * 200: All inference rules are returned
+
+ :param kwargs: attributes provided will be passed to the server.
+
+ :returns: a list of inference rules.
+ :rtype: list of :class:`keystoneclient.v3.roles.InferenceRule`
+
+ """
+ return super(InferenceRuleManager, self).list()
+
+ def update(self, **kwargs):
+ raise exceptions.MethodNotImplemented(
+ _('Update not supported for rule inferences'))
+
+ def find(self, **kwargs):
+ raise exceptions.MethodNotImplemented(
+ _('Find not supported for rule inferences'))
+
+ def put(self, **kwargs):
+ raise exceptions.MethodNotImplemented(
+ _('Put not supported for rule inferences'))
diff --git a/releasenotes/notes/bp-pci-dss-query-password-expired-users-b0c4b1bbdcf33f16.yaml b/releasenotes/notes/bp-pci-dss-query-password-expired-users-b0c4b1bbdcf33f16.yaml
new file mode 100644
index 0000000..2699a7f
--- /dev/null
+++ b/releasenotes/notes/bp-pci-dss-query-password-expired-users-b0c4b1bbdcf33f16.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Added ability to filter on multiple values with the same parameter key.
+ For example, we can now filter on user names that contain both ``test`` and
+ ``user`` using ``keystone.users.list(name__contains=['test', 'user'])``.
diff --git a/releasenotes/notes/bug-1616105-cc8b85eb056e99e2.yaml b/releasenotes/notes/bug-1616105-cc8b85eb056e99e2.yaml
new file mode 100644
index 0000000..e9c1c9c
--- /dev/null
+++ b/releasenotes/notes/bug-1616105-cc8b85eb056e99e2.yaml
@@ -0,0 +1,8 @@
+---
+fixes:
+ - >
+ [`bug 1616105 <https://bugs.launchpad.net/keystoneauth/+bug/1616105>`_]
+ Only log the response body when the ``Content-Type`` header is set to
+ ``application/json``. This avoids logging large binary objects (such as
+ images). Other ``Content-Type`` will not be logged. Additional
+ ``Content-Type`` strings can be added as required.
diff --git a/releasenotes/notes/bug-1654847-d2e9df994c7b617f.yaml b/releasenotes/notes/bug-1654847-d2e9df994c7b617f.yaml
new file mode 100644
index 0000000..5d066e9
--- /dev/null
+++ b/releasenotes/notes/bug-1654847-d2e9df994c7b617f.yaml
@@ -0,0 +1,5 @@
+---
+fixes:
+ - |
+ The ``X-Service-Token`` header value is now properly masked, and is
+ displayed as a hash value, in the log.
diff --git a/releasenotes/notes/deprecated_auth-d2a2bf537bdb88d3.yaml b/releasenotes/notes/deprecated_auth-d2a2bf537bdb88d3.yaml
index 82a723d..fbb3a47 100644
--- a/releasenotes/notes/deprecated_auth-d2a2bf537bdb88d3.yaml
+++ b/releasenotes/notes/deprecated_auth-d2a2bf537bdb88d3.yaml
@@ -3,10 +3,10 @@ deprecations:
- >
[`blueprint deprecate-to-ksa <https://blueprints.launchpad.net/python-keystoneclient/+spec/deprecate-to-ksa>`_]
Several modules related to authentication in keystoneclient have been
- deprecated in favor of [`keystoneauth <http://docs.openstack.org/developer/keystoneauth/>`_]
+ deprecated in favor of [`keystoneauth <https://docs.openstack.org/developer/keystoneauth/>`_]
These modules include: ``keystoneclient.session``, ``keystoneclient.adapter``,
``keystoneclient.httpclient``, ``keystoneclient.auth.base``,
``keystoneclient.auth.cli``, ``keystoneclient.auth.conf``,
``keystoneclient.auth.identity.base``, and ``keystoneclient.auth.token_endpoint``.
Tips for migrating to `keystoneauth` have been
- [`documented <http://docs.openstack.org/developer/keystoneauth/migrating.html>`_].
+ [`documented <https://docs.openstack.org/developer/keystoneauth/migrating.html>`_].
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index e316bc5..a2e09fc 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# 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
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index e288920..10af637 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,5 +6,6 @@
:maxdepth: 1
unreleased
+ ocata
newton
mitaka
diff --git a/releasenotes/source/ocata.rst b/releasenotes/source/ocata.rst
new file mode 100644
index 0000000..9515f6c
--- /dev/null
+++ b/releasenotes/source/ocata.rst
@@ -0,0 +1,6 @@
+============================
+ Ocata Series Release Notes
+============================
+
+.. release-notes::
+ :branch: origin/stable/ocata
diff --git a/requirements.txt b/requirements.txt
index 42f0343..a30ce16 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,15 +2,15 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-pbr>=1.8 # Apache-2.0
+pbr!=2.1.0,>=2.0.0 # Apache-2.0
debtcollector>=1.2.0 # Apache-2.0
-keystoneauth1>=2.14.0 # Apache-2.0
-oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
+keystoneauth1>=2.20.0 # Apache-2.0
+oslo.config>=3.22.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
-oslo.utils>=3.18.0 # Apache-2.0
+oslo.utils>=3.20.0 # Apache-2.0
positional>=1.1.1 # Apache-2.0
-requests!=2.12.2,>=2.10.0 # Apache-2.0
+requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
six>=1.9.0 # MIT
-stevedore>=1.17.1 # Apache-2.0
+stevedore>=1.20.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index f907550..9e913a7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@ description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
-home-page = http://docs.openstack.org/developer/python-keystoneclient
+home-page = https://docs.openstack.org/developer/python-keystoneclient
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -16,7 +16,6 @@ classifier =
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
[files]
diff --git a/setup.py b/setup.py
index 782bb21..566d844 100644
--- a/setup.py
+++ b/setup.py
@@ -25,5 +25,5 @@ except ImportError:
pass
setuptools.setup(
- setup_requires=['pbr>=1.8'],
+ setup_requires=['pbr>=2.0.0'],
pbr=True)
diff --git a/test-requirements.txt b/test-requirements.txt
index e29ae1d..5b58c4f 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -5,18 +5,18 @@
hacking<0.11,>=0.10.0
flake8-docstrings==0.2.1.post1 # MIT
-coverage>=4.0 # Apache-2.0
+coverage!=4.4,>=4.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
keyring>=5.5.1 # MIT/PSF
-lxml>=2.3 # BSD
+lxml!=3.7.0,>=2.3 # BSD
mock>=2.0 # BSD
oauthlib>=0.6 # BSD
oslosphinx>=4.7.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
reno>=1.8.0 # Apache-2.0
requests-mock>=1.1 # Apache-2.0
-sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
-tempest>=12.1.0 # Apache-2.0
+sphinx>=1.5.1 # BSD
+tempest>=14.0.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 --git a/tools/tox_install.sh b/tools/tox_install.sh
new file mode 100755
index 0000000..e61b63a
--- /dev/null
+++ b/tools/tox_install.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+# Client constraint file contains this client version pin that is in conflict
+# with installing the client from source. We should remove the version pin in
+# the constraints file before applying it for from-source installation.
+
+CONSTRAINTS_FILE="$1"
+shift 1
+
+set -e
+
+# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
+# published to logs.openstack.org for easy debugging.
+localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
+
+if [[ "$CONSTRAINTS_FILE" != http* ]]; then
+ CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE"
+fi
+# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
+curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile"
+
+pip install -c"$localfile" openstack-requirements
+
+# This is the main purpose of the script: Allow local installation of
+# the current repo. It is listed in constraints file and thus any
+# install will be constrained and we need to unconstrain it.
+edit-constraints "$localfile" -- "$CLIENT_NAME"
+
+pip install -c"$localfile" -U "$@"
+exit $?
diff --git a/tox.ini b/tox.ini
index 11016bd..08ba962 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,12 +1,14 @@
[tox]
-minversion = 1.6
+minversion = 2.0
skipsdist = True
-envlist = py34,py27,pep8,releasenotes
+envlist = py35,py27,pep8,releasenotes
[testenv]
usedevelop = True
-install_command = pip install -U {opts} {packages}
+install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
setenv = VIRTUAL_ENV={envdir}
+ BRANCH_NAME=master
+ CLIENT_NAME=python-keystoneclient
OS_STDOUT_NOCAPTURE=False
OS_STDERR_NOCAPTURE=False
@@ -37,7 +39,8 @@ commands = python setup.py testr --coverage --testr-args='{posargs}'
commands = oslo_debug_helper -t keystoneclient/tests {posargs}
[testenv:functional]
-setenv = OS_TEST_PATH=./keystoneclient/tests/functional
+setenv = {[testenv]setenv}
+ OS_TEST_PATH=./keystoneclient/tests/functional
passenv = OS_*
[flake8]
@@ -61,7 +64,6 @@ commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasen
[hacking]
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