3
from urllib import urlretrieve
7
from charmhelpers.fetch import (
11
from charmhelpers.payload.archive import (
15
from charmhelpers.core.host import mkdir
18
This class is a plugin for charmhelpers.fetch.install_remote.
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/.
23
install_remote("https://example.com/some/archive.tar.gz")
24
# Installs the contents of archive.tar.gz in $CHARM_DIR/fetched/.
26
See charmhelpers.fetch.archiveurl.get_archivehandler for supported archive types.
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)):
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)
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)
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):
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))
70
self.download(source, dld_file)
71
except urllib2.URLError as e:
72
raise UnhandledSource(e.reason)
74
raise UnhandledSource(e.strerror)
75
return extract(dld_file)
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"
82
if validate == 'md5' and len(hashsum) != 32:
83
raise ValueError("HashSum must be = 32 characters when using md5"
85
tempfile, headers = urlretrieve(url)
86
self.validate_file(tempfile, hashsum, validate)
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")
98
with open(source) as f:
101
if hashsum != m.hexdigest():
102
msg = "Hash Mismatch on {} expected {} got {}"
103
raise ValueError(msg.format(source, hashsum, m.hexdigest()))