~cjwatson/charms/trusty/apache2/extra-open-ports

« back to all changes in this revision

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

  • Committer: Tom Haddon
  • Date: 2015-03-10 10:48:31 UTC
  • mfrom: (62.1.1 apache2)
  • Revision ID: tom.haddon@canonical.com-20150310104831-xj1o52oaspg8q55t
[bloodearnest,r=mthaddon] Update charm-helpers to latest rev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2014-2015 Canonical Limited.
 
2
#
 
3
# This file is part of charm-helpers.
 
4
#
 
5
# charm-helpers is free software: you can redistribute it and/or modify
 
6
# it under the terms of the GNU Lesser General Public License version 3 as
 
7
# published by the Free Software Foundation.
 
8
#
 
9
# charm-helpers is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU Lesser General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU Lesser General Public License
 
15
# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
 
16
 
1
17
import os
2
 
import urllib2
 
18
import hashlib
 
19
import re
 
20
 
3
21
from charmhelpers.fetch import (
4
22
    BaseFetchHandler,
5
23
    UnhandledSource
8
26
    get_archive_handler,
9
27
    extract,
10
28
)
11
 
from charmhelpers.core.host import mkdir
 
29
from charmhelpers.core.host import mkdir, check_hash
 
30
 
 
31
import six
 
32
if six.PY3:
 
33
    from urllib.request import (
 
34
        build_opener, install_opener, urlopen, urlretrieve,
 
35
        HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler,
 
36
    )
 
37
    from urllib.parse import urlparse, urlunparse, parse_qs
 
38
    from urllib.error import URLError
 
39
else:
 
40
    from urllib import urlretrieve
 
41
    from urllib2 import (
 
42
        build_opener, install_opener, urlopen,
 
43
        HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler,
 
44
        URLError
 
45
    )
 
46
    from urlparse import urlparse, urlunparse, parse_qs
 
47
 
 
48
 
 
49
def splituser(host):
 
50
    '''urllib.splituser(), but six's support of this seems broken'''
 
51
    _userprog = re.compile('^(.*)@(.*)$')
 
52
    match = _userprog.match(host)
 
53
    if match:
 
54
        return match.group(1, 2)
 
55
    return None, host
 
56
 
 
57
 
 
58
def splitpasswd(user):
 
59
    '''urllib.splitpasswd(), but six's support of this is missing'''
 
60
    _passwdprog = re.compile('^([^:]*):(.*)$', re.S)
 
61
    match = _passwdprog.match(user)
 
62
    if match:
 
63
        return match.group(1, 2)
 
64
    return user, None
12
65
 
13
66
 
14
67
class ArchiveUrlFetchHandler(BaseFetchHandler):
15
 
    """Handler for archives via generic URLs"""
 
68
    """
 
69
    Handler to download archive files from arbitrary URLs.
 
70
 
 
71
    Can fetch from http, https, ftp, and file URLs.
 
72
 
 
73
    Can install either tarballs (.tar, .tgz, .tbz2, etc) or zip files.
 
74
 
 
75
    Installs the contents of the archive in $CHARM_DIR/fetched/.
 
76
    """
16
77
    def can_handle(self, source):
17
78
        url_parts = self.parse_url(source)
18
79
        if url_parts.scheme not in ('http', 'https', 'ftp', 'file'):
22
83
        return False
23
84
 
24
85
    def download(self, source, dest):
 
86
        """
 
87
        Download an archive file.
 
88
 
 
89
        :param str source: URL pointing to an archive file.
 
90
        :param str dest: Local path location to download archive file to.
 
91
        """
25
92
        # propogate all exceptions
26
93
        # URLError, OSError, etc
27
 
        response = urllib2.urlopen(source)
 
94
        proto, netloc, path, params, query, fragment = urlparse(source)
 
95
        if proto in ('http', 'https'):
 
96
            auth, barehost = splituser(netloc)
 
97
            if auth is not None:
 
98
                source = urlunparse((proto, barehost, path, params, query, fragment))
 
99
                username, password = splitpasswd(auth)
 
100
                passman = HTTPPasswordMgrWithDefaultRealm()
 
101
                # Realm is set to None in add_password to force the username and password
 
102
                # to be used whatever the realm
 
103
                passman.add_password(None, source, username, password)
 
104
                authhandler = HTTPBasicAuthHandler(passman)
 
105
                opener = build_opener(authhandler)
 
106
                install_opener(opener)
 
107
        response = urlopen(source)
28
108
        try:
29
109
            with open(dest, 'w') as dest_file:
30
110
                dest_file.write(response.read())
33
113
                os.unlink(dest)
34
114
            raise e
35
115
 
36
 
    def install(self, source):
 
116
    # Mandatory file validation via Sha1 or MD5 hashing.
 
117
    def download_and_validate(self, url, hashsum, validate="sha1"):
 
118
        tempfile, headers = urlretrieve(url)
 
119
        check_hash(tempfile, hashsum, validate)
 
120
        return tempfile
 
121
 
 
122
    def install(self, source, dest=None, checksum=None, hash_type='sha1'):
 
123
        """
 
124
        Download and install an archive file, with optional checksum validation.
 
125
 
 
126
        The checksum can also be given on the `source` URL's fragment.
 
127
        For example::
 
128
 
 
129
            handler.install('http://example.com/file.tgz#sha1=deadbeef')
 
130
 
 
131
        :param str source: URL pointing to an archive file.
 
132
        :param str dest: Local destination path to install to. If not given,
 
133
            installs to `$CHARM_DIR/archives/archive_file_name`.
 
134
        :param str checksum: If given, validate the archive file after download.
 
135
        :param str hash_type: Algorithm used to generate `checksum`.
 
136
            Can be any hash alrgorithm supported by :mod:`hashlib`,
 
137
            such as md5, sha1, sha256, sha512, etc.
 
138
 
 
139
        """
37
140
        url_parts = self.parse_url(source)
38
141
        dest_dir = os.path.join(os.environ.get('CHARM_DIR'), 'fetched')
39
142
        if not os.path.exists(dest_dir):
40
 
            mkdir(dest_dir, perms=0755)
 
143
            mkdir(dest_dir, perms=0o755)
41
144
        dld_file = os.path.join(dest_dir, os.path.basename(url_parts.path))
42
145
        try:
43
146
            self.download(source, dld_file)
44
 
        except urllib2.URLError as e:
 
147
        except URLError as e:
45
148
            raise UnhandledSource(e.reason)
46
149
        except OSError as e:
47
150
            raise UnhandledSource(e.strerror)
48
 
        return extract(dld_file)
 
151
        options = parse_qs(url_parts.fragment)
 
152
        for key, value in options.items():
 
153
            if not six.PY3:
 
154
                algorithms = hashlib.algorithms
 
155
            else:
 
156
                algorithms = hashlib.algorithms_available
 
157
            if key in algorithms:
 
158
                check_hash(dld_file, value, key)
 
159
        if checksum:
 
160
            check_hash(dld_file, checksum, hash_type)
 
161
        return extract(dld_file, dest)