diff --git a/charmhelpers/contrib/openstack/context.py b/charmhelpers/contrib/openstack/context.py index df00c9702..2c21ed357 100644 --- a/charmhelpers/contrib/openstack/context.py +++ b/charmhelpers/contrib/openstack/context.py @@ -463,6 +463,7 @@ def __call__(self): int_host = format_ipv6_addr(int_host) or int_host svc_protocol = rdata.get('service_protocol') or 'http' auth_protocol = rdata.get('auth_protocol') or 'http' + admin_role = rdata.get('admin_role') or 'Admin' int_protocol = rdata.get('internal_protocol') or 'http' api_version = rdata.get('api_version') or '2.0' ctxt.update({'service_port': rdata.get('service_port'), @@ -474,6 +475,7 @@ def __call__(self): 'admin_tenant_name': rdata.get('service_tenant'), 'admin_user': rdata.get('service_username'), 'admin_password': rdata.get('service_password'), + 'admin_role': admin_role, 'service_protocol': svc_protocol, 'auth_protocol': auth_protocol, 'internal_protocol': int_protocol, diff --git a/charmhelpers/contrib/openstack/templates/section-keystone-authtoken b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken index c9b01528e..dbad506fe 100644 --- a/charmhelpers/contrib/openstack/templates/section-keystone-authtoken +++ b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken @@ -12,4 +12,6 @@ signing_dir = {{ signing_dir }} {% if service_type -%} service_type = {{ service_type }} {% endif -%} +service_token_roles = {{ admin_role }} +service_token_roles_required = True {% endif -%} diff --git a/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka index 14c25b4db..139a05128 100644 --- a/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka +++ b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka @@ -22,4 +22,6 @@ signing_dir = {{ signing_dir }} {% if use_memcache == true %} memcached_servers = {{ memcache_url }} {% endif -%} +service_token_roles = {{ admin_role }} +service_token_roles_required = True {% endif -%} diff --git a/charmhelpers/contrib/openstack/templates/section-service-user b/charmhelpers/contrib/openstack/templates/section-service-user new file mode 100644 index 000000000..c740cc284 --- /dev/null +++ b/charmhelpers/contrib/openstack/templates/section-service-user @@ -0,0 +1,11 @@ +{% if auth_host -%} +[service_user] +send_service_user_token = true +auth_type = password +auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} +project_domain_id = default +user_domain_id = default +project_name = {{ admin_tenant_name }} +username = {{ admin_user }} +password = {{ admin_password }} +{% endif -%} diff --git a/tests/contrib/openstack/test_os_contexts.py b/tests/contrib/openstack/test_os_contexts.py index c2b9d179a..fde43dc0e 100644 --- a/tests/contrib/openstack/test_os_contexts.py +++ b/tests/contrib/openstack/test_os_contexts.py @@ -224,6 +224,11 @@ def relation_units(self, relation_id): } IDENTITY_SERVICE_RELATION_VERSIONED.update(IDENTITY_SERVICE_RELATION_HTTPS) +IDENTITY_SERVICE_RELATION_ADMIN_ROLE = { + 'admin_role': 'Role', +} +IDENTITY_SERVICE_RELATION_ADMIN_ROLE.update(IDENTITY_SERVICE_RELATION_HTTPS) + IDENTITY_CREDENTIALS_RELATION_VERSIONED = { 'api_version': '3', 'service_tenant_id': 'svc-proj-id', @@ -1029,6 +1034,7 @@ def test_identity_service_context_with_data(self, *args): result = identity_service() expected = { 'admin_password': 'foo', + 'admin_role': 'Admin', 'admin_tenant_name': 'admin', 'admin_tenant_id': None, 'admin_domain_id': None, @@ -1085,6 +1091,7 @@ def test_identity_service_context_with_altname(self, *args): result = identity_service() expected = { 'admin_password': 'foo', + 'admin_role': 'Admin', 'admin_tenant_name': 'admin', 'admin_tenant_id': None, 'admin_domain_id': None, @@ -1116,6 +1123,7 @@ def test_identity_service_context_with_cache(self, *args): result = identity_service() expected = { 'admin_password': 'foo', + 'admin_role': 'Admin', 'admin_tenant_name': 'admin', 'admin_tenant_id': None, 'admin_domain_id': None, @@ -1146,6 +1154,7 @@ def test_identity_service_context_with_data_http(self, *args): result = identity_service() expected = { 'admin_password': 'foo', + 'admin_role': 'Admin', 'admin_tenant_name': 'admin', 'admin_tenant_id': '123456', 'admin_domain_id': None, @@ -1175,6 +1184,7 @@ def test_identity_service_context_with_data_https(self, *args): result = identity_service() expected = { 'admin_password': 'foo', + 'admin_role': 'Admin', 'admin_tenant_name': 'admin', 'admin_tenant_id': None, 'admin_domain_id': None, @@ -1205,6 +1215,7 @@ def test_identity_service_context_with_data_versioned(self, *args): result = identity_service() expected = { 'admin_password': 'foo', + 'admin_role': 'Admin', 'admin_domain_name': 'admin_domain', 'admin_tenant_name': 'admin', 'admin_tenant_id': 'svc-proj-id', @@ -1227,6 +1238,37 @@ def test_identity_service_context_with_data_versioned(self, *args): result.pop('keystone_authtoken') self.assertEquals(result, expected) + @patch.object(context, 'filter_installed_packages', return_value=[]) + @patch.object(context, 'os_release', return_value='rocky') + def test_identity_service_context_with_admin_role(self, *args): + '''Test shared-db context with admin role supplied from keystone''' + relation = FakeRelation( + relation_data=IDENTITY_SERVICE_RELATION_ADMIN_ROLE) + self.relation_get.side_effect = relation.get + identity_service = context.IdentityServiceContext() + result = identity_service() + expected = { + 'admin_password': 'foo', + 'admin_role': 'Role', + 'admin_tenant_name': 'admin', + 'admin_tenant_id': None, + 'admin_domain_id': None, + 'admin_user': 'adam', + 'auth_host': 'keystone-host.local', + 'auth_port': '35357', + 'auth_protocol': 'https', + 'service_host': 'keystonehost.local', + 'service_port': '5000', + 'service_protocol': 'https', + 'service_type': 'volume', + 'internal_host': 'keystone-internal.local', + 'internal_port': '5000', + 'internal_protocol': 'https', + 'api_version': '2.0', + } + result.pop('keystone_authtoken') + self.assertEquals(result, expected) + def test_identity_credentials_context_with_data_versioned(self): '''Test identity-credentials context with api version supplied from keystone''' relation = FakeRelation( @@ -1263,6 +1305,7 @@ def test_identity_service_context_with_ipv6(self, format_ipv6_addr, *args): result = identity_service() expected = { 'admin_password': 'foo', + 'admin_role': 'Admin', 'admin_tenant_name': 'admin', 'admin_tenant_id': '123456', 'admin_domain_id': None, diff --git a/tox.ini b/tox.ini index a11577657..a72939a57 100644 --- a/tox.ini +++ b/tox.ini @@ -17,6 +17,7 @@ ignore_basepython_conflict = true # https://stackoverflow.com/a/38133283 requires = pip < 20.3 virtualenv < 20.0 + tox < 4.0 # NOTE: https://wiki.canonical.com/engineering/OpenStack/InstallLatestToxOnOsci minversion = 3.2.0