Merge lp:~teknico/charm-helpers/lint-fixes into lp:charm-helpers

Proposed by Nicola Larosa
Status: Merged
Merge reported by: James Page
Merged at revision: not available
Proposed branch: lp:~teknico/charm-helpers/lint-fixes
Merge into: lp:charm-helpers
Diff against target: 1224 lines (+172/-156)
22 files modified
README.test (+3/-0)
bin/contrib/saltstack/salt-call (+3/-2)
charmhelpers/cli/commands.py (+2/-2)
charmhelpers/cli/host.py (+2/-1)
charmhelpers/core/hookenv.py (+33/-32)
charmhelpers/core/host.py (+19/-19)
charmhelpers/fetch/__init__.py (+18/-16)
charmhelpers/fetch/archiveurl.py (+5/-4)
charmhelpers/fetch/bzrurl.py (+9/-7)
charmhelpers/payload/__init__.py (+3/-1)
charmhelpers/payload/archive.py (+3/-2)
charmhelpers/payload/execd.py (+2/-1)
tests/contrib/hahelpers/test_ceph_utils.py (+3/-3)
tests/core/test_hookenv.py (+4/-4)
tests/core/test_host.py (+7/-6)
tests/fetch/test_archiveurl.py (+4/-2)
tests/fetch/test_bzrurl.py (+3/-5)
tests/fetch/test_fetch.py (+12/-14)
tests/payload/test_archive.py (+6/-4)
tests/payload/test_execd.py (+6/-4)
tests/tools/test_charm_helper_sync.py (+19/-19)
tools/charm_helpers_sync/charm_helpers_sync.py (+6/-8)
To merge this branch: bzr merge lp:~teknico/charm-helpers/lint-fixes
Reviewer Review Type Date Requested Status
James Page Abstain
Matthew Wedgwood Pending
Review via email: mp+181859@code.launchpad.net

Description of the change

Lint and styling fixes. Sorry for the diff size.

To post a comment you must log in.
lp:~teknico/charm-helpers/lint-fixes updated
74. By Nicola Larosa

Merge from trunk, one conflict resolved.

Revision history for this message
James Page (james-page) wrote :

Hi Nicola

Looking at the current master branch, this is all now fixed up.

Thanks for the MP

