Linux Netfilter development
 help / color / mirror / Atom feed
* [PATCH nft] tests: shell: drop metainfo from the json dump comparison
@ 2026-09-11 14:59 Avinash Duduskar
  0 siblings, 0 replies; only message in thread
From: Avinash Duduskar @ 2026-09-11 14:59 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Pablo Neira Ayuso, Phil Sutter

The comparison reads nothing from the metainfo object, but 431 dumps carry
one, so bumping json_schema_version would mean regenerating all of them.

Add a helper that drops the object and apply it to both sides of the dump
diff. Nothing else changes: json-pretty.sh stays as it is, so what nft
emits, what `nft -j --check` is fed and what DUMPGEN writes are untouched.

Nothing then asserts that nft still emits metainfo, so add a test. It also
pins that the jq and python3 branches agree and that both reject a
malformed ruleset, which a host with jq never exercises.

Link: https://lore.kernel.org/netfilter-devel/amnGEhXHD5175bxC@orbyte.nwl.cc/
Suggested-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Avinash Duduskar <avinash.duduskar@gmail.com>
---
 tests/shell/helpers/json-strip-metainfo.sh    | 39 +++++++++++
 tests/shell/helpers/test-wrapper.sh           | 24 ++++++-
 tests/shell/testcases/json/0009metainfo_0     | 69 +++++++++++++++++++
 .../json/dumps/0009metainfo_0.nodump          |  0
 4 files changed, 129 insertions(+), 3 deletions(-)
 create mode 100755 tests/shell/helpers/json-strip-metainfo.sh
 create mode 100755 tests/shell/testcases/json/0009metainfo_0
 create mode 100644 tests/shell/testcases/json/dumps/0009metainfo_0.nodump

diff --git a/tests/shell/helpers/json-strip-metainfo.sh b/tests/shell/helpers/json-strip-metainfo.sh
new file mode 100755
index 00000000..0115250d
--- /dev/null
+++ b/tests/shell/helpers/json-strip-metainfo.sh
@@ -0,0 +1,39 @@
+#!/bin/bash -e
+
+exec_strip() {
+	# `jq` and the python3 fallback must produce the same output. Both
+	# reject a non-array "nftables" rather than reshaping it. They are not
+	# equivalent on every malformed input, only on what the test pins, and
+	# only nft's own output reaches this.
+
+	if command -v jq &>/dev/null ; then
+		exec jq '
+			if (.nftables | type) != "array" then
+				error("nftables is not an array")
+			else
+				.nftables |= map(select(has("metainfo") | not))
+			end'
+	fi
+
+	# Fallback to python3.
+	exec python3 -c '
+import json
+import sys
+
+parsed = json.load(sys.stdin)
+if not isinstance(parsed.get("nftables"), list):
+    sys.exit("nftables is not an array")
+parsed["nftables"] = [x for x in parsed["nftables"] if "metainfo" not in x]
+print(json.dumps(parsed, indent=2))
+'
+}
+
+[ "$#" -le 1 ] || { echo "At most one argument supported" ; exit 1 ; }
+
+if [ "$#" -eq 1 ] ; then
+	# One argument passed. This must be a JSON file.
+	[ -f "$1" ] || { echo "File \"$1\" does not exist" ; exit 1 ; }
+	exec_strip < "$1"
+fi
+
+exec_strip
diff --git a/tests/shell/helpers/test-wrapper.sh b/tests/shell/helpers/test-wrapper.sh
index 78a01d56..49b5d66e 100755
--- a/tests/shell/helpers/test-wrapper.sh
+++ b/tests/shell/helpers/test-wrapper.sh
@@ -54,6 +54,12 @@ json_pretty() {
 	"$NFT_TEST_BASEDIR/helpers/json-pretty.sh" "$@" 2>&1 || :
 }
 
+json_strip_metainfo() {
+	# No `|| :` here. A failure must fail the dump check, not silently
+	# leave a truncated file for the diff to compare.
+	"$NFT_TEST_BASEDIR/helpers/json-strip-metainfo.sh" "$@"
+}
+
 TEST="$1"
 TESTBASE="$(basename "$TEST")"
 TESTDIR="$(dirname "$TEST")"
@@ -228,11 +234,23 @@ if [ "$rc_test" -ne 77 -a "$dump_written" != y ] ; then
 		diff_check_setcount "$DUMPFILE" "$NFT_TEST_TESTTMPDIR/ruleset-after"
 	fi
 	if [ "$NFT_TEST_HAVE_json" != n -a -f "$JDUMPFILE" ] ; then
