Files
charm-ceph-radosgw/unit_tests/test_ceph_radosgw_utils.py
James Page 2858f1b02d Switch to using systemd units for radosgw
Switch to using systemd configurations to manage radosgw instances;
the radosgw init script is obsolete and will be removed at some
point in time, and the newer style of managing radosgw daemons is
inline with current best-practice.

This changeset also changes the way cephx keys are issues; before
all rgw instances shared a key, now a key is issued per host.

The key is named 'rgw.`hostname`' to identify the application
and host using the key.

Existing deployments using the radosgw init script will be switched
to use the new systemd named units; this occurs once the new key
for the unit has been presented by the ceph-mon cluster over the
mon relation. A small period of outage will occur as the radosgw
init based daemon is stopped and disabled prior to the start of
the new systemd based radosgw unit.

This commit also includes a resync for charmhelpers to pickup
support for '@' in NRPE service check names.

Change-Id: Ic0d634e619185931633712cb3e3685051a28749d
Depends-On: I289b75a2935184817b424c5eceead16235c3f53b
Closes-Bug: 1808140
2019-01-24 13:04:52 +00:00

294 lines
12 KiB
Python

# Copyright 2016 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.
from mock import (
call,
patch,
mock_open,
MagicMock,
)
import utils
from test_utils import CharmTestCase
TO_PATCH = [
'application_version_set',
'get_upstream_version',
'format_endpoint',
'https',
'relation_ids',
'relation_get',
'related_units',
'socket',
'cmp_pkgrevno',
'init_is_systemd',
]
class CephRadosGWUtilTests(CharmTestCase):
def setUp(self):
super(CephRadosGWUtilTests, self).setUp(utils, TO_PATCH)
self.get_upstream_version.return_value = '10.2.2'
self.socket.gethostname.return_value = 'testhost'
def test_assess_status(self):
with patch.object(utils, 'assess_status_func') as asf:
callee = MagicMock()
asf.return_value = callee
utils.assess_status('test-config')
asf.assert_called_once_with('test-config')
callee.assert_called_once_with()
self.get_upstream_version.assert_called_with(
utils.VERSION_PACKAGE
)
self.application_version_set.assert_called_with('10.2.2')
@patch.object(utils, 'get_optional_interfaces')
@patch.object(utils, 'check_optional_relations')
@patch.object(utils, 'REQUIRED_INTERFACES')
@patch.object(utils, 'services')
@patch.object(utils, 'make_assess_status_func')
def test_assess_status_func(self,
make_assess_status_func,
services,
REQUIRED_INTERFACES,
check_optional_relations,
get_optional_interfaces):
services.return_value = 's1'
REQUIRED_INTERFACES.copy.return_value = {'int': ['test 1']}
get_optional_interfaces.return_value = {'opt': ['test 2']}
utils.assess_status_func('test-config')
# ports=None whilst port checks are disabled.
make_assess_status_func.assert_called_once_with(
'test-config',
{'int': ['test 1'], 'opt': ['test 2']},
charm_func=check_optional_relations,
services='s1', ports=None)
def test_pause_unit_helper(self):
with patch.object(utils, '_pause_resume_helper') as prh:
utils.pause_unit_helper('random-config')
prh.assert_called_once_with(utils.pause_unit, 'random-config')
with patch.object(utils, '_pause_resume_helper') as prh:
utils.resume_unit_helper('random-config')
prh.assert_called_once_with(utils.resume_unit, 'random-config')
@patch.object(utils, 'services')
def test_pause_resume_helper(self, services):
f = MagicMock()
services.return_value = 's1'
with patch.object(utils, 'assess_status_func') as asf:
asf.return_value = 'assessor'
utils._pause_resume_helper(f, 'some-config')
asf.assert_called_once_with('some-config')
# ports=None whilst port checks are disabled.
f.assert_called_once_with('assessor', services='s1', ports=None)
@patch.object(utils, 'get_keystone_client_from_relation')
@patch.object(utils, 'is_ipv6', lambda addr: False)
@patch.object(utils, 'get_ks_signing_cert')
@patch.object(utils, 'get_ks_ca_cert')
@patch.object(utils, 'mkdir')
def test_setup_keystone_certs(self, mock_mkdir,
mock_get_ks_ca_cert,
mock_get_ks_signing_cert,
mock_get_keystone_client):
auth_host = 'foo/bar'
auth_port = 80
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
mock_ksclient = MagicMock()
mock_ksclient.auth_endpoint = auth_url
mock_get_keystone_client.return_value = mock_ksclient
configs = MagicMock()
configs.complete_contexts.return_value = ['identity-service']
utils.setup_keystone_certs(configs)
mock_get_ks_signing_cert.assert_has_calls([call(mock_ksclient,
auth_url,
'/var/lib/ceph/nss')])
mock_get_ks_ca_cert.assert_has_calls([call(mock_ksclient, auth_url,
'/var/lib/ceph/nss')])
@patch.object(utils, 'client_v3')
@patch.object(utils, 'client')
@patch.object(utils, 'related_units')
@patch.object(utils, 'relation_ids')
@patch.object(utils, 'is_ipv6', lambda addr: False)
@patch.object(utils, 'relation_get')
def test_get_ks_client_from_relation(self, mock_relation_get,
mock_relation_ids,
mock_related_units,
mock_client,
mock_client_v3):
auth_host = 'foo/bar'
auth_port = 80
admin_token = '666'
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
self.format_endpoint.return_value = auth_url
mock_relation_ids.return_value = ['identity-service:5']
mock_related_units.return_value = ['keystone/1']
rel_data = {'auth_host': auth_host,
'auth_port': auth_port,
'admin_token': admin_token,
'api_version': '2'}
mock_relation_get.return_value = rel_data
utils.get_keystone_client_from_relation()
mock_client.Client.assert_called_with(endpoint=auth_url,
token=admin_token)
auth_url = 'http://%s:%s/v3' % (auth_host, auth_port)
self.format_endpoint.return_value = auth_url
rel_data['api_version'] = '3'
mock_relation_get.return_value = rel_data
utils.get_keystone_client_from_relation()
mock_client_v3.Client.assert_called_with(endpoint=auth_url,
token=admin_token)
@patch.object(utils, 'client_v3')
@patch.object(utils, 'client')
@patch.object(utils, 'related_units')
@patch.object(utils, 'relation_ids')
@patch.object(utils, 'is_ipv6', lambda addr: False)
@patch.object(utils, 'relation_get')
def test_get_ks_client_from_relation_not_available(self, mock_relation_get,
mock_relation_ids,
mock_related_units,
mock_client,
mock_client_v3):
mock_relation_ids.return_value = ['identity-service:5']
mock_related_units.return_value = ['keystone/1']
rel_data = {'auth_port': '5000',
'admin_token': 'foo',
'api_version': '2'}
mock_relation_get.return_value = rel_data
ksclient = utils.get_keystone_client_from_relation()
self.assertIsNone(ksclient)
@patch.object(utils, 'get_ks_cert')
@patch.object(utils.subprocess, 'Popen')
@patch.object(utils.subprocess, 'check_output')
def test_get_ks_signing_cert(self, mock_check_output, mock_Popen,
mock_get_ks_cert):
auth_host = 'foo/bar'
auth_port = 80
admin_token = '666'
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
m = mock_open()
with patch.object(utils, 'open', m, create=True):
mock_get_ks_cert.return_value = 'signing_cert_data'
utils.get_ks_signing_cert(admin_token, auth_url, '/foo/bar')
mock_get_ks_cert.return_value = None
with self.assertRaises(utils.KSCertSetupException):
utils.get_ks_signing_cert(admin_token, auth_url, '/foo/bar')
c = ['openssl', 'x509', '-in',
'/foo/bar/signing_certificate.pem',
'-pubkey']
mock_check_output.assert_called_with(c)
@patch.object(utils, 'get_ks_cert')
@patch.object(utils.subprocess, 'Popen')
@patch.object(utils.subprocess, 'check_output')
def test_get_ks_ca_cert(self, mock_check_output, mock_Popen,
mock_get_ks_cert):
auth_host = 'foo/bar'
auth_port = 80
admin_token = '666'
auth_url = 'http://%s:%s/v2.0' % (auth_host, auth_port)
m = mock_open()
with patch.object(utils, 'open', m, create=True):
mock_get_ks_cert.return_value = 'ca_cert_data'
utils.get_ks_ca_cert(admin_token, auth_url, '/foo/bar')
mock_get_ks_cert.return_value = None
with self.assertRaises(utils.KSCertSetupException):
utils.get_ks_ca_cert(admin_token, auth_url, '/foo/bar')
c = ['openssl', 'x509', '-in', '/foo/bar/ca.pem',
'-pubkey']
mock_check_output.assert_called_with(c)
def _setup_relation_data(self, data):
self.relation_ids.return_value = data.keys()
self.related_units.side_effect = (
lambda rid: data[rid].keys()
)
self.relation_get.side_effect = (
lambda attr, rid, unit: data[rid][unit].get(attr)
)
def test_systemd_based_radosgw_old_style(self):
_relation_data = {
'mon:1': {
'ceph-mon/0': {
'radosgw_key': 'testkey',
},
'ceph-mon/1': {
'radosgw_key': 'testkey',
},
'ceph-mon/2': {
'radosgw_key': 'testkey',
},
}
}
self._setup_relation_data(_relation_data)
self.assertFalse(utils.systemd_based_radosgw())
def test_systemd_based_radosgw_new_style(self):
_relation_data = {
'mon:1': {
'ceph-mon/0': {
'rgw.testhost_key': 'testkey',
},
'ceph-mon/1': {
'rgw.testhost_key': 'testkey',
},
'ceph-mon/2': {
'rgw.testhost_key': 'testkey',
},
}
}
self._setup_relation_data(_relation_data)
self.assertTrue(utils.systemd_based_radosgw())
def test_request_per_unit_key(self):
self.init_is_systemd.return_value = False
self.cmp_pkgrevno.return_value = -1
self.assertFalse(utils.request_per_unit_key())
self.init_is_systemd.return_value = True
self.cmp_pkgrevno.return_value = 1
self.assertTrue(utils.request_per_unit_key())
self.init_is_systemd.return_value = False
self.cmp_pkgrevno.return_value = 1
self.assertFalse(utils.request_per_unit_key())
self.cmp_pkgrevno.assert_called_with('radosgw', '12.2.0')
@patch.object(utils, 'systemd_based_radosgw')
def test_service_name(self, mock_systemd_based_radosgw):
mock_systemd_based_radosgw.return_value = True
self.assertEqual(utils.service_name(),
'ceph-radosgw@rgw.testhost')
mock_systemd_based_radosgw.return_value = False
self.assertEqual(utils.service_name(),
'radosgw')