Repository: osbuild/osbuild
Pull request #384 (2020-05-08 09:31:41)

tmpfs: drop unused module
The osbuild/tmpfs.py module is unused. Drop it.


Patch:
diff --git a/osbuild/objectstore.py b/osbuild/objectstore.py
index 8624a670..6fe58eca 100644
--- a/osbuild/objectstore.py
+++ b/osbuild/objectstore.py
@@ -7,7 +7,7 @@ import tempfile
 import weakref
 from typing import Optional
 
-import osbuild.util.rmrf as rmrf
+from osbuild.util import ctx, rmrf
 from . import treesum
 
 
@@ -16,19 +16,6 @@ __all__ = [
 ]
 
 
-@contextlib.contextmanager
-def suppress_oserror(*errnos):
-    """A context manager that suppresses any OSError with an errno in `errnos`.
-
-    Like contextlib.suppress, but can differentiate between OSErrors.
-    """
-    try:
-        yield
-    except OSError as e:
-        if e.errno not in errnos:
-            raise e
-
-
 def mount(source, target, bind=True, ro=True, private=True, mode="0755"):
     options = []
     if bind:
@@ -145,7 +132,7 @@ class Object:
         self._check_readers()
         self._check_writer()
         self.init()
-        with suppress_oserror(errno.ENOTEMPTY, errno.EEXIST):
+        with ctx.suppress_oserror(errno.ENOTEMPTY, errno.EEXIST):
             os.rename(self._tree, destination)
         self.reset()
 
diff --git a/osbuild/tmpfs.py b/osbuild/tmpfs.py
deleted file mode 100644
index d55b7f2d..00000000
--- a/osbuild/tmpfs.py
+++ /dev/null
@@ -1,36 +0,0 @@
-
-import os
-import subprocess
-import tempfile
-
-
-__all__ = [
-    "TmpFs",
-]
-
-
-class TmpFs:
-    def __init__(self, path="/run/osbuild"):
-        self.path = path
-        self.root = None
-        self.mounted = False
-
-    def __enter__(self):
-        self.root = tempfile.mkdtemp(prefix="osbuild-tmpfs-", dir=self.path)
-        try:
-            subprocess.run(["mount", "-t", "tmpfs", "-o", "mode=0755", "tmpfs", self.root], check=True)
-            self.mounted = True
-        except subprocess.CalledProcessError:
-            os.rmdir(self.root)
-            self.root = None
-            raise
-        return self.root
-
-    def __exit__(self, exc_type, exc_value, exc_tb):
-        if not self.root:
-            return
-        if self.mounted:
-            subprocess.run(["umount", "--lazy", self.root], check=True)
-            self.mounted = False
-        os.rmdir(self.root)
-        self.root = None
diff --git a/osbuild/util/ctx.py b/osbuild/util/ctx.py
new file mode 100644
index 00000000..68c8ff45
--- /dev/null
+++ b/osbuild/util/ctx.py
@@ -0,0 +1,35 @@
+"""ContextManager Utilities
+
+This module implements helpers around python context-managers, with-statements,
+and RAII. It is meant as a supplement to `contextlib` from the python standard
+library.
+"""
+
+import contextlib
+
+
+__all__ = [
+    "suppress_oserror",
+]
+
+
+@contextlib.contextmanager
+def suppress_oserror(*errnos):
+    """Suppress OSError Exceptions
+
+    This is an extension to `contextlib.suppress()` from the python standard
+    library. It catches any `OSError` exceptions and suppresses them. However,
+    it only catches the exceptions that match the specified error numbers.
+
+    Parameters
+    ----------
+    errnos
+        A list of error numbers to match on. If none are specified, this
+        function has no effect.
+    """
+
+    try:
+        yield
+    except OSError as e:
+        if e.errno not in errnos:
+            raise e
diff --git a/schutzbot/Jenkinsfile b/schutzbot/Jenkinsfile
new file mode 100644
index 00000000..68fd19cc
--- /dev/null
+++ b/schutzbot/Jenkinsfile
@@ -0,0 +1,29 @@
+pipeline {
+    agent none
+
+    options {
+        timestamps()
+        ansiColor('xterm')
+    }
+
+    stages {
+        stage("Functional Testing") {
+            parallel {
+                stage('Fedora 31 image') {
+                    agent { label "fedora31" }
+                    environment { TEST_TYPE = "image" }
+                    steps {
+                        sh "schutzbot/run_tests.sh"
+                    }
+                }
+                stage('Fedora 32 image') {
+                    agent { label "fedora32" }
+                    environment { TEST_TYPE = "image" }
+                    steps {
+                        sh "schutzbot/run_tests.sh"
+                    }
+                }
+            }
+        }
+    }
+}


