Start all services in a container
A container service layer is a dictionary of services so there may be more than once service defintion. Account for this by iterating of the services and starting each one in turn. Change-Id: I4cba2e0cda156a6852e71059b0dc0cb1948ce9e6
This commit is contained in:
parent
017e56809d
commit
8ea8f2855a
@ -256,6 +256,15 @@ class PebbleHandler(ops.charm.Object):
|
||||
else:
|
||||
self.status = ''
|
||||
|
||||
def _start_all(self) -> None:
|
||||
"""Start services in container."""
|
||||
container = self.charm.unit.get_container(self.container_name)
|
||||
services = container.get_services()
|
||||
for service_name, service in services.items():
|
||||
if service.is_running():
|
||||
container.stop(service_name)
|
||||
container.start(service_name)
|
||||
|
||||
|
||||
class ServicePebbleHandler(PebbleHandler):
|
||||
"""Container handler for containers which manage a service."""
|
||||
@ -272,7 +281,7 @@ class ServicePebbleHandler(PebbleHandler):
|
||||
self._state.service_ready = True
|
||||
|
||||
def start_service(self) -> None:
|
||||
"""Start service in container."""
|
||||
"""Check and start services in container."""
|
||||
container = self.charm.unit.get_container(self.container_name)
|
||||
if not container:
|
||||
logger.debug(f'{self.container_name} container is not ready. '
|
||||
@ -283,10 +292,7 @@ class ServicePebbleHandler(PebbleHandler):
|
||||
self.service_name,
|
||||
self.get_layer(),
|
||||
combine=True)
|
||||
service = container.get_service(self.service_name)
|
||||
if service.is_running():
|
||||
container.stop(self.service_name)
|
||||
container.start(self.service_name)
|
||||
self._start_all()
|
||||
|
||||
|
||||
class WSGIPebbleHandler(PebbleHandler):
|
||||
@ -316,7 +322,7 @@ class WSGIPebbleHandler(PebbleHandler):
|
||||
self.wsgi_service_name = wsgi_service_name
|
||||
|
||||
def start_wsgi(self) -> None:
|
||||
"""Start WSGI service."""
|
||||
"""Check and start services in container."""
|
||||
container = self.charm.unit.get_container(self.container_name)
|
||||
if not container:
|
||||
logger.debug(
|
||||
@ -329,11 +335,7 @@ class WSGIPebbleHandler(PebbleHandler):
|
||||
self.service_name,
|
||||
self.get_layer(),
|
||||
combine=True)
|
||||
service = container.get_service(self.wsgi_service_name)
|
||||
if service.is_running():
|
||||
container.stop(self.wsgi_service_name)
|
||||
|
||||
container.start(self.wsgi_service_name)
|
||||
self._start_all()
|
||||
|
||||
def start_service(self) -> None:
|
||||
"""Start the service."""
|
||||
|
@ -25,6 +25,7 @@ import sys
|
||||
import typing
|
||||
import unittest
|
||||
import collections
|
||||
from typing import List
|
||||
|
||||
from mock import MagicMock, Mock, patch
|
||||
|
||||
@ -140,11 +141,23 @@ class ContainerCalls:
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Init container calls."""
|
||||
self.start = collections.defaultdict(list)
|
||||
self.push = collections.defaultdict(list)
|
||||
self.pull = collections.defaultdict(list)
|
||||
self.execute = collections.defaultdict(list)
|
||||
self.remove_path = collections.defaultdict(list)
|
||||
|
||||
def add_start(self, container_name: str, call: typing.Dict) -> None:
|
||||
"""Log a start call."""
|
||||
self.start[container_name].append(call)
|
||||
|
||||
def started_services(self, container_name: str) -> List:
|
||||
"""Distinct unordered list of services that were started."""
|
||||
return list(set([
|
||||
svc
|
||||
for svc_list in self.start[container_name]
|
||||
for svc in svc_list]))
|
||||
|
||||
def add_push(self, container_name: str, call: typing.Dict) -> None:
|
||||
"""Log a push call."""
|
||||
self.push[container_name].append(call)
|
||||
@ -607,6 +620,14 @@ def get_harness(
|
||||
process_mock.wait_output.return_value = ('', None)
|
||||
return process_mock
|
||||
|
||||
def start_services(
|
||||
self, services: List[str], timeout: float = 30.0,
|
||||
delay: float = 0.1,) -> None:
|
||||
"""Record start service events."""
|
||||
container_calls.add_start(
|
||||
self.container_name,
|
||||
services)
|
||||
|
||||
class _OSTestingModelBackend(_TestingModelBackend):
|
||||
def get_pebble(self, socket_path: str) -> _OSTestingPebbleClient:
|
||||
"""Get the testing pebble client."""
|
||||
|
@ -24,11 +24,13 @@ from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import ops.framework
|
||||
from typing import List
|
||||
|
||||
sys.path.append("unit_tests/lib") # noqa
|
||||
sys.path.append("src") # noqa
|
||||
|
||||
import ops_sunbeam.charm as sunbeam_charm
|
||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||
|
||||
CHARM_CONFIG = """
|
||||
options:
|
||||
@ -255,3 +257,48 @@ class MyAPICharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||
def healthcheck_http_url(self) -> str:
|
||||
"""Healthcheck HTTP URL for the service."""
|
||||
return f'http://localhost:{self.default_public_ingress_port}/v3'
|
||||
|
||||
|
||||
class MultiSvcPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||
"""Test pebble handler for multi service charm."""
|
||||
|
||||
def get_layer(self) -> dict:
|
||||
"""Glance API service pebble layer.
|
||||
|
||||
:returns: pebble layer configuration for glance api service
|
||||
"""
|
||||
return {
|
||||
"summary": f"{self.service_name} layer",
|
||||
"description": "pebble config layer for glance api service",
|
||||
"services": {
|
||||
f"{self.service_name}": {
|
||||
"override": "replace",
|
||||
"summary": f"{self.service_name} standalone",
|
||||
"command": "/usr/bin/glance-api",
|
||||
"startup": "disabled",
|
||||
},
|
||||
"apache forwarder": {
|
||||
"override": "replace",
|
||||
"summary": "apache",
|
||||
"command": "/usr/sbin/apache2ctl -DFOREGROUND",
|
||||
"startup": "disabled",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TestMultiSvcCharm(MyAPICharm):
|
||||
"""Test class of multi service charm."""
|
||||
|
||||
def get_pebble_handlers(self) -> List[sunbeam_chandlers.PebbleHandler]:
|
||||
"""Pebble handlers for the service."""
|
||||
return [
|
||||
MultiSvcPebbleHandler(
|
||||
self,
|
||||
self.service_name,
|
||||
self.service_name,
|
||||
self.container_configs,
|
||||
self.template_dir,
|
||||
self.openstack_release,
|
||||
self.configure_charm
|
||||
)]
|
||||
|
@ -73,21 +73,18 @@ class TestOSBaseOperatorCharm(test_utils.CharmTestCase):
|
||||
self.harness.charm.relation_handlers_ready())
|
||||
|
||||
|
||||
class TestOSBaseOperatorAPICharm(test_utils.CharmTestCase):
|
||||
class _TestOSBaseOperatorAPICharm(test_utils.CharmTestCase):
|
||||
"""Test for the OSBaseOperatorAPICharm class."""
|
||||
|
||||
PATCHES = []
|
||||
|
||||
@mock.patch(
|
||||
'charms.observability_libs.v0.kubernetes_service_patch.'
|
||||
'KubernetesServicePatch')
|
||||
def setUp(self, mock_svc_patch: mock.patch) -> None:
|
||||
def setUp(self, charm_to_test: test_charms.MyAPICharm) -> None:
|
||||
"""Charm test class setup."""
|
||||
self.container_calls = test_utils.ContainerCalls()
|
||||
|
||||
super().setUp(sunbeam_charm, self.PATCHES)
|
||||
self.harness = test_utils.get_harness(
|
||||
test_charms.MyAPICharm,
|
||||
charm_to_test,
|
||||
test_charms.API_CHARM_METADATA,
|
||||
self.container_calls,
|
||||
charm_config=test_charms.CHARM_CONFIG,
|
||||
@ -116,6 +113,17 @@ class TestOSBaseOperatorAPICharm(test_utils.CharmTestCase):
|
||||
"""Set pebble ready event."""
|
||||
self.harness.container_pebble_ready('my-service')
|
||||
|
||||
|
||||
class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm):
|
||||
"""Test Charm with services."""
|
||||
|
||||
@mock.patch(
|
||||
'charms.observability_libs.v0.kubernetes_service_patch.'
|
||||
'KubernetesServicePatch')
|
||||
def setUp(self, mock_svc_patch: mock.patch) -> None:
|
||||
"""Run test class setup."""
|
||||
super().setUp(test_charms.MyAPICharm)
|
||||
|
||||
def test_write_config(self) -> None:
|
||||
"""Test when charm is ready configs are written correctly."""
|
||||
test_utils.add_complete_ingress_relation(self.harness)
|
||||
@ -150,6 +158,20 @@ class TestOSBaseOperatorAPICharm(test_utils.CharmTestCase):
|
||||
group='root',
|
||||
)
|
||||
|
||||
def test_start_services(self) -> None:
|
||||
"""Test service is started."""
|
||||
test_utils.add_complete_ingress_relation(self.harness)
|
||||
self.harness.set_leader()
|
||||
test_utils.add_complete_peer_relation(self.harness)
|
||||
self.set_pebble_ready()
|
||||
self.harness.charm.leader_set({'foo': 'bar'})
|
||||
test_utils.add_api_relations(self.harness)
|
||||
test_utils.add_complete_cloud_credentials_relation(self.harness)
|
||||
self.harness.set_can_connect('my-service', True)
|
||||
self.assertEqual(
|
||||
self.container_calls.started_services('my-service'),
|
||||
['wsgi-my-service'])
|
||||
|
||||
def test__on_database_changed(self) -> None:
|
||||
"""Test database is requested."""
|
||||
rel_id = self.harness.add_relation('peers', 'my-service')
|
||||
@ -300,3 +322,28 @@ class TestOSBaseOperatorAPICharm(test_utils.CharmTestCase):
|
||||
self.harness, ingress_rel_id, 'public')
|
||||
self.assertTrue(
|
||||
self.harness.charm.relation_handlers_ready())
|
||||
|
||||
|
||||
class TestOSBaseOperatorMultiSVCAPICharm(_TestOSBaseOperatorAPICharm):
|
||||
"""Test Charm with multiple services."""
|
||||
|
||||
@mock.patch(
|
||||
'charms.observability_libs.v0.kubernetes_service_patch.'
|
||||
'KubernetesServicePatch')
|
||||
def setUp(self, mock_svc_patch: mock.patch) -> None:
|
||||
"""Charm test class setip."""
|
||||
super().setUp(test_charms.TestMultiSvcCharm)
|
||||
|
||||
def test_start_services(self) -> None:
|
||||
"""Test multiple services are started."""
|
||||
test_utils.add_complete_ingress_relation(self.harness)
|
||||
self.harness.set_leader()
|
||||
test_utils.add_complete_peer_relation(self.harness)
|
||||
self.set_pebble_ready()
|
||||
self.harness.charm.leader_set({'foo': 'bar'})
|
||||
test_utils.add_api_relations(self.harness)
|
||||
test_utils.add_complete_cloud_credentials_relation(self.harness)
|
||||
self.harness.set_can_connect('my-service', True)
|
||||
self.assertEqual(
|
||||
sorted(self.container_calls.started_services('my-service')),
|
||||
sorted(['apache forwarder', 'my-service']))
|
||||
|
Loading…
Reference in New Issue
Block a user