Merge pull request #1135 from gnuoy/keystone-ldap-k8s
Add Keystone LDAP on K8s tests
This commit is contained in:
@@ -488,6 +488,10 @@ class LdapTests(BaseKeystoneTest):
|
||||
"""Run class setup for running Keystone ldap-tests."""
|
||||
super(LdapTests, cls).setUpClass()
|
||||
|
||||
def get_ldap_ips(self):
|
||||
"""Return the ip addresses for the ldap servers."""
|
||||
return zaza.model.get_app_ips("ldap-server")
|
||||
|
||||
def _get_ldap_config(self):
|
||||
"""Generate ldap config for current model.
|
||||
|
||||
@@ -495,7 +499,7 @@ class LdapTests(BaseKeystoneTest):
|
||||
for the keystone-ldap application.
|
||||
:rtype: Tuple[bool, Dict[str,str]]
|
||||
"""
|
||||
ldap_ips = zaza.model.get_app_ips("ldap-server")
|
||||
ldap_ips = self.get_ldap_ips()
|
||||
self.assertTrue(ldap_ips, "Should be at least one ldap server")
|
||||
return {
|
||||
'ldap-server': "ldap://{}".format(ldap_ips[0]),
|
||||
@@ -703,7 +707,7 @@ class LdapExplicitCharmConfigTests(LdapTests):
|
||||
for the keystone-ldap application.
|
||||
:rtype: Tuple[bool, Dict[str,str]]
|
||||
"""
|
||||
ldap_ips = zaza.model.get_app_ips("ldap-server")
|
||||
ldap_ips = self.get_ldap_ips()
|
||||
self.assertTrue(ldap_ips, "Should be at least one ldap server")
|
||||
return {
|
||||
'ldap-server': "ldap://{}".format(ldap_ips[0]),
|
||||
@@ -730,6 +734,15 @@ class LdapExplicitCharmConfigTests(LdapTests):
|
||||
' group_tree_dn: "group_tree_dn_foobar"}',
|
||||
}
|
||||
|
||||
def get_domain_config(self):
|
||||
"""Return rendered domain config."""
|
||||
units = zaza.model.get_units("keystone-ldap",
|
||||
model_name=self.model_name)
|
||||
result = zaza.model.run_on_unit(
|
||||
units[0].name,
|
||||
"cat /etc/keystone/domains/keystone.userdomain.conf")
|
||||
return result['stdout']
|
||||
|
||||
def test_200_config_flags_precedence(self):
|
||||
"""Validates precedence when the same config options are used."""
|
||||
application_name = 'keystone-ldap'
|
||||
@@ -755,28 +768,24 @@ class LdapExplicitCharmConfigTests(LdapTests):
|
||||
zaza.model.wait_for_application_states(
|
||||
states=test_config.get("target_deploy_status", {})
|
||||
)
|
||||
units = zaza.model.get_units("keystone-ldap",
|
||||
model_name=self.model_name)
|
||||
result = zaza.model.run_on_unit(
|
||||
units[0].name,
|
||||
"cat /etc/keystone/domains/keystone.userdomain.conf")
|
||||
contents = self.get_domain_config()
|
||||
# not present in charm config, but present in config flags
|
||||
self.assertIn("use_pool = True", result['stdout'],
|
||||
self.assertIn("use_pool = True", contents,
|
||||
"use_pool value is expected to be present and "
|
||||
"set to True in the config file")
|
||||
# ldap-config-flags overriding empty charm config value
|
||||
self.assertIn("group_objectclass = posixGroup",
|
||||
result['stdout'],
|
||||
contents,
|
||||
"group_objectclass is expected to be present and"
|
||||
" set to posixGroup in the config file")
|
||||
# overridden by charm config, not written to file
|
||||
self.assertNotIn(
|
||||
"group_tree_dn_foobar",
|
||||
result['stdout'],
|
||||
contents,
|
||||
"user_tree_dn ldap-config-flags value needs to be "
|
||||
"overridden by ldap-user-tree-dn in config file")
|
||||
# complementing the above, value used is from charm setting
|
||||
self.assertIn("group_tree_dn = ou=groups", result['stdout'],
|
||||
self.assertIn("group_tree_dn = ou=groups", contents,
|
||||
"user_tree_dn value is expected to be present "
|
||||
"and set to dc=test,dc=com in the config file")
|
||||
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
# Copyright 2023 Canonical Ltd.
|
||||
#
|
||||
# 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.
|
||||
"""Keystone LDAP tests on k8s."""
|
||||
|
||||
import json
|
||||
import tenacity
|
||||
import contextlib
|
||||
from keystoneauth1.exceptions.connection import ConnectFailure
|
||||
from keystoneauth1.exceptions.http import NotFound as http_NotFound
|
||||
import logging
|
||||
from requests.exceptions import ConnectionError
|
||||
import zaza.openstack.charm_tests.keystone.tests as ks_tests
|
||||
import zaza.openstack.charm_tests.tempest.tests as tempest_tests
|
||||
import zaza.charm_lifecycle.utils as lifecycle_utils
|
||||
import zaza.model
|
||||
import subprocess
|
||||
|
||||
|
||||
class KeystoneLookupError(Exception):
|
||||
"""An error looking up data in keystone."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class LdapExplicitCharmConfigTestsK8S(ks_tests.LdapExplicitCharmConfigTests):
|
||||
"""Keystone LDAP tests for K8s deployment."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run class setup for running Keystone ldap-tests."""
|
||||
cls.model_name = zaza.model.get_juju_model()
|
||||
cls.test_config = lifecycle_utils.get_charm_config(fatal=False)
|
||||
cls.default_api_version = 3
|
||||
cls.api_v3 = 3
|
||||
cls.keystone_ips = cls.get_internal_ips("keystone", cls.model_name)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def v3_keystone_preferred(self):
|
||||
"""Set the preferred keystone api to v3 within called context."""
|
||||
with contextlib.nullcontext():
|
||||
yield
|
||||
|
||||
@staticmethod
|
||||
def get_internal_ips(application, model_name):
|
||||
"""Return the internal ip addresses an application."""
|
||||
status = zaza.model.get_status(model_name=model_name)
|
||||
units = status['applications'][application]["units"]
|
||||
return [v.address for v in units.values()]
|
||||
|
||||
def get_ldap_ips(self):
|
||||
"""Return the ip addresses for the ldap servers."""
|
||||
return self.get_internal_ips("ldap-server", self.model_name)
|
||||
|
||||
def get_domain_config(self):
|
||||
"""Collect the rendered domain config file."""
|
||||
# libjuju does not support ssh to a payload container
|
||||
cmd = [
|
||||
"juju",
|
||||
"ssh",
|
||||
"-m",
|
||||
self.model_name,
|
||||
"--container",
|
||||
"keystone",
|
||||
zaza.model.get_lead_unit("keystone").entity_id,
|
||||
'cat /etc/keystone/domains/keystone.userdomain.conf']
|
||||
out = subprocess.check_output(cmd)
|
||||
return out.decode()
|
||||
|
||||
def _get_ldap_config(self):
|
||||
"""Generate ldap config for current model.
|
||||
|
||||
:return: tuple of whether ldap-server is running and if so, config
|
||||
for the keystone-ldap application.
|
||||
:rtype: Tuple[bool, Dict[str,str]]
|
||||
"""
|
||||
ldap_ips = self.get_ldap_ips()
|
||||
self.assertTrue(ldap_ips, "Should be at least one ldap server")
|
||||
config_flags = json.dumps({
|
||||
'url': "ldap://{}".format(ldap_ips[0]),
|
||||
'user': 'cn=admin,dc=test,dc=com',
|
||||
"use_pool": True,
|
||||
'password': 'crapper',
|
||||
'suffix': 'dc=test,dc=com',
|
||||
'query_scope': 'one',
|
||||
'user_objectclass': 'inetOrgPerson',
|
||||
'user_id_attribute': 'cn',
|
||||
'user_name_attribute': 'sn',
|
||||
'user_enabled_attribute': 'enabled',
|
||||
'user_enabled_invert': False,
|
||||
'user_enabled_mask': 0,
|
||||
'user_enabled_default': 'True',
|
||||
'group_tree_dn': 'ou=groups,dc=test,dc=com',
|
||||
'group_id_attribute': 'cn',
|
||||
'group_name_attribute': 'cn',
|
||||
'group_member_attribute': 'memberUid',
|
||||
'group_members_are_ids': True,
|
||||
"group_objectclass": "posixGroup",
|
||||
})
|
||||
return {
|
||||
"ldap-config-flags": config_flags,
|
||||
"domain-name": "userdomain"}
|
||||
|
||||
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=2, max=60),
|
||||
reraise=True, stop=tenacity.stop_after_attempt(5),
|
||||
retry=tenacity.retry_if_exception_type(
|
||||
KeystoneLookupError))
|
||||
def _find_keystone_v3_group(self, group, domain):
|
||||
logging.info('Looking for group: {}'.format(group))
|
||||
try:
|
||||
return super()._find_keystone_v3_group(group, domain)
|
||||
except (AttributeError, http_NotFound, ConnectionError,
|
||||
ConnectFailure):
|
||||
raise KeystoneLookupError
|
||||
|
||||
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=2, max=60),
|
||||
reraise=True, stop=tenacity.stop_after_attempt(5),
|
||||
retry=tenacity.retry_if_exception_type(
|
||||
KeystoneLookupError))
|
||||
def _find_keystone_v3_user(self, username, domain, group=None):
|
||||
logging.info('Looking for user: {}'.format(username))
|
||||
try:
|
||||
return super()._find_keystone_v3_user(
|
||||
username,
|
||||
domain,
|
||||
group=group)
|
||||
except (AttributeError, http_NotFound, ConnectionError,
|
||||
ConnectFailure):
|
||||
raise KeystoneLookupError
|
||||
|
||||
|
||||
class KeystoneTempestTestK8S(tempest_tests.TempestTestScaleK8SBase):
|
||||
"""Test keystone k8s scale out and scale back."""
|
||||
|
||||
application_name = "keystone"
|
||||
Reference in New Issue
Block a user