~charmers/charms/trusty/rabbitmq-server/trunk

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/fetch/__init__.py

  • Committer: james.page at ubuntu
  • Date: 2015-01-23 08:23:05 UTC
  • mfrom: (79 rabbitmq-server)
  • mto: This revision was merged to the branch mainline in revision 80.
  • Revision ID: james.page@ubuntu.com-20150123082305-5uf1uk14iov78hl2
Rebase on next branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
import importlib
 
2
from tempfile import NamedTemporaryFile
2
3
import time
3
4
from yaml import safe_load
4
5
from charmhelpers.core.host import (
5
6
    lsb_release
6
7
)
7
 
from urlparse import (
8
 
    urlparse,
9
 
    urlunparse,
10
 
)
11
8
import subprocess
12
9
from charmhelpers.core.hookenv import (
13
10
    config,
15
12
)
16
13
import os
17
14
 
 
15
import six
 
16
if six.PY3:
 
17
    from urllib.parse import urlparse, urlunparse
 
18
else:
 
19
    from urlparse import urlparse, urlunparse
 
20
 
18
21
 
19
22
CLOUD_ARCHIVE = """# Ubuntu Cloud Archive
20
23
deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
71
74
FETCH_HANDLERS = (
72
75
    'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler',
73
76
    'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler',
 
77
    'charmhelpers.fetch.giturl.GitUrlFetchHandler',
74
78
)
75
79
 
76
80
APT_NO_LOCK = 100  # The return code for "couldn't acquire lock" in APT.
116
120
 
117
121
def filter_installed_packages(packages):
118
122
    """Returns a list of packages that require installation"""
119
 
    import apt_pkg
120
 
    apt_pkg.init()
121
 
 
122
 
    # Tell apt to build an in-memory cache to prevent race conditions (if
123
 
    # another process is already building the cache).
124
 
    apt_pkg.config.set("Dir::Cache::pkgcache", "")
125
 
 
126
 
    cache = apt_pkg.Cache()
 
123
    cache = apt_cache()
127
124
    _pkgs = []
128
125
    for package in packages:
129
126
        try:
136
133
    return _pkgs
137
134
 
138
135
 
 
136
def apt_cache(in_memory=True):
 
137
    """Build and return an apt cache"""
 
138
    import apt_pkg
 
139
    apt_pkg.init()
 
140
    if in_memory:
 
141
        apt_pkg.config.set("Dir::Cache::pkgcache", "")
 
142
        apt_pkg.config.set("Dir::Cache::srcpkgcache", "")
 
143
    return apt_pkg.Cache()
 
144
 
 
145
 
139
146
def apt_install(packages, options=None, fatal=False):
140
147
    """Install one or more packages"""
141
148
    if options is None:
144
151
    cmd = ['apt-get', '--assume-yes']
145
152
    cmd.extend(options)
146
153
    cmd.append('install')
147
 
    if isinstance(packages, basestring):
 
154
    if isinstance(packages, six.string_types):
148
155
        cmd.append(packages)
149
156
    else:
150
157
        cmd.extend(packages)
177
184
def apt_purge(packages, fatal=False):
178
185
    """Purge one or more packages"""
179
186
    cmd = ['apt-get', '--assume-yes', 'purge']
180
 
    if isinstance(packages, basestring):
 
187
    if isinstance(packages, six.string_types):
181
188
        cmd.append(packages)
182
189
    else:
183
190
        cmd.extend(packages)
188
195
def apt_hold(packages, fatal=False):
189
196
    """Hold one or more packages"""
190
197
    cmd = ['apt-mark', 'hold']
191
 
    if isinstance(packages, basestring):
 
198
    if isinstance(packages, six.string_types):
192
199
        cmd.append(packages)
193
200
    else:
194
201
        cmd.extend(packages)
201
208
 
202
209
 
203
210
def add_source(source, key=None):
 
211
    """Add a package source to this system.
 
212
 
 
213
    @param source: a URL or sources.list entry, as supported by
 
214
    add-apt-repository(1). Examples::
 
215
 
 
216
        ppa:charmers/example
 
217
        deb https://stub:key@private.example.com/ubuntu trusty main
 
218
 
 
219
    In addition:
 
220
        'proposed:' may be used to enable the standard 'proposed'
 
221
        pocket for the release.
 
222
        'cloud:' may be used to activate official cloud archive pockets,
 
223
        such as 'cloud:icehouse'
 
224
        'distro' may be used as a noop
 
225
 
 
226
    @param key: A key to be added to the system's APT keyring and used
 
227
    to verify the signatures on packages. Ideally, this should be an
 
228
    ASCII format GPG public key including the block headers. A GPG key
 
