ADR 0059's recovery path: the pieces that run when the host will not start. internal/upgrade -- two facts, neither of them the host judging its health. Whether the executable this process started from has been replaced on disk, and which version last completed a reconcile. The first design was wrong and the tests caught it, not review. It asked /proc/self/exe whether it was marked deleted. That is Linux procfs behaviour rather than a fact about files, and it catches only unlink -- a binary swapped by rename onto the same path reads as untouched, which is exactly what a package manager does. Now the identity is captured at start and compared later: no procfs, and neither case missed. known-good is one bare line. The reader is a shell script on a machine where the host is failing to start, so it must not need a parser to be present and working. Written only after a clean apply, which is the whole claim -- not health, because a disconnected node is ordinary and a failing resource is the machine's problem rather than the binary's. packaging/ -- the unit, the rollback unit, and the rollback script. The script shares no code with the host and calls none of it: a binary that cannot start cannot be its own recovery. POSIX sh, nothing that has to be installed. The unit carries Restart=always with a comment saying why on-failure would break every upgrade. Both are tested and both sets of tests were confirmed to bite. Injecting five faults broke exactly the intended tests -- except one, and chasing why it did not found a placebo assertion I had written: `check "exits zero" ... "0" "0"` compares a literal to itself and can never fail. Replaced with the real exit code, after which the injection bites. Also caught: an injection that produced a build failure rather than a test failure, which my grep read as "no failure". Re-run so it compiled, and the test did bite. The script test runs in `make check`, so it is a gate rather than something that was run once. Verified against the real binary: known-good is written beside the store after a clean apply and is NOT written after a failed one.
45 lines
1.6 KiB
Makefile
45 lines
1.6 KiB
Makefile
# The gate. Green is the definition of done (novox/hq how-we-build §5).
|
|
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo development)
|
|
LDFLAGS := -s -w -X main.version=$(VERSION)
|
|
|
|
# The bundle a host carries is built INTO it (novox/hq ADR 0038, ADR 0041): a host that needed
|
|
# a second file to arrive with it is not "copy it and run it".
|
|
BUNDLE ?=
|
|
|
|
.PHONY: check test vet fmt build clean host
|
|
|
|
check: fmt vet test packaging-test build
|
|
|
|
packaging-test:
|
|
@./packaging/rollback_test.sh
|
|
|
|
fmt:
|
|
@test -z "$$(gofmt -l . )" || { echo "unformatted:"; gofmt -l . ; exit 1; }
|
|
|
|
vet:
|
|
go vet ./...
|
|
|
|
# Structure and logic, and the same checks against this machine. The boundary is never mocked.
|
|
test:
|
|
go test ./... -count=1
|
|
|
|
# A default build carries no bundle and refuses to reconcile, which is the honest state for a
|
|
# host nobody has told what a substrate is.
|
|
build:
|
|
CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o mesh-host ./cmd/mesh-host
|
|
|
|
# A host for a real machine, carrying a real bundle: make host BUNDLE=path/to/substrate.lock
|
|
host:
|
|
@test -n "$(BUNDLE)" || { echo "BUNDLE= is required; a host with no bundle cannot raise a first node"; exit 1; }
|
|
@test -f "$(BUNDLE)" || { echo "no such bundle: $(BUNDLE)"; exit 1; }
|
|
@cp internal/bundle/substrate.lock internal/bundle/substrate.lock.default
|
|
@cp "$(BUNDLE)" internal/bundle/substrate.lock
|
|
@CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o mesh-host ./cmd/mesh-host; \
|
|
status=$$?; \
|
|
mv internal/bundle/substrate.lock.default internal/bundle/substrate.lock; \
|
|
exit $$status
|
|
@echo "built carrying $(BUNDLE)"
|
|
|
|
clean:
|
|
rm -f mesh-host
|