Test patch:
diff --git a/schutzbot/run_tests.sh b/schutzbot/run_tests.sh
new file mode 100755
index 00000000..c5bc945d
--- /dev/null
+++ b/schutzbot/run_tests.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+set -euxo pipefail
+
+# Read variables about the OS.
+source /etc/os-release
+
+# Create temporary directories for Ansible.
+sudo mkdir -vp /opt/ansible_{local,remote}
+sudo chmod -R 777 /opt/ansible_{local,remote}
+
+# Remove Fedora modular repositories to speed up dnf-json.
+sudo rm -rfv /etc/yum.repos.d/fedora*modular*
+
+# Ensure /tmp is mounted on tmpfs.
+sudo systemctl enable tmp.mount || \
+  sudo systemctl unmask tmp.mount && sudo systemctl start tmp.mount
+
+# Restart systemd to work around some Fedora issues in cloud images.
+sudo systemctl restart systemd-journald
+
+# Get the current journald cursor.
+export JOURNALD_CURSOR=$(sudo journalctl --quiet -n 1 --show-cursor | tail -n 1 | grep -oP 's\=.*$')
+
+# Add a function to preserve the system journal if something goes wrong.
+preserve_journal() {
+  sudo journalctl --after-cursor=${JOURNALD_CURSOR} > systemd-journald.log
+  exit 1
+}
+trap "preserve_journal" ERR
+
+# Ensure Ansible is installed.
+if ! rpm -q ansible; then
+  sudo dnf -y install ansible
+fi
+
+# Write a simple hosts file for Ansible.
+echo -e "[test_instances]\nlocalhost ansible_connection=local" > hosts.ini
+
+# Set Ansible's config file location.
+export ANSIBLE_CONFIG=ansible-osbuild/ansible.cfg
+
+# Clone the latest version of ansible-osbuild.
+
+# Get the current SHA of osbuild.
+OSBUILD_VERSION=$(git rev-parse HEAD)
+
+# Deploy the software.
+git clone https://github.com/osbuild/ansible-osbuild.git ansible-osbuild
+ansible-playbook \
+  -i hosts.ini \
+  -e osbuild_repo=${WORKSPACE} \
+  -e osbuild_version=$(git rev-parse HEAD) \
+  -e cleanup_composer_directories=yes \
+  ansible-osbuild/playbook.yml
+
+# Run the tests only on Fedora 31 for now.
+if [[ $NAME == "Fedora" ]] && [[ $VERSION_ID == "31" ]]; then
+  ansible-playbook \
+    -e workspace=${WORKSPACE} \
+    -e journald_cursor="${JOURNALD_CURSOR}" \
+    -e test_type=${TEST_TYPE:-image} \
+    -i hosts.ini \
+    ansible-osbuild/repos/osbuild-composer/jenkins/test.yml
+fi
+
+# Collect the systemd journal anyway if we made it all the way to the end.
+sudo journalctl --after-cursor=${JOURNALD_CURSOR} > systemd-journald.log
diff --git a/test/mod/test_util_ctx.py b/test/mod/test_util_ctx.py
new file mode 100644
index 00000000..0bc2e3cc
--- /dev/null
+++ b/test/mod/test_util_ctx.py
@@ -0,0 +1,43 @@
+#
+# Tests for the 'osbuild.util.ctx' module.
+#
+
+import errno
+import unittest
+
+from osbuild.util import ctx
+
+
+class TestUtilCtx(unittest.TestCase):
+    def test_suppress_oserror(self):
+        #
+        # Verify the `suppress_oserror()` function.
+        #
+
+        # Empty list and empty statement is a no-op.
+        with ctx.suppress_oserror():
+            pass
+
+        # Single errno matches raised errno.
+        with ctx.suppress_oserror(errno.EPERM):
+            raise OSError(errno.EPERM, "Operation not permitted")
+
+        # Many errnos match raised errno regardless of their order.
+        with ctx.suppress_oserror(errno.EPERM, errno.ENOENT, errno.ESRCH):
+            raise OSError(errno.EPERM, "Operation not permitted")
+        with ctx.suppress_oserror(errno.ENOENT, errno.EPERM, errno.ESRCH):
+            raise OSError(errno.EPERM, "Operation not permitted")
+        with ctx.suppress_oserror(errno.ENOENT, errno.ESRCH, errno.EPERM):
+            raise OSError(errno.EPERM, "Operation not permitted")
+
+        # Empty list re-raises exceptions.
+        with self.assertRaises(OSError):
+            with ctx.suppress_oserror():
+                raise OSError(errno.EPERM, "Operation not permitted")
+
+        # Non-matching lists re-raise exceptions.
+        with self.assertRaises(OSError):
+            with ctx.suppress_oserror(errno.ENOENT):
+                raise OSError(errno.EPERM, "Operation not permitted")
+            with ctx.suppress_oserror(errno.ENOENT, errno.ESRCH):
+                raise OSError(errno.EPERM, "Operation not permitted")