Skip to content

Commit

Permalink
Add class for keystone audit middleware testing
Browse files Browse the repository at this point in the history
Added general class for testing keystone audit middleware
functionality in charms. Tests correct rendering of
api-paste.ini file and allows charms without an api-paste.ini
file to skip the tests.

Example usage with charm Heat:

tests/tests.yaml

tests:
  - zaza.openstack.charm_tests.audit.tests.KeystoneAuditMiddlewareTest

tests_options:
  audit-middleware:
    service: heat
  • Loading branch information
MylesJP committed Jun 19, 2024
1 parent 79bc345 commit ff5cdf5
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 40 deletions.
20 changes: 20 additions & 0 deletions zaza/openstack/charm_tests/audit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2024 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 audit middleware.
Collection of code for setting up and testing Keystone audit middleware
functionality.
"""
119 changes: 119 additions & 0 deletions zaza/openstack/charm_tests/audit/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/usr/bin/env python3
#
# Copyright 2024 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 audit middleware API logging testing.
These methods test the rendering of the charm api-paste.ini file to
ensure the appropriate sections are rendered or not rendered depending
on the state of the audit-middleware configuration option.
"""

import textwrap
import logging
import zaza.model
import zaza.openstack.charm_tests.test_utils as test_utils


class KeystoneAuditMiddlewareTest(test_utils.OpenStackBaseTest):
"""Keystone audit middleware test class."""

@classmethod
def setUpClass(cls):
"""Run class setup for Keystone audit middleware tests."""
super(KeystoneAuditMiddlewareTest, cls).setUpClass()
test_config = cls.test_config['tests_options']['audit-middleware']
cls.service_name = test_config['service']

cls.application_name = test_config.get('application', cls.service_name)
logging.info('Using application name: %s', cls.application_name)

cls.initial_audit_middleware = zaza.model.get_application_config(
cls.application_name)['audit-middleware']['value']

@classmethod
def tearDownClass(cls):
"""Restore the audit-middleware configuration to its original state."""
super(KeystoneAuditMiddlewareTest, cls).tearDownClass()
logging.info("Running teardown on %s" % cls.application_name)
zaza.model.set_application_config(
cls.application_name,
{'audit-middleware': str(cls.initial_audit_middleware)},
model_name=cls.model_name
)
zaza.model.wait_for_application_states(
states={cls.application_name: {
'workload-status': 'active',
'workload-status-message': 'Unit is ready'}},
model_name=cls.model_name
)

def fetch_api_paste_content(self):
"""Fetch content of api-paste.ini file."""
api_paste_ini_path = f"/etc/{self.service_name}/api-paste.ini"
lead_unit = zaza.model.get_lead_unit_name(
self.application_name,
model_name=self.model_name
)
try:
return zaza.model.file_contents(
lead_unit,
api_paste_ini_path,
)
except zaza.model.CommandRunFailed as e:
self.fail("Error fetching api-paste.ini: %s" % e)

def test_101_apipaste_includes_audit_section(self):
"""Test api-paste.ini renders audit section when enabled."""
expected_content = textwrap.dedent(f"""\
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/{self.service_name}/api_audit_map.conf
service_name = {self.service_name}
""")

set_default = {'audit-middleware': False}
set_alternate = {'audit-middleware': True}

with self.config_change(default_config=set_default,
alternate_config=set_alternate,
application_name=self.application_name):
api_paste_content = self.fetch_api_paste_content()
self.assertIn(expected_content, api_paste_content)

def test_102_apipaste_excludes_audit_section(self):
"""Test api_paste.ini does not render audit section when disabled."""
section_heading = '[filter:audit]'
set_default = {'audit-middleware': True}
set_alternate = {'audit-middleware': False}

with self.config_change(default_config=set_default,
alternate_config=set_alternate,
application_name=self.application_name):
api_paste_content = self.fetch_api_paste_content()
self.assertNotIn(section_heading, api_paste_content)


class IronicAuditMiddlewareTest(KeystoneAuditMiddlewareTest):
"""Ironic-API audit middleware test class."""

def test_101_apipaste_includes_audit_section(self):
"""Test api-paste.ini renders audit section when enabled."""
self.skipTest('ironic-api does not use an api-paste.ini file')

def test_102_apipaste_excludes_audit_section(self):
"""Test api_paste.ini does not render audit section when disabled."""
self.skipTest('ironic-api does not use an api-paste.ini file')
40 changes: 0 additions & 40 deletions zaza/openstack/charm_tests/cinder/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,46 +305,6 @@ def verify(stdin, stdout, stderr):
'lsblk -sn -o SIZE /dev/vdb',
privkey=privkey, verify=verify)

def test_300_apipaste_includes_audit_section(self):
"""Test api-paste.ini renders audit section when enabled."""
service_name = 'cinder'
api_paste_ini_path = f"/etc/{service_name}/api-paste.ini"
expected_content = [
"[filter:audit]",
"paste.filter_factory = keystonemiddleware.audit:filter_factory",
f"audit_map_file = /etc/{service_name}/api_audit_map.conf",
f"service_name = {service_name}"
]

set_default = {'audit-middleware': False}
set_alternate = {'audit-middleware': True}

with self.config_change(set_default, set_alternate):
try:
api_paste_content = zaza.model.file_contents(
self.lead_unit,
api_paste_ini_path,
)
except Exception as e:
self.fail("Error fetching api-paste.ini: {}".format(str(e)))
for line in expected_content:
self.assertIn(line, api_paste_content)

def test_301_apipaste_excludes_audit_section(self):
"""Test api_paste.ini does not render audit section when disabled."""
service_name = 'cinder'
section_heading = '[filter:audit]'
api_paste_ini_path = f"/etc/{service_name}/api-paste.ini"

try:
api_paste_content = zaza.model.file_contents(
self.lead_unit,
api_paste_ini_path
)
except Exception as e:
self.fail("Error fetching api-paste.ini: {}".format(str(e)))
self.assertNotIn(section_heading, api_paste_content)

@property
def services(self):
"""Return a list services for the selected OpenStack release."""
Expand Down

0 comments on commit ff5cdf5

Please sign in to comment.