
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: I8757d430ab233f4a21d1d599f1a7a9a0dc68d7ef
500 lines
21 KiB
Python
500 lines
21 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.
|
|
|
|
import importlib
|
|
import subprocess
|
|
import sys
|
|
import uuid
|
|
|
|
import unittest
|
|
|
|
from unittest.mock import (
|
|
call,
|
|
patch,
|
|
MagicMock,
|
|
)
|
|
import lib.swift_utils
|
|
# python-apt is not installed as part of test-requirements but is imported by
|
|
# some charmhelpers modules so create a fake import.
|
|
sys.modules['apt'] = MagicMock()
|
|
sys.modules['apt_pkg'] = MagicMock()
|
|
|
|
with patch('charmhelpers.contrib.hardening.harden.harden') as mock_dec, \
|
|
patch('lib.swift_utils.sync_builders_and_rings_if_changed') as rdec, \
|
|
patch('charmhelpers.core.hookenv.log'), \
|
|
patch('lib.swift_utils.register_configs'):
|
|
mock_dec.side_effect = (lambda *dargs, **dkwargs: lambda f:
|
|
lambda *args, **kwargs: f(*args, **kwargs))
|
|
rdec.side_effect = lambda f: f
|
|
import hooks.swift_hooks as swift_hooks
|
|
importlib.reload(swift_hooks)
|
|
|
|
|
|
# @unittest.skip("debugging ...")
|
|
class SwiftHooksTestCase(unittest.TestCase):
|
|
|
|
@patch.object(swift_hooks, "relation_get")
|
|
@patch.object(swift_hooks, "local_unit")
|
|
def test_is_all_peers_stopped(self, mock_local_unit, mock_relation_get):
|
|
token1 = str(uuid.uuid4())
|
|
token2 = str(uuid.uuid4())
|
|
mock_relation_get.return_value = token1
|
|
|
|
responses = [{'some-other-key': token1}]
|
|
self.assertFalse(swift_hooks.is_all_peers_stopped(responses))
|
|
|
|
responses = [{'stop-proxy-service-ack': token1},
|
|
{'stop-proxy-service-ack': token2}]
|
|
self.assertFalse(swift_hooks.is_all_peers_stopped(responses))
|
|
|
|
responses = [{'stop-proxy-service-ack': token1},
|
|
{'stop-proxy-service-ack': token1},
|
|
{'some-other-key': token1}]
|
|
self.assertFalse(swift_hooks.is_all_peers_stopped(responses))
|
|
|
|
responses = [{'stop-proxy-service-ack': token1},
|
|
{'stop-proxy-service-ack': token1}]
|
|
self.assertTrue(swift_hooks.is_all_peers_stopped(responses))
|
|
|
|
mock_relation_get.return_value = token2
|
|
|
|
responses = [{'stop-proxy-service-ack': token1},
|
|
{'stop-proxy-service-ack': token1}]
|
|
self.assertFalse(swift_hooks.is_all_peers_stopped(responses))
|
|
|
|
@patch.object(swift_hooks, 'config')
|
|
@patch('charmhelpers.contrib.openstack.ip.config')
|
|
@patch.object(swift_hooks, 'CONFIGS')
|
|
@patch('charmhelpers.core.hookenv.local_unit')
|
|
@patch('charmhelpers.core.hookenv.service_name')
|
|
@patch('charmhelpers.contrib.openstack.ip.unit_get')
|
|
@patch('charmhelpers.contrib.openstack.ip.is_clustered')
|
|
@patch.object(swift_hooks, 'relation_set')
|
|
def test_keystone_joined(self, _relation_set, _is_clustered, _unit_get,
|
|
_service_name, _local_unit, _CONFIGS, _ip_config,
|
|
_config):
|
|
config_dict = {
|
|
'bind-port': '1234',
|
|
'region': 'RegionOne',
|
|
'operator-roles': 'Operator,Monitor'
|
|
}
|
|
|
|
def foo(key=None):
|
|
if key is None:
|
|
return config_dict
|
|
else:
|
|
return config_dict.get(key)
|
|
|
|
_config.side_effect = foo
|
|
_ip_config.side_effect = foo
|
|
_unit_get.return_value = 'swift-proxy'
|
|
_local_unit.return_value = 'swift-proxy/0'
|
|
_service_name.return_value = 'swift-proxy'
|
|
_is_clustered.return_value = False
|
|
|
|
swift_hooks.keystone_joined()
|
|
|
|
_relation_set.assert_called_with(
|
|
admin_url=None,
|
|
internal_url=None,
|
|
public_url=None,
|
|
region=None,
|
|
relation_id=None,
|
|
s3_admin_url='http://swift-proxy:1234',
|
|
s3_internal_url='http://swift-proxy:1234',
|
|
s3_public_url='http://swift-proxy:1234',
|
|
s3_region='RegionOne',
|
|
s3_service='s3',
|
|
service=None,
|
|
swift_admin_url='http://swift-proxy:1234',
|
|
swift_internal_url='http://swift-proxy:1234/v1/AUTH_$(tenant_id)s',
|
|
swift_public_url='http://swift-proxy:1234/v1/AUTH_$(tenant_id)s',
|
|
swift_region='RegionOne',
|
|
swift_service='swift'
|
|
)
|
|
|
|
@patch.object(swift_hooks, 'config')
|
|
@patch('charmhelpers.contrib.openstack.ip.config')
|
|
@patch.object(swift_hooks, 'CONFIGS')
|
|
@patch('charmhelpers.core.hookenv.local_unit')
|
|
@patch('charmhelpers.core.hookenv.service_name')
|
|
@patch('charmhelpers.contrib.openstack.ip.unit_get')
|
|
@patch('charmhelpers.contrib.openstack.ip.is_clustered')
|
|
@patch.object(swift_hooks, 'relation_set')
|
|
def test_keystone_joined_public_hostname(self,
|
|
_relation_set,
|
|
_is_clustered,
|
|
_unit_get,
|
|
_service_name,
|
|
_local_unit,
|
|
_CONFIGS,
|
|
_ip_config,
|
|
_config):
|
|
config_dict = {
|
|
'bind-port': '1234',
|
|
'region': 'RegionOne',
|
|
'operator-roles': 'Operator,Monitor',
|
|
'os-public-hostname': 'public.example.com'
|
|
}
|
|
|
|
def foo(key=None):
|
|
if key is None:
|
|
return config_dict
|
|
else:
|
|
return config_dict.get(key)
|
|
|
|
_config.side_effect = _ip_config.side_effect = foo
|
|
_unit_get.return_value = _service_name.return_value = 'swift-proxy'
|
|
_local_unit.return_value = 'swift-proxy/0'
|
|
_is_clustered.return_value = False
|
|
|
|
swift_hooks.keystone_joined()
|
|
|
|
_relation_set.assert_called_with(
|
|
admin_url=None,
|
|
internal_url=None,
|
|
public_url=None,
|
|
region=None,
|
|
relation_id=None,
|
|
s3_admin_url='http://swift-proxy:1234',
|
|
s3_internal_url='http://swift-proxy:1234',
|
|
s3_public_url='http://public.example.com:1234',
|
|
s3_region='RegionOne',
|
|
s3_service='s3',
|
|
service=None,
|
|
swift_admin_url='http://swift-proxy:1234',
|
|
swift_internal_url='http://swift-proxy:1234/v1/AUTH_$(tenant_id)s',
|
|
swift_public_url=('http://public.example.com' +
|
|
':1234/v1/AUTH_$(tenant_id)s'),
|
|
swift_region='RegionOne',
|
|
swift_service='swift'
|
|
)
|
|
|
|
@patch.object(swift_hooks.time, 'time')
|
|
@patch.object(swift_hooks, 'get_host_ip')
|
|
@patch.object(swift_hooks, 'is_elected_leader')
|
|
@patch.object(swift_hooks, 'related_units')
|
|
@patch.object(swift_hooks, 'relation_ids')
|
|
@patch.object(swift_hooks, 'relation_set')
|
|
def test_update_rsync_acls(self, mock_rel_set, mock_rel_ids,
|
|
mock_rel_units, mock_is_leader,
|
|
mock_get_host_ip, mock_time):
|
|
mock_time.return_value = 1234
|
|
mock_is_leader.return_value = True
|
|
mock_rel_ids.return_value = ['storage:1']
|
|
mock_rel_units.return_value = ['unit/0', 'unit/1']
|
|
|
|
def fake_get_host_ip(rid, unit):
|
|
if unit == 'unit/0':
|
|
return '10.0.0.1'
|
|
elif unit == 'unit/1':
|
|
return '10.0.0.2'
|
|
|
|
mock_get_host_ip.side_effect = fake_get_host_ip
|
|
swift_hooks.update_rsync_acls()
|
|
calls = [call(rsync_allowed_hosts='10.0.0.1 10.0.0.2',
|
|
relation_id='storage:1', timestamp=1234)]
|
|
mock_rel_set.assert_has_calls(calls)
|
|
|
|
@patch.object(swift_hooks, 'get_relation_ip')
|
|
@patch.object(swift_hooks, 'relation_set')
|
|
@patch.object(swift_hooks, 'config')
|
|
def test_cluster_joined(self, mock_config, mock_relation_set,
|
|
mock_get_relation_ip):
|
|
mock_get_relation_ip.side_effect = [
|
|
'10.0.2.1',
|
|
'10.0.1.1',
|
|
'10.0.0.1',
|
|
'10.0.0.100']
|
|
config = {'os-public-network': '10.0.0.0/24',
|
|
'os-internal-network': '10.0.1.0/24',
|
|
'os-admin-network': '10.0.2.0/24'}
|
|
|
|
def fake_config(key):
|
|
return config.get(key)
|
|
|
|
mock_config.side_effect = fake_config
|
|
|
|
swift_hooks.cluster_joined()
|
|
mock_relation_set.assert_has_calls(
|
|
[call(relation_id=None,
|
|
relation_settings={'private-address': '10.0.0.100',
|
|
'admin-address': '10.0.2.1',
|
|
'internal-address': '10.0.1.1',
|
|
'public-address': '10.0.0.1'})])
|
|
|
|
@patch.object(swift_hooks, 'generate_ha_relation_data')
|
|
@patch.object(swift_hooks, 'relation_set')
|
|
def test_ha_relation_joined(self, relation_set, generate_ha_relation_data):
|
|
generate_ha_relation_data.return_value = {'rel_data': 'data'}
|
|
swift_hooks.ha_relation_joined(relation_id='rid:23')
|
|
relation_set.assert_called_once_with(
|
|
relation_id='rid:23', rel_data='data')
|
|
|
|
@patch.object(swift_hooks, 'clear_storage_rings_available')
|
|
@patch.object(swift_hooks, 'is_elected_leader')
|
|
@patch.object(swift_hooks, 'get_relation_ip')
|
|
@patch.object(swift_hooks, 'try_initialize_swauth')
|
|
@patch.object(swift_hooks, 'mark_www_rings_deleted')
|
|
@patch.object(swift_hooks, 'service_stop')
|
|
@patch.object(swift_hooks, 'relation_set')
|
|
def test_swift_storage_joined(self, relation_set, service_stop,
|
|
mark_www_rings_deleted,
|
|
try_initialize_swauth,
|
|
get_relation_ip,
|
|
is_elected_leader,
|
|
mock_clear_storage_rings_available):
|
|
is_elected_leader.return_value = False
|
|
get_relation_ip.return_value = '10.10.20.243'
|
|
swift_hooks.storage_joined(rid='swift-storage:23')
|
|
get_relation_ip.assert_called_with('swift-storage')
|
|
relation_set.assert_called_with(
|
|
relation_id='swift-storage:23',
|
|
relation_settings={'private-address': '10.10.20.243'}
|
|
)
|
|
try_initialize_swauth.assert_called_once()
|
|
mock_clear_storage_rings_available.assert_called_once()
|
|
|
|
@patch.object(swift_hooks, 'log')
|
|
@patch.object(swift_hooks, 'service_restart')
|
|
@patch.object(swift_hooks.openstack, 'is_unit_paused_set')
|
|
@patch.object(swift_hooks, 'update_rings')
|
|
@patch.object(swift_hooks, 'config')
|
|
@patch.object(swift_hooks, 'get_zone')
|
|
@patch.object(swift_hooks, 'update_rsync_acls')
|
|
@patch.object(swift_hooks, 'get_host_ip')
|
|
@patch.object(swift_hooks, 'is_elected_leader')
|
|
@patch.object(swift_hooks, 'relation_get')
|
|
def test_swift_storage_changed(self, relation_get, is_elected_leader,
|
|
get_host_ip, update_rsync_acls, get_zone,
|
|
config, update_rings, is_unit_paused_set,
|
|
service_restart, log):
|
|
is_elected_leader.return_value = True
|
|
get_host_ip.return_value = '10.0.0.10'
|
|
rel_data = {
|
|
'account_port': '6002',
|
|
'container_port': '6001',
|
|
'device': 'vdc',
|
|
'egress-subnets': '10.5.0.37/32',
|
|
'ingress-address': '10.5.0.37',
|
|
'object_port': '6000',
|
|
'private-address': '10.5.0.37',
|
|
'zone': '1'}
|
|
relation_get.side_effect = lambda x: rel_data.get(x)
|
|
swift_hooks.storage_changed()
|
|
update_rings.assert_called_once_with([{
|
|
'ip': '10.0.0.10',
|
|
'zone': 1,
|
|
'account_port': 6002,
|
|
'object_port': 6000,
|
|
'container_port': 6001,
|
|
'device': 'vdc'}])
|
|
|
|
@patch.object(swift_hooks, 'status_set')
|
|
@patch.object(swift_hooks, 'is_leader')
|
|
@patch.object(lib.swift_utils, 'leader_get')
|
|
@patch.object(lib.swift_utils, 'leader_set')
|
|
def test_rings_distributor_joined(self, leader_set, leader_get, is_leader,
|
|
status_set):
|
|
leader_get.return_value = None
|
|
is_leader.return_value = True
|
|
swift_hooks.rings_distributor_joined()
|
|
leader_set.assert_called_once_with(
|
|
{'swift-proxy-rings-distributor': True})
|
|
leader_set.reset_mock()
|
|
is_leader.return_value = False
|
|
swift_hooks.rings_distributor_joined()
|
|
self.assertFalse(leader_set.called)
|
|
|
|
@patch.object(swift_hooks, 'status_set')
|
|
@patch.object(lib.swift_utils, 'leader_get')
|
|
def test_rings_distributor_joined_consumer(self, leader_get, status_set):
|
|
leader_get.return_value = True
|
|
with self.assertRaises(lib.swift_utils.SwiftProxyCharmException):
|
|
swift_hooks.rings_distributor_joined()
|
|
status_set.assert_called_once_with(
|
|
'blocked',
|
|
('Swift Proxy cannot act as both rings distributor and rings '
|
|
'consumer'))
|
|
|
|
@patch.object(swift_hooks, 'broadcast_rings_available')
|
|
def test_rings_distributor_changed(self, broadcast_rings_available):
|
|
swift_hooks.rings_distributor_changed()
|
|
broadcast_rings_available.assert_called_once_with()
|
|
|
|
@patch.object(swift_hooks, 'is_leader')
|
|
@patch.object(lib.swift_utils, 'leader_set')
|
|
def test_rings_distributor_departed(self, leader_set, is_leader):
|
|
is_leader.return_value = True
|
|
swift_hooks.rings_distributor_departed()
|
|
leader_set.assert_called_once_with(
|
|
{'swift-proxy-rings-distributor': None})
|
|
leader_set.reset_mock()
|
|
is_leader.return_value = False
|
|
swift_hooks.rings_distributor_departed()
|
|
self.assertFalse(leader_set.called)
|
|
|
|
@patch.object(swift_hooks, 'is_leader')
|
|
@patch.object(lib.swift_utils, 'leader_get')
|
|
@patch.object(lib.swift_utils, 'leader_set')
|
|
def test_rings_consumer_joined(self, leader_set, leader_get, is_leader):
|
|
leader_data = {}
|
|
leader_get.side_effect = lambda x: leader_data.get(x)
|
|
is_leader.return_value = True
|
|
swift_hooks.rings_consumer_joined()
|
|
leader_set.assert_called_once_with(
|
|
{'swift-proxy-rings-consumer': True})
|
|
leader_set.reset_mock()
|
|
is_leader.return_value = False
|
|
swift_hooks.rings_consumer_joined()
|
|
self.assertFalse(leader_set.called)
|
|
|
|
@patch.object(lib.swift_utils, 'leader_get')
|
|
@patch.object(swift_hooks, 'status_set')
|
|
def test_rings_consumer_joined_distributor(self, status_set, leader_get):
|
|
leader_data = {
|
|
'swift-proxy-rings-distributor': True}
|
|
leader_get.side_effect = lambda x: leader_data.get(x)
|
|
swift_hooks.rings_consumer_joined()
|
|
status_set.assert_called_once_with(
|
|
'blocked',
|
|
('Swift Proxy cannot act as both rings distributor and rings '
|
|
'consumer'))
|
|
|
|
@patch.object(lib.swift_utils, 'leader_get')
|
|
@patch.object(swift_hooks, 'status_set')
|
|
def test_rings_consumer_joined_consumer(self, status_set, leader_get):
|
|
leader_data = {
|
|
'swift-proxy-rings-consumer': True}
|
|
leader_get.side_effect = lambda x: leader_data.get(x)
|
|
swift_hooks.rings_consumer_joined()
|
|
status_set.assert_called_once_with(
|
|
'blocked',
|
|
'Swift Proxy already acting as rings consumer')
|
|
|
|
@patch.object(swift_hooks, 'get_swift_hash')
|
|
@patch.object(swift_hooks, 'broadcast_rings_available')
|
|
@patch.object(swift_hooks, 'fetch_swift_rings_and_builders')
|
|
@patch.object(swift_hooks, 'relation_get')
|
|
def test_rings_consumer_changed(self, relation_get,
|
|
fetch_swift_rings_and_builders,
|
|
broadcast_rings_available,
|
|
get_swift_hash):
|
|
rel_data = {
|
|
'rings_url': 'http://some-url:999',
|
|
'swift_hash': 'swhash'}
|
|
relation_get.side_effect = lambda x: rel_data.get(x)
|
|
get_swift_hash.return_value = 'swhash'
|
|
swift_hooks.rings_consumer_changed()
|
|
fetch_swift_rings_and_builders.assert_called_once_with(
|
|
'http://some-url:999')
|
|
broadcast_rings_available.assert_called_once_with()
|
|
|
|
@patch.object(swift_hooks, 'log')
|
|
@patch.object(swift_hooks, 'get_swift_hash')
|
|
@patch.object(swift_hooks, 'broadcast_rings_available')
|
|
@patch.object(swift_hooks, 'fetch_swift_rings_and_builders')
|
|
@patch.object(swift_hooks, 'relation_get')
|
|
def test_rings_consumer_changed_no_url(self, relation_get,
|
|
fetch_swift_rings_and_builders,
|
|
broadcast_rings_available,
|
|
get_swift_hash,
|
|
log):
|
|
rel_data = {'swift_hash': 'swhash'}
|
|
relation_get.side_effect = lambda x: rel_data.get(x)
|
|
swift_hooks.rings_consumer_changed()
|
|
self.assertFalse(fetch_swift_rings_and_builders.called)
|
|
self.assertFalse(broadcast_rings_available.called)
|
|
log.assert_called_once_with(
|
|
'rings_consumer_relation_changed: Peer not ready?')
|
|
|
|
@patch.object(swift_hooks, 'log')
|
|
@patch.object(swift_hooks, 'get_swift_hash')
|
|
@patch.object(swift_hooks, 'broadcast_rings_available')
|
|
@patch.object(swift_hooks, 'fetch_swift_rings_and_builders')
|
|
@patch.object(swift_hooks, 'relation_get')
|
|
def test_rings_consumer_changed_empty_str(self, relation_get,
|
|
fetch_swift_rings_and_builders,
|
|
broadcast_rings_available,
|
|
get_swift_hash,
|
|
log):
|
|
rel_data = {
|
|
'rings_url': '',
|
|
'swift_hash': 'swhash'}
|
|
relation_get.side_effect = lambda x: rel_data.get(x)
|
|
swift_hooks.rings_consumer_changed()
|
|
self.assertFalse(fetch_swift_rings_and_builders.called)
|
|
self.assertFalse(broadcast_rings_available.called)
|
|
log.assert_called_once_with(
|
|
'rings_consumer_relation_changed: Peer not ready?')
|
|
|
|
@patch.object(swift_hooks, 'status_set')
|
|
@patch.object(swift_hooks, 'get_swift_hash')
|
|
@patch.object(swift_hooks, 'broadcast_rings_available')
|
|
@patch.object(swift_hooks, 'fetch_swift_rings_and_builders')
|
|
@patch.object(swift_hooks, 'relation_get')
|
|
def test_rings_consumer_changed_hash_miss(self, relation_get,
|
|
fetch_swift_rings_and_builders,
|
|
broadcast_rings_available,
|
|
get_swift_hash,
|
|
status_set):
|
|
rel_data = {
|
|
'rings_url': 'http://some-url:999',
|
|
'swift_hash': 'swhash'}
|
|
relation_get.side_effect = lambda x: rel_data.get(x)
|
|
get_swift_hash.return_value = 'mismatch'
|
|
with self.assertRaises(lib.swift_utils.SwiftProxyCharmException):
|
|
swift_hooks.rings_consumer_changed()
|
|
self.assertFalse(fetch_swift_rings_and_builders.called)
|
|
self.assertFalse(broadcast_rings_available.called)
|
|
status_set.assert_called_once_with(
|
|
'blocked',
|
|
'Swift hash has to be unique in multi-region setup')
|
|
|
|
@patch.object(swift_hooks, 'log')
|
|
@patch.object(swift_hooks, 'get_swift_hash')
|
|
@patch.object(swift_hooks, 'broadcast_rings_available')
|
|
@patch.object(swift_hooks, 'fetch_swift_rings_and_builders')
|
|
@patch.object(swift_hooks, 'relation_get')
|
|
def test_rings_consumer_changed_fetch_fail(self, relation_get,
|
|
fetch_swift_rings_and_builders,
|
|
broadcast_rings_available,
|
|
get_swift_hash,
|
|
log):
|
|
rel_data = {
|
|
'rings_url': 'http://some-url:999',
|
|
'swift_hash': 'swhash'}
|
|
relation_get.side_effect = lambda x: rel_data.get(x)
|
|
get_swift_hash.return_value = 'swhash'
|
|
|
|
def _fetch(url):
|
|
raise subprocess.CalledProcessError('cmd', 1)
|
|
fetch_swift_rings_and_builders.side_effect = _fetch
|
|
swift_hooks.rings_consumer_changed()
|
|
fetch_swift_rings_and_builders.assert_called_once_with(
|
|
'http://some-url:999')
|
|
log.assert_called_once_with(
|
|
('Failed to sync rings from http://some-url:999 - no longer '
|
|
'available from that unit?'),
|
|
level='WARNING')
|
|
broadcast_rings_available.assert_called_once_with()
|
|
|
|
@patch.object(swift_hooks, 'is_leader')
|
|
@patch.object(lib.swift_utils, 'leader_set')
|
|
def test_rings_consumer_departed(self, leader_set, is_leader):
|
|
is_leader.return_value = True
|
|
swift_hooks.rings_consumer_departed()
|
|
leader_set.assert_called_once_with(
|
|
{'swift-proxy-rings-consumer': None})
|