Merge ~barryprice/charm-nrpe/+git/nrpe-charm:master into ~nrpe-charmers/charm-nrpe:master

Proposed by Barry Price
Status: Superseded
Proposed branch: ~barryprice/charm-nrpe/+git/nrpe-charm:master
Merge into: ~nrpe-charmers/charm-nrpe:master
Diff against target: 544 lines (+227/-58)
11 files modified
bin/charm_helpers_sync.py (+27/-22)
hooks/charmhelpers/core/hookenv.py (+69/-2)
hooks/charmhelpers/core/host.py (+74/-1)
hooks/charmhelpers/core/host_factory/ubuntu.py (+1/-0)
hooks/charmhelpers/core/services/base.py (+6/-15)
hooks/charmhelpers/core/strutils.py (+11/-5)
hooks/charmhelpers/core/templating.py (+18/-9)
hooks/charmhelpers/core/unitdata.py (+3/-1)
hooks/charmhelpers/fetch/snap.py (+16/-0)
hooks/charmhelpers/fetch/ubuntu.py (+1/-1)
metadata.yaml (+1/-2)
Reviewer Review Type Date Requested Status
Junien F Approve
Review via email: mp+337038@code.launchpad.net

Commit message

Freshen charm_helpers_sync.py, make sync, remove yakkety and zesty support (EOL), add artful & bionic support

To post a comment you must log in.
Revision history for this message
Junien F (axino) wrote :

+1

