Add source-checksum option #619

Closed
wants to merge 67 commits into
from
Commits
Jump to file or symbol
Failed to load files and symbols.
+452 −39
Split
@@ -0,0 +1 @@
+b40dd8099c3dd299b889d1981fb1e87dd058134cf38146615648753dffb7bead6eac0e9bacb53c553873406eae702632e432e70383b2a345ddc5768f6c7007e4 checksum.tar.gz
Binary file not shown.
@@ -40,3 +40,31 @@ parts:
plugin: tar-content
source: simple.tar.bz2
destination: destdir1/destdir2
+ checksum-md5:
+ plugin: tar-content
+ source: checksum.tar.gz
+ source-checksum: d9210476aac5f367b14e513bdefdee08
+ checksum-sha1:
+ plugin: tar-content
+ source: checksum.tar.gz
+ source-checksum: 8d5a102a7e7e99f8962a4de2c348a96b7d54200b
+ checksum-sha224:
+ plugin: tar-content
+ source: checksum.tar.gz
+ source-checksum: de2fb61252548af3c87c4aab17e82601691d19e37fd3d29ea6288e56
+ checksum-sha256:
+ plugin: tar-content
+ source: checksum.tar.gz
+ source-checksum: 85e9d25042f92fa0a53c19d303f67036b9aab81e4c63d85319cd5577ca0d5081
+ checksum-sha384:
+ plugin: tar-content
+ source: checksum.tar.gz
+ source-checksum: 4d3110ea9d1683dde8624432bd4ce8afc00766f5de01077aa8d51f313e16dee18196e5f1a5d38325942408793b4b7e78
+ checksum-sha512:
+ plugin: tar-content
+ source: checksum.tar.gz
+ source-checksum: b40dd8099c3dd299b889d1981fb1e87dd058134cf38146615648753dffb7bead6eac0e9bacb53c553873406eae702632e432e70383b2a345ddc5768f6c7007e4
+ checksum-file:
+ plugin: tar-content
+ source: checksum.tar.gz
+ source-checksum: CHECKSUM
@@ -0,0 +1 @@
+c4f762b25393dafd1b3cd34c9bda82ecda6af552a46cc17d0c3abd9f19beb3abed2bd9948559aec8a81aa9d242cc112c08ef5ba80d4456ec0517ca517b8d3253 simple.zip
@@ -10,3 +10,45 @@ parts:
source: simple.zip
files:
"*": .
+ checksum-md5:
+ plugin: copy
+ source: simple.zip
+ source-checksum: fe049cfba688aa1af88bc78191d7f904
+ files:
+ "*": .
+ checksum-sha1:
+ plugin: copy
+ source: simple.zip
+ source-checksum: 30fdfacb19b557a762932c5a3a867cdc698e447f
+ files:
+ "*": .
+ checksum-sha224:
+ plugin: copy
+ source: simple.zip
+ source-checksum: 3a7aa780dee4d9c1ed25359d7514589876ff0dc5f1cd7ac7d20e79ab
+ files:
+ "*": .
+ checksum-sha256:
+ plugin: copy
+ source: simple.zip
+ source-checksum: 035ae7da4bd0ff39960466353e0810f51d17193a13e8b75e767391820aed484c
+ files:
+ "*": .
+ checksum-sha384:
+ plugin: copy
+ source: simple.zip
+ source-checksum: fa50da87b4fdd9c28a3c8b653d4cc37f942d91cd4da4099c0b0c85713971ca72948014aadde2eb824ff82176e0f32a96
+ files:
+ "*": .
+ checksum-sha512:
+ plugin: copy
+ source: simple.zip
+ source-checksum: c4f762b25393dafd1b3cd34c9bda82ecda6af552a46cc17d0c3abd9f19beb3abed2bd9948559aec8a81aa9d242cc112c08ef5ba80d4456ec0517ca517b8d3253
+ files:
+ "*": .
+ checksum-file:
+ plugin: copy
+ source: simple.zip
+ source-checksum: CHECKSUM
+ files:
+ "*": .
@@ -46,6 +46,10 @@ def schema(cls):
'type': 'string',
'default': '',
},
+ 'source-checksum': {
+ 'type': 'string',
+ 'default': '',
+ },
'source-branch': {
'type': 'string',
'default': '',
@@ -62,8 +66,9 @@ def schema(cls):
'required': [
'source',
],
- 'pull-properties': ['source', 'source-type', 'source-branch',
- 'source-tag', 'source-subdir'],
+ 'pull-properties': ['source', 'source-type', 'source-checksum',
+ 'source-branch', 'source-tag',
+ 'source-subdir'],
'build-properties': []
}
@@ -113,6 +118,7 @@ def pull(self):
part properties to retrieve source code:
- source
+ - source-checksum
- source-branch
- source-tag
- source-type
@@ -33,6 +33,12 @@
control system or compression algorithm. The source-type key can tell
snapcraft exactly how to treat that content.
+ - source-checksum: checksum-of-file
+
+ Snapcraft will use either a file, URL, or raw checksum specified here to
+ verify the integrity of the source. The source-type needs to be either tar
+ or zip.
+
- source-branch: <branch-name>
Snapcraft will checkout a specific branch from the source tree. This
@@ -70,13 +76,14 @@
import subprocess
import tempfile
import zipfile
+import hashlib
+import urllib
import glob
from snapcraft.internal import common
from snapcraft.internal.indicators import download_requests_stream
-
-logging.getLogger('urllib3').setLevel(logging.CRITICAL)
+logging.getLogger('urllib').setLevel(logging.CRITICAL)
class IncompatibleOptionsError(Exception):
@@ -85,11 +92,18 @@ def __init__(self, message):
self.message = message
+class ChecksumDoesNotMatch(Exception):
+
+ def __init__(self, message):
+ self.message = message
+
+
class Base:
- def __init__(self, source, source_dir, source_tag=None,
- source_branch=None):
+ def __init__(self, source, source_dir, source_checksum=None,
+ source_tag=None, source_branch=None):
self.source = source
+ self.source_checksum = source_checksum
self.source_dir = source_dir
self.source_tag = source_tag
self.source_branch = source_branch
@@ -128,12 +142,16 @@ def download(self):
class Bazaar(Base):
- def __init__(self, source, source_dir, source_tag=None,
- source_branch=None):
- super().__init__(source, source_dir, source_tag, source_branch)
+ def __init__(self, source, source_dir, source_checksum=None,
+ source_tag=None, source_branch=None):
+ super().__init__(
+ source, source_dir, source_checksum, source_tag, source_branch)
if source_branch:
raise IncompatibleOptionsError(
'can\'t specify a source-branch for a bzr source')
+ elif source_checksum:
+ raise IncompatibleOptionsError(
+ 'can\'t specify a source-checksum for a bzr source')
def pull(self):
tag_opts = []
@@ -152,13 +170,17 @@ def pull(self):
class Git(Base):
- def __init__(self, source, source_dir, source_tag=None,
- source_branch=None):
- super().__init__(source, source_dir, source_tag, source_branch)
+ def __init__(self, source, source_dir, source_checksum=None,
+ source_tag=None, source_branch=None):
+ super().__init__(
+ source, source_dir, source_checksum, source_tag, source_branch)
if source_tag and source_branch:
raise IncompatibleOptionsError(
'can\'t specify both source-tag and source-branch for '
'a git source')
+ elif source_checksum:
+ raise IncompatibleOptionsError(
+ 'can\'t specify source-checksum for a git source')
def pull(self):
if os.path.exists(os.path.join(self.source_dir, '.git')):
@@ -188,13 +210,17 @@ def pull(self):
class Mercurial(Base):
- def __init__(self, source, source_dir, source_tag=None,
- source_branch=None):
- super().__init__(source, source_dir, source_tag, source_branch)
+ def __init__(self, source, source_dir, source_checksum=None,
+ source_tag=None, source_branch=None):
+ super().__init__(
+ source, source_dir, source_checksum, source_tag, source_branch)
if source_tag and source_branch:
raise IncompatibleOptionsError(
'can\'t specify both source-tag and source-branch for a '
'mercurial source')
+ elif source_checksum:
+ raise IncompatibleOptionsError(
+ 'can\'t specify source-checksum for a mercurial source')
def pull(self):
if os.path.exists(os.path.join(self.source_dir, '.hg')):
@@ -215,9 +241,10 @@ def pull(self):
class Subversion(Base):
- def __init__(self, source, source_dir, source_tag=None,
- source_branch=None):
- super().__init__(source, source_dir, source_tag, source_branch)
+ def __init__(self, source, source_dir, source_checksum=None,
+ source_tag=None, source_branch=None):
+ super().__init__(
+ source, source_dir, source_checksum, source_tag, source_branch)
if source_tag:
if source_branch:
raise IncompatibleOptionsError(
@@ -229,6 +256,9 @@ def __init__(self, source, source_dir, source_tag=None,
elif source_branch:
raise IncompatibleOptionsError(
"Can't specify source-branch for a Subversion source")
+ elif source_checksum:
+ raise IncompatibleOptionsError(
+ "Can't specify source-checksum for a Subversion source")
def pull(self):
if os.path.exists(os.path.join(self.source_dir, '.svn')):
@@ -247,9 +277,10 @@ def pull(self):
class Tar(FileBase):
- def __init__(self, source, source_dir, source_tag=None,
- source_branch=None):
- super().__init__(source, source_dir, source_tag, source_branch)
+ def __init__(self, source, source_dir, source_checksum=None,
+ source_tag=None, source_branch=None):
+ super().__init__(
+ source, source_dir, source_checksum, source_tag, source_branch)
if source_tag:
raise IncompatibleOptionsError(
'can\'t specify a source-tag for a tar source')
@@ -261,6 +292,9 @@ def provision(self, dst, clean_target=True, keep_tarball=False):
# TODO add unit tests.
tarball = os.path.join(self.source_dir, os.path.basename(self.source))
+ if self.source_checksum:
+ verify_checksum(self.source_checksum, tarball)
+
if clean_target:
tmp_tarball = tempfile.NamedTemporaryFile().name
shutil.move(tarball, tmp_tarball)
@@ -318,9 +352,10 @@ def _strip_prefix(self, common, member):
class Zip(FileBase):
- def __init__(self, source, source_dir, source_tag=None,
- source_branch=None):
- super().__init__(source, source_dir, source_tag, source_branch)
+ def __init__(self, source, source_dir, source_checksum=None,
+ source_tag=None, source_branch=None):
+ super().__init__(source, source_dir, source_checksum,
+ source_tag, source_branch)
if source_tag:
raise IncompatibleOptionsError(
'can\'t specify a source-tag for a zip source')
@@ -331,6 +366,9 @@ def __init__(self, source, source_dir, source_tag=None,
def provision(self, dst, clean_target=True, keep_zip=False):
zip = os.path.join(self.source_dir, os.path.basename(self.source))
@sergiusens

sergiusens Jul 30, 2016

Collaborator

please use a different name than zip, zip is a python keyword and it might get confusing.

@tsimonq2

tsimonq2 Jul 31, 2016

Contributor

@sergiusens This is applicable to the original source as well and should be dealt with in a separate PR, I'm just using what has already been given to me for variable names.

+ if self.source_checksum:
+ verify_checksum(self.source_checksum, zip)
+
if clean_target:
tmp_zip = tempfile.NamedTemporaryFile().name
shutil.move(zip, tmp_zip)
@@ -377,12 +415,13 @@ def get(sourcedir, builddir, options):
:param options: source options.
"""
source_type = getattr(options, 'source_type', None)
+ source_checksum = getattr(options, 'source_checksum', None)
source_tag = getattr(options, 'source_tag', None)
source_branch = getattr(options, 'source_branch', None)
handler_class = _get_source_handler(source_type, options.source)
- handler = handler_class(options.source, sourcedir, source_tag,
- source_branch)
+ handler = handler_class(options.source, sourcedir, source_checksum,
+ source_tag, source_branch)
handler.pull()
@@ -456,3 +495,52 @@ def _get_source_type_from_uri(source, ignore_errors=False):
raise ValueError('local source is not a directory')
return source_type
+
+
+def verify_checksum(source_checksum, checkfile):
+ if source_checksum.startswith('http'):
+ response = urllib.request.urlopen(source_checksum)
+ data = response.read()
+ source_checksum = data.decode('utf-8')
+ if (' ' in source_checksum):
+ source_checksum = source_checksum.split(' ', 1)[0]
+ else:
+ print('No file name detected in the checksum file, perhaps an '
+ 'invalid checksum file?')
+ if os.path.isfile(source_checksum):
+ filename = source_checksum
+ try:
+ filework = open(filename, 'r')
+ source_checksum = filework.read()
+ if (' ' in source_checksum):
+ source_checksum = source_checksum.split(' ', 1)[0]
+ else:
+ print('No file name detected in the checksum file, perhaps an '
+ 'invalid checksum file?')
+ finally:
+ filework.close()
+
+ _HASH_FUNCTIONS = {
+ 32: hashlib.md5(),
+ 40: hashlib.sha1(),
+ 56: hashlib.sha224(),
+ 64: hashlib.sha256(),
+ 96: hashlib.sha384(),
+ 128: hashlib.sha512()
+ }
+
+ try:
+ checksum = _HASH_FUNCTIONS[len(source_checksum)]
+ except KeyError:
+ raise IncompatibleOptionsError('Invalid checksum format')
+
+ with open(checkfile, 'rb') as f:
+ for chunk in iter(lambda: f.read(4096), b''):
+ checksum.update(chunk)
+
+ checksum = checksum.hexdigest()
+
+ if checksum != source_checksum:
+ raise ChecksumDoesNotMatch(
+ "the checksum ( {0} ) doesn't match the file ( {1} )".format(
+ source_checksum, checksum))
@@ -28,10 +28,11 @@
class MockOptions:
- def __init__(self, source=None, source_type=None, source_branch=None,
- source_tag=None, source_subdir=None):
+ def __init__(self, source=None, source_type=None, source_checksum=None,
+ source_branch=None, source_tag=None, source_subdir=None):
self.source = source
self.source_type = source_type
+ self.source_checksum = source_checksum
self.source_branch = source_branch
self.source_tag = source_tag
self.source_subdir = source_subdir
Oops, something went wrong.