4cbac58ba1
The mock third party library was needed for mock support in py2 runtimes. Since we now only support py36 and later, we can use the standard lib unittest.mock module instead. Note that https://github.com/openstack/charms.openstack is used during tests and he need `mock`, unfortunatelly it doesn't declare `mock` in its requirements so it retrieve mock from other charm project (cross dependency). So we depend on charms.openstack first and when Ib1ed5b598a52375e29e247db9ab4786df5b6d142 will be merged then CI will pass without errors. Depends-On: Ib1ed5b598a52375e29e247db9ab4786df5b6d142 Change-Id: Ib658c7f61fe4aceafc1919e366d24ce81ec1dd63
196 lines
6.7 KiB
Python
196 lines
6.7 KiB
Python
# Copyright 2020 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.
|
|
|
|
import unittest.mock as mock
|
|
from contextlib import contextmanager
|
|
|
|
from actions import service
|
|
from hooks import utils
|
|
|
|
from test_utils import CharmTestCase
|
|
|
|
|
|
class CompletedProcessMock:
|
|
def __init__(self, stdout=b'', stderr=b''):
|
|
self.stdout = stdout
|
|
self.stderr = stderr
|
|
|
|
|
|
class ServiceActionTests(CharmTestCase):
|
|
_PRESENT_SERVICES = [
|
|
"ceph-osd@0.service",
|
|
"ceph-osd@1.service",
|
|
"ceph-osd@2.service",
|
|
]
|
|
|
|
_TARGET_ALL = 'ceph-osd.target'
|
|
|
|
_CHECK_CALL_TIMEOUT = 300
|
|
|
|
def __init__(self, methodName='runTest'):
|
|
super(ServiceActionTests, self).__init__(methodName)
|
|
|
|
def setUp(self, obj=None, patches=None):
|
|
super(ServiceActionTests, self).setUp(
|
|
service,
|
|
['subprocess', 'function_fail',
|
|
'log', 'assess_status', 'shutil']
|
|
)
|
|
present_services = '\n'.join(self._PRESENT_SERVICES).encode('utf-8')
|
|
|
|
self.shutil.which.return_value = '/bin/systemctl'
|
|
self.subprocess.check_call.return_value = None
|
|
self.subprocess.run.return_value = CompletedProcessMock(
|
|
stdout=present_services)
|
|
|
|
@contextmanager
|
|
def func_call_arguments(self, osds=None):
|
|
with mock.patch("utils.function_get") as mock_function_get:
|
|
self._func_args = {'osds': osds}
|
|
mock_function_get.side_effect = \
|
|
lambda arg: self._func_args.get(arg)
|
|
yield
|
|
|
|
def assert_action_start_fail(self, msg):
|
|
self.assert_function_fail(service.START, msg)
|
|
|
|
def assert_action_stop_fail(self, msg):
|
|
self.assert_function_fail(service.STOP, msg)
|
|
|
|
def assert_function_fail(self, action, msg):
|
|
expected_error = "Action '{}' failed: {}".format(action, msg)
|
|
self.function_fail.assert_called_with(expected_error)
|
|
|
|
@staticmethod
|
|
def call_action_start():
|
|
service.main(['start'])
|
|
|
|
@staticmethod
|
|
def call_action_stop():
|
|
service.main(['stop'])
|
|
|
|
def test_systemctl_execute_all(self):
|
|
action = 'start'
|
|
services = utils.ALL
|
|
|
|
expected_call = mock.call(['systemctl', action, self._TARGET_ALL],
|
|
timeout=self._CHECK_CALL_TIMEOUT)
|
|
|
|
service.systemctl_execute(action, services)
|
|
|
|
self.subprocess.check_call.assert_has_calls([expected_call])
|
|
|
|
def systemctl_execute_specific(self):
|
|
action = 'start'
|
|
services = ['ceph-osd@1.service', 'ceph-osd@2.service']
|
|
|
|
systemctl_call = ['systemctl', action] + services
|
|
expected_call = mock.call(systemctl_call,
|
|
timeout=self._CHECK_CALL_TIMEOUT)
|
|
|
|
service.systemctl_execute(action, services)
|
|
|
|
self.subprocess.check_call.assert_has_calls([expected_call])
|
|
|
|
def test_id_translation(self):
|
|
service_ids = {1, utils.ALL, 2}
|
|
expected_names = [
|
|
'ceph-osd@1.service',
|
|
utils.ALL,
|
|
'ceph-osd@2.service',
|
|
]
|
|
service_names = service.osd_ids_to_service_names(service_ids)
|
|
self.assertEqual(sorted(service_names), sorted(expected_names))
|
|
|
|
def test_skip_service_presence_check(self):
|
|
service_list = [utils.ALL]
|
|
|
|
service.check_service_is_present(service_list)
|
|
|
|
self.subprocess.run.assert_not_called()
|
|
|
|
def test_raise_all_missing_services(self):
|
|
missing_service_id = '99,100'
|
|
missing_list = []
|
|
for id_ in missing_service_id.split(','):
|
|
missing_list.append("ceph-osd@{}.service".format(id_))
|
|
|
|
service_list_cmd = ['systemctl', 'list-units', '--full', '--all',
|
|
'--no-pager', '-t', 'service']
|
|
|
|
err_msg = 'Some services are not present on this ' \
|
|
'unit: {}'.format(missing_list)
|
|
|
|
with self.assertRaises(RuntimeError, msg=err_msg):
|
|
service.check_service_is_present(missing_list)
|
|
|
|
self.subprocess.run.assert_called_with(service_list_cmd,
|
|
stdout=self.subprocess.PIPE,
|
|
timeout=30)
|
|
|
|
def test_fail_execute_unknown_action(self):
|
|
action = 'foo'
|
|
err_msg = 'Unknown action "{}"'.format(action)
|
|
with self.assertRaises(RuntimeError, msg=err_msg):
|
|
service.execute_action(action)
|
|
|
|
@mock.patch.object(service, 'systemctl_execute')
|
|
def test_execute_action(self, _):
|
|
with self.func_call_arguments(osds=utils.ALL):
|
|
service.execute_action(service.START)
|
|
service.systemctl_execute.assert_called_with(service.START,
|
|
[utils.ALL])
|
|
|
|
service.execute_action(service.STOP)
|
|
service.systemctl_execute.assert_called_with(service.STOP,
|
|
[utils.ALL])
|
|
|
|
@mock.patch.object(service, 'execute_action')
|
|
def test_action_stop(self, execute_action):
|
|
self.call_action_stop()
|
|
execute_action.assert_called_with(service.STOP)
|
|
|
|
@mock.patch.object(service, 'execute_action')
|
|
def test_action_start(self, execute_action):
|
|
self.call_action_start()
|
|
execute_action.assert_called_with(service.START)
|
|
|
|
def test_actions_requires_systemd(self):
|
|
"""Actions will fail if systemd is not present on the system"""
|
|
self.shutil.which.return_value = None
|
|
expected_error = 'This action requires systemd'
|
|
with self.func_call_arguments(osds='all'):
|
|
self.call_action_start()
|
|
self.assert_action_start_fail(expected_error)
|
|
|
|
self.call_action_stop()
|
|
self.assert_action_stop_fail(expected_error)
|
|
|
|
self.subprocess.check_call.assert_not_called()
|
|
|
|
def test_unknown_action(self):
|
|
action = 'foo'
|
|
err_msg = 'Action {} undefined'.format(action)
|
|
service.main([action])
|
|
self.function_fail.assert_called_with(err_msg)
|
|
|
|
@mock.patch.object(service, 'execute_action')
|
|
def test_action_failure(self, start_function):
|
|
err_msg = 'Test Error'
|
|
service.execute_action.side_effect = RuntimeError(err_msg)
|
|
|
|
self.call_action_start()
|
|
|
|
self.assert_action_start_fail(err_msg)
|