review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision f2747e0ca46b6acd170929458192bf8f6538a19b

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/bin/charm_helpers_sync.py b/bin/charm_helpers_sync.py
index f67fdb9..e3f0e74 100644
--- a/bin/charm_helpers_sync.py
+++ b/bin/charm_helpers_sync.py
@@ -2,19 +2,17 @@
22
3# Copyright 2014-2015 Canonical Limited.3# Copyright 2014-2015 Canonical Limited.
4#4#
5# This file is part of charm-helpers.5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
6#8#
7# charm-helpers is free software: you can redistribute it and/or modify9# http://www.apache.org/licenses/LICENSE-2.0
8# it under the terms of the GNU Lesser General Public License version 3 as
9# published by the Free Software Foundation.
10#10#
11# charm-helpers is distributed in the hope that it will be useful,11# Unless required by applicable law or agreed to in writing, software
12# but WITHOUT ANY WARRANTY; without even the implied warranty of12# distributed under the License is distributed on an "AS IS" BASIS,
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# GNU Lesser General Public License for more details.14# See the License for the specific language governing permissions and
15#15# limitations under the License.
16# You should have received a copy of the GNU Lesser General Public License
17# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
1816
19# Authors:17# Authors:
20# Adam Gandelman <adamg@ubuntu.com>18# Adam Gandelman <adamg@ubuntu.com>
@@ -31,7 +29,7 @@ from fnmatch import fnmatch
3129
32import six30import six
3331
34CHARM_HELPERS_BRANCH = 'lp:charm-helpers'32CHARM_HELPERS_REPO = 'https://github.com/juju/charm-helpers'
3533
3634
37def parse_config(conf_file):35def parse_config(conf_file):
@@ -41,10 +39,16 @@ def parse_config(conf_file):
41 return yaml.load(open(conf_file).read())39 return yaml.load(open(conf_file).read())
4240
4341
44def clone_helpers(work_dir, branch):42def clone_helpers(work_dir, repo):
45 dest = os.path.join(work_dir, 'charm-helpers')43 dest = os.path.join(work_dir, 'charm-helpers')
46 logging.info('Checking out %s to %s.' % (branch, dest))44 logging.info('Cloning out %s to %s.' % (repo, dest))
47 cmd = ['bzr', 'checkout', '--lightweight', branch, dest]45 branch = None
46 if '@' in repo:
47 repo, branch = repo.split('@', 1)
48 cmd = ['git', 'clone', '--depth=1']
49 if branch is not None:
50 cmd += ['--branch', branch]
51 cmd += [repo, dest]
48 subprocess.check_call(cmd)52 subprocess.check_call(cmd)
49 return dest53 return dest
5054
@@ -193,14 +197,15 @@ def sync_helpers(include, src, dest, options=None):
193 inc, opts = extract_options(m, global_options)197 inc, opts = extract_options(m, global_options)
194 sync(src, dest, '%s.%s' % (k, inc), opts)198 sync(src, dest, '%s.%s' % (k, inc), opts)
195199
200
196if __name__ == '__main__':201if __name__ == '__main__':
197 parser = optparse.OptionParser()202 parser = optparse.OptionParser()
198 parser.add_option('-c', '--config', action='store', dest='config',203 parser.add_option('-c', '--config', action='store', dest='config',
199 default=None, help='helper config file')204 default=None, help='helper config file')
200 parser.add_option('-D', '--debug', action='store_true', dest='debug',205 parser.add_option('-D', '--debug', action='store_true', dest='debug',
201 default=False, help='debug')206 default=False, help='debug')
202 parser.add_option('-b', '--branch', action='store', dest='branch',207 parser.add_option('-r', '--repository', action='store', dest='repo',
203 help='charm-helpers bzr branch (overrides config)')208 help='charm-helpers git repository (overrides config)')
204 parser.add_option('-d', '--destination', action='store', dest='dest_dir',209 parser.add_option('-d', '--destination', action='store', dest='dest_dir',
205 help='sync destination dir (overrides config)')210 help='sync destination dir (overrides config)')
206 (opts, args) = parser.parse_args()211 (opts, args) = parser.parse_args()
@@ -219,10 +224,10 @@ if __name__ == '__main__':
219 else:224 else:
220 config = {}225 config = {}
221226
222 if 'branch' not in config:227 if 'repo' not in config:
223 config['branch'] = CHARM_HELPERS_BRANCH228 config['repo'] = CHARM_HELPERS_REPO
224 if opts.branch:229 if opts.repo:
225 config['branch'] = opts.branch230 config['repo'] = opts.repo
226 if opts.dest_dir:231 if opts.dest_dir:
227 config['destination'] = opts.dest_dir232 config['destination'] = opts.dest_dir
228233
@@ -242,7 +247,7 @@ if __name__ == '__main__':
242 sync_options = config['options']247 sync_options = config['options']
243 tmpd = tempfile.mkdtemp()248 tmpd = tempfile.mkdtemp()
244 try:249 try:
245 checkout = clone_helpers(tmpd, config['branch'])250 checkout = clone_helpers(tmpd, config['repo'])
246 sync_helpers(config['include'], checkout, config['destination'],251 sync_helpers(config['include'], checkout, config['destination'],
247 options=sync_options)252 options=sync_options)
248 except Exception as e:253 except Exception as e:
diff --git a/hooks/charmhelpers/core/hookenv.py b/hooks/charmhelpers/core/hookenv.py
index c7feeaf..7ed1cc4 100644
--- a/hooks/charmhelpers/core/hookenv.py
+++ b/hooks/charmhelpers/core/hookenv.py
@@ -22,6 +22,7 @@ from __future__ import print_function
22import copy22import copy
23from distutils.version import LooseVersion23from distutils.version import LooseVersion
24from functools import wraps24from functools import wraps
25from collections import namedtuple
25import glob26import glob
26import os27import os
27import json28import json
@@ -38,6 +39,7 @@ if not six.PY3:
38else:39else:
39 from collections import UserDict40 from collections import UserDict
4041
42
41CRITICAL = "CRITICAL"43CRITICAL = "CRITICAL"
42ERROR = "ERROR"44ERROR = "ERROR"
43WARNING = "WARNING"45WARNING = "WARNING"
@@ -343,6 +345,7 @@ class Config(dict):
343345
344 """346 """
345 with open(self.path, 'w') as f:347 with open(self.path, 'w') as f:
348 os.fchmod(f.fileno(), 0o600)
346 json.dump(self, f)349 json.dump(self, f)
347350
348 def _implicit_save(self):351 def _implicit_save(self):
@@ -654,7 +657,7 @@ def _port_op(op_name, port, protocol="TCP"):
654 _args.append('{}/{}'.format(port, protocol))657 _args.append('{}/{}'.format(port, protocol))
655 try:658 try:
656 subprocess.check_call(_args)659 subprocess.check_call(_args)
657 except:660 except subprocess.CalledProcessError:
658 # Older Juju pre 2.3 doesn't support ICMP661 # Older Juju pre 2.3 doesn't support ICMP
659 # so treat it as a no-op if it fails.662 # so treat it as a no-op if it fails.
660 if not icmp:663 if not icmp:
@@ -685,6 +688,17 @@ def close_ports(start, end, protocol="TCP"):
685 subprocess.check_call(_args)688 subprocess.check_call(_args)
686689
687690
691def opened_ports():
692 """Get the opened ports
693
694 *Note that this will only show ports opened in a previous hook*
695
696 :returns: Opened ports as a list of strings: ``['8080/tcp', '8081-8083/tcp']``
697 """
698 _args = ['opened-ports', '--format=json']
699 return json.loads(subprocess.check_output(_args).decode('UTF-8'))
700
701
688@cached702@cached
689def unit_get(attribute):703def unit_get(attribute):
690 """Get the unit ID for the remote unit"""704 """Get the unit ID for the remote unit"""
@@ -806,6 +820,10 @@ class Hooks(object):
806 return wrapper820 return wrapper
807821
808822
823class NoNetworkBinding(Exception):
824 pass
825
826
809def charm_dir():827def charm_dir():
810 """Return the root directory of the current charm"""828 """Return the root directory of the current charm"""
811 d = os.environ.get('JUJU_CHARM_DIR')829 d = os.environ.get('JUJU_CHARM_DIR')
@@ -1092,7 +1110,17 @@ def network_get_primary_address(binding):
1092 :raise: NotImplementedError if run on Juju < 2.01110 :raise: NotImplementedError if run on Juju < 2.0
1093 '''1111 '''
1094 cmd = ['network-get', '--primary-address', binding]1112 cmd = ['network-get', '--primary-address', binding]
1095 return subprocess.check_output(cmd).decode('UTF-8').strip()1113 try:
1114 response = subprocess.check_output(
1115 cmd,
1116 stderr=subprocess.STDOUT).decode('UTF-8').strip()
1117 except CalledProcessError as e:
1118 if 'no network config found for binding' in e.output.decode('UTF-8'):
1119 raise NoNetworkBinding("No network binding for {}"
1120 .format(binding))
1121 else:
1122 raise
1123 return response
10961124
10971125
1098@translate_exc(from_exc=OSError, to_exc=NotImplementedError)1126@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
@@ -1153,3 +1181,42 @@ def meter_info():
1153 """Get the meter status information, if running in the meter-status-changed1181 """Get the meter status information, if running in the meter-status-changed
1154 hook."""1182 hook."""
1155 return os.environ.get('JUJU_METER_INFO')1183 return os.environ.get('JUJU_METER_INFO')
1184
1185
1186def iter_units_for_relation_name(relation_name):
1187 """Iterate through all units in a relation
1188
1189 Generator that iterates through all the units in a relation and yields
1190 a named tuple with rid and unit field names.
1191
1192 Usage:
1193 data = [(u.rid, u.unit)
1194 for u in iter_units_for_relation_name(relation_name)]
1195
1196 :param relation_name: string relation name
1197 :yield: Named Tuple with rid and unit field names
1198 """
1199 RelatedUnit = namedtuple('RelatedUnit', 'rid, unit')
1200 for rid in relation_ids(relation_name):
1201 for unit in related_units(rid):
1202 yield RelatedUnit(rid, unit)
1203
1204
1205def ingress_address(rid=None, unit=None):
1206 """
1207 Retrieve the ingress-address from a relation when available. Otherwise,
1208 return the private-address. This function is to be used on the consuming
1209 side of the relation.
1210
1211 Usage:
1212 addresses = [ingress_address(rid=u.rid, unit=u.unit)
1213 for u in iter_units_for_relation_name(relation_name)]
1214
1215 :param rid: string relation id
1216 :param unit: string unit name
1217 :side effect: calls relation_get
1218 :return: string IP address
1219 """
1220 settings = relation_get(rid=rid, unit=unit)
1221 return (settings.get('ingress-address') or
1222 settings.get('private-address'))
diff --git a/hooks/charmhelpers/core/host.py b/hooks/charmhelpers/core/host.py
index 5656e2f..fd14d60 100644
--- a/hooks/charmhelpers/core/host.py
+++ b/hooks/charmhelpers/core/host.py
@@ -34,7 +34,7 @@ import six
3434
35from contextlib import contextmanager35from contextlib import contextmanager
36from collections import OrderedDict36from collections import OrderedDict
37from .hookenv import log, DEBUG37from .hookenv import log, DEBUG, local_unit
38from .fstab import Fstab38from .fstab import Fstab
39from charmhelpers.osplatform import get_platform39from charmhelpers.osplatform import get_platform
4040
@@ -441,6 +441,49 @@ def add_user_to_group(username, group):
441 subprocess.check_call(cmd)441 subprocess.check_call(cmd)
442442
443443
444def chage(username, lastday=None, expiredate=None, inactive=None,
445 mindays=None, maxdays=None, root=None, warndays=None):
446 """Change user password expiry information
447
448 :param str username: User to update
449 :param str lastday: Set when password was changed in YYYY-MM-DD format
450 :param str expiredate: Set when user's account will no longer be
451 accessible in YYYY-MM-DD format.
452 -1 will remove an account expiration date.
453 :param str inactive: Set the number of days of inactivity after a password
454 has expired before the account is locked.
455 -1 will remove an account's inactivity.
456 :param str mindays: Set the minimum number of days between password
457 changes to MIN_DAYS.
458 0 indicates the password can be changed anytime.
459 :param str maxdays: Set the maximum number of days during which a
460 password is valid.
461 -1 as MAX_DAYS will remove checking maxdays
462 :param str root: Apply changes in the CHROOT_DIR directory
463 :param str warndays: Set the number of days of warning before a password
464 change is required
465 :raises subprocess.CalledProcessError: if call to chage fails
466 """
467 cmd = ['chage']
468 if root:
469 cmd.extend(['--root', root])
470 if lastday:
471 cmd.extend(['--lastday', lastday])
472 if expiredate:
473 cmd.extend(['--expiredate', expiredate])
474 if inactive:
475 cmd.extend(['--inactive', inactive])
476 if mindays:
477 cmd.extend(['--mindays', mindays])
478 if maxdays:
479 cmd.extend(['--maxdays', maxdays])
480 if warndays:
481 cmd.extend(['--warndays', warndays])
482 cmd.append(username)
483 subprocess.check_call(cmd)
484
485remove_password_expiry = functools.partial(chage, expiredate='-1', inactive='-1', mindays='0', maxdays='-1')
486
444def rsync(from_path, to_path, flags='-r', options=None, timeout=None):487def rsync(from_path, to_path, flags='-r', options=None, timeout=None):
445 """Replicate the contents of a path"""488 """Replicate the contents of a path"""
446 options = options or ['--delete', '--executability']489 options = options or ['--delete', '--executability']
@@ -506,6 +549,8 @@ def write_file(path, content, owner='root', group='root', perms=0o444):
506 with open(path, 'wb') as target:549 with open(path, 'wb') as target:
507 os.fchown(target.fileno(), uid, gid)550 os.fchown(target.fileno(), uid, gid)
508 os.fchmod(target.fileno(), perms)551 os.fchmod(target.fileno(), perms)
552 if six.PY3 and isinstance(content, six.string_types):
553 content = content.encode('UTF-8')
509 target.write(content)554 target.write(content)
510 return555 return
511 # the contents were the same, but we might still need to change the556 # the contents were the same, but we might still need to change the
@@ -946,3 +991,31 @@ def updatedb(updatedb_text, new_path):
946 lines[i] = 'PRUNEPATHS="{}"'.format(' '.join(paths))991 lines[i] = 'PRUNEPATHS="{}"'.format(' '.join(paths))
947 output = "\n".join(lines)992 output = "\n".join(lines)
948 return output993 return output
994
995
996def modulo_distribution(modulo=3, wait=30):
997 """ Modulo distribution
998
999 This helper uses the unit number, a modulo value and a constant wait time
1000 to produce a calculated wait time distribution. This is useful in large
1001 scale deployments to distribute load during an expensive operation such as
1002 service restarts.
1003
1004 If you have 1000 nodes that need to restart 100 at a time 1 minute at a
1005 time:
1006
1007 time.wait(modulo_distribution(modulo=100, wait=60))
1008 restart()
1009
1010 If you need restarts to happen serially set modulo to the exact number of
1011 nodes and set a high constant wait time:
1012
1013 time.wait(modulo_distribution(modulo=10, wait=120))
1014 restart()
1015
1016 @param modulo: int The modulo number creates the group distribution
1017 @param wait: int The constant time wait value
1018 @return: int Calculated time to wait for unit operation
1019 """
1020 unit_number = int(local_unit().split('/')[1])
1021 return (unit_number % modulo) * wait
diff --git a/hooks/charmhelpers/core/host_factory/ubuntu.py b/hooks/charmhelpers/core/host_factory/ubuntu.py
index d8dc378..99451b5 100644
--- a/hooks/charmhelpers/core/host_factory/ubuntu.py
+++ b/hooks/charmhelpers/core/host_factory/ubuntu.py
@@ -20,6 +20,7 @@ UBUNTU_RELEASES = (
20 'yakkety',20 'yakkety',
21 'zesty',21 'zesty',
22 'artful',22 'artful',
23 'bionic',
23)24)
2425
2526
diff --git a/hooks/charmhelpers/core/services/base.py b/hooks/charmhelpers/core/services/base.py
index 345b60d..ca9dc99 100644
--- a/hooks/charmhelpers/core/services/base.py
+++ b/hooks/charmhelpers/core/services/base.py
@@ -313,26 +313,17 @@ class PortManagerCallback(ManagerCallback):
313 with open(port_file) as fp:313 with open(port_file) as fp:
314 old_ports = fp.read().split(',')314 old_ports = fp.read().split(',')
315 for old_port in old_ports:315 for old_port in old_ports:
316 if bool(old_port) and not self.ports_contains(old_port, new_ports):316 if bool(old_port):
317 hookenv.close_port(old_port)317 old_port = int(old_port)
318 if old_port not in new_ports:
319 hookenv.close_port(old_port)
318 with open(port_file, 'w') as fp:320 with open(port_file, 'w') as fp:
319 fp.write(','.join(str(port) for port in new_ports))321 fp.write(','.join(str(port) for port in new_ports))
320 for port in new_ports:322 for port in new_ports:
321 # A port is either a number or 'ICMP'
322 protocol = 'TCP'
323 if str(port).upper() == 'ICMP':
324 protocol = 'ICMP'
325 if event_name == 'start':323 if event_name == 'start':
326 hookenv.open_port(port, protocol)324 hookenv.open_port(port)
327 elif event_name == 'stop':325 elif event_name == 'stop':
328 hookenv.close_port(port, protocol)326 hookenv.close_port(port)
329
330 def ports_contains(self, port, ports):
331 if not bool(port):
332 return False
333 if str(port).upper() != 'ICMP':
334 port = int(port)
335 return port in ports
336327
337328
338def service_stop(service_name):329def service_stop(service_name):
diff --git a/hooks/charmhelpers/core/strutils.py b/hooks/charmhelpers/core/strutils.py
index 685dabd..e8df045 100644
--- a/hooks/charmhelpers/core/strutils.py
+++ b/hooks/charmhelpers/core/strutils.py
@@ -61,13 +61,19 @@ def bytes_from_string(value):
61 if isinstance(value, six.string_types):61 if isinstance(value, six.string_types):
62 value = six.text_type(value)62 value = six.text_type(value)
63 else:63 else:
64 msg = "Unable to interpret non-string value '%s' as boolean" % (value)64 msg = "Unable to interpret non-string value '%s' as bytes" % (value)
65 raise ValueError(msg)65 raise ValueError(msg)
66 matches = re.match("([0-9]+)([a-zA-Z]+)", value)66 matches = re.match("([0-9]+)([a-zA-Z]+)", value)
67 if not matches:67 if matches:
68 msg = "Unable to interpret string value '%s' as bytes" % (value)68 size = int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)])
69 raise ValueError(msg)69 else:
70 return int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)])70 # Assume that value passed in is bytes
71 try:
72 size = int(value)
73 except ValueError:
74 msg = "Unable to interpret string value '%s' as bytes" % (value)
75 raise ValueError(msg)
76 return size
7177
7278
73class BasicStringComparator(object):79class BasicStringComparator(object):
diff --git a/hooks/charmhelpers/core/templating.py b/hooks/charmhelpers/core/templating.py
index 7b801a3..9014015 100644
--- a/hooks/charmhelpers/core/templating.py
+++ b/hooks/charmhelpers/core/templating.py
@@ -20,7 +20,8 @@ from charmhelpers.core import hookenv
2020
2121
22def render(source, target, context, owner='root', group='root',22def render(source, target, context, owner='root', group='root',
23 perms=0o444, templates_dir=None, encoding='UTF-8', template_loader=None):23 perms=0o444, templates_dir=None, encoding='UTF-8',
24 template_loader=None, config_template=None):
24 """25 """
25 Render a template.26 Render a template.
2627
@@ -32,6 +33,9 @@ def render(source, target, context, owner='root', group='root',
32 The context should be a dict containing the values to be replaced in the33 The context should be a dict containing the values to be replaced in the
33 template.34 template.
3435
36 config_template may be provided to render from a provided template instead
37 of loading from a file.
38
35 The `owner`, `group`, and `perms` options will be passed to `write_file`.39 The `owner`, `group`, and `perms` options will be passed to `write_file`.
3640
37 If omitted, `templates_dir` defaults to the `templates` folder in the charm.41 If omitted, `templates_dir` defaults to the `templates` folder in the charm.
@@ -65,14 +69,19 @@ def render(source, target, context, owner='root', group='root',
65 if templates_dir is None:69 if templates_dir is None:
66 templates_dir = os.path.join(hookenv.charm_dir(), 'templates')70 templates_dir = os.path.join(hookenv.charm_dir(), 'templates')
67 template_env = Environment(loader=FileSystemLoader(templates_dir))71 template_env = Environment(loader=FileSystemLoader(templates_dir))
68 try:72
69 source = source73 # load from a string if provided explicitly
70 template = template_env.get_template(source)74 if config_template is not None:
71 except exceptions.TemplateNotFound as e:75 template = template_env.from_string(config_template)
72 hookenv.log('Could not load template %s from %s.' %76 else:
73 (source, templates_dir),77 try:
74 level=hookenv.ERROR)78 source = source
75 raise e79 template = template_env.get_template(source)
80 except exceptions.TemplateNotFound as e:
81 hookenv.log('Could not load template %s from %s.' %
82 (source, templates_dir),
83 level=hookenv.ERROR)
84 raise e
76 content = template.render(context)85 content = template.render(context)
77 if target is not None:86 if target is not None:
78 target_dir = os.path.dirname(target)87 target_dir = os.path.dirname(target)
diff --git a/hooks/charmhelpers/core/unitdata.py b/hooks/charmhelpers/core/unitdata.py
index 54ec969..6d7b494 100644
--- a/hooks/charmhelpers/core/unitdata.py
+++ b/hooks/charmhelpers/core/unitdata.py
@@ -175,6 +175,8 @@ class Storage(object):
175 else:175 else:
176 self.db_path = os.path.join(176 self.db_path = os.path.join(
177 os.environ.get('CHARM_DIR', ''), '.unit-state.db')177 os.environ.get('CHARM_DIR', ''), '.unit-state.db')
178 with open(self.db_path, 'a') as f:
179 os.fchmod(f.fileno(), 0o600)
178 self.conn = sqlite3.connect('%s' % self.db_path)180 self.conn = sqlite3.connect('%s' % self.db_path)
179 self.cursor = self.conn.cursor()181 self.cursor = self.conn.cursor()
180 self.revision = None182 self.revision = None
@@ -358,7 +360,7 @@ class Storage(object):
358 try:360 try:
359 yield self.revision361 yield self.revision
360 self.revision = None362 self.revision = None
361 except:363 except Exception:
362 self.flush(False)364 self.flush(False)
363 self.revision = None365 self.revision = None
364 raise366 raise
diff --git a/hooks/charmhelpers/fetch/snap.py b/hooks/charmhelpers/fetch/snap.py
index 112a54c..395836c 100644
--- a/hooks/charmhelpers/fetch/snap.py
+++ b/hooks/charmhelpers/fetch/snap.py
@@ -41,6 +41,10 @@ class CouldNotAcquireLockException(Exception):
41 pass41 pass
4242
4343
44class InvalidSnapChannel(Exception):
45 pass
46
47
44def _snap_exec(commands):48def _snap_exec(commands):
45 """49 """
46 Execute snap commands.50 Execute snap commands.
@@ -132,3 +136,15 @@ def snap_refresh(packages, *flags):
132136
133 log(message, level='INFO')137 log(message, level='INFO')
134 return _snap_exec(['refresh'] + flags + packages)138 return _snap_exec(['refresh'] + flags + packages)
139
140
141def valid_snap_channel(channel):
142 """ Validate snap channel exists
143
144 :raises InvalidSnapChannel: When channel does not exist
145 :return: Boolean
146 """
147 if channel.lower() in SNAP_CHANNELS:
148 return True
149 else:
150 raise InvalidSnapChannel("Invalid Snap Channel: {}".format(channel))
diff --git a/hooks/charmhelpers/fetch/ubuntu.py b/hooks/charmhelpers/fetch/ubuntu.py
index 40e1cb5..910e96a 100644
--- a/hooks/charmhelpers/fetch/ubuntu.py
+++ b/hooks/charmhelpers/fetch/ubuntu.py
@@ -572,7 +572,7 @@ def get_upstream_version(package):
572 cache = apt_cache()572 cache = apt_cache()
573 try:573 try:
574 pkg = cache[package]574 pkg = cache[package]
575 except:575 except Exception:
576 # the package is unknown to the current apt cache.576 # the package is unknown to the current apt cache.
577 return None577 return None
578578
diff --git a/metadata.yaml b/metadata.yaml
index d0f196f..ace705f 100644
--- a/metadata.yaml
+++ b/metadata.yaml
@@ -29,6 +29,5 @@ requires:
29series:29series:
30 - xenial30 - xenial
31 - trusty31 - trusty
32 - zesty
33 - yakkety
34 - artful32 - artful
33 - bionic

Subscribers

People subscribed via source and target branches