pixie-lab-dibs 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/.github/workflows/test.yml +21 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/CHANGELOG.md +13 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/Makefile +4 -1
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/PKG-INFO +4 -3
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/README.md +3 -2
- pixie_lab_dibs-0.1.1/scripts/service-smoke.sh +42 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/src/dibs/__init__.py +1 -1
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/src/dibs/cli.py +31 -4
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/tests/test_cli.py +26 -2
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/.github/workflows/format.yml +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/.github/workflows/publish.yml +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/.gitignore +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/.pre-commit-config.yaml +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/LICENSE +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/LICENSES/BSD-3-Clause.txt +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/REUSE.toml +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/pyproject.toml +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/src/dibs/_sources.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/src/dibs/_sshsig.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/src/dibs/_store.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/src/dibs/_users.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/src/dibs/app.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/tests/__init__.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/tests/conftest.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/tests/test_app.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/tests/test_sources.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/tests/test_store.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/tests/test_users.py +0 -0
- {pixie_lab_dibs-0.1.0 → pixie_lab_dibs-0.1.1}/uv.lock +0 -0
|
@@ -40,3 +40,24 @@ jobs:
|
|
|
40
40
|
|
|
41
41
|
- name: make build
|
|
42
42
|
run: make build
|
|
43
|
+
|
|
44
|
+
service:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v7
|
|
48
|
+
|
|
49
|
+
- uses: astral-sh/setup-uv@v7.2.1
|
|
50
|
+
|
|
51
|
+
# The pixie server dibs first ran on resolves users from files only,
|
|
52
|
+
# without nss-systemd. Match it, so a unit that relies on systemd's
|
|
53
|
+
# dynamic users fails here rather than there.
|
|
54
|
+
- name: Resolve users from /etc/passwd only
|
|
55
|
+
run: |
|
|
56
|
+
sudo sed -i 's/^passwd:.*/passwd: files/' /etc/nsswitch.conf
|
|
57
|
+
grep '^passwd' /etc/nsswitch.conf
|
|
58
|
+
|
|
59
|
+
- name: make build
|
|
60
|
+
run: make build
|
|
61
|
+
|
|
62
|
+
- name: make service-smoke
|
|
63
|
+
run: make service-smoke
|
|
@@ -10,6 +10,19 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.1.1] - 2026-10-27
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `dibs server install` over a running service now restarts it, so
|
|
18
|
+
installing again after an upgrade runs the new version. It used to
|
|
19
|
+
write the new unit and leave the old process running.
|
|
20
|
+
- The system service refused every key at `dibs register` on hosts that
|
|
21
|
+
resolve users without nss-systemd, plain Debian among them: under
|
|
22
|
+
`DynamicUser=`, `ssh-keygen` could not look its own user up. `dibs server install` now creates a `dibs` system user and runs the service
|
|
23
|
+
as it. Run `sudo dibs server install` again to update an installed
|
|
24
|
+
service.
|
|
25
|
+
|
|
13
26
|
## [0.1.0] - 2026-10-27
|
|
14
27
|
|
|
15
28
|
### Added
|
|
@@ -9,7 +9,7 @@ UV ?= uv
|
|
|
9
9
|
PRECOMMIT ?= pre-commit
|
|
10
10
|
|
|
11
11
|
.DEFAULT_GOAL := help
|
|
12
|
-
.PHONY: help format lint typecheck test build check
|
|
12
|
+
.PHONY: help format lint typecheck test build check service-smoke
|
|
13
13
|
|
|
14
14
|
help: ## Show this help
|
|
15
15
|
@grep -hE '^[a-z-]+:.*?## ' $(MAKEFILE_LIST) \
|
|
@@ -35,3 +35,6 @@ build: ## Build the sdist and wheel, and check every command the wheel installs
|
|
|
35
35
|
done
|
|
36
36
|
|
|
37
37
|
check: lint typecheck test ## Everything but format
|
|
38
|
+
|
|
39
|
+
service-smoke: ## Install the built wheel as the real service and claim against it (sudo, systemd)
|
|
40
|
+
./scripts/service-smoke.sh
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pixie-lab-dibs
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Call dibs on shared lab machines: see who is using what, claim it, release it
|
|
5
5
|
Project-URL: Homepage, https://github.com/safl/pixie-lab-dibs
|
|
6
6
|
Author-email: "Simon A. F. Lund" <os@safl.dk>
|
|
@@ -81,8 +81,9 @@ sudo dibs server install
|
|
|
81
81
|
|
|
82
82
|
The command writes a systemd unit for the `dibs` you ran, enables it
|
|
83
83
|
and starts it. `dibs server start`, `stop`, `restart`, `status` and
|
|
84
|
-
`logs` manage it from then on. The service runs as a
|
|
85
|
-
user
|
|
84
|
+
`logs` manage it from then on. The service runs as a `dibs` system
|
|
85
|
+
user, which the install creates, with write access only to
|
|
86
|
+
`/var/lib/dibs`. Without root, `dibs server install --user` installs a per-user service instead. For a quick try,
|
|
86
87
|
`dibs serve` runs the server in the foreground.
|
|
87
88
|
|
|
88
89
|
The system service can only run a `dibs` installed outside your home
|
|
@@ -59,8 +59,9 @@ sudo dibs server install
|
|
|
59
59
|
|
|
60
60
|
The command writes a systemd unit for the `dibs` you ran, enables it
|
|
61
61
|
and starts it. `dibs server start`, `stop`, `restart`, `status` and
|
|
62
|
-
`logs` manage it from then on. The service runs as a
|
|
63
|
-
user
|
|
62
|
+
`logs` manage it from then on. The service runs as a `dibs` system
|
|
63
|
+
user, which the install creates, with write access only to
|
|
64
|
+
`/var/lib/dibs`. Without root, `dibs server install --user` installs a per-user service instead. For a quick try,
|
|
64
65
|
`dibs serve` runs the server in the foreground.
|
|
65
66
|
|
|
66
67
|
The system service can only run a `dibs` installed outside your home
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# SPDX-FileCopyrightText: Simon A. F. Lund <os@safl.dk>
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
#
|
|
6
|
+
# Install the built wheel as the real systemd service, then register, log
|
|
7
|
+
# in and claim against it. This is the path the unit tests stub out, and
|
|
8
|
+
# where dibs once broke: under systemd, ssh-keygen must be able to look its
|
|
9
|
+
# own user up. Needs root through sudo and a running systemd; CI's runner
|
|
10
|
+
# has both. Run from the repository root after "make build".
|
|
11
|
+
set -eu
|
|
12
|
+
|
|
13
|
+
wheel=$(ls dist/*.whl)
|
|
14
|
+
tmp=$(mktemp -d)
|
|
15
|
+
trap 'sudo dibs server uninstall >/dev/null 2>&1 || true; rm -rf "$tmp"' EXIT
|
|
16
|
+
|
|
17
|
+
sudo env "PATH=$PATH" UV_TOOL_DIR=/opt/uv-tools UV_TOOL_BIN_DIR=/usr/local/bin \
|
|
18
|
+
uv tool install --python /usr/bin/python3 "$wheel"
|
|
19
|
+
|
|
20
|
+
sudo mkdir -p /etc/dibs
|
|
21
|
+
printf '[[thing]]\nname = "box"\nmac = "aa:bb:cc:dd:ee:ff"\n' | sudo tee /etc/dibs/dibs.toml >/dev/null
|
|
22
|
+
sudo /usr/local/bin/dibs server install --port 8090
|
|
23
|
+
|
|
24
|
+
export DIBS_URL=http://127.0.0.1:8090 XDG_CONFIG_HOME="$tmp/config"
|
|
25
|
+
for _ in $(seq 1 30); do curl -fsS "$DIBS_URL/healthz" >/dev/null 2>&1 && break; sleep 1; done
|
|
26
|
+
|
|
27
|
+
ssh-keygen -q -t ed25519 -N '' -f "$tmp/key"
|
|
28
|
+
dibs register ci -i "$tmp/key.pub"
|
|
29
|
+
sudo dibs admin approve ci
|
|
30
|
+
dibs login
|
|
31
|
+
dibs claim box --for smoke
|
|
32
|
+
dibs list | grep -qx "'ci' has claimed 'box' for 'smoke' expires in: 'never'"
|
|
33
|
+
dibs release --mine
|
|
34
|
+
|
|
35
|
+
# Installing again, as an upgrade does, must replace the running process.
|
|
36
|
+
before=$(systemctl show -p MainPID --value dibs.service)
|
|
37
|
+
sudo /usr/local/bin/dibs server install --port 8090
|
|
38
|
+
after=$(systemctl show -p MainPID --value dibs.service)
|
|
39
|
+
[ "$before" != "$after" ] || { echo "install did not restart the running service" >&2; exit 1; }
|
|
40
|
+
for _ in $(seq 1 30); do curl -fsS "$DIBS_URL/healthz" >/dev/null 2>&1 && break; sleep 1; done
|
|
41
|
+
dibs list | grep -qx "'box' is free"
|
|
42
|
+
echo "service smoke test passed"
|
|
@@ -41,6 +41,7 @@ EXIT_ERROR = 2
|
|
|
41
41
|
|
|
42
42
|
DEFAULT_PORT = 8090
|
|
43
43
|
UNIT_NAME = "dibs.service"
|
|
44
|
+
SERVICE_USER = "dibs"
|
|
44
45
|
|
|
45
46
|
_DURATION_RE = re.compile(r"^(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$")
|
|
46
47
|
_DEFAULT_KEYS = ("id_ed25519.pub", "id_ecdsa.pub", "id_rsa.pub")
|
|
@@ -405,10 +406,12 @@ ConfigurationDirectory=dibs
|
|
|
405
406
|
Restart=on-failure"""
|
|
406
407
|
target = "default.target"
|
|
407
408
|
else:
|
|
408
|
-
#
|
|
409
|
-
#
|
|
409
|
+
# A static user rather than DynamicUser=: ssh-keygen looks its own
|
|
410
|
+
# user up, and a dynamic user resolves only where nss-systemd is set
|
|
411
|
+
# up, which plain Debian does not do.
|
|
410
412
|
service = f"""ExecStart={serve}
|
|
411
|
-
|
|
413
|
+
User={SERVICE_USER}
|
|
414
|
+
Group={SERVICE_USER}
|
|
412
415
|
StateDirectory=dibs
|
|
413
416
|
ConfigurationDirectory=dibs
|
|
414
417
|
Restart=on-failure
|
|
@@ -444,6 +447,25 @@ def _systemctl(user: bool, *argv: str) -> None:
|
|
|
444
447
|
raise CliError(f"{' '.join(cmd)} failed")
|
|
445
448
|
|
|
446
449
|
|
|
450
|
+
def ensure_service_user() -> None:
|
|
451
|
+
"""Create the ``dibs`` system user the system service runs as, once."""
|
|
452
|
+
if subprocess.run(["getent", "passwd", SERVICE_USER], capture_output=True).returncode == 0:
|
|
453
|
+
return
|
|
454
|
+
cmd = [
|
|
455
|
+
"useradd",
|
|
456
|
+
"--system",
|
|
457
|
+
"--user-group",
|
|
458
|
+
"--home-dir",
|
|
459
|
+
"/var/lib/dibs",
|
|
460
|
+
"--no-create-home",
|
|
461
|
+
"--shell",
|
|
462
|
+
"/usr/sbin/nologin",
|
|
463
|
+
SERVICE_USER,
|
|
464
|
+
]
|
|
465
|
+
if subprocess.run(cmd).returncode != 0:
|
|
466
|
+
raise CliError(f"{' '.join(cmd)} failed; run dibs server install as root")
|
|
467
|
+
|
|
468
|
+
|
|
447
469
|
def cmd_server_install(args: argparse.Namespace) -> None:
|
|
448
470
|
exe = _exe()
|
|
449
471
|
if not args.user and str(exe).startswith(str(Path.home())):
|
|
@@ -455,11 +477,16 @@ def cmd_server_install(args: argparse.Namespace) -> None:
|
|
|
455
477
|
if args.print:
|
|
456
478
|
sys.stdout.write(text)
|
|
457
479
|
return
|
|
480
|
+
if not args.user:
|
|
481
|
+
ensure_service_user()
|
|
458
482
|
path = unit_path(args.user)
|
|
459
483
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
460
484
|
path.write_text(text)
|
|
461
485
|
_systemctl(args.user, "daemon-reload")
|
|
462
|
-
_systemctl(args.user, "enable",
|
|
486
|
+
_systemctl(args.user, "enable", UNIT_NAME)
|
|
487
|
+
# Restart rather than start, so installing over a running service, as
|
|
488
|
+
# an upgrade does, replaces the process with the new code and unit.
|
|
489
|
+
_systemctl(args.user, "restart", UNIT_NAME)
|
|
463
490
|
print(f"Installed {path} and started it; dibs listens on port {args.port}.")
|
|
464
491
|
if args.user:
|
|
465
492
|
print("To keep it running while you are logged out: loginctl enable-linger")
|
|
@@ -128,7 +128,7 @@ def test_system_unit_runs_this_dibs_sandboxed() -> None:
|
|
|
128
128
|
text = cli.unit_text(Path("/usr/local/bin/dibs"), user=False, host="0.0.0.0", port=8090)
|
|
129
129
|
assert "ExecStart=/usr/local/bin/dibs serve --host 0.0.0.0 --port 8090\n" in text
|
|
130
130
|
for line in (
|
|
131
|
-
"
|
|
131
|
+
"User=dibs",
|
|
132
132
|
"StateDirectory=dibs",
|
|
133
133
|
"ProtectHome=yes",
|
|
134
134
|
"WantedBy=multi-user.target",
|
|
@@ -155,7 +155,8 @@ def test_server_install_writes_enables_and_controls(
|
|
|
155
155
|
assert "--port 9000 --user" in unit.read_text()
|
|
156
156
|
assert calls == [
|
|
157
157
|
["systemctl", "--user", "daemon-reload"],
|
|
158
|
-
["systemctl", "--user", "enable", "
|
|
158
|
+
["systemctl", "--user", "enable", "dibs.service"],
|
|
159
|
+
["systemctl", "--user", "restart", "dibs.service"],
|
|
159
160
|
]
|
|
160
161
|
calls.clear()
|
|
161
162
|
cli.main(["server", "stop", "--user"])
|
|
@@ -175,6 +176,29 @@ def test_server_install_writes_enables_and_controls(
|
|
|
175
176
|
]
|
|
176
177
|
|
|
177
178
|
|
|
179
|
+
def test_system_unit_avoids_dynamic_user() -> None:
|
|
180
|
+
"""ssh-keygen needs its user to resolve, and a DynamicUser= user does
|
|
181
|
+
not resolve without nss-systemd (plain Debian has none)."""
|
|
182
|
+
text = cli.unit_text(Path("/usr/local/bin/dibs"), user=False, host="0.0.0.0", port=8090)
|
|
183
|
+
assert "DynamicUser" not in text and "User=dibs\nGroup=dibs\n" in text
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@pytest.mark.parametrize(("exists", "want_useradd"), [(True, False), (False, True)])
|
|
187
|
+
def test_service_user_is_created_once(
|
|
188
|
+
monkeypatch: pytest.MonkeyPatch, exists: bool, want_useradd: bool
|
|
189
|
+
) -> None:
|
|
190
|
+
calls: list[list[str]] = []
|
|
191
|
+
|
|
192
|
+
def fake_run(cmd: list[str], *a: Any, **kw: Any) -> subprocess.CompletedProcess[str]:
|
|
193
|
+
calls.append(cmd)
|
|
194
|
+
return subprocess.CompletedProcess(cmd, 0 if exists or cmd[0] != "getent" else 2)
|
|
195
|
+
|
|
196
|
+
monkeypatch.setattr(cli.subprocess, "run", fake_run)
|
|
197
|
+
cli.ensure_service_user()
|
|
198
|
+
assert calls[0] == ["getent", "passwd", "dibs"]
|
|
199
|
+
assert any(c[0] == "useradd" and c[-1] == "dibs" for c in calls) == want_useradd
|
|
200
|
+
|
|
201
|
+
|
|
178
202
|
def test_system_install_refuses_a_dibs_in_home(monkeypatch: pytest.MonkeyPatch, capsys) -> None:
|
|
179
203
|
monkeypatch.setattr(cli, "_exe", lambda: Path.home() / ".local" / "bin" / "dibs")
|
|
180
204
|
assert cli.main(["server", "install"]) == cli.EXIT_ERROR
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|