[train] Ensure get_requests_for_local_unit doesn't fail on incomplete relation

This is a rebuild/make sync for charms to pickup the fix in charmhelpers to fix
any inadvertant accesses of ['ca'] in the relation data before it is available
from vault in the certificates relation.  Fix in charmhelpers is in [1].

[1] https://github.com/juju/charm-helpers/pull/832
Closes-Bug: #2028683

Change-Id: Ie679f22092dc317c7550a68e9ba493c291d65806
This commit is contained in:
Alex Kavanagh 2023-08-18 15:04:43 +01:00
parent 6fddfaac8a
commit 42e0e5ba17
13 changed files with 95 additions and 29 deletions

View File

@ -224,6 +224,13 @@ def https():
return True
if config_get('ssl_cert') and config_get('ssl_key'):
return True
# Local import to avoid ciruclar dependency.
import charmhelpers.contrib.openstack.cert_utils as cert_utils
if (
cert_utils.get_certificate_request() and not
cert_utils.get_requests_for_local_unit("certificates")
):
return False
for r_id in relation_ids('certificates'):
for unit in relation_list(r_id):
ca = relation_get('ca', rid=r_id, unit=unit)
@ -327,7 +334,7 @@ def valid_hacluster_config():
'''
vip = config_get('vip')
dns = config_get('dns-ha')
if not(bool(vip) ^ bool(dns)):
if not (bool(vip) ^ bool(dns)):
msg = ('HA: Either vip or dns-ha must be set but not both in order to '
'use high availability')
status_set('blocked', msg)

View File

@ -552,7 +552,7 @@ def port_has_listener(address, port):
"""
cmd = ['nc', '-z', address, str(port)]
result = subprocess.call(cmd)
return not(bool(result))
return not (bool(result))
def assert_charm_supports_ipv6():

View File

@ -652,7 +652,7 @@ def patch_ports_on_bridge(bridge):
uuid_for_port(
interface['options']['peer'])),
interface['options']['peer'])
yield(Patch(this_end, other_end))
yield Patch(this_end, other_end)
# We expect one result and it is ok if it turns out to be a port
# for a different bridge. However we need a break here to satisfy
# the for/else check which is in place to detect interface referring

View File

@ -199,7 +199,7 @@ class SimpleOVSDB(object):
decoded_set = []
for el in data[1]:
decoded_set.append(self._deserialize_ovsdb(el))
return(decoded_set)
return decoded_set
# fall back to normal processing below
break

View File

@ -409,13 +409,33 @@ def get_requests_for_local_unit(relation_name=None):
relation_name = relation_name or 'certificates'
bundles = []
for rid in relation_ids(relation_name):
sent = relation_get(rid=rid, unit=local_unit())
legacy_keys = ['certificate_name', 'common_name']
is_legacy_request = set(sent).intersection(legacy_keys)
for unit in related_units(rid):
data = relation_get(rid=rid, unit=unit)
if data.get(raw_certs_key):
bundles.append({
'ca': data['ca'],
'chain': data.get('chain'),
'certs': json.loads(data[raw_certs_key])})
# Note: Bug#2028683 - data may not be available if the certificates
# relation hasn't been populated by the providing charm. If no 'ca'
# in the data then don't attempt the bundle at all.
if data.get('ca'):
if data.get(raw_certs_key):
bundles.append({
'ca': data['ca'],
'chain': data.get('chain'),
'certs': json.loads(data[raw_certs_key])
})
elif is_legacy_request:
bundles.append({
'ca': data['ca'],
'chain': data.get('chain'),
'certs': {
sent['common_name']: {
'cert': data.get(local_name + '.server.cert'),
'key': data.get(local_name + '.server.key')
}
}
})
return bundles

View File

@ -310,7 +310,7 @@ def ssh_known_hosts_lines(application_name, user=None):
for hosts_line in hosts:
if hosts_line.rstrip():
known_hosts_list.append(hosts_line.rstrip())
return(known_hosts_list)
return known_hosts_list
def ssh_authorized_keys_lines(application_name, user=None):
@ -327,7 +327,7 @@ def ssh_authorized_keys_lines(application_name, user=None):
for authkey_line in keys:
if authkey_line.rstrip():
authorized_keys_list.append(authkey_line.rstrip())
return(authorized_keys_list)
return authorized_keys_list
def ssh_compute_remove(public_key, application_name, user=None):

View File

@ -1039,7 +1039,7 @@ def _determine_os_workload_status(
state, message, lambda: charm_func(configs))
if state is None:
state, message = _ows_check_services_running(services, ports)
state, message = ows_check_services_running(services, ports)
if state is None:
state = 'active'
@ -1213,7 +1213,12 @@ def _ows_check_charm_func(state, message, charm_func_with_configs):
return state, message
@deprecate("use ows_check_services_running() instead", "2022-05", log=juju_log)
def _ows_check_services_running(services, ports):
return ows_check_services_running(services, ports)
def ows_check_services_running(services, ports):
"""Check that the services that should be running are actually running
and that any ports specified are being listened to.
@ -1320,7 +1325,7 @@ def _check_listening_on_services_ports(services, test=False):
@param test: default=False, if False, test for closed, otherwise open.
@returns OrderedDict(service: [port-not-open, ...]...), [boolean]
"""
test = not(not(test)) # ensure test is True or False
test = not (not (test)) # ensure test is True or False
all_ports = list(itertools.chain(*services.values()))
ports_states = [port_has_listener('0.0.0.0', p) for p in all_ports]
map_ports = OrderedDict()
@ -1544,7 +1549,7 @@ def is_unit_paused_set():
with unitdata.HookData()() as t:
kv = t[0]
# transform something truth-y into a Boolean.
return not(not(kv.get('unit-paused')))
return not (not (kv.get('unit-paused')))
except Exception:
return False
@ -2143,7 +2148,7 @@ def is_unit_upgrading_set():
with unitdata.HookData()() as t:
kv = t[0]
# transform something truth-y into a Boolean.
return not(not(kv.get('unit-upgrading')))
return not (not (kv.get('unit-upgrading')))
except Exception:
return False
@ -2599,6 +2604,23 @@ def get_subordinate_release_packages(os_release, package_type='deb'):
return SubordinatePackages(install, purge)
def get_subordinate_services():
"""Iterate over subordinate relations and get service information.
In a similar fashion as with get_subordinate_release_packages(),
principle charms can retrieve a list of services advertised by their
subordinate charms. This is useful to know about subordinate services when
pausing, resuming or upgrading a principle unit.
:returns: Name of all services advertised by all subordinates
:rtype: Set[str]
"""
services = set()
for rdata in container_scoped_relation_get('services'):
services |= set(json.loads(rdata or '[]'))
return services
os_restart_on_change = partial(
pausable_restart_on_change,
can_restart_now_f=deferred_events.check_and_record_restart_request,

View File

@ -813,8 +813,10 @@ def get_mon_map(service):
ceph command fails.
"""
try:
octopus_or_later = cmp_pkgrevno('ceph-common', '15.0.0') >= 0
mon_status_cmd = 'quorum_status' if octopus_or_later else 'mon_status'
mon_status = check_output(['ceph', '--id', service,
'mon_status', '--format=json'])
mon_status_cmd, '--format=json'])
if six.PY3:
mon_status = mon_status.decode('UTF-8')
try:

View File

@ -926,7 +926,7 @@ def pwgen(length=None):
random_generator = random.SystemRandom()
random_chars = [
random_generator.choice(alphanumeric_chars) for _ in range(length)]
return(''.join(random_chars))
return ''.join(random_chars)
def is_phy_iface(interface):

View File

@ -52,7 +52,7 @@ def _snap_exec(commands):
:param commands: List commands
:return: Integer exit code
"""
assert type(commands) == list
assert isinstance(commands, list)
retry_count = 0
return_code = None

View File

@ -10,6 +10,7 @@
pyparsing<3.0.0 # aodhclient is pinned in zaza and needs pyparsing < 3.0.0, but cffi also needs it, so pin here.
cffi==1.14.6; python_version < '3.6' # cffi 1.15.0 drops support for py35.
setuptools<50.0.0 # https://github.com/pypa/setuptools/commit/04e3df22df840c6bb244e9b27bc56750c44b7c85
testtools<2.6.0
requests>=2.18.4

View File

@ -23,6 +23,7 @@ skip_missing_interpreters = False
# lead to fetching the latest pip in the func* tox targets, see
# https://stackoverflow.com/a/38133283
requires =
tox < 4.0.0
pip < 20.3
virtualenv < 20.0
setuptools < 50.0.0
@ -79,7 +80,8 @@ commands = stestr run --slowest {posargs}
[testenv:pep8]
basepython = python3
deps = flake8==3.9.2
charm-tools==2.8.3
PyYAML==6.0.1
charm-tools==2.8.6
commands = flake8 {posargs} hooks unit_tests tests actions lib files
charm-proof

View File

@ -161,6 +161,7 @@ class NeutronAPIHooksTests(CharmTestCase):
_port_calls = [call(port) for port in _ports]
self.determine_packages.return_value = _pkgs
self.determine_ports.return_value = _ports
self.patch('additional_install_locations')
self._call_hook('install')
self.configure_installation_source.assert_called_with(
'distro'
@ -183,6 +184,8 @@ class NeutronAPIHooksTests(CharmTestCase):
_port_calls = [call(port) for port in _ports]
self.determine_packages.return_value = _pkgs
self.determine_ports.return_value = _ports
self.patch('additional_install_locations')
self.patch('maybe_set_os_install_release')
self._call_hook('install')
self.configure_installation_source.assert_called_with(
'distro'
@ -201,6 +204,7 @@ class NeutronAPIHooksTests(CharmTestCase):
self.dvr_router_present.return_value = False
self.l3ha_router_present.return_value = False
self.relation_ids.side_effect = self._fake_relids
self.patch('additional_install_locations')
_n_api_rel_joined = self.patch('neutron_api_relation_joined')
_n_plugin_api_rel_joined =\
self.patch('neutron_plugin_api_relation_joined')
@ -241,6 +245,7 @@ class NeutronAPIHooksTests(CharmTestCase):
self.remove_old_packages.return_value = False
self.openstack_upgrade_available.return_value = True
self.test_config.set('action-managed-upgrade', True)
self.patch('additional_install_locations')
self._call_hook('config-changed')
@ -250,6 +255,7 @@ class NeutronAPIHooksTests(CharmTestCase):
self.remove_old_packages.return_value = True
self.services.return_value = ['neutron-server']
self.openstack_upgrade_available.return_value = False
self.patch('additional_install_locations')
self._call_hook('config-changed')
self.remove_old_packages.assert_called_once_with()
self.service_restart.assert_called_once_with('neutron-server')
@ -267,14 +273,17 @@ class NeutronAPIHooksTests(CharmTestCase):
self.relation_ids.return_value = ['neutron-plugin-api-subordinate:1']
self.CONFIGS.complete_contexts.return_value = ['amqp']
self._call_hook('amqp-relation-changed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
self.CONFIGS.write.assert_called_with(NEUTRON_CONF)
self.relation_ids.assert_called_with('neutron-plugin-api-subordinate')
plugin_joined.assert_called_with(
relid='neutron-plugin-api-subordinate:1')
def test_amqp_departed(self):
self.CONFIGS.complete_contexts.return_value = {
'amqp': None
}
self._call_hook('amqp-relation-departed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
self.CONFIGS.write.assert_called_with(NEUTRON_CONF)
def test_db_joined(self):
self.get_relation_ip.return_value = '10.0.0.1'
@ -395,7 +404,7 @@ class NeutronAPIHooksTests(CharmTestCase):
_api_rel_joined = self.patch('neutron_api_relation_joined')
self.relation_ids.side_effect = self._fake_relids
self._call_hook('identity-service-relation-changed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
self.CONFIGS.write.assert_called_with(NEUTRON_CONF)
self.assertTrue(_api_rel_joined.called)
@patch.object(hooks, 'canonical_url')
@ -464,7 +473,7 @@ class NeutronAPIHooksTests(CharmTestCase):
})
self._call_hook('vsd-rest-api-relation-changed')
config_file = '/etc/neutron/plugins/nuage/nuage_plugin.ini'
self.assertTrue(self.CONFIGS.write.called_with(config_file))
self.CONFIGS.write.assert_called_with(config_file)
def test_vsd_api_relation_joined(self):
self.os_release.return_value = 'kilo'
@ -488,12 +497,12 @@ class NeutronAPIHooksTests(CharmTestCase):
def test_neutron_api_relation_changed(self):
self.CONFIGS.complete_contexts.return_value = ['shared-db']
self._call_hook('neutron-api-relation-changed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
self.CONFIGS.write.assert_called_with(NEUTRON_CONF)
def test_neutron_api_relation_changed_incomplere_ctxt(self):
self.CONFIGS.complete_contexts.return_value = []
self._call_hook('neutron-api-relation-changed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
self.CONFIGS.write.assert_called_with(NEUTRON_CONF)
@patch.object(hooks, 'is_api_ready')
def test_neutron_load_balancer_relation_joined(self, is_api_ready):
@ -1023,11 +1032,11 @@ class NeutronAPIHooksTests(CharmTestCase):
})
self.relation_ids.side_effect = self._fake_relids
self._call_hook('external-dns-relation-joined')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
self.CONFIGS.write_all.assert_called_with()
def test_designate_peer_departed(self):
self._call_hook('external-dns-relation-departed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
self.CONFIGS.write_all.assert_called_with()
def test_infoblox_peer_changed(self):
self.is_db_initialised.return_value = True
@ -1036,12 +1045,15 @@ class NeutronAPIHooksTests(CharmTestCase):
})
self.os_release.return_value = 'queens'
self.relation_ids.side_effect = self._fake_relids
self.CONFIGS.complete_contexts.return_value = {
'infoblox-neutron': None
}
self._call_hook('infoblox-neutron-relation-changed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
self.CONFIGS.write.assert_called_with(NEUTRON_CONF)
def test_infoblox_peer_departed(self):
self._call_hook('infoblox-neutron-relation-departed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
self.CONFIGS.write_all.assert_called_with()
@patch.object(hooks, 'NeutronApiSDNContext')
@patch.object(hooks, 'NeutronCCContext')