mirror of
https://github.com/kevinveenbirkenbach/docker-volume-backup.git
synced 2026-11-06 02:57:59 +00:00
feat(seed): read the database password from stdin with --password-stdin
baudolo-seed took the database password only as a positional argument, so it showed in the process list of the host that seeds the backup catalog (infinito-nexus/core #1213). --password-stdin reads it from stdin without the trailing newline. The positional argument still works for existing callers but warns on stderr that it is visible in the process list; giving the password both ways, or neither, is rejected with a usage error and writes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4214d3709a
commit
5817078643
3 files changed
+106
-2
No files matched your search
@@ -65,6 +65,32 @@ def run_seed_expect_fail(
|
||||
)
|
||||
|
||||
|
||||
def run_seed_stdin(
|
||||
csv_path: Path, instance: str, database: str, username: str, stdin: str, *extra: str
|
||||
) -> subprocess.CompletedProcess:
|
||||
"""
|
||||
Run the real CLI with --password-stdin, feeding the password on stdin.
|
||||
Any extra arguments are appended after the username.
|
||||
"""
|
||||
return subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"baudolo.seed",
|
||||
"--password-stdin",
|
||||
str(csv_path),
|
||||
instance,
|
||||
database,
|
||||
username,
|
||||
*extra,
|
||||
],
|
||||
input=stdin,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
|
||||
def read_csv_semicolon(path: Path) -> list[dict]:
|
||||
with path.open("r", encoding="utf-8", newline="") as f:
|
||||
reader = csv.DictReader(f, delimiter=";")
|
||||
@@ -195,6 +221,61 @@ class TestSeedIntegration(unittest.TestCase):
|
||||
self.assertEqual(len(rows), 1)
|
||||
self.assertEqual(rows[0]["database"], "my_db-1")
|
||||
|
||||
def test_password_stdin_stores_the_password_without_its_trailing_newline(
|
||||
self,
|
||||
) -> None:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = Path(td) / "databases.csv"
|
||||
|
||||
cp = run_seed_stdin(p, "docker.test", "appdb", "alice", "s3cr;et\n")
|
||||
|
||||
self.assertEqual(cp.returncode, 0, cp.stderr)
|
||||
self.assertNotIn("WARNING", cp.stderr)
|
||||
rows = read_csv_semicolon(p)
|
||||
self.assertEqual(len(rows), 1)
|
||||
self.assertEqual(rows[0]["username"], "alice")
|
||||
self.assertEqual(rows[0]["password"], "s3cr;et")
|
||||
|
||||
def test_password_argument_still_works_with_a_deprecation_warning(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = Path(td) / "databases.csv"
|
||||
|
||||
cp = run_seed(p, "docker.test", "appdb", "alice", "secret")
|
||||
|
||||
self.assertIn("--password-stdin", cp.stderr)
|
||||
self.assertEqual(read_csv_semicolon(p)[0]["password"], "secret")
|
||||
|
||||
def test_password_given_both_ways_is_rejected(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = Path(td) / "databases.csv"
|
||||
|
||||
cp = run_seed_stdin(p, "docker.test", "appdb", "alice", "pw\n", "pw")
|
||||
|
||||
self.assertNotEqual(cp.returncode, 0)
|
||||
self.assertFalse(p.exists(), "Should not create file on invalid input")
|
||||
|
||||
def test_password_given_no_way_is_rejected(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = Path(td) / "databases.csv"
|
||||
|
||||
cp = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"baudolo.seed",
|
||||
str(p),
|
||||
"docker.test",
|
||||
"appdb",
|
||||
"alice",
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
self.assertNotEqual(cp.returncode, 0)
|
||||
self.assertFalse(p.exists(), "Should not create file on invalid input")
|
||||
|
||||
def test_file_is_semicolon_delimited_and_has_header(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = Path(td) / "databases.csv"
|
||||
|
||||
Reference in new issue
Block a user