diff mbox series

[3/5] tests/vm: Detect the image changed on server

Message ID 20190329210804.22121-4-wainersm@redhat.com (mailing list archive)
State New, archived
Headers show
Series tests/vm: Python 3, improve image caching, and misc | expand

Commit Message

Wainer dos Santos Moschetta March 29, 2019, 9:08 p.m. UTC
The current implementation of basevm does not check if the image
file to be downloaded has changed on server side before honouring
the cache. So any change on server-side file can go unnoticed,
keeping the cached image.

This change implements a simple mechanism to detect the image
file changed by using the sha256sum file stored on server. It
compares with the expected checksum and then abort the execution
on mismatch.

Signed-off-by: Wainer dos Santos Moschetta <wainersm@redhat.com>
---
 tests/vm/basevm.py | 31 ++++++++++++++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)

Comments

Alex Bennée May 29, 2019, 7:05 p.m. UTC | #1
Wainer dos Santos Moschetta <wainersm@redhat.com> writes:

> The current implementation of basevm does not check if the image
> file to be downloaded has changed on server side before honouring
> the cache. So any change on server-side file can go unnoticed,
> keeping the cached image.
>
> This change implements a simple mechanism to detect the image
> file changed by using the sha256sum file stored on server. It
> compares with the expected checksum and then abort the execution
> on mismatch.
>
> Signed-off-by: Wainer dos Santos Moschetta <wainersm@redhat.com>
> ---
>  tests/vm/basevm.py | 31 ++++++++++++++++++++++++++++++-
>  1 file changed, 30 insertions(+), 1 deletion(-)
>
> diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py
> index 083befce9f..4dfad2dc9b 100755
> --- a/tests/vm/basevm.py
> +++ b/tests/vm/basevm.py
> @@ -27,6 +27,7 @@ import tempfile
>  import shutil
>  import multiprocessing
>  import traceback
> +import urllib.request
>
>  SSH_KEY = open(os.path.join(os.path.dirname(__file__),
>                 "..", "keys", "id_rsa")).read()
> @@ -81,6 +82,18 @@ class BaseVM(object):
>          self._data_args = []
>
>      def _download_with_cache(self, url, sha256sum=None):
> +
> +        def fetch_image_hash(url):
> +            fetch_url = "%s.sha256sum" % url

OK this fails with the FreeBSD code as they use the form:

  https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.0/CHECKSUM.SHA256-FreeBSD-12.0-RELEASE-amd64

I guess we need to have a method that can be overridden for this.

> +            try:
> +                with urllib.request.urlopen(fetch_url) as response:
> +                    content = response.read()
> +            except  urllib.error.URLError as error:
> +                logging.error("Failed to fetch image checksum file: %s",
> +                        fetch_url)
> +                raise error
> +            return content.decode().strip()
> +
>          def check_sha256sum(fname):
>              if not sha256sum:
>                  return True
> @@ -91,8 +104,24 @@ class BaseVM(object):
>          if not os.path.exists(cache_dir):
>              os.makedirs(cache_dir)
>          fname = os.path.join(cache_dir, hashlib.sha1(url.encode()).hexdigest())
> -        if os.path.exists(fname) and check_sha256sum(fname):
> +
> +        if os.path.exists(fname) and sha256sum is None:
>              return fname
> +
> +        if sha256sum:
> +            image_checksum = fetch_image_hash(url)
> +            # Check the url points to a known image file.
> +            if image_checksum != sha256sum:
> +                logging.error("Image %s checksum (%s) does not match " +
> +                        "expected (%s).", url, image_checksum, sha256sum)
> +                raise Exception("Image checksum failed.")
> +            # Check the cached image is up to date.
> +            if os.path.exists(fname):
> +                if check_sha256sum(fname):
> +                    return fname
> +                logging.warning("Invalid cached image. Attempt to download " +
> +                        "the updated one.")
> +
>          logging.debug("Downloading %s to %s...", url, fname)
>          subprocess.check_call(["wget", "-c", url, "-O", fname + ".download"],
>                                stdout=self._stdout, stderr=self._stderr)


--
Alex Bennée
diff mbox series

Patch

diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py
index 083befce9f..4dfad2dc9b 100755
--- a/tests/vm/basevm.py
+++ b/tests/vm/basevm.py
@@ -27,6 +27,7 @@  import tempfile
 import shutil
 import multiprocessing
 import traceback
+import urllib.request
 
 SSH_KEY = open(os.path.join(os.path.dirname(__file__),
                "..", "keys", "id_rsa")).read()
@@ -81,6 +82,18 @@  class BaseVM(object):
         self._data_args = []
 
     def _download_with_cache(self, url, sha256sum=None):
+
+        def fetch_image_hash(url):
+            fetch_url = "%s.sha256sum" % url
+            try:
+                with urllib.request.urlopen(fetch_url) as response:
+                    content = response.read()
+            except  urllib.error.URLError as error:
+                logging.error("Failed to fetch image checksum file: %s",
+                        fetch_url)
+                raise error
+            return content.decode().strip()
+
         def check_sha256sum(fname):
             if not sha256sum:
                 return True
@@ -91,8 +104,24 @@  class BaseVM(object):
         if not os.path.exists(cache_dir):
             os.makedirs(cache_dir)
         fname = os.path.join(cache_dir, hashlib.sha1(url.encode()).hexdigest())
-        if os.path.exists(fname) and check_sha256sum(fname):
+
+        if os.path.exists(fname) and sha256sum is None:
             return fname
+
+        if sha256sum:
+            image_checksum = fetch_image_hash(url)
+            # Check the url points to a known image file.
+            if image_checksum != sha256sum:
+                logging.error("Image %s checksum (%s) does not match " +
+                        "expected (%s).", url, image_checksum, sha256sum)
+                raise Exception("Image checksum failed.")
+            # Check the cached image is up to date.
+            if os.path.exists(fname):
+                if check_sha256sum(fname):
+                    return fname
+                logging.warning("Invalid cached image. Attempt to download " +
+                        "the updated one.")
+
         logging.debug("Downloading %s to %s...", url, fname)
         subprocess.check_call(["wget", "-c", url, "-O", fname + ".download"],
                               stdout=self._stdout, stderr=self._stderr)