From 8862fa3b2a07e98b4cf50f0317c92564670cb9b0 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Thu, 10 Feb 2022 11:39:02 +0000 Subject: [PATCH] Include target container when logging container calls --- .../advanced_sunbeam_openstack/test_utils.py | 82 ++++++++++++++++--- ops-sunbeam/unit_tests/test_core.py | 27 +++--- 2 files changed, 84 insertions(+), 25 deletions(-) diff --git a/ops-sunbeam/advanced_sunbeam_openstack/test_utils.py b/ops-sunbeam/advanced_sunbeam_openstack/test_utils.py index 7c757f4c..5ff0c044 100644 --- a/ops-sunbeam/advanced_sunbeam_openstack/test_utils.py +++ b/ops-sunbeam/advanced_sunbeam_openstack/test_utils.py @@ -26,6 +26,7 @@ import pathlib import sys import typing import unittest +import collections from mock import MagicMock, Mock, patch @@ -37,6 +38,48 @@ from ops import framework, model from ops.testing import Harness, _TestingModelBackend, _TestingPebbleClient +class ContainerCalls: + """Object to log container calls.""" + + def __init__(self) -> None: + """Init container calls.""" + self.push = collections.defaultdict(list) + self.pull = collections.defaultdict(list) + self.execute = collections.defaultdict(list) + self.remove_path = collections.defaultdict(list) + + def add_push(self, container_name: str, call: typing.Dict) -> None: + """Log a push call.""" + self.push[container_name].append(call) + + def add_pull(self, container_name: str, call: typing.Dict) -> None: + """Log a pull call.""" + self.pull[container_name].append(call) + + def add_execute(self, container_name: str, call: typing.List) -> None: + """Log a execute call.""" + self.execute[container_name].append(call) + + def add_remove_path(self, container_name: str, call: str) -> None: + """Log a remove path call.""" + self.remove_path[container_name].append(call) + + def updated_files(self, container_name: str) -> typing.List: + """Return a list of files that have been updated in a container.""" + return [c['path'] for c in self.push.get(container_name, [])] + + def file_update_calls( + self, + container_name: str, + file_name: str + ) -> typing.List: + """Return the update call for File_name in container_name.""" + return [ + c + for c in self.push.get(container_name, []) + if c['path'] == file_name] + + class CharmTestCase(unittest.TestCase): """Class to make mocking easier.""" @@ -218,7 +261,13 @@ def add_all_relations(harness: Harness) -> None: """Add all the relations there are test relations for.""" for key in harness._meta.relations.keys(): if test_relations.get(key): - test_relations['key'](harness) + test_relations[key](harness) + + +def set_all_pebbles_ready(harness: Harness) -> None: + """Set all known pebble handlers to ready.""" + for container in harness._meta.containers: + harness.container_pebble_ready(container) def get_harness( @@ -245,22 +294,30 @@ def get_harness( group: str = None, ) -> None: """Capture push events and store in container_calls.""" - container_calls["push"][path] = { - "source": source, - "permissions": permissions, - "user": user, - "group": group, - } + container_calls.add_push( + self.container_name, + { + "path": path, + "source": source, + "permissions": permissions, + "user": user, + "group": group, + } + ) def pull(self, path: str, *, encoding: str = "utf-8") -> None: """Capture pull events and store in container_calls.""" - container_calls["pull"].append(path) + container_calls.add_pull( + self.container_name, + path) reader = io.StringIO("0") return reader def remove_path(self, path: str, *, recursive: bool = False) -> None: """Capture remove events and store in container_calls.""" - container_calls["remove_path"].append(path) + container_calls.add_remove_path( + self.container_name, + path) def exec( self, @@ -280,7 +337,9 @@ def get_harness( encoding: str = 'utf-8', combine_stderr: bool = False ) -> None: - container_calls["exec"].append(command) + container_calls.add_execute( + self.container_name, + command) process_mock = MagicMock() process_mock.wait_output.return_value = (None, None) return process_mock @@ -291,6 +350,9 @@ def get_harness( client = self._pebble_clients.get(socket_path, None) if client is None: client = _OSTestingPebbleClient(self) + # Extract container name from: + # /charm/containers/placement-api/pebble.socket + client.container_name = socket_path.split('/')[3] self._pebble_clients[socket_path] = client return client diff --git a/ops-sunbeam/unit_tests/test_core.py b/ops-sunbeam/unit_tests/test_core.py index e2ac9aba..849e5ab9 100644 --- a/ops-sunbeam/unit_tests/test_core.py +++ b/ops-sunbeam/unit_tests/test_core.py @@ -34,11 +34,7 @@ class TestOSBaseOperatorCharm(test_utils.CharmTestCase): def setUp(self) -> None: """Charm test class setup.""" - self.container_calls = { - 'push': {}, - 'exec': [], - 'pull': [], - 'remove_path': []} + self.container_calls = test_utils.ContainerCalls() super().setUp(sunbeam_charm, self.PATCHES) self.harness = test_utils.get_harness( test_charms.MyCharm, @@ -65,8 +61,8 @@ class TestOSBaseOperatorCharm(test_utils.CharmTestCase): """Test writing config when charm is ready.""" self.set_pebble_ready() self.assertEqual( - self.container_calls['push'], - {}) + self.container_calls.push['my-service'], + []) def test_container_names(self) -> None: """Test container name list is correct.""" @@ -90,11 +86,7 @@ class TestOSBaseOperatorAPICharm(test_utils.CharmTestCase): 'KubernetesServicePatch') def setUp(self, mock_svc_patch: mock.patch) -> None: """Charm test class setup.""" - self.container_calls = { - 'push': {}, - 'exec': [], - 'pull': [], - 'remove_path': []} + self.container_calls = test_utils.ContainerCalls() super().setUp(sunbeam_charm, self.PATCHES) self.harness = test_utils.get_harness( @@ -131,16 +123,21 @@ class TestOSBaseOperatorAPICharm(test_utils.CharmTestCase): 'bar'] expect_string = '\n' + '\n'.join(expect_entries) self.assertEqual( - self.container_calls['push']['/etc/my-service/my-service.conf'], + self.container_calls.file_update_calls( + 'my-service', + '/etc/my-service/my-service.conf')[0], { + 'path': '/etc/my-service/my-service.conf', 'group': 'my-service', 'permissions': None, 'source': expect_string, 'user': 'my-service'}) self.assertEqual( - self.container_calls['push'][ - '/etc/apache2/sites-available/wsgi-my-service.conf'], + self.container_calls.file_update_calls( + 'my-service', + '/etc/apache2/sites-available/wsgi-my-service.conf')[0], { + 'path': '/etc/apache2/sites-available/wsgi-my-service.conf', 'group': 'root', 'permissions': None, 'source': expect_string,