[trivial] charm-helpers sync

This commit is contained in:
Edward Hope-Morley 2015-05-27 10:51:19 +01:00
parent 1cf8583944
commit f605024585
2 changed files with 92 additions and 31 deletions

View File

@ -28,7 +28,6 @@ import yaml
import subprocess import subprocess
import sys import sys
import errno import errno
import tempfile
from subprocess import CalledProcessError from subprocess import CalledProcessError
import six import six
@ -363,33 +362,29 @@ def relation_set(relation_id=None, relation_settings=None, **kwargs):
"""Set relation information for the current unit""" """Set relation information for the current unit"""
relation_settings = relation_settings if relation_settings else {} relation_settings = relation_settings if relation_settings else {}
relation_cmd_line = ['relation-set'] relation_cmd_line = ['relation-set']
accepts_file = "--file" in subprocess.check_output(
relation_cmd_line + ["--help"])
if relation_id is not None: if relation_id is not None:
relation_cmd_line.extend(('-r', relation_id)) relation_cmd_line.extend(('-r', relation_id))
settings = relation_settings.copy() for k, v in (list(relation_settings.items()) + list(kwargs.items())):
settings.update(kwargs) if v is None:
if accepts_file: relation_cmd_line.append('{}='.format(k))
# --file was introduced in Juju 1.23.2. Use it by default if else:
# available, since otherwise we'll break if the relation data is relation_cmd_line.append('{}={}'.format(k, v))
# too big. Ideally we should tell relation-set to read the data from subprocess.check_call(relation_cmd_line)
# stdin, but that feature is broken in 1.23.2: Bug #1454678.
with tempfile.NamedTemporaryFile(delete=False) as settings_file:
settings_file.write(yaml.safe_dump(settings).encode("utf-8"))
subprocess.check_call(
relation_cmd_line + ["--file", settings_file.name])
os.remove(settings_file.name)
else:
for key, value in settings.items():
if value is None:
relation_cmd_line.append('{}='.format(key))
else:
relation_cmd_line.append('{}={}'.format(key, value))
subprocess.check_call(relation_cmd_line)
# Flush cache of any relation-gets for local unit # Flush cache of any relation-gets for local unit
flush(local_unit()) flush(local_unit())
def relation_clear(r_id=None):
''' Clears any relation data already set on relation r_id '''
settings = relation_get(rid=r_id,
unit=local_unit())
for setting in settings:
if setting not in ['public-address', 'private-address']:
settings[setting] = None
relation_set(relation_id=r_id,
**settings)
@cached @cached
def relation_ids(reltype=None): def relation_ids(reltype=None):
"""A list of relation_ids""" """A list of relation_ids"""
@ -681,3 +676,48 @@ def status_get():
return 'unknown' return 'unknown'
else: else:
raise raise
def translate_exc(from_exc, to_exc):
def inner_translate_exc1(f):
def inner_translate_exc2(*args, **kwargs):
try:
return f(*args, **kwargs)
except from_exc:
raise to_exc
return inner_translate_exc2
return inner_translate_exc1
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def is_leader():
"""Does the current unit hold the juju leadership
Uses juju to determine whether the current unit is the leader of its peers
"""
cmd = ['is-leader', '--format=json']
return json.loads(subprocess.check_output(cmd).decode('UTF-8'))
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def leader_get(attribute=None):
"""Juju leader get value(s)"""
cmd = ['leader-get', '--format=json'] + [attribute or '-']
return json.loads(subprocess.check_output(cmd).decode('UTF-8'))
@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
def leader_set(settings=None, **kwargs):
"""Juju leader set value(s)"""
log("Juju leader-set '%s'" % (settings), level=DEBUG)
cmd = ['leader-set']
settings = settings or {}
settings.update(kwargs)
for k, v in settings.iteritems():
if v is None:
cmd.append('{}='.format(k))
else:
cmd.append('{}={}'.format(k, v))
subprocess.check_call(cmd)

View File

@ -15,8 +15,8 @@
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
import os import os
import re
import json import json
from inspect import getargspec
from collections import Iterable, OrderedDict from collections import Iterable, OrderedDict
from charmhelpers.core import host from charmhelpers.core import host
@ -132,8 +132,8 @@ class ServiceManager(object):
if hook_name == 'stop': if hook_name == 'stop':
self.stop_services() self.stop_services()
else: else:
self.provide_data()
self.reconfigure_services() self.reconfigure_services()
self.provide_data()
cfg = hookenv.config() cfg = hookenv.config()
if cfg.implicit_save: if cfg.implicit_save:
cfg.save() cfg.save()
@ -145,15 +145,36 @@ class ServiceManager(object):
A provider must have a `name` attribute, which indicates which relation A provider must have a `name` attribute, which indicates which relation
to set data on, and a `provide_data()` method, which returns a dict of to set data on, and a `provide_data()` method, which returns a dict of
data to set. data to set.
The `provide_data()` method can optionally accept two parameters:
* ``remote_service`` The name of the remote service that the data will
be provided to. The `provide_data()` method will be called once
for each connected service (not unit). This allows the method to
tailor its data to the given service.
* ``service_ready`` Whether or not the service definition had all of
its requirements met, and thus the ``data_ready`` callbacks run.
Note that the ``provided_data`` methods are now called **after** the
``data_ready`` callbacks are run. This gives the ``data_ready`` callbacks
a chance to generate any data necessary for the providing to the remote
services.
""" """
hook_name = hookenv.hook_name() for service_name, service in self.services.items():
for service in self.services.values(): service_ready = self.is_ready(service_name)
for provider in service.get('provided_data', []): for provider in service.get('provided_data', []):
if re.match(r'{}-relation-(joined|changed)'.format(provider.name), hook_name): for relid in hookenv.relation_ids(provider.name):
data = provider.provide_data() units = hookenv.related_units(relid)
_ready = provider._is_ready(data) if hasattr(provider, '_is_ready') else data if not units:
if _ready: continue
hookenv.relation_set(None, data) remote_service = units[0].split('/')[0]
argspec = getargspec(provider.provide_data)
if len(argspec.args) > 1:
data = provider.provide_data(remote_service, service_ready)
else:
data = provider.provide_data()
if data:
hookenv.relation_set(relid, data)
def reconfigure_services(self, *service_names): def reconfigure_services(self, *service_names):
""" """