review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README.test'
--- README.test 2013-05-22 21:11:01 +0000
+++ README.test 2013-09-02 08:01:21 +0000
@@ -1,7 +1,10 @@
1Required Packages for Running Tests1Required Packages for Running Tests
2-----------------------------------2-----------------------------------
3
3python-shelltoolbox4python-shelltoolbox
4python-tempita5python-tempita
5python-nose6python-nose
6python-mock7python-mock
7python-testtools8python-testtools
9
10Also, install flake8 from PyPI.
811
=== modified file 'bin/contrib/saltstack/salt-call'
--- bin/contrib/saltstack/salt-call 2013-06-19 09:54:19 +0000
+++ bin/contrib/saltstack/salt-call 2013-09-02 08:01:21 +0000
@@ -1,8 +1,9 @@
1#!/usr/bin/env python1#!/usr/bin/env python
2'''2
3"""
3Directly call a salt command in the modules, does not require a running salt4Directly call a salt command in the modules, does not require a running salt
4minion to run.5minion to run.
5'''6"""
67
7from salt.scripts import salt_call8from salt.scripts import salt_call
89
910
=== modified file 'charmhelpers/cli/commands.py'
--- charmhelpers/cli/commands.py 2013-06-19 23:00:23 +0000
+++ charmhelpers/cli/commands.py 2013-09-02 08:01:21 +0000
@@ -1,2 +1,2 @@
1from . import CommandLine1# from . import CommandLine
2import host2# import host
33
=== modified file 'charmhelpers/cli/host.py'
--- charmhelpers/cli/host.py 2013-06-20 15:03:24 +0000
+++ charmhelpers/cli/host.py 2013-09-02 08:01:21 +0000
@@ -4,9 +4,10 @@
44
5@cmdline.subcommand()5@cmdline.subcommand()
6def mounts():6def mounts():
7 "List mounts"7 """List mounts."""
8 return host.mounts()8 return host.mounts()
99
10
10@cmdline.subcommand_builder('service', description="Control system services")11@cmdline.subcommand_builder('service', description="Control system services")
11def service(subparser):12def service(subparser):
12 subparser.add_argument("action", help="The action to perform (start, stop, etc...)")13 subparser.add_argument("action", help="The action to perform (start, stop, etc...)")
1314
=== modified file 'charmhelpers/core/hookenv.py'
--- charmhelpers/core/hookenv.py 2013-07-18 16:13:49 +0000
+++ charmhelpers/core/hookenv.py 2013-09-02 08:01:21 +0000
@@ -1,14 +1,15 @@
1"Interactions with the Juju environment"
2# Copyright 2013 Canonical Ltd.1# Copyright 2013 Canonical Ltd.
3#2#
4# Authors:3# Authors:
5# Charm Helpers Developers <juju@lists.ubuntu.com>4# Charm Helpers Developers <juju@lists.ubuntu.com>
65
6"""Interactions with the Juju environment."""
7
8import json
7import os9import os
8import json
9import yaml
10import subprocess10import subprocess
11import UserDict11import UserDict
12import yaml
1213
13CRITICAL = "CRITICAL"14CRITICAL = "CRITICAL"
14ERROR = "ERROR"15ERROR = "ERROR"
@@ -21,7 +22,8 @@
2122
2223
23def cached(func):24def cached(func):
24 ''' Cache return values for multiple executions of func + args25 """
26 Decorator that caches return values for multiple executions of func + args.
2527
26 For example:28 For example:
2729
@@ -32,7 +34,7 @@
32 unit_get('test')34 unit_get('test')
3335
34 will cache the result of unit_get + 'test' for future calls.36 will cache the result of unit_get + 'test' for future calls.
35 '''37 """
36 def wrapper(*args, **kwargs):38 def wrapper(*args, **kwargs):
37 global cache39 global cache
38 key = str((func, args, kwargs))40 key = str((func, args, kwargs))
@@ -46,8 +48,8 @@
4648
4749
48def flush(key):50def flush(key):
49 ''' Flushes any entries from function cache where the51 """Flush any entries from function cache where the
50 key is found in the function+args '''52 key is found in the function+args."""
51 flush_list = []53 flush_list = []
52 for item in cache:54 for item in cache:
53 if key in item:55 if key in item:
@@ -57,7 +59,7 @@
5759
5860
59def log(message, level=None):61def log(message, level=None):
60 "Write a message to the juju log"62 """Write a message to the juju log."""
61 command = ['juju-log']63 command = ['juju-log']
62 if level:64 if level:
63 command += ['-l', level]65 command += ['-l', level]
@@ -66,7 +68,7 @@
6668
6769
68class Serializable(UserDict.IterableUserDict):70class Serializable(UserDict.IterableUserDict):
69 "Wrapper, an object that can be serialized to yaml or json"71 """Wrapper, an object that can be serialized to yaml or json."""
7072
71 def __init__(self, obj):73 def __init__(self, obj):
72 # wrap the object74 # wrap the object
@@ -96,16 +98,16 @@
96 self.data = state98 self.data = state
9799
98 def json(self):100 def json(self):
99 "Serialize the object to json"101 """Serialize the object to json."""
100 return json.dumps(self.data)102 return json.dumps(self.data)
101103
102 def yaml(self):104 def yaml(self):
103 "Serialize the object to yaml"105 """Serialize the object to yaml."""
104 return yaml.dump(self.data)106 return yaml.dump(self.data)
105107
106108
107def execution_environment():109def execution_environment():
108 """A convenient bundling of the current execution context"""110 """A convenient bundling of the current execution context."""
109 context = {}111 context = {}
110 context['conf'] = config()112 context['conf'] = config()
111 if relation_id():113 if relation_id():
@@ -119,38 +121,38 @@
119121
120122
121def in_relation_hook():123def in_relation_hook():
122 "Determine whether we're running in a relation hook"124 """Determine whether we're running in a relation hook."""
123 return 'JUJU_RELATION' in os.environ125 return 'JUJU_RELATION' in os.environ
124126
125127
126def relation_type():128def relation_type():
127 "The scope for the current relation hook"129 """The scope for the current relation hook."""
128 return os.environ.get('JUJU_RELATION', None)130 return os.environ.get('JUJU_RELATION', None)
129131
130132
131def relation_id():133def relation_id():
132 "The relation ID for the current relation hook"134 """The relation ID for the current relation hook."""
133 return os.environ.get('JUJU_RELATION_ID', None)135 return os.environ.get('JUJU_RELATION_ID', None)
134136
135137
136def local_unit():138def local_unit():
137 "Local unit ID"139 """Local unit ID."""
138 return os.environ['JUJU_UNIT_NAME']140 return os.environ['JUJU_UNIT_NAME']
139141
140142
141def remote_unit():143def remote_unit():
142 "The remote unit for the current relation hook"144 """The remote unit for the current relation hook."""
143 return os.environ['JUJU_REMOTE_UNIT']145 return os.environ['JUJU_REMOTE_UNIT']
144146
145147
146def service_name():148def service_name():
147 "The name service group this unit belongs to"149 """The name service group this unit belongs to."""
148 return local_unit().split('/')[0]150 return local_unit().split('/')[0]
149151
150152
151@cached153@cached
152def config(scope=None):154def config(scope=None):
153 "Juju charm configuration"155 """Juju charm configuration."""
154 config_cmd_line = ['config-get']156 config_cmd_line = ['config-get']
155 if scope is not None:157 if scope is not None:
156 config_cmd_line.append(scope)158 config_cmd_line.append(scope)
@@ -192,7 +194,7 @@
192194
193@cached195@cached
194def relation_ids(reltype=None):196def relation_ids(reltype=None):
195 "A list of relation_ids"197 """A list of relation_ids."""
196 reltype = reltype or relation_type()198 reltype = reltype or relation_type()
197 relid_cmd_line = ['relation-ids', '--format=json']199 relid_cmd_line = ['relation-ids', '--format=json']
198 if reltype is not None:200 if reltype is not None:
@@ -203,7 +205,7 @@
203205
204@cached206@cached
205def related_units(relid=None):207def related_units(relid=None):
206 "A list of related units"208 """A list of related units."""
207 relid = relid or relation_id()209 relid = relid or relation_id()
208 units_cmd_line = ['relation-list', '--format=json']210 units_cmd_line = ['relation-list', '--format=json']
209 if relid is not None:211 if relid is not None:
@@ -213,7 +215,7 @@
213215
214@cached216@cached
215def relation_for_unit(unit=None, rid=None):217def relation_for_unit(unit=None, rid=None):
216 "Get the json represenation of a unit's relation"218 """Get the json represenation of a unit's relation."""
217 unit = unit or remote_unit()219 unit = unit or remote_unit()
218 relation = relation_get(unit=unit, rid=rid)220 relation = relation_get(unit=unit, rid=rid)
219 for key in relation:221 for key in relation:
@@ -225,7 +227,7 @@
225227
226@cached228@cached
227def relations_for_id(relid=None):229def relations_for_id(relid=None):
228 "Get relations of a specific relation ID"230 """Get relations of a specific relation ID."""
229 relation_data = []231 relation_data = []
230 relid = relid or relation_ids()232 relid = relid or relation_ids()
231 for unit in related_units(relid):233 for unit in related_units(relid):
@@ -237,7 +239,7 @@
237239
238@cached240@cached
239def relations_of_type(reltype=None):241def relations_of_type(reltype=None):
240 "Get relations of a specific type"242 """Get relations of a specific type."""
241 relation_data = []243 relation_data = []
242 reltype = reltype or relation_type()244 reltype = reltype or relation_type()
243 for relid in relation_ids(reltype):245 for relid in relation_ids(reltype):
@@ -249,7 +251,7 @@
249251
250@cached252@cached
251def relation_types():253def relation_types():
252 "Get a list of relation types supported by this charm"254 """Get a list of relation types supported by this charm."""
253 charmdir = os.environ.get('CHARM_DIR', '')255 charmdir = os.environ.get('CHARM_DIR', '')
254 mdf = open(os.path.join(charmdir, 'metadata.yaml'))256 mdf = open(os.path.join(charmdir, 'metadata.yaml'))
255 md = yaml.safe_load(mdf)257 md = yaml.safe_load(mdf)
@@ -278,14 +280,14 @@
278280
279281
280def open_port(port, protocol="TCP"):282def open_port(port, protocol="TCP"):
281 "Open a service network port"283 """Open a service network port."""
282 _args = ['open-port']284 _args = ['open-port']
283 _args.append('{}/{}'.format(port, protocol))285 _args.append('{}/{}'.format(port, protocol))
284 subprocess.check_call(_args)286 subprocess.check_call(_args)
285287
286288
287def close_port(port, protocol="TCP"):289def close_port(port, protocol="TCP"):
288 "Close a service network port"290 """Close a service network port."""
289 _args = ['close-port']291 _args = ['close-port']
290 _args.append('{}/{}'.format(port, protocol))292 _args.append('{}/{}'.format(port, protocol))
291 subprocess.check_call(_args)293 subprocess.check_call(_args)
@@ -327,11 +329,10 @@
327 def wrapper(decorated):329 def wrapper(decorated):
328 for hook_name in hook_names:330 for hook_name in hook_names:
329 self.register(hook_name, decorated)331 self.register(hook_name, decorated)
330 else:332 self.register(decorated.__name__, decorated)
331 self.register(decorated.__name__, decorated)333 if '_' in decorated.__name__:
332 if '_' in decorated.__name__:334 self.register(
333 self.register(335 decorated.__name__.replace('_', '-'), decorated)
334 decorated.__name__.replace('_', '-'), decorated)
335 return decorated336 return decorated
336 return wrapper337 return wrapper
337338
338339
=== modified file 'charmhelpers/core/host.py'
--- charmhelpers/core/host.py 2013-08-23 16:42:43 +0000
+++ charmhelpers/core/host.py 2013-09-02 08:01:21 +0000
@@ -1,19 +1,19 @@
1"""Tools for working with the host system"""
2# Copyright 2012 Canonical Ltd.1# Copyright 2012 Canonical Ltd.
3#2#
4# Authors:3# Authors:
5# Nick Moffitt <nick.moffitt@canonical.com>4# Nick Moffitt <nick.moffitt@canonical.com>
6# Matthew Wedgwood <matthew.wedgwood@canonical.com>5# Matthew Wedgwood <matthew.wedgwood@canonical.com>
76
7"""Tools for working with the host system."""
8
9from collections import OrderedDict
10import grp
11import hashlib
8import os12import os
9import pwd13import pwd
10import grp
11import random14import random
12import string15import string
13import subprocess16import subprocess
14import hashlib
15
16from collections import OrderedDict
1717
18from hookenv import log18from hookenv import log
1919
@@ -55,7 +55,7 @@
5555
5656
57def adduser(username, password=None, shell='/bin/bash', system_user=False):57def adduser(username, password=None, shell='/bin/bash', system_user=False):
58 """Add a user"""58 """Add a user."""
59 try:59 try:
60 user_info = pwd.getpwnam(username)60 user_info = pwd.getpwnam(username)
61 log('user {0} already exists!'.format(username))61 log('user {0} already exists!'.format(username))
@@ -77,7 +77,7 @@
7777
7878
79def add_user_to_group(username, group):79def add_user_to_group(username, group):
80 """Add a user to a group"""80 """Add a user to a group."""
81 cmd = [81 cmd = [
82 'gpasswd', '-a',82 'gpasswd', '-a',
83 username,83 username,
@@ -88,7 +88,7 @@
8888
8989
90def rsync(from_path, to_path, flags='-r', options=None):90def rsync(from_path, to_path, flags='-r', options=None):
91 """Replicate the contents of a path"""91 """Replicate the contents of a path."""
92 options = options or ['--delete', '--executability']92 options = options or ['--delete', '--executability']
93 cmd = ['/usr/bin/rsync', flags]93 cmd = ['/usr/bin/rsync', flags]
94 cmd.extend(options)94 cmd.extend(options)
@@ -99,7 +99,7 @@
9999
100100
101def symlink(source, destination):101def symlink(source, destination):
102 """Create a symbolic link"""102 """Create a symbolic link."""
103 log("Symlinking {} as {}".format(source, destination))103 log("Symlinking {} as {}".format(source, destination))
104 cmd = [104 cmd = [
105 'ln',105 'ln',
@@ -111,7 +111,7 @@
111111
112112
113def mkdir(path, owner='root', group='root', perms=0555, force=False):113def mkdir(path, owner='root', group='root', perms=0555, force=False):
114 """Create a directory"""114 """Create a directory."""
115 log("Making dir {} {}:{} {:o}".format(path, owner, group,115 log("Making dir {} {}:{} {:o}".format(path, owner, group,
116 perms))116 perms))
117 uid = pwd.getpwnam(owner).pw_uid117 uid = pwd.getpwnam(owner).pw_uid
@@ -127,7 +127,7 @@
127127
128128
129def write_file(path, content, owner='root', group='root', perms=0444):129def write_file(path, content, owner='root', group='root', perms=0444):
130 """Create or overwrite a file with the contents of a string"""130 """Create or overwrite a file with the contents of a string."""
131 log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))131 log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))
132 uid = pwd.getpwnam(owner).pw_uid132 uid = pwd.getpwnam(owner).pw_uid
133 gid = grp.getgrnam(group).gr_gid133 gid = grp.getgrnam(group).gr_gid
@@ -138,7 +138,7 @@
138138
139139
140def mount(device, mountpoint, options=None, persist=False):140def mount(device, mountpoint, options=None, persist=False):
141 '''Mount a filesystem'''141 """Mount a filesystem."""
142 cmd_args = ['mount']142 cmd_args = ['mount']
143 if options is not None:143 if options is not None:
144 cmd_args.extend(['-o', options])144 cmd_args.extend(['-o', options])
@@ -155,7 +155,7 @@
155155
156156
157def umount(mountpoint, persist=False):157def umount(mountpoint, persist=False):
158 '''Unmount a filesystem'''158 """Unmount a filesystem."""
159 cmd_args = ['umount', mountpoint]159 cmd_args = ['umount', mountpoint]
160 try:160 try:
161 subprocess.check_output(cmd_args)161 subprocess.check_output(cmd_args)
@@ -169,7 +169,7 @@
169169
170170
171def mounts():171def mounts():
172 '''List of all mounted volumes as [[mountpoint,device],[...]]'''172 """List of all mounted volumes as [[mountpoint,device],[...]]."""
173 with open('/proc/mounts') as f:173 with open('/proc/mounts') as f:
174 # [['/mount/point','/dev/path'],[...]]174 # [['/mount/point','/dev/path'],[...]]
175 system_mounts = [m[1::-1] for m in [l.strip().split()175 system_mounts = [m[1::-1] for m in [l.strip().split()
@@ -178,7 +178,7 @@
178178
179179
180def file_hash(path):180def file_hash(path):
181 ''' Generate a md5 hash of the contents of 'path' or None if not found '''181 """Generate a md5 hash of the contents of 'path' or None if not found."""
182 if os.path.exists(path):182 if os.path.exists(path):
183 h = hashlib.md5()183 h = hashlib.md5()
184 with open(path, 'r') as source:184 with open(path, 'r') as source:
@@ -189,7 +189,7 @@
189189
190190
191def restart_on_change(restart_map):191def restart_on_change(restart_map):
192 ''' Restart services based on configuration files changing192 """Restart services based on configuration files changing.
193193
194 This function is used a decorator, for example194 This function is used a decorator, for example
195195
@@ -202,7 +202,7 @@
202 In this example, the cinder-api and cinder-volume services202 In this example, the cinder-api and cinder-volume services
203 would be restarted if /etc/ceph/ceph.conf is changed by the203 would be restarted if /etc/ceph/ceph.conf is changed by the
204 ceph_client_changed function.204 ceph_client_changed function.
205 '''205 """
206 def wrap(f):206 def wrap(f):
207 def wrapped_f(*args):207 def wrapped_f(*args):
208 checksums = {}208 checksums = {}
@@ -220,7 +220,7 @@
220220
221221
222def lsb_release():222def lsb_release():
223 '''Return /etc/lsb-release in a dict'''223 """Return /etc/lsb-release in a dict."""
224 d = {}224 d = {}
225 with open('/etc/lsb-release', 'r') as lsb:225 with open('/etc/lsb-release', 'r') as lsb:
226 for l in lsb:226 for l in lsb:
@@ -230,7 +230,7 @@
230230
231231
232def pwgen(length=None):232def pwgen(length=None):
233 '''Generate a random pasword.'''233 """Generate a random password."""
234 if length is None:234 if length is None:
235 length = random.choice(range(35, 45))235 length = random.choice(range(35, 45))
236 alphanumeric_chars = [236 alphanumeric_chars = [
237237
=== modified file 'charmhelpers/fetch/__init__.py'
--- charmhelpers/fetch/__init__.py 2013-08-21 11:45:37 +0000
+++ charmhelpers/fetch/__init__.py 2013-09-02 08:01:21 +0000
@@ -1,18 +1,19 @@
1import importlib1import importlib
2from yaml import safe_load2import subprocess
3from charmhelpers.core.host import (
4 lsb_release
5)
6from urlparse import (3from urlparse import (
7 urlparse,4 urlparse,
8 urlunparse,5 urlunparse,
9)6)
10import subprocess7
8import apt_pkg
9from yaml import safe_load
10
11from charmhelpers.core.hookenv import (11from charmhelpers.core.hookenv import (
12 config,12 config,
13 log,13 log,
14)14)
15import apt_pkg15from charmhelpers.core.host import lsb_release
16
1617
17CLOUD_ARCHIVE = """# Ubuntu Cloud Archive18CLOUD_ARCHIVE = """# Ubuntu Cloud Archive
18deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main19deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
@@ -23,7 +24,7 @@
2324
2425
25def filter_installed_packages(packages):26def filter_installed_packages(packages):
26 """Returns a list of packages that require installation"""27 """Return a list of packages that require installation."""
27 apt_pkg.init()28 apt_pkg.init()
28 cache = apt_pkg.Cache()29 cache = apt_pkg.Cache()
29 _pkgs = []30 _pkgs = []
@@ -39,7 +40,7 @@
3940
4041
41def apt_install(packages, options=None, fatal=False):42def apt_install(packages, options=None, fatal=False):
42 """Install one or more packages"""43 """Install one or more packages."""
43 options = options or []44 options = options or []
44 cmd = ['apt-get', '-y']45 cmd = ['apt-get', '-y']
45 cmd.extend(options)46 cmd.extend(options)
@@ -57,7 +58,7 @@
5758
5859
59def apt_update(fatal=False):60def apt_update(fatal=False):
60 """Update local apt cache"""61 """Update local apt cache."""
61 cmd = ['apt-get', 'update']62 cmd = ['apt-get', 'update']
62 if fatal:63 if fatal:
63 subprocess.check_call(cmd)64 subprocess.check_call(cmd)
@@ -66,7 +67,7 @@
6667
6768
68def apt_purge(packages, fatal=False):69def apt_purge(packages, fatal=False):
69 """Purge one or more packages"""70 """Purge one or more packages."""
70 cmd = ['apt-get', '-y', 'purge']71 cmd = ['apt-get', '-y', 'purge']
71 if isinstance(packages, basestring):72 if isinstance(packages, basestring):
72 cmd.append(packages)73 cmd.append(packages)
@@ -105,7 +106,7 @@
105 sources_var='install_sources',106 sources_var='install_sources',
106 keys_var='install_keys'):107 keys_var='install_keys'):
107 """108 """
108 Configure multiple sources from charm configuration109 Configure multiple sources from charm configuration.
109110
110 Example config:111 Example config:
111 install_sources:112 install_sources:
@@ -144,13 +145,14 @@
144145
145def install_remote(source):146def install_remote(source):
146 """147 """
147 Install a file tree from a remote source148 Install a file tree from a remote source.
148149
149 The specified source should be a url of the form:150 The specified source should be a url of the form:
150 scheme://[host]/path[#[option=value][&...]]151 scheme://[host]/path[#[option=value][&...]]
151152
152 Schemes supported are based on this modules submodules153 Schemes supported are based on this modules submodules.
153 Options supported are submodule-specific"""154 Options supported are submodule-specific.
155 """
154 # We ONLY check for True here because can_handle may return a string156 # We ONLY check for True here because can_handle may return a string
155 # explaining why it can't handle a given source.157 # explaining why it can't handle a given source.
156 handlers = [h for h in plugins() if h.can_handle(source) is True]158 handlers = [h for h in plugins() if h.can_handle(source) is True]
@@ -172,7 +174,7 @@
172174
173175
174class BaseFetchHandler(object):176class BaseFetchHandler(object):
175 """Base class for FetchHandler implementations in fetch plugins"""177 """Base class for FetchHandler implementations in fetch plugins."""
176 def can_handle(self, source):178 def can_handle(self, source):
177 """Returns True if the source can be handled. Otherwise returns179 """Returns True if the source can be handled. Otherwise returns
178 a string explaining why it cannot"""180 a string explaining why it cannot"""
@@ -187,7 +189,7 @@
187 return urlparse(url)189 return urlparse(url)
188190
189 def base_url(self, url):191 def base_url(self, url):
190 """Return url without querystring or fragment"""192 """Return url without querystring or fragment."""
191 parts = list(self.parse_url(url))193 parts = list(self.parse_url(url))
192 parts[4:] = ['' for i in parts[4:]]194 parts[4:] = ['' for i in parts[4:]]
193 return urlunparse(parts)195 return urlunparse(parts)
194196
=== modified file 'charmhelpers/fetch/archiveurl.py'
--- charmhelpers/fetch/archiveurl.py 2013-07-10 18:57:39 +0000
+++ charmhelpers/fetch/archiveurl.py 2013-09-02 08:01:21 +0000
@@ -1,5 +1,7 @@
1import os1import os
2import urllib22import urllib2
3
4from charmhelpers.core.host import mkdir
3from charmhelpers.fetch import (5from charmhelpers.fetch import (
4 BaseFetchHandler,6 BaseFetchHandler,
5 UnhandledSource7 UnhandledSource
@@ -8,11 +10,11 @@
8 get_archive_handler,10 get_archive_handler,
9 extract,11 extract,
10)12)
11from charmhelpers.core.host import mkdir
1213
1314
14class ArchiveUrlFetchHandler(BaseFetchHandler):15class ArchiveUrlFetchHandler(BaseFetchHandler):
15 """Handler for archives via generic URLs"""16 """Handler for archives via generic URLs."""
17
16 def can_handle(self, source):18 def can_handle(self, source):
17 url_parts = self.parse_url(source)19 url_parts = self.parse_url(source)
18 if url_parts.scheme not in ('http', 'https', 'ftp', 'file'):20 if url_parts.scheme not in ('http', 'https', 'ftp', 'file'):
@@ -22,8 +24,7 @@
22 return False24 return False
2325
24 def download(self, source, dest):26 def download(self, source, dest):
25 # propogate all exceptions27 # Propagate all exceptions: URLError, OSError, etc.
26 # URLError, OSError, etc
27 response = urllib2.urlopen(source)28 response = urllib2.urlopen(source)
28 try:29 try:
29 with open(dest, 'w') as dest_file:30 with open(dest, 'w') as dest_file:
3031
=== modified file 'charmhelpers/fetch/bzrurl.py'
--- charmhelpers/fetch/bzrurl.py 2013-08-22 10:19:51 +0000
+++ charmhelpers/fetch/bzrurl.py 2013-09-02 08:01:21 +0000
@@ -1,10 +1,10 @@
1import os1import os
2
3from charmhelpers.core.host import mkdir
2from charmhelpers.fetch import (4from charmhelpers.fetch import (
3 BaseFetchHandler,5 BaseFetchHandler,
4 UnhandledSource6 UnhandledSource
5)7)
6from charmhelpers.core.host import mkdir
7
8try:8try:
9 from bzrlib.branch import Branch9 from bzrlib.branch import Branch
10except ImportError:10except ImportError:
@@ -12,8 +12,10 @@
12 apt_install("python-bzrlib")12 apt_install("python-bzrlib")
13 from bzrlib.branch import Branch13 from bzrlib.branch import Branch
1414
15
15class BzrUrlFetchHandler(BaseFetchHandler):16class BzrUrlFetchHandler(BaseFetchHandler):
16 """Handler for bazaar branches via generic and lp URLs"""17 """Handler for bazaar branches via generic and lp URLs."""
18
17 def can_handle(self, source):19 def can_handle(self, source):
18 url_parts = self.parse_url(source)20 url_parts = self.parse_url(source)
19 if url_parts.scheme not in ('bzr+ssh', 'lp'):21 if url_parts.scheme not in ('bzr+ssh', 'lp'):
@@ -23,7 +25,7 @@
2325
24 def branch(self, source, dest):26 def branch(self, source, dest):
25 url_parts = self.parse_url(source)27 url_parts = self.parse_url(source)
26 # If we use lp:branchname scheme we need to load plugins28 # If we use lp:branchname scheme we need to load plugins.
27 if not self.can_handle(source):29 if not self.can_handle(source):
28 raise UnhandledSource("Cannot handle {}".format(source))30 raise UnhandledSource("Cannot handle {}".format(source))
29 if url_parts.scheme == "lp":31 if url_parts.scheme == "lp":
@@ -37,8 +39,9 @@
3739
38 def install(self, source):40 def install(self, source):
39 url_parts = self.parse_url(source)41 url_parts = self.parse_url(source)
40 branch_name = url_parts.path.strip("/").split("/")[-1]42 branch_name = url_parts.path.strip('/').split('/')[-1]
41 dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", branch_name)43 dest_dir = os.path.join(
44 os.environ.get('CHARM_DIR'), 'fetched', branch_name)
42 if not os.path.exists(dest_dir):45 if not os.path.exists(dest_dir):
43 mkdir(dest_dir, perms=0755)46 mkdir(dest_dir, perms=0755)
44 try:47 try:
@@ -46,4 +49,3 @@
46 except OSError as e:49 except OSError as e:
47 raise UnhandledSource(e.strerror)50 raise UnhandledSource(e.strerror)
48 return dest_dir51 return dest_dir
49
5052
=== modified file 'charmhelpers/payload/__init__.py'
--- charmhelpers/payload/__init__.py 2013-05-18 23:25:48 +0000
+++ charmhelpers/payload/__init__.py 2013-09-02 08:01:21 +0000
@@ -1,1 +1,3 @@
1"Tools for working with files injected into a charm just before deployment."1"""
2Tools for working with files injected into a charm just before deployment.
3"""
24
=== modified file 'charmhelpers/payload/archive.py'
--- charmhelpers/payload/archive.py 2013-07-03 23:04:00 +0000
+++ charmhelpers/payload/archive.py 2013-09-02 08:01:21 +0000
@@ -1,6 +1,7 @@
1import os1import os
2import tarfile2import tarfile
3import zipfile3import zipfile
4
4from charmhelpers.core import (5from charmhelpers.core import (
5 host,6 host,
6 hookenv,7 hookenv,
@@ -46,12 +47,12 @@
4647
4748
48def extract_tarfile(archive_name, destpath):49def extract_tarfile(archive_name, destpath):
49 "Unpack a tar archive, optionally compressed"50 """Unpack a tar archive, optionally compressed."""
50 archive = tarfile.open(archive_name)51 archive = tarfile.open(archive_name)
51 archive.extractall(destpath)52 archive.extractall(destpath)
5253
5354
54def extract_zipfile(archive_name, destpath):55def extract_zipfile(archive_name, destpath):
55 "Unpack a zip file"56 """Unpack a zip file."""
56 archive = zipfile.ZipFile(archive_name)57 archive = zipfile.ZipFile(archive_name)
57 archive.extractall(destpath)58 archive.extractall(destpath)
5859
=== modified file 'charmhelpers/payload/execd.py'
--- charmhelpers/payload/execd.py 2013-06-07 12:34:25 +0000
+++ charmhelpers/payload/execd.py 2013-09-02 08:01:21 +0000
@@ -1,8 +1,9 @@
1#!/usr/bin/env python1#!/usr/bin/env python
22
3import os3import os
4import subprocess
4import sys5import sys
5import subprocess6
6from charmhelpers.core import hookenv7from charmhelpers.core import hookenv
78
89
910
=== modified file 'tests/contrib/hahelpers/test_ceph_utils.py'
--- tests/contrib/hahelpers/test_ceph_utils.py 2013-08-13 01:12:03 +0000
+++ tests/contrib/hahelpers/test_ceph_utils.py 2013-09-02 08:01:21 +0000
@@ -126,6 +126,6 @@
126 fstype = 'xfs'126 fstype = 'xfs'
127 ceph_utils.make_filesystem(device, fstype)127 ceph_utils.make_filesystem(device, fstype)
128 self.check_call.assert_called_with(['mkfs', '-t', fstype, device])128 self.check_call.assert_called_with(['mkfs', '-t', fstype, device])
129 self.log.assert_called_with('ceph: Formatting block device %s as '129 self.log.assert_called_with(
130 'filesystem %s.' % (device, fstype), level='INFO')130 'ceph: Formatting block device %s as filesystem %s.' % (
131131 device, fstype), level='INFO')
132132
=== modified file 'tests/core/test_hookenv.py'
--- tests/core/test_hookenv.py 2013-08-07 17:05:39 +0000
+++ tests/core/test_hookenv.py 2013-09-02 08:01:21 +0000
@@ -1,9 +1,7 @@
1import cPickle as pickle
1import json2import json
23from mock import call, MagicMock, mock_open, patch
3import cPickle as pickle
4from mock import patch, call, mock_open
5from StringIO import StringIO4from StringIO import StringIO
6from mock import MagicMock
7from testtools import TestCase5from testtools import TestCase
8import yaml6import yaml
97
@@ -25,6 +23,7 @@
2523
2624
27class SerializableTest(TestCase):25class SerializableTest(TestCase):
26
28 def test_serializes_object_to_json(self):27 def test_serializes_object_to_json(self):
29 foo = {28 foo = {
30 'bar': 'baz',29 'bar': 'baz',
@@ -115,6 +114,7 @@
115114
116115
117class HelpersTest(TestCase):116class HelpersTest(TestCase):
117
118 def setUp(self):118 def setUp(self):
119 super(HelpersTest, self).setUp()119 super(HelpersTest, self).setUp()
120 # Reset hookenv cache for each test120 # Reset hookenv cache for each test
121121
=== modified file 'tests/core/test_host.py'
--- tests/core/test_host.py 2013-08-23 16:42:43 +0000
+++ tests/core/test_host.py 2013-09-02 08:01:21 +0000
@@ -1,7 +1,7 @@
1from collections import OrderedDict1from collections import OrderedDict
2from contextlib import contextmanager2from contextlib import contextmanager
3import io
3import subprocess4import subprocess
4import io
55
6from mock import patch, call, MagicMock6from mock import patch, call, MagicMock
7from testtools import TestCase7from testtools import TestCase
@@ -18,19 +18,20 @@
18 """rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 018 """rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
19""").strip().split('\n')19""").strip().split('\n')
2020
21LSB_RELEASE = u'''DISTRIB_ID=Ubuntu21LSB_RELEASE = u"""DISTRIB_ID=Ubuntu
22DISTRIB_RELEASE=13.1022DISTRIB_RELEASE=13.10
23DISTRIB_CODENAME=saucy23DISTRIB_CODENAME=saucy
24DISTRIB_DESCRIPTION="Ubuntu Saucy Salamander (development branch)"24DISTRIB_DESCRIPTION="Ubuntu Saucy Salamander (development branch)"
25'''25"""
2626
2727
28@contextmanager28@contextmanager
29def patch_open():29def patch_open():
30 '''Patch open() to allow mocking both open() itself and the file that is30 """Patch open() to allow mocking both open() itself and the file that is
31 yielded.31 yielded.
3232
33 Yields the mock for "open" and "file", respectively.'''33 Yields the mock for "open" and "file", respectively.
34 """
34 mock_open = MagicMock(spec=open)35 mock_open = MagicMock(spec=open)
35 mock_file = MagicMock(spec=file)36 mock_file = MagicMock(spec=file)
3637
@@ -45,7 +46,7 @@
4546
46@contextmanager47@contextmanager
47def mock_open(filename, contents=None):48def mock_open(filename, contents=None):
48 ''' Slightly simpler mock of open to return contents for filename '''49 """Slightly simpler mock of open to return contents for filename """
49 def mock_file(*args):50 def mock_file(*args):
50 if args[0] == filename:51 if args[0] == filename:
51 return io.StringIO(contents)52 return io.StringIO(contents)
5253
=== modified file 'tests/fetch/test_archiveurl.py'
--- tests/fetch/test_archiveurl.py 2013-07-10 18:57:39 +0000
+++ tests/fetch/test_archiveurl.py 2013-09-02 08:01:21 +0000
@@ -1,16 +1,18 @@
1import os1import os
2from testtools import TestCase2import urllib2
3from urlparse import urlparse3from urlparse import urlparse
4
4from mock import (5from mock import (
5 MagicMock,6 MagicMock,
6 patch,7 patch,
7 mock_open,8 mock_open,
8)9)
10from testtools import TestCase
11
9from charmhelpers.fetch import (12from charmhelpers.fetch import (
10 archiveurl,13 archiveurl,
11 UnhandledSource,14 UnhandledSource,
12)15)
13import urllib2
1416
1517
16class ArchiveUrlFetchHandlerTest(TestCase):18class ArchiveUrlFetchHandlerTest(TestCase):
1719
=== modified file 'tests/fetch/test_bzrurl.py'
--- tests/fetch/test_bzrurl.py 2013-08-21 11:45:37 +0000
+++ tests/fetch/test_bzrurl.py 2013-09-02 08:01:21 +0000
@@ -1,10 +1,12 @@
1import os1import os
2from testtools import TestCase
3from urlparse import urlparse2from urlparse import urlparse
3
4from mock import (4from mock import (
5 MagicMock,5 MagicMock,
6 patch,6 patch,
7)7)
8from testtools import TestCase
9
8from charmhelpers.fetch import (10from charmhelpers.fetch import (
9 bzrurl,11 bzrurl,
10 UnhandledSource,12 UnhandledSource,
@@ -41,7 +43,6 @@
41 )43 )
42 self.fh = bzrurl.BzrUrlFetchHandler()44 self.fh = bzrurl.BzrUrlFetchHandler()
4345
44
45 def test_handles_bzr_urls(self):46 def test_handles_bzr_urls(self):
46 for url in self.valid_urls:47 for url in self.valid_urls:
47 result = self.fh.can_handle(url)48 result = self.fh.can_handle(url)
@@ -50,7 +51,6 @@
50 result = self.fh.can_handle(url)51 result = self.fh.can_handle(url)
51 self.assertNotEqual(result, True, url)52 self.assertNotEqual(result, True, url)
5253
53
54 @patch('bzrlib.branch.Branch.open')54 @patch('bzrlib.branch.Branch.open')
55 def test_branch(self, _open):55 def test_branch(self, _open):
56 dest_path = "/destination/path"56 dest_path = "/destination/path"
@@ -65,7 +65,6 @@
65 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):65 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):
66 self.assertRaises(UnhandledSource, self.fh.branch, url, dest_path)66 self.assertRaises(UnhandledSource, self.fh.branch, url, dest_path)
6767
68
69 @patch('charmhelpers.fetch.bzrurl.mkdir')68 @patch('charmhelpers.fetch.bzrurl.mkdir')
70 def test_installs(self, _mkdir):69 def test_installs(self, _mkdir):
71 self.fh.branch = MagicMock()70 self.fh.branch = MagicMock()
@@ -77,4 +76,3 @@
77 where = self.fh.install(url)76 where = self.fh.install(url)
78 self.assertEqual(where, dest)77 self.assertEqual(where, dest)
79 _mkdir.assert_called_with(where, perms=0755)78 _mkdir.assert_called_with(where, perms=0755)
80
8179
=== modified file 'tests/fetch/test_fetch.py'
--- tests/fetch/test_fetch.py 2013-08-21 15:45:53 +0000
+++ tests/fetch/test_fetch.py 2013-09-02 08:01:21 +0000
@@ -1,13 +1,16 @@
1from contextlib import contextmanager1from contextlib import contextmanager
2from testtools import TestCase2from urlparse import urlparse
3
3from mock import (4from mock import (
4 patch,5 patch,
5 MagicMock,6 MagicMock,
6 call,7 call,
7)8)
8from urlparse import urlparse9from testtools import TestCase
10import yaml
11
9from charmhelpers import fetch12from charmhelpers import fetch
10import yaml13
1114
12FAKE_APT_CACHE = {15FAKE_APT_CACHE = {
13 # an installed package16 # an installed package
@@ -38,10 +41,10 @@
3841
39@contextmanager42@contextmanager
40def patch_open():43def patch_open():
41 '''Patch open() to allow mocking both open() itself and the file that is44 """Patch open() to allow mocking both open() itself and the file that is
42 yielded.45 yielded.
4346
44 Yields the mock for "open" and "file", respectively.'''47 Yields the mock for "open" and "file", respectively."""
45 mock_open = MagicMock(spec=open)48 mock_open = MagicMock(spec=open)
46 mock_file = MagicMock(spec=file)49 mock_file = MagicMock(spec=file)
4750
@@ -96,9 +99,9 @@
96 @patch.object(fetch, 'apt_install')99 @patch.object(fetch, 'apt_install')
97 def test_add_source_cloud(self, apt_install, filter_pkg):100 def test_add_source_cloud(self, apt_install, filter_pkg):
98 source = "cloud:havana-updates"101 source = "cloud:havana-updates"
99 result = '''# Ubuntu Cloud Archive102 result = """# Ubuntu Cloud Archive
100deb http://ubuntu-cloud.archive.canonical.com/ubuntu havana-updates main103deb http://ubuntu-cloud.archive.canonical.com/ubuntu havana-updates main
101'''104"""
102 with patch_open() as (mock_open, mock_file):105 with patch_open() as (mock_open, mock_file):
103 fetch.add_source(source=source)106 fetch.add_source(source=source)
104 mock_file.write.assert_called_with(result)107 mock_file.write.assert_called_with(result)
@@ -352,27 +355,24 @@
352 check_call.assert_called_with(['apt-get', '-y', '--foo', '--bar',355 check_call.assert_called_with(['apt-get', '-y', '--foo', '--bar',
353 'install', 'foo', 'bar'])356 'install', 'foo', 'bar'])
354357
355
356 @patch('subprocess.check_call')358 @patch('subprocess.check_call')
357 @patch.object(fetch, 'log')359 @patch.object(fetch, 'log')
358 def test_purges_apt_packages_as_string_fatal(self, log, mock_call):360 def test_purges_apt_packages_as_string_fatal(self, log, mock_call):
359 packages = 'irrelevant names'361 packages = 'irrelevant names'
360 mock_call.side_effect = OSError('fail')362 mock_call.side_effect = OSError('fail')
361363
362 mock_call.assertRaises(OSError, fetch.apt_purge, packages, fatal=True )364 mock_call.assertRaises(OSError, fetch.apt_purge, packages, fatal=True)
363 log.assert_called()365 log.assert_called()
364366
365
366 @patch('subprocess.check_call')367 @patch('subprocess.check_call')
367 @patch.object(fetch, 'log')368 @patch.object(fetch, 'log')
368 def test_purges_apt_packages_fatal(self, log, mock_call):369 def test_purges_apt_packages_fatal(self, log, mock_call):
369 packages = ['irrelevant', 'names']370 packages = ['irrelevant', 'names']
370 mock_call.side_effect = OSError('fail')371 mock_call.side_effect = OSError('fail')
371372
372 mock_call.assertRaises(OSError, fetch.apt_purge, packages, fatal=True )373 mock_call.assertRaises(OSError, fetch.apt_purge, packages, fatal=True)
373 log.assert_called()374 log.assert_called()
374375
375
376 @patch('subprocess.call')376 @patch('subprocess.call')
377 @patch.object(fetch, 'log')377 @patch.object(fetch, 'log')
378 def test_purges_apt_packages_as_string_nofatal(self, log, mock_call):378 def test_purges_apt_packages_as_string_nofatal(self, log, mock_call):
@@ -383,7 +383,6 @@
383 log.assert_called()383 log.assert_called()
384 mock_call.assert_called_with(['apt-get', '-y', 'purge', 'foo bar'])384 mock_call.assert_called_with(['apt-get', '-y', 'purge', 'foo bar'])
385385
386
387 @patch('subprocess.call')386 @patch('subprocess.call')
388 @patch.object(fetch, 'log')387 @patch.object(fetch, 'log')
389 def test_purges_apt_packages_nofatal(self, log, mock_call):388 def test_purges_apt_packages_nofatal(self, log, mock_call):
@@ -395,7 +394,6 @@
395 mock_call.assert_called_with(['apt-get', '-y', 'purge', 'foo',394 mock_call.assert_called_with(['apt-get', '-y', 'purge', 'foo',
396 'bar'])395 'bar'])
397396
398
399 @patch('subprocess.check_call')397 @patch('subprocess.check_call')
400 def test_apt_update_fatal(self, check_call):398 def test_apt_update_fatal(self, check_call):
401 fetch.apt_update(fatal=True)399 fetch.apt_update(fatal=True)
402400
=== modified file 'tests/payload/test_archive.py'
--- tests/payload/test_archive.py 2013-07-03 23:04:00 +0000
+++ tests/payload/test_archive.py 2013-09-02 08:01:21 +0000
@@ -1,13 +1,15 @@
1import os1import os
2from testtools import TestCase2from shutil import rmtree
3import subprocess
4from tempfile import mkdtemp
5
3from mock import (6from mock import (
4 patch,7 patch,
5 MagicMock,8 MagicMock,
6)9)
10from testtools import TestCase
11
7from charmhelpers.payload import archive12from charmhelpers.payload import archive
8from tempfile import mkdtemp
9from shutil import rmtree
10import subprocess
1113
1214
13class ArchiveTestCase(TestCase):15class ArchiveTestCase(TestCase):
1416
=== modified file 'tests/payload/test_execd.py'
--- tests/payload/test_execd.py 2013-07-11 08:31:49 +0000
+++ tests/payload/test_execd.py 2013-09-02 08:01:21 +0000
@@ -1,11 +1,11 @@
1from testtools import TestCase
2from mock import patch
3import os1import os
4import shutil2import shutil
5import stat3import stat
6
7from tempfile import mkdtemp4from tempfile import mkdtemp
85
6from mock import patch
7from testtools import TestCase
8
9from charmhelpers.payload import execd9from charmhelpers.payload import execd
1010
1111
@@ -58,7 +58,9 @@
58 expected_file = os.path.join(self.test_charm_dir, execd_dir,58 expected_file = os.path.join(self.test_charm_dir, execd_dir,
59 module_dir, 'charm-pre-install-success')59 module_dir, 'charm-pre-install-success')
60 files = os.listdir(os.path.dirname(expected_file))60 files = os.listdir(os.path.dirname(expected_file))
61 self.assertTrue(os.path.exists(expected_file), "files were: %s. charmdir is: %s" % (files, self.test_charm_dir))61 self.assertTrue(
62 os.path.exists(expected_file),
63 'files were: %s. charmdir is: %s' % (files, self.test_charm_dir))
6264
63 def test_execd_preinstall(self):65 def test_execd_preinstall(self):
64 """All charm-pre-install hooks are executed."""66 """All charm-pre-install hooks are executed."""
6567
=== modified file 'tests/tools/test_charm_helper_sync.py'
--- tests/tools/test_charm_helper_sync.py 2013-07-12 01:28:42 +0000
+++ tests/tools/test_charm_helper_sync.py 2013-09-02 08:01:21 +0000
@@ -19,7 +19,7 @@
1919
20class HelperSyncTests(unittest.TestCase):20class HelperSyncTests(unittest.TestCase):
21 def test_clone_helpers(self):21 def test_clone_helpers(self):
22 '''It properly branches the correct helpers branch'''22 """It properly branches the correct helpers branch."""
23 with patch('subprocess.check_call') as check_call:23 with patch('subprocess.check_call') as check_call:
24 sync.clone_helpers(work_dir='/tmp/foo', branch='lp:charm-helpers')24 sync.clone_helpers(work_dir='/tmp/foo', branch='lp:charm-helpers')
25 check_call.assert_called_with(['bzr', 'branch',25 check_call.assert_called_with(['bzr', 'branch',
@@ -27,19 +27,19 @@
27 '/tmp/foo/charm-helpers'])27 '/tmp/foo/charm-helpers'])
2828
29 def test_module_path(self):29 def test_module_path(self):
30 '''It converts a python module path to a filesystem path'''30 """It converts a python module path to a filesystem path."""
31 self.assertEquals(sync._module_path('some.test.module'),31 self.assertEquals(sync._module_path('some.test.module'),
32 'some/test/module')32 'some/test/module')
3333
34 def test_src_path(self):34 def test_src_path(self):
35 '''It renders the correct path to module within charm-helpers tree'''35 """It renders the correct path to module within charm-helpers tree."""
36 path = sync._src_path(src='/tmp/charm-helpers',36 path = sync._src_path(src='/tmp/charm-helpers',
37 module='contrib.openstack')37 module='contrib.openstack')
38 self.assertEquals('/tmp/charm-helpers/charmhelpers/contrib/openstack',38 self.assertEquals('/tmp/charm-helpers/charmhelpers/contrib/openstack',
39 path)39 path)
4040
41 def test_dest_path(self):41 def test_dest_path(self):
42 '''It correctly finds the correct install path within a charm'''42 """It correctly finds the correct install path within a charm."""
43 path = sync._dest_path(dest='/tmp/mycharm/hooks/charmhelpers',43 path = sync._dest_path(dest='/tmp/mycharm/hooks/charmhelpers',
44 module='contrib.openstack')44 module='contrib.openstack')
45 self.assertEquals('/tmp/mycharm/hooks/charmhelpers/contrib/openstack',45 self.assertEquals('/tmp/mycharm/hooks/charmhelpers/contrib/openstack',
@@ -49,7 +49,7 @@
49 @patch('os.path.exists')49 @patch('os.path.exists')
50 @patch('os.walk')50 @patch('os.walk')
51 def test_ensure_init(self, walk, exists, _open):51 def test_ensure_init(self, walk, exists, _open):
52 '''It ensures all subdirectories of a parent are python importable'''52 """It ensures all subdirectories of a parent are python importable."""
53 # os walk53 # os walk
54 # os.path.join54 # os.path.join
55 # os.path.exists55 # os.path.exists
@@ -73,7 +73,7 @@
73 @patch('os.makedirs')73 @patch('os.makedirs')
74 @patch('os.path.exists')74 @patch('os.path.exists')
75 def test_sync_pyfile(self, exists, mkdirs, copy, isfile, ensure_init):75 def test_sync_pyfile(self, exists, mkdirs, copy, isfile, ensure_init):
76 '''It correctly syncs a py src file from src to dest'''76 """It correctly syncs a py src file from src to dest."""
77 exists.return_value = False77 exists.return_value = False
78 isfile.return_value = True78 isfile.return_value = True
79 sync.sync_pyfile('/tmp/charm-helpers/core/host',79 sync.sync_pyfile('/tmp/charm-helpers/core/host',
@@ -88,7 +88,7 @@
88 ensure_init.assert_called_with('hooks/charmhelpers/core')88 ensure_init.assert_called_with('hooks/charmhelpers/core')
8989
90 def _test_filter_dir(self, opts, isfile, isdir):90 def _test_filter_dir(self, opts, isfile, isdir):
91 '''It filters non-python files and non-module dirs from source'''91 """It filters non-python files and non-module dirs from source."""
92 files = {92 files = {
93 'bad_file.bin': 'f',93 'bad_file.bin': 'f',
94 'some_dir': 'd',94 'some_dir': 'd',
@@ -119,7 +119,7 @@
119 @patch('os.path.isdir')119 @patch('os.path.isdir')
120 @patch('os.path.isfile')120 @patch('os.path.isfile')
121 def test_filter_dir_no_opts(self, isfile, isdir):121 def test_filter_dir_no_opts(self, isfile, isdir):
122 '''It filters out all non-py files by default'''122 """It filters out all non-py files by default."""
123 result = self._test_filter_dir(opts=None, isfile=isfile, isdir=isdir)123 result = self._test_filter_dir(opts=None, isfile=isfile, isdir=isdir)
124 ex = ['bad_file.bin', 'bad_file.img', 'some_dir']124 ex = ['bad_file.bin', 'bad_file.img', 'some_dir']
125 self.assertEquals(ex, result)125 self.assertEquals(ex, result)
@@ -127,7 +127,7 @@
127 @patch('os.path.isdir')127 @patch('os.path.isdir')
128 @patch('os.path.isfile')128 @patch('os.path.isfile')
129 def test_filter_dir_with_include(self, isfile, isdir):129 def test_filter_dir_with_include(self, isfile, isdir):
130 '''It includes non-py files if specified as an include opt'''130 """It includes non-py files if specified as an include opt."""
131 result = self._test_filter_dir(opts=['inc=*.img'],131 result = self._test_filter_dir(opts=['inc=*.img'],
132 isfile=isfile, isdir=isdir)132 isfile=isfile, isdir=isdir)
133 ex = ['bad_file.bin', 'some_dir']133 ex = ['bad_file.bin', 'some_dir']
@@ -136,7 +136,7 @@
136 @patch('os.path.isdir')136 @patch('os.path.isdir')
137 @patch('os.path.isfile')137 @patch('os.path.isfile')
138 def test_filter_dir_include_all(self, isfile, isdir):138 def test_filter_dir_include_all(self, isfile, isdir):
139 '''It does not filter anything if option specified to include all'''139 """It does not filter anything if option specified to include all."""
140 self.assertEquals(sync.get_filter(opts=['inc=*']), None)140 self.assertEquals(sync.get_filter(opts=['inc=*']), None)
141141
142 @patch('tools.charm_helpers_sync.charm_helpers_sync.get_filter')142 @patch('tools.charm_helpers_sync.charm_helpers_sync.get_filter')
@@ -146,7 +146,7 @@
146 @patch('os.path.exists')146 @patch('os.path.exists')
147 def test_sync_directory(self, exists, rmtree, copytree, ensure_init,147 def test_sync_directory(self, exists, rmtree, copytree, ensure_init,
148 _filter):148 _filter):
149 '''It correctly syncs src directory to dest directory'''149 """It correctly syncs src directory to dest directory."""
150 _filter.return_value = None150 _filter.return_value = None
151 sync.sync_directory('/tmp/charm-helpers/charmhelpers/core',151 sync.sync_directory('/tmp/charm-helpers/charmhelpers/core',
152 'hooks/charmhelpers/core')152 'hooks/charmhelpers/core')
@@ -158,7 +158,7 @@
158158
159 @patch('os.path.isfile')159 @patch('os.path.isfile')
160 def test_is_pyfile(self, isfile):160 def test_is_pyfile(self, isfile):
161 '''It correctly identifies incomplete path to a py src file as such'''161 """It correctly identifies incomplete path to a py src file as such."""
162 sync._is_pyfile('/tmp/charm-helpers/charmhelpers/core/host')162 sync._is_pyfile('/tmp/charm-helpers/charmhelpers/core/host')
163 isfile.assert_called_with(163 isfile.assert_called_with(
164 '/tmp/charm-helpers/charmhelpers/core/host.py'164 '/tmp/charm-helpers/charmhelpers/core/host.py'
@@ -167,7 +167,7 @@
167 @patch('tools.charm_helpers_sync.charm_helpers_sync.sync_directory')167 @patch('tools.charm_helpers_sync.charm_helpers_sync.sync_directory')
168 @patch('os.path.isdir')168 @patch('os.path.isdir')
169 def test_syncs_directory(self, is_dir, sync_dir):169 def test_syncs_directory(self, is_dir, sync_dir):
170 '''It correctly syncs a module directory'''170 """It correctly syncs a module directory."""
171 is_dir.return_value = True171 is_dir.return_value = True
172 sync.sync(src='/tmp/charm-helpers',172 sync.sync(src='/tmp/charm-helpers',
173 dest='hooks/charmhelpers',173 dest='hooks/charmhelpers',
@@ -181,7 +181,7 @@
181 @patch('tools.charm_helpers_sync.charm_helpers_sync._is_pyfile')181 @patch('tools.charm_helpers_sync.charm_helpers_sync._is_pyfile')
182 @patch('os.path.isdir')182 @patch('os.path.isdir')
183 def test_syncs_file(self, is_dir, is_pyfile, sync_pyfile):183 def test_syncs_file(self, is_dir, is_pyfile, sync_pyfile):
184 '''It correctly syncs a module file'''184 """It correctly syncs a module file."""
185 is_dir.return_value = False185 is_dir.return_value = False
186 is_pyfile.return_value = True186 is_pyfile.return_value = True
187 sync.sync(src='/tmp/charm-helpers',187 sync.sync(src='/tmp/charm-helpers',
@@ -195,7 +195,7 @@
195 @patch('tools.charm_helpers_sync.charm_helpers_sync.sync')195 @patch('tools.charm_helpers_sync.charm_helpers_sync.sync')
196 @patch('os.path.isdir')196 @patch('os.path.isdir')
197 def test_sync_helpers_from_config(self, isdir, _sync):197 def test_sync_helpers_from_config(self, isdir, _sync):
198 '''It correctly syncs a list of included helpers'''198 """It correctly syncs a list of included helpers."""
199 include = yaml.load(INCLUDE)['include']199 include = yaml.load(INCLUDE)['include']
200 isdir.return_value = True200 isdir.return_value = True
201 sync.sync_helpers(include=include,201 sync.sync_helpers(include=include,
@@ -219,14 +219,14 @@
219 self.assertEquals(ex_calls, _sync.call_args_list)219 self.assertEquals(ex_calls, _sync.call_args_list)
220220
221 def test_extract_option_no_globals(self):221 def test_extract_option_no_globals(self):
222 '''It extracts option from an included item with no global options'''222 """It extracts option from an included item with no global options."""
223 inc = 'contrib.openstack.templates|inc=*.template'223 inc = 'contrib.openstack.templates|inc=*.template'
224 result = sync.extract_options(inc)224 result = sync.extract_options(inc)
225 ex = ('contrib.openstack.templates', ['inc=*.template'])225 ex = ('contrib.openstack.templates', ['inc=*.template'])
226 self.assertEquals(ex, result)226 self.assertEquals(ex, result)
227227
228 def test_extract_option_with_global_as_string(self):228 def test_extract_option_with_global_as_string(self):
229 '''It extracts option for include with global options as str'''229 """It extracts option for include with global options as str."""
230 inc = 'contrib.openstack.templates|inc=*.template'230 inc = 'contrib.openstack.templates|inc=*.template'
231 result = sync.extract_options(inc, global_options='inc=foo.*')231 result = sync.extract_options(inc, global_options='inc=foo.*')
232 ex = ('contrib.openstack.templates',232 ex = ('contrib.openstack.templates',
@@ -234,14 +234,14 @@
234 self.assertEquals(ex, result)234 self.assertEquals(ex, result)
235235
236 def test_extract_option_with_globals(self):236 def test_extract_option_with_globals(self):
237 '''It extracts option from an included item with global options'''237 """It extracts option from an included item with global options."""
238 inc = 'contrib.openstack.templates|inc=*.template'238 inc = 'contrib.openstack.templates|inc=*.template'
239 result = sync.extract_options(inc, global_options=['inc=*.cfg'])239 result = sync.extract_options(inc, global_options=['inc=*.cfg'])
240 ex = ('contrib.openstack.templates', ['inc=*.template', 'inc=*.cfg'])240 ex = ('contrib.openstack.templates', ['inc=*.template', 'inc=*.cfg'])
241 self.assertEquals(ex, result)241 self.assertEquals(ex, result)
242242
243 def test_extract_multiple_options_with_globals(self):243 def test_extract_multiple_options_with_globals(self):
244 '''It extracts multiple options from an included item'''244 """It extracts multiple options from an included item."""
245 inc = 'contrib.openstack.templates|inc=*.template,inc=foo.*'245 inc = 'contrib.openstack.templates|inc=*.template,inc=foo.*'
246 result = sync.extract_options(inc, global_options=['inc=*.cfg'])246 result = sync.extract_options(inc, global_options=['inc=*.cfg'])
247 ex = ('contrib.openstack.templates',247 ex = ('contrib.openstack.templates',
248248
=== modified file 'tools/charm_helpers_sync/charm_helpers_sync.py'
--- tools/charm_helpers_sync/charm_helpers_sync.py 2013-07-12 01:28:42 +0000
+++ tools/charm_helpers_sync/charm_helpers_sync.py 2013-09-02 08:01:21 +0000
@@ -1,22 +1,19 @@
1#!/usr/bin/python1#!/usr/bin/python
2#2#
3# Copyright 2013 Canonical Ltd.3# Copyright 2013 Canonical Ltd.
4
5# Authors:4# Authors:
6# Adam Gandelman <adamg@ubuntu.com>5# Adam Gandelman <adamg@ubuntu.com>
7#
86
7from fnmatch import fnmatch
9import logging8import logging
10import optparse9import optparse
11import os10import os
11import shutil
12import subprocess12import subprocess
13import shutil
14import sys13import sys
15import tempfile14import tempfile
16import yaml15import yaml
1716
18from fnmatch import fnmatch
19
20CHARM_HELPERS_BRANCH = 'lp:charm-helpers'17CHARM_HELPERS_BRANCH = 'lp:charm-helpers'
2118
2219
@@ -52,13 +49,13 @@
5249
5350
54def ensure_init(path):51def ensure_init(path):
55 '''52 """
56 ensure directories leading up to path are importable, omitting53 Ensure directories leading up to path are importable, omitting
57 parent directory, eg path='/hooks/helpers/foo'/:54 parent directory, eg path='/hooks/helpers/foo'/:
58 hooks/55 hooks/
59 hooks/helpers/__init__.py56 hooks/helpers/__init__.py
60 hooks/helpers/foo/__init__.py57 hooks/helpers/foo/__init__.py
61 '''58 """
62 for d, dirs, files in os.walk(os.path.join(*path.split('/')[:2])):59 for d, dirs, files in os.walk(os.path.join(*path.split('/')[:2])):
63 _i = os.path.join(d, '__init__.py')60 _i = os.path.join(d, '__init__.py')
64 if not os.path.exists(_i):61 if not os.path.exists(_i):
@@ -165,6 +162,7 @@
165 inc, opts = extract_options(m, global_options)162 inc, opts = extract_options(m, global_options)
166 sync(src, dest, '%s.%s' % (k, inc), opts)163 sync(src, dest, '%s.%s' % (k, inc), opts)
167164
165
168if __name__ == '__main__':166if __name__ == '__main__':
169 parser = optparse.OptionParser()167 parser = optparse.OptionParser()
170 parser.add_option('-c', '--config', action='store', dest='config',168 parser.add_option('-c', '--config', action='store', dest='config',

Subscribers

People subscribed via source and target branches