← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad-buildd:lpcraft-as-buildd-user into launchpad-buildd:master

 

Colin Watson has proposed merging ~cjwatson/launchpad-buildd:lpcraft-as-buildd-user into launchpad-buildd:master.

Commit message:
Run lpcraft as the buildd user to allow nvidia.runtime=true to work

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad-buildd/+git/launchpad-buildd/+merge/436283

LXC's `nvidia` hook refuses to run if `both 0 0` is set in LXD's `raw.idmap` key (mapping UID/GID 0 outside the container to UID/GID 0 inside).  I'm not exactly sure why that is, but running `lpcraft` as a non-root user is fairly straightforward, so just do that to work around it.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad-buildd:lpcraft-as-buildd-user into launchpad-buildd:master.
diff --git a/debian/changelog b/debian/changelog
index 867c9f5..61afd5a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,6 +2,7 @@ launchpad-buildd (227) UNRELEASED; urgency=medium
 
   * Tolerate receiving "builder_constraints": None.
   * Check the appropriate server.key path for the LXD snap.
+  * Run lpcraft as the buildd user to allow nvidia.runtime=true to work.
 
  -- Colin Watson <cjwatson@xxxxxxxxxx>  Tue, 24 Jan 2023 13:13:27 +0000
 
diff --git a/lpbuildd/target/run_ci.py b/lpbuildd/target/run_ci.py
index e75cdc9..62757e2 100644
--- a/lpbuildd/target/run_ci.py
+++ b/lpbuildd/target/run_ci.py
@@ -157,6 +157,16 @@ class RunCI(BuilderProxyOperationMixin, Operation):
             help="perform malware scans on output files",
         )
 
+    def run_build_command(self, args, **kwargs):
+        # Run build commands as the `buildd` user, since `lpcraft` can only
+        # start containers with `nvidia.runtime=true` if it's run as a
+        # non-root user.
+        super().run_build_command(
+            ["runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--"]
+            + args,
+            **kwargs,
+        )
+
     def run_job(self):
         logger.info("Running job phase...")
         env = self.build_proxy_environment(proxy_url=self.args.proxy_url)
@@ -167,6 +177,7 @@ class RunCI(BuilderProxyOperationMixin, Operation):
         job_output_path = os.path.join(
             output_path, self.args.job_name, str(self.args.job_index))
         self.backend.run(["mkdir", "-p", job_output_path])
+        self.backend.run(["chown", "-R", "buildd:buildd", output_path])
         lpcraft_args = [
             "lpcraft",
             "-v",
diff --git a/lpbuildd/target/tests/test_run_ci.py b/lpbuildd/target/tests/test_run_ci.py
index daee43f..3eac5fc 100644
--- a/lpbuildd/target/tests/test_run_ci.py
+++ b/lpbuildd/target/tests/test_run_ci.py
@@ -388,7 +388,9 @@ class TestRunCI(TestCase):
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
             RanCommand(["mkdir", "-p", "/build/output/test/0"]),
+            RanCommand(["chown", "-R", "buildd:buildd", "/build/output"]),
             RanBuildCommand([
+                "runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--",
                 "/bin/bash", "-o", "pipefail", "-c",
                 "lpcraft -v run-one --output-directory /build/output test 0 2>&1 "  # noqa: E501
                 "| tee /build/output/test/0/log",
@@ -412,7 +414,9 @@ class TestRunCI(TestCase):
             }
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
             RanCommand(["mkdir", "-p", "/build/output/test/0"]),
+            RanCommand(["chown", "-R", "buildd:buildd", "/build/output"]),
             RanBuildCommand([
+                "runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--",
                 "/bin/bash", "-o", "pipefail", "-c",
                 "lpcraft -v run-one --output-directory /build/output test 0 2>&1 "  # noqa: E501
                 "| tee /build/output/test/0/log",
@@ -431,7 +435,9 @@ class TestRunCI(TestCase):
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
             RanCommand(["mkdir", "-p", "/build/output/test/0"]),
+            RanCommand(["chown", "-R", "buildd:buildd", "/build/output"]),
             RanBuildCommand([
+                "runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--",
                 "/bin/bash", "-o", "pipefail", "-c",
                 "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
@@ -456,7 +462,9 @@ class TestRunCI(TestCase):
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
             RanCommand(["mkdir", "-p", "/build/output/test/0"]),
+            RanCommand(["chown", "-R", "buildd:buildd", "/build/output"]),
             RanBuildCommand([
+                "runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--",
                 "/bin/bash", "-o", "pipefail", "-c",
                 "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
@@ -479,7 +487,9 @@ class TestRunCI(TestCase):
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
             RanCommand(["mkdir", "-p", "/build/output/test/0"]),
+            RanCommand(["chown", "-R", "buildd:buildd", "/build/output"]),
             RanBuildCommand([
+                "runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--",
                 "/bin/bash", "-o", "pipefail", "-c",
                 "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
@@ -501,7 +511,9 @@ class TestRunCI(TestCase):
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
             RanCommand(["mkdir", "-p", "/build/output/test/0"]),
+            RanCommand(["chown", "-R", "buildd:buildd", "/build/output"]),
             RanBuildCommand([
+                "runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--",
                 "/bin/bash", "-o", "pipefail", "-c",
                 "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
@@ -522,7 +534,9 @@ class TestRunCI(TestCase):
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
             RanCommand(["mkdir", "-p", "/build/output/test/0"]),
+            RanCommand(["chown", "-R", "buildd:buildd", "/build/output"]),
             RanBuildCommand([
+                "runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--",
                 "/bin/bash", "-o", "pipefail", "-c",
                 "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
@@ -530,7 +544,8 @@ class TestRunCI(TestCase):
                 "| tee /build/output/test/0/log",
                 ], cwd="/build/tree"),
             RanBuildCommand(
-                ["clamscan", "--recursive", "/build/output/test/0"],
+                ["runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--",
+                 "clamscan", "--recursive", "/build/output/test/0"],
                 cwd="/build/tree"),
             ]))
 
@@ -538,7 +553,7 @@ class TestRunCI(TestCase):
         class FailClamscan(FakeMethod):
             def __call__(self, run_args, *args, **kwargs):
                 super().__call__(run_args, *args, **kwargs)
-                if run_args[0] == "clamscan":
+                if run_args[0] == "runuser" and "clamscan" in run_args:
                     raise subprocess.CalledProcessError(1, run_args)
 
         self.useFixture(FakeLogger())
@@ -563,7 +578,9 @@ class TestRunCI(TestCase):
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
             RanCommand(["mkdir", "-p", "/build/output/test/0"]),
+            RanCommand(["chown", "-R", "buildd:buildd", "/build/output"]),
             RanBuildCommand([
+                "runuser", "-u", "buildd", "-g", "buildd", "-G", "lxd", "--",
                 "/bin/bash", "-o", "pipefail", "-c",
                 "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
@@ -590,7 +607,7 @@ class TestRunCI(TestCase):
         class FailInstall(FakeMethod):
             def __call__(self, run_args, *args, **kwargs):
                 super().__call__(run_args, *args, **kwargs)
-                if run_args[0] == "/bin/bash":
+                if run_args[0] == "runuser" and "/bin/bash" in run_args:
                     raise subprocess.CalledProcessError(1, run_args)
 
         self.useFixture(FakeLogger())