229
    id may also be used, but be aware that only insecure protocols are
 
230
    available to retrieve the actual public key from a public keyserver
 
231
    placing your Juju environment at risk. ppa and cloud archive keys
 
232
    are securely added automtically, so sould not be provided.
 
233
    """
204
234
    if source is None:
205
235
        log('Source is not present. Skipping')
206
236
        return
225
255
        release = lsb_release()['DISTRIB_CODENAME']
226
256
        with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
227
257
            apt.write(PROPOSED_POCKET.format(release))
 
258
    elif source == 'distro':
 
259
        pass
 
260
    else:
 
261
        log("Unknown source: {!r}".format(source))
 
262
 
228
263
    if key:
229
 
        subprocess.check_call(['apt-key', 'adv', '--keyserver',
230
 
                               'hkp://keyserver.ubuntu.com:80', '--recv',
231
 
                               key])
 
264
        if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key:
 
265
            with NamedTemporaryFile('w+') as key_file:
 
266
                key_file.write(key)
 
267
                key_file.flush()
 
268
                key_file.seek(0)
 
269
                subprocess.check_call(['apt-key', 'add', '-'], stdin=key_file)
 
270
        else:
 
271
            # Note that hkp: is in no way a secure protocol. Using a
 
272
            # GPG key id is pointless from a security POV unless you
 
273
            # absolutely trust your network and DNS.
 
274
            subprocess.check_call(['apt-key', 'adv', '--keyserver',
 
275
                                   'hkp://keyserver.ubuntu.com:80', '--recv',
 
276
                                   key])
232
277
 
233
278
 
234
279
def configure_sources(update=False,
238
283
    Configure multiple sources from charm configuration.
239
284
 
240
285
    The lists are encoded as yaml fragments in the configuration.
241
 
    The frament needs to be included as a string.
 
286
    The frament needs to be included as a string. Sources and their
 
287
    corresponding keys are of the types supported by add_source().
242
288
 
243
289
    Example config:
244
290
        install_sources: |
253
299
    sources = safe_load((config(sources_var) or '').strip()) or []
254
300
    keys = safe_load((config(keys_var) or '').strip()) or None
255
301
 
256
 
    if isinstance(sources, basestring):
 
302
    if isinstance(sources, six.string_types):
257
303
        sources = [sources]
258
304
 
259
305
    if keys is None:
260
306
        for source in sources:
261
307
            add_source(source, None)
262
308
    else:
263
 
        if isinstance(keys, basestring):
 
309
        if isinstance(keys, six.string_types):
264
310
            keys = [keys]
265
311
 
266
312
        if len(sources) != len(keys):
272
318
        apt_update(fatal=True)
273
319
 
274
320
 
275
 
def install_remote(source):
 
321
def install_remote(source, *args, **kwargs):
276
322
    """
277
323
    Install a file tree from a remote source
278
324
 
279
325
    The specified source should be a url of the form:
280
326
        scheme://[host]/path[#[option=value][&...]]
281
327
 
282
 
    Schemes supported are based on this modules submodules
283
 
    Options supported are submodule-specific"""
 
328
    Schemes supported are based on this modules submodules.
 
329
    Options supported are submodule-specific.
 
330
    Additional arguments are passed through to the submodule.
 
331
 
 
332
    For example::
 
333
 
 
334
        dest = install_remote('http://example.com/archive.tgz',
 
335
                              checksum='deadbeef',
 
336
                              hash_type='sha1')
 
337
 
 
338
    This will download `archive.tgz`, validate it using SHA1 and, if
 
339
    the file is ok, extract it and return the directory in which it
 
340
    was extracted.  If the checksum fails, it will raise
 
341
    :class:`charmhelpers.core.host.ChecksumError`.
 
342
    """
284
343
    # We ONLY check for True here because can_handle may return a string
285
344
    # explaining why it can't handle a given source.
286
345
    handlers = [h for h in plugins() if h.can_handle(source) is True]
287
346
    installed_to = None
288
347
    for handler in handlers:
289
348
        try:
290
 
            installed_to = handler.install(source)
 
349
            installed_to = handler.install(source, *args, **kwargs)
291
350
        except UnhandledSource:
292
351
            pass
293
352
    if not installed_to:
344
403
        while result is None or result == APT_NO_LOCK:
345
404
            try:
346
405
                result = subprocess.check_call(cmd, env=env)
347
 
            except subprocess.CalledProcessError, e:
 
406
            except subprocess.CalledProcessError as e:
348
407
                retry_count = retry_count + 1
349
408
                if retry_count > APT_NO_LOCK_RETRY_COUNT:
350
409
                    raise