-		if ! $DIFF -u "$JDUMPFILE" "$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty" &> "$NFT_TEST_TESTTMPDIR/ruleset-diff.json" ; then
-			show_file "$NFT_TEST_TESTTMPDIR/ruleset-diff.json" "Failed \`$DIFF -u \"$JDUMPFILE\" \"$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty\"\`" >> "$NFT_TEST_TESTTMPDIR/rc-failed-dump"
+		# Drop metainfo from both sides: the comparison ignores it, so
+		# a json_schema_version bump needs no dump regeneration.
+		rc_strip=0
+		json_strip_metainfo "$JDUMPFILE" \
+			> "$NFT_TEST_TESTTMPDIR/dump-expected.json-stripped" \
+			2> "$NFT_TEST_TESTTMPDIR/strip-err" || rc_strip=1
+		json_strip_metainfo "$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty" \
+			> "$NFT_TEST_TESTTMPDIR/ruleset-after.json-stripped" \
+			2>> "$NFT_TEST_TESTTMPDIR/strip-err" || rc_strip=1
+		if [ "$rc_strip" -ne 0 ] ; then
+			show_file "$NFT_TEST_TESTTMPDIR/strip-err" "Command \`json-strip-metainfo.sh\` failed" >> "$NFT_TEST_TESTTMPDIR/rc-failed-dump"
+			rc_dump=1
+		elif ! $DIFF -u "$NFT_TEST_TESTTMPDIR/dump-expected.json-stripped" "$NFT_TEST_TESTTMPDIR/ruleset-after.json-stripped" &> "$NFT_TEST_TESTTMPDIR/ruleset-diff.json" ; then
+			show_file "$NFT_TEST_TESTTMPDIR/ruleset-diff.json" "Failed \`$DIFF -u \"$NFT_TEST_TESTTMPDIR/dump-expected.json-stripped\" \"$NFT_TEST_TESTTMPDIR/ruleset-after.json-stripped\"\` (metainfo stripped from \"$JDUMPFILE\" and the live dump)" >> "$NFT_TEST_TESTTMPDIR/rc-failed-dump"
 			rc_dump=1
 		else
-			rm -f "$NFT_TEST_TESTTMPDIR/ruleset-diff.json"
+			rm -f "$NFT_TEST_TESTTMPDIR/ruleset-diff.json" "$NFT_TEST_TESTTMPDIR/strip-err"
 		fi
 	fi
 fi
diff --git a/tests/shell/testcases/json/0009metainfo_0 b/tests/shell/testcases/json/0009metainfo_0
new file mode 100755
index 00000000..63b726a0
--- /dev/null
+++ b/tests/shell/testcases/json/0009metainfo_0
@@ -0,0 +1,69 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_json)
+
+# The dump comparison strips the metainfo object, so nothing else asserts
+# that nft still emits one. Both branches of the strip helper are exercised
+# here too, because a host with jq never runs the python3 fallback. The
+# ruleset below is scaffolding for those checks, hence the .nodump stub.
+
+set -e
+
+STRIP="$NFT_TEST_BASEDIR/helpers/json-strip-metainfo.sh"
+
+TMP="$NFT_TEST_TESTTMPDIR/metainfo"
+mkdir -p "$TMP"
+
+$NFT flush ruleset
+$NFT add table ip t
+$NFT add chain ip t c
+
+$NFT -j list ruleset > "$TMP/ruleset.json"
+
+if ! grep -q '"metainfo"' "$TMP/ruleset.json" ; then
+	echo "E: nft no longer emits a metainfo object" >&2
+	exit 1
+fi
+if ! grep -q '"json_schema_version"' "$TMP/ruleset.json" ; then
+	echo "E: metainfo carries no json_schema_version" >&2
+	exit 1
+fi
+
+"$STRIP" "$TMP/ruleset.json" > "$TMP/stripped.json"
+
+if grep -q '"metainfo"' "$TMP/stripped.json" ; then
+	echo "E: metainfo survived the strip" >&2
+	cat "$TMP/stripped.json" >&2
+	exit 1
+fi
+if ! grep -q '"table"' "$TMP/stripped.json" ; then
+	echo "E: the strip removed more than metainfo" >&2
+	cat "$TMP/stripped.json" >&2
+	exit 1
+fi
+
+# Both branches must agree. Hide jq so the fallback runs.
+if command -v python3 &>/dev/null ; then
+	mkdir -p "$TMP/nojq"
+	ln -s "$(command -v python3)" "$TMP/nojq/python3"
+	PATH="$TMP/nojq" "$STRIP" "$TMP/ruleset.json" > "$TMP/stripped-python3.json"
+	if ! cmp "$TMP/stripped.json" "$TMP/stripped-python3.json" ; then
+		echo "E: jq and python3 branches disagree" >&2
+		diff -u "$TMP/stripped.json" "$TMP/stripped-python3.json" >&2 || :
+		exit 1
+	fi
+fi
+
+# Malformed input must fail, not be silently reshaped.
+for bad in '{"nftables":{"a":1}}' '{"a":1}' ; do
+	if echo "$bad" | "$STRIP" > /dev/null 2>&1 ; then
+		echo "E: strip accepted malformed input: $bad" >&2
+		exit 1
+	fi
+	if command -v python3 &>/dev/null ; then
+		if echo "$bad" | PATH="$TMP/nojq" "$STRIP" > /dev/null 2>&1 ; then
+			echo "E: python3 branch accepted malformed input: $bad" >&2
+			exit 1
+		fi
+	fi
+done
diff --git a/tests/shell/testcases/json/dumps/0009metainfo_0.nodump b/tests/shell/testcases/json/dumps/0009metainfo_0.nodump
new file mode 100644
index 00000000..e69de29b

base-commit: fcef13a358a0755a0bd980910d47989b064a756d
-- 
2.55.0


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-11 14:59 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-11 14:59 [PATCH nft] tests: shell: drop metainfo from the json dump comparison Avinash Duduskar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox