~ggouzi/obinstall/obinstall

« back to all changes in this revision

Viewing changes to demos/app-bundles/reddit/reddit_store/precise/reddit/lib/charmhelpers/fetch/archiveurl.py

  • Committer: MMorana
  • Date: 2016-03-24 01:18:40 UTC
  • Revision ID: mass@ubuntu.com-20160324011840-blxydmf7ca4ggle0
Splitting out demos from install

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
import os
2
 
import urllib2
3
 
from urllib import urlretrieve
4
 
import urlparse
5
 
import hashlib
6
 
 
7
 
from charmhelpers.fetch import (
8
 
    BaseFetchHandler,
9
 
    UnhandledSource
10
 
)
11
 
from charmhelpers.payload.archive import (
12
 
    get_archive_handler,
13
 
    extract,
14
 
)
15
 
from charmhelpers.core.host import mkdir
16
 
 
17
 
"""
18
 
This class is a plugin for charmhelpers.fetch.install_remote.
19
 
 
20
 
It grabs, validates and installs remote archives fetched over "http", "https", "ftp" or "file" protocols. The contents of the archive are installed in $CHARM_DIR/fetched/.
21
 
 
22
 
Example usage:
23
 
install_remote("https://example.com/some/archive.tar.gz")
24
 
# Installs the contents of archive.tar.gz in $CHARM_DIR/fetched/.
25
 
 
26
 
See charmhelpers.fetch.archiveurl.get_archivehandler for supported archive types.
27
 
"""
28
 
class ArchiveUrlFetchHandler(BaseFetchHandler):
29
 
    """Handler for archives via generic URLs"""
30
 
    def can_handle(self, source):
31
 
        url_parts = self.parse_url(source)
32
 
        if url_parts.scheme not in ('http', 'https', 'ftp', 'file'):
33
 
            return "Wrong source type"
34
 
        if get_archive_handler(self.base_url(source)):
35
 
            return True
36
 
        return False
37
 
 
38
 
    def download(self, source, dest):
39
 
        # propogate all exceptions
40
 
        # URLError, OSError, etc
41
 
        proto, netloc, path, params, query, fragment = urlparse.urlparse(source)
42
 
        if proto in ('http', 'https'):
43
 
            auth, barehost = urllib2.splituser(netloc)
44
 
            if auth is not None:
45
 
                source = urlparse.urlunparse((proto, barehost, path, params, query, fragment))
46
 
                username, password = urllib2.splitpasswd(auth)
47
 
                passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
48
 
                # Realm is set to None in add_password to force the username and password
49
 
                # to be used whatever the realm
50
 
                passman.add_password(None, source, username, password)
51
 
                authhandler = urllib2.HTTPBasicAuthHandler(passman)
52
 
                opener = urllib2.build_opener(authhandler)
53
 
                urllib2.install_opener(opener)
54
 
        response = urllib2.urlopen(source)
55
 
        try:
56
 
            with open(dest, 'w') as dest_file:
57
 
                dest_file.write(response.read())
58
 
        except Exception as e:
59
 
            if os.path.isfile(dest):
60
 
                os.unlink(dest)
61
 
            raise e
62
 
 
63
 
    def install(self, source):
64
 
        url_parts = self.parse_url(source)
65
 
        dest_dir = os.path.join(os.environ.get('CHARM_DIR'), 'fetched')
66
 
        if not os.path.exists(dest_dir):
67
 
            mkdir(dest_dir, perms=0755)
68
 
        dld_file = os.path.join(dest_dir, os.path.basename(url_parts.path))
69
 
        try:
70
 
            self.download(source, dld_file)
71
 
        except urllib2.URLError as e:
72
 
            raise UnhandledSource(e.reason)
73
 
        except OSError as e:
74
 
            raise UnhandledSource(e.strerror)
75
 
        return extract(dld_file)
76
 
 
77
 
    # Mandatory file validation via Sha1 or MD5 hashing.
78
 
    def download_and_validate(self, url, hashsum, validate="sha1"):
79
 
        if validate == 'sha1' and len(hashsum) != 40:
80
 
            raise ValueError("HashSum must be = 40 characters when using sha1"
81
 
                             " validation")
82
 
        if validate == 'md5' and len(hashsum) != 32:
83
 
            raise ValueError("HashSum must be = 32 characters when using md5"
84
 
                             " validation")
85
 
        tempfile, headers = urlretrieve(url)
86
 
        self.validate_file(tempfile, hashsum, validate)
87
 
        return tempfile
88
 
 
89
 
    # Predicate method that returns status of hash matching expected hash.
90
 
    def validate_file(self, source, hashsum, vmethod='sha1'):
91
 
        if vmethod != 'sha1' and vmethod != 'md5':
92
 
            raise ValueError("Validation Method not supported")
93
 
 
94
 
        if vmethod == 'md5':
95
 
            m = hashlib.md5()
96
 
        if vmethod == 'sha1':
97
 
            m = hashlib.sha1()
98
 
        with open(source) as f:
99
 
            for line in f:
100
 
                m.update(line)
101
 
        if hashsum != m.hexdigest():
102
 
            msg = "Hash Mismatch on {} expected {} got {}"
103
 
            raise ValueError(msg.format(source, hashsum, m.hexdigest()))