Repository: osbuild/osbuild
Pull request #1889 (2024-09-17 16:25:19)

stages/selinux: don't require file_contexts if labels passed
With the labels option the user is specifying the exact context they want to set on the path so it's not necessary to supply a context here. This can be also useful in the case where you want to set some labels and you haven't yet populated the tree yet.

dustymabe: > There's a [negative test for the schema validation](https://github.com/osbuild/osbuild/blob/3c9098aa3e4107bc8369316d842276c14b35b8f0/stages/test/test_selinux.py#L44-L48) that needs to be adjusted.

adjusted

Patch:
diff --git a/stages/org.osbuild.selinux b/stages/org.osbuild.selinux
index bb45298d..563d827b 100755
--- a/stages/org.osbuild.selinux
+++ b/stages/org.osbuild.selinux
@@ -8,11 +8,14 @@ from osbuild.util import selinux
 
 
 def main(tree, options):
-    file_contexts = os.path.join(f"{tree}", options["file_contexts"])
+    file_contexts = options.get("file_contexts")
     exclude_paths = options.get("exclude_paths")
-    if exclude_paths:
-        exclude_paths = [os.path.join(tree, p.lstrip("/")) for p in exclude_paths]
-    selinux.setfiles(file_contexts, os.fspath(tree), "", exclude_paths=exclude_paths)
+
+    if file_contexts:
+        file_contexts = os.path.join(f"{tree}", options["file_contexts"])
+        if exclude_paths:
+            exclude_paths = [os.path.join(tree, p.lstrip("/")) for p in exclude_paths]
+        selinux.setfiles(file_contexts, os.fspath(tree), "", exclude_paths=exclude_paths)
 
     labels = options.get("labels", {})
     for path, label in labels.items():
diff --git a/stages/org.osbuild.selinux.meta.json b/stages/org.osbuild.selinux.meta.json
index ea1bb3ef..30dbddae 100644
--- a/stages/org.osbuild.selinux.meta.json
+++ b/stages/org.osbuild.selinux.meta.json
@@ -20,8 +20,17 @@
   "schema_2": {
     "options": {
       "additionalProperties": false,
-      "required": [
-        "file_contexts"
+      "anyOf": [
+        {
+          "required": [
+            "file_contexts"
+          ]
+        },
+        {
+          "required": [
+            "labels"
+          ]
+        }
       ],
       "properties": {
         "file_contexts": {


Test patch:
diff --git a/stages/test/test_selinux.py b/stages/test/test_selinux.py
index af1e1b56..7cb51bcd 100644
--- a/stages/test/test_selinux.py
+++ b/stages/test/test_selinux.py
@@ -10,13 +10,15 @@ from osbuild import testutil
 STAGE_NAME = "org.osbuild.selinux"
 
 
-def get_test_input(test_data, implicit_file_contexts=True):
+def get_test_input(test_data, file_contexts=False, labels=False):
     test_input = {
         "type": STAGE_NAME,
         "options": {}
     }
-    if implicit_file_contexts:
+    if file_contexts:
         test_input["options"]["file_contexts"] = "some-context"
+    if labels:
+        test_input["options"]["labels"] = {"/path/to/file": "label_to_apply"}
 
     test_input["options"].update(test_data)
     return test_input
@@ -33,7 +35,7 @@ def get_test_input(test_data, implicit_file_contexts=True):
     ({"force_autorelabel": "foo"}, "'foo' is not of type 'boolean'"),
 ])
 def test_schema_validation_selinux(stage_schema, test_data, expected_err):
-    res = stage_schema.validate(get_test_input(test_data))
+    res = stage_schema.validate(get_test_input(test_data, file_contexts=True, labels=False))
     if expected_err == "":
         assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}"
     else:
@@ -41,11 +43,15 @@ def test_schema_validation_selinux(stage_schema, test_data, expected_err):
         testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1)
 
 
-def test_schema_validation_selinux_file_context_required(stage_schema):
-    res = stage_schema.validate(get_test_input({}, implicit_file_contexts=False))
+def test_schema_validation_selinux_required_options(stage_schema):
+    res = stage_schema.validate(get_test_input({}, file_contexts=False, labels=False))
     assert res.valid is False
-    expected_err = "'file_contexts' is a required property"
+    expected_err = "{} is not valid under any of the given schemas"
     testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1)
+    res = stage_schema.validate(get_test_input({}, file_contexts=True, labels=False))
+    assert res.valid is True
+    res = stage_schema.validate(get_test_input({}, file_contexts=False, labels=True))
+    assert res.valid is True
 
 
 @patch("osbuild.util.selinux.setfiles")