From e616b4c1200804df5df98429b05b62b5366eda87 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Wed, 22 Apr 2026 14:25:19 -0400 Subject: [PATCH 1/2] so-telegraf-cred: make executable and harden error handling so-telegraf-cred was committed with mode 644, causing `so-telegraf-cred add "$MINION_ID"` in so-minion's add_telegraf_to_minion to fail with "Permission denied" and log "Failed to provision postgres telegraf cred for ". Mark it executable. Also bail early in seed_creds_file if mkdir/printf/chmod fail, and in so-yaml.py loadYaml surface a clear stderr message with the filename instead of an unhandled FileNotFoundError traceback. --- salt/manager/tools/sbin/so-telegraf-cred | 8 ++++---- salt/manager/tools/sbin/so-yaml.py | 13 ++++++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) mode change 100644 => 100755 salt/manager/tools/sbin/so-telegraf-cred diff --git a/salt/manager/tools/sbin/so-telegraf-cred b/salt/manager/tools/sbin/so-telegraf-cred old mode 100644 new mode 100755 index b2b1ba030..61d718499 --- a/salt/manager/tools/sbin/so-telegraf-cred +++ b/salt/manager/tools/sbin/so-telegraf-cred @@ -21,11 +21,11 @@ usage() { } seed_creds_file() { - mkdir -p "$(dirname "$CREDS")" + mkdir -p "$(dirname "$CREDS")" || return 1 if [[ ! -f "$CREDS" ]]; then - (umask 027 && printf 'telegraf:\n postgres_creds: {}\n' > "$CREDS") + (umask 027 && printf 'telegraf:\n postgres_creds: {}\n' > "$CREDS") || return 1 chown socore:socore "$CREDS" 2>/dev/null || true - chmod 640 "$CREDS" + chmod 640 "$CREDS" || return 1 fi } @@ -36,7 +36,7 @@ MID=$2 case "$OP" in add) SAFE=$(echo "$MID" | tr '.-' '__' | tr '[:upper:]' '[:lower:]') - seed_creds_file + seed_creds_file || exit 1 if so-yaml.py get -r "$CREDS" "telegraf.postgres_creds.${MID}.user" >/dev/null 2>&1; then exit 0 fi diff --git a/salt/manager/tools/sbin/so-yaml.py b/salt/manager/tools/sbin/so-yaml.py index 98d2bb8f9..d0d5209f9 100755 --- a/salt/manager/tools/sbin/so-yaml.py +++ b/salt/manager/tools/sbin/so-yaml.py @@ -39,9 +39,16 @@ def showUsage(args): def loadYaml(filename): - file = open(filename, "r") - content = file.read() - return yaml.safe_load(content) + try: + with open(filename, "r") as file: + content = file.read() + return yaml.safe_load(content) + except FileNotFoundError: + print(f"File not found: {filename}", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"Error reading file {filename}: {e}", file=sys.stderr) + sys.exit(1) def writeYaml(filename, content): From d5c0ec4404ff9d93c867d2d9c86fc8c8deae8c19 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Wed, 22 Apr 2026 14:30:51 -0400 Subject: [PATCH 2/2] so-yaml_test: cover loadYaml error paths Exercises the FileNotFoundError and generic-exception branches added to loadYaml in the previous commit, restoring 100% coverage required by the build. --- salt/manager/tools/sbin/so-yaml_test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/salt/manager/tools/sbin/so-yaml_test.py b/salt/manager/tools/sbin/so-yaml_test.py index 2da8a0be9..56581f7e3 100644 --- a/salt/manager/tools/sbin/so-yaml_test.py +++ b/salt/manager/tools/sbin/so-yaml_test.py @@ -973,3 +973,21 @@ class TestReplaceListObject(unittest.TestCase): expected = "key1:\n- id: '1'\n status: updated\n- id: '2'\n status: inactive\n" self.assertEqual(actual, expected) + + +class TestLoadYaml(unittest.TestCase): + + def test_load_yaml_missing_file(self): + with patch('sys.exit', new=MagicMock()) as sysmock: + with patch('sys.stderr', new=StringIO()) as mock_stderr: + soyaml.loadYaml("/tmp/so-yaml_test-does-not-exist.yaml") + sysmock.assert_called_with(1) + self.assertIn("File not found:", mock_stderr.getvalue()) + + def test_load_yaml_read_error(self): + with patch('sys.exit', new=MagicMock()) as sysmock: + with patch('sys.stderr', new=StringIO()) as mock_stderr: + with patch('builtins.open', side_effect=PermissionError("denied")): + soyaml.loadYaml("/tmp/so-yaml_test-unreadable.yaml") + sysmock.assert_called_with(1) + self.assertIn("Error reading file", mock_stderr.getvalue())