Add functional tests and support config flags
This commit is contained in:
parent
f64121c61f
commit
7deda62ef5
3
charms/keystone-ldap-k8s/.stestr.conf
Normal file
3
charms/keystone-ldap-k8s/.stestr.conf
Normal file
@ -0,0 +1,3 @@
|
||||
[DEFAULT]
|
||||
test_path=./tests/unit
|
||||
top_dir=./tests
|
@ -44,9 +44,7 @@ options:
|
||||
default:
|
||||
description: |
|
||||
Additional LDAP configuration options.
|
||||
For simple configurations use a comma separated string of key=value pairs.
|
||||
"user_allow_create=False, user_allow_update=False, user_allow_delete=False"
|
||||
For more complex configurations use a json like string with double quotes
|
||||
Use a json like string with double quotes
|
||||
and braces around all the options and single quotes around complex values.
|
||||
"{user_tree_dn: 'DC=dc1,DC=ad,DC=example,DC=com',
|
||||
user_allow_create: False,
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright 2021 Canonical Ltd.
|
||||
# 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.
|
||||
@ -17,46 +17,91 @@
|
||||
#
|
||||
# Learn more at: https://juju.is/docs/sdk
|
||||
|
||||
"""Charm the service.
|
||||
"""Keystone LDAP configuration.
|
||||
|
||||
Refer to the following post for a quick-start guide that will help you
|
||||
develop a new k8s charm using the Operator Framework:
|
||||
|
||||
https://discourse.charmhub.io/t/4208
|
||||
Send domain configuration to the keystone charm.
|
||||
"""
|
||||
import jinja2
|
||||
import json
|
||||
import logging
|
||||
from typing import (
|
||||
Callable,
|
||||
List,
|
||||
Mapping,
|
||||
)
|
||||
from typing import Callable, List, Mapping
|
||||
|
||||
import charms.keystone_ldap_k8s.v0.domain_config as sunbeam_dc_svc
|
||||
import jinja2
|
||||
import ops.charm
|
||||
import ops_sunbeam.charm as sunbeam_charm
|
||||
import ops_sunbeam.config_contexts as config_contexts
|
||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||
from ops.main import main
|
||||
from ops.model import ActiveStatus, BlockedStatus, WaitingStatus
|
||||
|
||||
# Log messages can be retrieved using juju debug-log
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
VALID_LOG_LEVELS = ["info", "debug", "warning", "error", "critical"]
|
||||
import ops_sunbeam.charm as sunbeam_charm
|
||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||
import charms.keystone_ldap_k8s.v0.domain_config as sunbeam_dc_svc
|
||||
import ops_sunbeam.config_contexts as config_contexts
|
||||
import json
|
||||
LDAP_OPTINONS = [
|
||||
"server",
|
||||
"user",
|
||||
"password",
|
||||
"suffix",
|
||||
"readonly",
|
||||
"query_scope",
|
||||
"user_tree_dn",
|
||||
"user_filter",
|
||||
"user_objectclass",
|
||||
"user_id_attribute",
|
||||
"user_name_attribute",
|
||||
"user_enabled_attribute",
|
||||
"user_enabled_invert",
|
||||
"user_enabled_mask",
|
||||
"user_enabled_default",
|
||||
"user_enabled_emulation",
|
||||
"user_enabled_emulation_dn",
|
||||
"group_tree_dn",
|
||||
"group_objectclass",
|
||||
"group_id_attribute",
|
||||
"group_name_attribute",
|
||||
"group_member_attribute",
|
||||
"group_members_are_ids",
|
||||
"use_pool",
|
||||
"pool_size",
|
||||
"pool_retry_max",
|
||||
"pool_connection_timeout",
|
||||
]
|
||||
|
||||
class LDAPConfigFlagsContext(config_contexts.ConfigContext):
|
||||
|
||||
class LDAPConfigContext(config_contexts.ConfigContext):
|
||||
"""Configuration context for cinder parameters."""
|
||||
|
||||
def context(self) -> dict:
|
||||
"""Generate context information for cinder config."""
|
||||
# LDAP config follows the patterns that if a user has
|
||||
# explicitly set a value then it should be rendered
|
||||
# otherwise the option is omitted. This is slighttly
|
||||
# complicated by the fact that the model.config does
|
||||
# not include settings that have not been set.
|
||||
context = {}
|
||||
config_flags = {}
|
||||
config = self.charm.model.config.get
|
||||
for option in LDAP_OPTINONS:
|
||||
config_option = "ldap-" + option.replace("_", "-")
|
||||
config_value = config(config_option)
|
||||
if config_value is not None and config_value != "":
|
||||
context[option] = config_value
|
||||
raw_config_flags = config("ldap-config-flags")
|
||||
if raw_config_flags:
|
||||
config_flags = json.loads(raw_config_flags)
|
||||
return {'flags': config_flags}
|
||||
for key, value in config_flags.items():
|
||||
if key in context.keys():
|
||||
logger.warning(
|
||||
"Ignoring {} passed via ldap-config-flags, please use charm config to manage this setting".format(
|
||||
key
|
||||
)
|
||||
)
|
||||
else:
|
||||
context[key] = value
|
||||
if context.get("server"):
|
||||
# Should probably change the config.yaml rather than having to
|
||||
# rename the key
|
||||
context["url"] = context.pop("server")
|
||||
return {"config": context}
|
||||
|
||||
|
||||
class DomainConfigProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||
@ -95,15 +140,13 @@ class DomainConfigProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||
|
||||
class KeystoneLDAPK8SCharm(sunbeam_charm.OSBaseOperatorCharm):
|
||||
"""Charm the service."""
|
||||
|
||||
DOMAIN_CONFIG_RELATION_NAME = "domain-config"
|
||||
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
self.send_domain_config()
|
||||
|
||||
def get_relation_handlers(
|
||||
self, handlers=None
|
||||
) -> List[sunbeam_rhandlers.RelationHandler]:
|
||||
def get_relation_handlers(self, handlers=None) -> List[sunbeam_rhandlers.RelationHandler]:
|
||||
"""Relation handlers for the service."""
|
||||
handlers = handlers or []
|
||||
if self.can_add_handler(self.DOMAIN_CONFIG_RELATION_NAME, handlers):
|
||||
@ -115,37 +158,40 @@ class KeystoneLDAPK8SCharm(sunbeam_charm.OSBaseOperatorCharm):
|
||||
handlers.append(self.dc_handler)
|
||||
return super().get_relation_handlers(handlers)
|
||||
|
||||
|
||||
@property
|
||||
def config_contexts(self) -> List[config_contexts.ConfigContext]:
|
||||
"""Configuration contexts for the operator."""
|
||||
contexts = super().config_contexts
|
||||
contexts.append(LDAPConfigFlagsContext(self, "ldap_config_flags"))
|
||||
contexts.append(LDAPConfigContext(self, "ldap_config"))
|
||||
return contexts
|
||||
|
||||
|
||||
def send_domain_config(self, event=None):
|
||||
try:
|
||||
domain_name = self.config['domain-name']
|
||||
def send_domain_config(self, event=None) -> None:
|
||||
"""Send domain configuration to keystone."""
|
||||
try:
|
||||
domain_name = self.config["domain-name"]
|
||||
except KeyError:
|
||||
return
|
||||
loader = jinja2.FileSystemLoader(self.template_dir)
|
||||
_tmpl_env = jinja2.Environment(loader=loader)
|
||||
template = _tmpl_env.get_template("keystone.conf")
|
||||
self.dc_handler.domain_config.set_domain_info(
|
||||
domain_name=domain_name,
|
||||
config_contents=template.render(self.contexts()))
|
||||
domain_name=domain_name, config_contents=template.render(self.contexts())
|
||||
)
|
||||
|
||||
def configure_app_leader(self, event):
|
||||
def configure_app_leader(self, event) -> None:
|
||||
"""Configure application."""
|
||||
self.send_domain_config()
|
||||
self.set_leader_ready()
|
||||
|
||||
@property
|
||||
def databases(self) -> Mapping[str, str]:
|
||||
"""Config charm has no databases."""
|
||||
return {}
|
||||
|
||||
def get_pebble_handlers(self):
|
||||
"""Config charm has no containers."""
|
||||
return []
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: nocover
|
||||
main(KeystoneLDAPK8SCharm)
|
||||
|
@ -1,120 +1,7 @@
|
||||
[ldap]
|
||||
url = {{ options.ldap_server }}
|
||||
{% if options.ldap_user and options.ldap_password -%}
|
||||
user = {{ options.ldap_user }}
|
||||
password = {{ options.ldap_password }}
|
||||
{% endif -%}
|
||||
suffix = {{ options.ldap_suffix }}
|
||||
|
||||
user_allow_create = {{ not options.ldap_readonly }}
|
||||
user_allow_update = {{ not options.ldap_readonly }}
|
||||
user_allow_delete = {{ not options.ldap_readonly }}
|
||||
|
||||
group_allow_create = {{ not options.ldap_readonly }}
|
||||
group_allow_update = {{ not options.ldap_readonly }}
|
||||
group_allow_delete = {{ not options.ldap_readonly }}
|
||||
|
||||
{% if options.tls_ca_ldap -%}
|
||||
use_tls = {{ options.use_tls }}
|
||||
tls_req_cert = demand
|
||||
tls_cacertfile = {{ options.backend_ca_file }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_query_scope -%}
|
||||
query_scope = {{ options.ldap_query_scope }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_tree_dn -%}
|
||||
user_tree_dn = {{ options.ldap_user_tree_dn }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_filter -%}
|
||||
user_filter = {{ options.ldap_user_filter }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_objectclass -%}
|
||||
user_objectclass = {{ options.ldap_user_objectclass }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_id_attribute -%}
|
||||
user_id_attribute = {{ options.ldap_user_id_attribute }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_name_attribute -%}
|
||||
user_name_attribute = {{ options.ldap_user_name_attribute }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_enabled_attribute -%}
|
||||
user_enabled_attribute = {{ options.ldap_user_enabled_attribute }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_enabled_invert|length -%}
|
||||
user_enabled_invert = {{ options.ldap_user_enabled_invert }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_enabled_mask|length -%}
|
||||
user_enabled_mask = {{ options.ldap_user_enabled_mask }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_enabled_default -%}
|
||||
user_enabled_default = {{ options.ldap_user_enabled_default }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_enabled_emulation|length -%}
|
||||
user_enabled_emulation = {{ options.ldap_user_enabled_emulation }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_user_enabled_emulation_dn -%}
|
||||
user_enabled_emulation_dn = {{ options.ldap_user_enabled_emulation_dn }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_group_tree_dn -%}
|
||||
group_tree_dn = {{ options.ldap_group_tree_dn }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_group_objectclass -%}
|
||||
group_objectclass = {{ options.ldap_group_objectclass }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_group_id_attribute -%}
|
||||
group_id_attribute = {{ options.ldap_group_id_attribute }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_group_name_attribute -%}
|
||||
group_name_attribute = {{ options.ldap_group_name_attribute }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_group_member_attribute -%}
|
||||
group_member_attribute = {{ options.ldap_group_member_attribute }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_group_members_are_ids|length -%}
|
||||
group_members_are_ids = {{ options.ldap_group_members_are_ids }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_use_pool|length -%}
|
||||
use_pool = {{ options.ldap_use_pool }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_pool_size|length -%}
|
||||
pool_size = {{ options.ldap_pool_size }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_pool_retry_max|length -%}
|
||||
pool_retry_max = {{ options.ldap_pool_retry_max }}
|
||||
{% endif -%}
|
||||
|
||||
{% if options.ldap_pool_connection_timeout|length -%}
|
||||
pool_connection_timeout = {{ options.ldap_pool_connection_timeout }}
|
||||
{% endif -%}
|
||||
|
||||
# User supplied configuration flags
|
||||
{% if ldap_config_flags.flags -%}
|
||||
{% for key, value in ldap_config_flags.flags.items() -%}
|
||||
{% for key, value in ldap_config.config.items()|sort -%}
|
||||
{{ key }} = {{ value }}
|
||||
{% endfor -%}
|
||||
{% endif -%}
|
||||
|
||||
[identity]
|
||||
driver = ldap
|
||||
|
||||
|
@ -10,7 +10,8 @@ mock
|
||||
flake8
|
||||
stestr
|
||||
git+https://github.com/openstack-charmers/zaza.git@libjuju-3.1#egg=zaza
|
||||
git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack
|
||||
# git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack
|
||||
git+https://github.com/gnuoy/zaza-openstack-tests.git@keystone-ldap-k8s#egg=zaza.openstack
|
||||
git+https://opendev.org/openstack/tempest.git#egg=tempest
|
||||
ops
|
||||
# Subunit 1.4.3+ requires extras
|
||||
|
15
charms/keystone-ldap-k8s/tests/__init__.py
Normal file
15
charms/keystone-ldap-k8s/tests/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright 2022 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.
|
||||
|
||||
"""Tests for charm."""
|
75
charms/keystone-ldap-k8s/tests/bundles/smoke.yaml
Normal file
75
charms/keystone-ldap-k8s/tests/bundles/smoke.yaml
Normal file
@ -0,0 +1,75 @@
|
||||
bundle: kubernetes
|
||||
applications:
|
||||
|
||||
mysql:
|
||||
charm: ch:mysql-k8s
|
||||
channel: 8.0/stable
|
||||
scale: 1
|
||||
trust: false
|
||||
|
||||
# Currently traefik is required for networking things.
|
||||
# If this isn't present, the units will hang at "installing agent".
|
||||
traefik:
|
||||
charm: ch:traefik-k8s
|
||||
channel: 1.0/stable
|
||||
scale: 1
|
||||
trust: true
|
||||
|
||||
# required for glance
|
||||
rabbitmq:
|
||||
charm: ch:rabbitmq-k8s
|
||||
channel: 3.9/edge
|
||||
scale: 1
|
||||
trust: true
|
||||
|
||||
keystone:
|
||||
charm: ch:keystone-k8s
|
||||
channel: 2023.1/edge/gnuoy
|
||||
series: jammy
|
||||
scale: 1
|
||||
trust: true
|
||||
options:
|
||||
admin-role: admin
|
||||
storage:
|
||||
fernet-keys: 5M
|
||||
credential-keys: 5M
|
||||
|
||||
keystone-ldap:
|
||||
charm: ../../keystone-ldap-k8s.charm
|
||||
scale: 1
|
||||
|
||||
ldap-server:
|
||||
charm: ch:ldap-test-fixture-k8s
|
||||
channel: edge
|
||||
scale: 1
|
||||
|
||||
glance:
|
||||
charm: ch:glance-k8s
|
||||
channel: 2023.1/edge
|
||||
scale: 1
|
||||
trust: true
|
||||
storage:
|
||||
local-repository: 5G
|
||||
|
||||
relations:
|
||||
- - keystone:identity-service
|
||||
- glance:identity-service
|
||||
- - rabbitmq:amqp
|
||||
- glance:amqp
|
||||
|
||||
- - traefik:ingress
|
||||
- keystone:ingress-public
|
||||
- - traefik:ingress
|
||||
- glance:ingress-public
|
||||
|
||||
- - rabbitmq:amqp
|
||||
- keystone:amqp
|
||||
|
||||
- - mysql:database
|
||||
- keystone:database
|
||||
|
||||
- - mysql:database
|
||||
- glance:database
|
||||
|
||||
- - keystone:domain-config
|
||||
- keystone-ldap:domain-config
|
1
charms/keystone-ldap-k8s/tests/config.yaml
Symbolic link
1
charms/keystone-ldap-k8s/tests/config.yaml
Symbolic link
@ -0,0 +1 @@
|
||||
../config.yaml
|
49
charms/keystone-ldap-k8s/tests/tests.yaml
Normal file
49
charms/keystone-ldap-k8s/tests/tests.yaml
Normal file
@ -0,0 +1,49 @@
|
||||
gate_bundles:
|
||||
- smoke
|
||||
smoke_bundles:
|
||||
- smoke
|
||||
configure:
|
||||
- zaza.openstack.charm_tests.keystone.setup.wait_for_all_endpoints
|
||||
- zaza.openstack.charm_tests.keystone.setup.add_tempest_roles
|
||||
tests:
|
||||
- zaza.openstack.charm_tests.keystone.tests_ldap_k8s.LdapExplicitCharmConfigTestsK8S
|
||||
- zaza.openstack.charm_tests.keystone.tests.KeystoneTempestTestK8S
|
||||
tests_options:
|
||||
trust:
|
||||
- smoke
|
||||
ignore_hard_deploy_errors:
|
||||
- smoke
|
||||
|
||||
tempest:
|
||||
default:
|
||||
smoke: True
|
||||
exclude-list:
|
||||
- "tempest.api.image.v2.test_images.BasicOperationsImagesTest.test_register_upload_get_image_file"
|
||||
include-list:
|
||||
- "tempest.api.identity.v3.test_application_credentials.ApplicationCredentialsV3Test.test_create_application_credential"
|
||||
|
||||
target_deploy_status:
|
||||
traefik:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^$'
|
||||
traefik-public:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^$'
|
||||
rabbitmq:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^$'
|
||||
glance:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^$'
|
||||
keystone:
|
||||
workload-status: waiting
|
||||
workload-status-message-regex: '^.*domain-config.*integration incomplete.*$|^$'
|
||||
keystone-ldap:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^$'
|
||||
ldap-server:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^$'
|
||||
mysql:
|
||||
workload-status: active
|
||||
workload-status-message-regex: '^.*$'
|
15
charms/keystone-ldap-k8s/tests/unit/__init__.py
Normal file
15
charms/keystone-ldap-k8s/tests/unit/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright 2022 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.
|
||||
|
||||
"""Unit tests for charm."""
|
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2021 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.
|
||||
|
||||
"""Define keystone tests."""
|
||||
|
||||
import json
|
||||
import os
|
||||
from unittest.mock import ANY, MagicMock
|
||||
|
||||
import mock
|
||||
import ops_sunbeam.test_utils as test_utils
|
||||
|
||||
import charm
|
||||
|
||||
|
||||
class _KeystoneLDAPK8SCharm(charm.KeystoneLDAPK8SCharm):
|
||||
"""Create Keystone operator test charm."""
|
||||
|
||||
def __init__(self, framework):
|
||||
self.seen_events = []
|
||||
super().__init__(framework)
|
||||
|
||||
def _log_event(self, event):
|
||||
self.seen_events.append(type(event).__name__)
|
||||
|
||||
def configure_charm(self, event):
|
||||
super().configure_charm(event)
|
||||
self._log_event(event)
|
||||
|
||||
@property
|
||||
def public_ingress_address(self) -> str:
|
||||
return "10.0.0.10"
|
Loading…
x
Reference in New Issue
Block a user