Tighten up the leader_set(..) usage to handle any errors

This stops leader_set from throwing backtraces, and instead logs the
error and continues as though the charm is not the leader (which is the
case when leader_set(...) fails).

Changed the py35 tox job to invoke py3 (to allow it to also test under
py3.6 on artful+).

Note that the pep8 check is still py27, so the additional # NOQA is
to handle Py2 not having FileNotFoundError.

Change-Id: Ic25d29983db9a0738d83e66de4673bb50594b599
This commit is contained in:
Alex Kavanagh 2018-05-03 15:30:52 +01:00
parent d45ab82589
commit edfd6a55af
3 changed files with 41 additions and 42 deletions

View File

@ -205,25 +205,33 @@ def config_changed():
mon_secret = config('monitor-secret') mon_secret = config('monitor-secret')
else: else:
mon_secret = "{}".format(ceph.generate_monitor_secret()) mon_secret = "{}".format(ceph.generate_monitor_secret())
status_set('maintenance', 'Creating FSID and Monitor Secret')
opts = { opts = {
'fsid': fsid, 'fsid': fsid,
'monitor-secret': mon_secret, 'monitor-secret': mon_secret,
} }
log("Settings for the cluster are: {}".format(opts)) try:
leader_set(opts) leader_set(opts)
elif cfg.changed('no-bootstrap') and \ status_set('maintenance',
is_relation_made('bootstrap-source'): 'Created FSID and Monitor Secret')
log("Settings for the cluster are: {}".format(opts))
except Exception as e:
# we're probably not the leader an exception occured
# let's log it anyway.
log("leader_set failed: {}".format(str(e)))
elif (cfg.changed('no-bootstrap') and
is_relation_made('bootstrap-source')):
# User changed the no-bootstrap config option, we're the leader, # User changed the no-bootstrap config option, we're the leader,
# and the bootstrap-source relation has been made. The charm should # and the bootstrap-source relation has been made. The charm should
# be in a blocked state indicating that the no-bootstrap option # be in a blocked state indicating that the no-bootstrap option
# must be set. This block is invoked when the user is trying to # must be set. This block is invoked when the user is trying to
# get out of that scenario by enabling no-bootstrap. # get out of that scenario by enabling no-bootstrap.
bootstrap_source_relation_changed() bootstrap_source_relation_changed()
elif leader_get('fsid') is None or leader_get('monitor-secret') is None: # unconditionally verify that the fsid and monitor-secret are set now
# otherwise we exit until a leader does this.
if leader_get('fsid') is None or leader_get('monitor-secret') is None:
log('still waiting for leader to setup keys') log('still waiting for leader to setup keys')
status_set('waiting', 'Waiting for leader to setup keys') status_set('waiting', 'Waiting for leader to setup keys')
sys.exit(0) return
emit_cephconf() emit_cephconf()
@ -231,7 +239,12 @@ def config_changed():
if (not ceph.is_bootstrapped() and int(config('monitor-count')) == 1 and if (not ceph.is_bootstrapped() and int(config('monitor-count')) == 1 and
is_leader()): is_leader()):
status_set('maintenance', 'Bootstrapping single Ceph MON') status_set('maintenance', 'Bootstrapping single Ceph MON')
ceph.bootstrap_monitor_cluster(leader_get('monitor-secret')) # the following call raises an exception if it can't add the keyring
try:
ceph.bootstrap_monitor_cluster(leader_get('monitor-secret'))
except FileNotFoundError as e: # NOQA -- PEP8 is still PY2
log("Couldn't bootstrap the monitor yet: {}".format(str(e)))
return
ceph.wait_for_bootstrap() ceph.wait_for_bootstrap()
if cmp_pkgrevno('ceph', '12.0.0') >= 0: if cmp_pkgrevno('ceph', '12.0.0') >= 0:
status_set('maintenance', 'Bootstrapping single Ceph MGR') status_set('maintenance', 'Bootstrapping single Ceph MGR')
@ -323,15 +336,18 @@ def bootstrap_source_relation_changed():
assert curr_secret == mon_secret, \ assert curr_secret == mon_secret, \
"bootstrap secret '{}' != current secret '{}'".format( "bootstrap secret '{}' != current secret '{}'".format(
mon_secret, curr_secret) mon_secret, curr_secret)
opts = { opts = {
'fsid': fsid, 'fsid': fsid,
'monitor-secret': mon_secret, 'monitor-secret': mon_secret,
} }
try:
log('Updating leader settings for fsid and monitor-secret ' leader_set(opts)
'from remote relation data: {}'.format(opts)) log('Updating leader settings for fsid and monitor-secret '
leader_set(opts) 'from remote relation data: {}'.format(opts))
except Exception as e:
# we're probably not the leader an exception occured
# let's log it anyway.
log("leader_set failed: {}".format(str(e)))
# The leader unit needs to bootstrap itself as it won't receive the # The leader unit needs to bootstrap itself as it won't receive the
# leader-settings-changed hook elsewhere. # leader-settings-changed hook elsewhere.
@ -353,7 +369,12 @@ def mon_relation():
moncount = int(config('monitor-count')) moncount = int(config('monitor-count'))
if len(get_mon_hosts()) >= moncount: if len(get_mon_hosts()) >= moncount:
status_set('maintenance', 'Bootstrapping MON cluster') status_set('maintenance', 'Bootstrapping MON cluster')
ceph.bootstrap_monitor_cluster(leader_get('monitor-secret')) # the following call raises an exception if it can't add the keyring
try:
ceph.bootstrap_monitor_cluster(leader_get('monitor-secret'))
except FileNotFoundError as e: # NOQA -- PEP8 is still PY2
log("Couldn't bootstrap the monitor yet: {}".format(str(e)))
exit(0)
ceph.wait_for_bootstrap() ceph.wait_for_bootstrap()
ceph.wait_for_quorum() ceph.wait_for_quorum()
if cmp_pkgrevno('ceph', '12.0.0') >= 0: if cmp_pkgrevno('ceph', '12.0.0') >= 0:

13
tox.ini
View File

@ -2,9 +2,10 @@
# This file is managed centrally by release-tools and should not be modified # This file is managed centrally by release-tools and should not be modified
# within individual charm repos. # within individual charm repos.
[tox] [tox]
envlist = pep8,py27,py35,py36 ;envlist = pep8,py27,py35,py36
envlist = pep8,py27,py35
skipsdist = True skipsdist = True
skip_missing_interpreters = True ;skip_missing_interpreters = True
[testenv] [testenv]
setenv = VIRTUAL_ENV={envdir} setenv = VIRTUAL_ENV={envdir}
@ -24,13 +25,9 @@ deps = -r{toxinidir}/requirements.txt
# temporarily disable py27 # temporarily disable py27
commands = /bin/true commands = /bin/true
; keep zuul happy until we change the py35 job
[testenv:py35] [testenv:py35]
basepython = python3.5 basepython = python3
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:py36]
basepython = python3.6
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt

View File

@ -17,8 +17,7 @@ import unittest
import os import os
import yaml import yaml
from contextlib import contextmanager from mock import patch
from mock import patch, MagicMock
def load_config(): def load_config():
@ -145,21 +144,3 @@ class TestLeaderSettings(object):
elif attr in self.settings: elif attr in self.settings:
return self.settings[attr] return self.settings[attr]
return None return None
@contextmanager
def patch_open():
'''Patch open() to allow mocking both open() itself and the file that is
yielded.
Yields the mock for "open" and "file", respectively.'''
mock_open = MagicMock(spec=open)
mock_file = MagicMock(spec=file)
@contextmanager
def stub_open(*args, **kwargs):
mock_open(*args, **kwargs)
yield mock_file
with patch('__builtin__.open', stub_open):
yield mock_open, mock_file