fix: Say how long a deploy waited and whether logs existed #26
No reviewers
Labels
No labels
priority/P0
priority/P1
priority/P2
priority/P3
size/L
size/M
size/S
size/XL
size/XS
state/done
state/in-progress
state/in-review
state/needs-refinement
state/ready
type/bug
type/chore
type/feature
type/refactor
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
amtronics/platform-actions!26
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/23-deploy-failure-message"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Issue
Closes #23 · Follows #24
Problem
#23 and #22 are the same defect found twice an hour apart, from opposite ends: the pipeline side saw two red deploys on
ahmad/portfoliowhile production served the version they had just deployed, and the host side had the number that explains it — the container took 169 seconds to bind, and the sixty-second wait gave up 63 seconds before the application had opened its port.v3.5.2 (#24) fixed the wait itself. What it did not fix is the other half of #23's complaint: the failure said the container "did not become healthy" and then printed fifty lines of an empty log, which is the least informative output this step can produce.
Solution
Every outcome now reports how long it waited, and an empty log tail says so.
health-timeoutwould have helped.report_logsprints the tail when there is one and says(the container has produced no log output at all)when there is not — which is precisely the case of a container that died before writing anything.Review notes
Measured against a stub
docker, driving each branch and both log modes:The second line is worth a second look:
after 9s (budget 8s)is the overshoot the elapsed figure exists to expose.One acceptance criterion is deliberately not met, and I would rather say so than quietly pass it. Criterion 1 asks that a caller setting no new input behaves as it does today; v3.5.2 changed exactly that, taking the default from 60s to 600s. Keeping the old default would have left every repository on the broken behaviour until each one opted out. If you would rather have it back, it is a one-line change.
Risks and trade-offs
report_logscaptures the tail into a variable to test whether it is empty, so a very large tail is held in memory briefly. It is bounded by--tail 50.WIP: fix: Say how long a deploy waited and whether logs existedto fix: Say how long a deploy waited and whether logs existedAll five acceptance criteria on #23 assessed, four met and one deliberately not.
Verified by running the block against a stub
dockerdriving each branch and both log modes:api-1 is healthy after 5s.(exit 0)did not become healthy after 8s (budget 8s, last status: starting)then(the container has produced no log output at all)2>&1capturereported unhealthy after 0sthen the tailNo such containernow reaches the log, which is a better answer than an empty tailAC3 (container, elapsed, last status), AC4 (empty log said rather than shown) and AC5 (
health-timeoutdocumented atREADME.md:107) all hold. AC2 was delivered by #24 and is untouched here. On AC1 — that a caller setting no input behaves as before — your read is right and saying so in the description was the right call: #24 changed the default deliberately, and keeping 60s would have left every repository on the broken behaviour until it opted out. I would not revert it.printf '%s\n' "${LOGS}"rather thanechois worth keeping; a log line containing a%or a backslash would otherwise be mangled on its way to the reader.One finding, inline. It is not introduced by this change — it came in with the
unknowngrace in #24 and is already live onv3— but this PR rewrites those exact lines, so it is the natural place to raise it. Had this PR introduced it I would have requested changes.@ -366,4 +380,4 @@exit 1fiif [ "${STATUS}" = "unknown" ] && [ "$(date +%s)" -ge "${UNKNOWN_GRACE}" ]; thenA single failed
docker inspectafter the grace fails the whole deploy, and blames the wrong thing.UNKNOWN_GRACEis an absolute deadline set once atline 369, not a run of consecutive observations. Past it, the firstunknownseen at any point exits 1. Andunknownis not only "no such container" —|| echo unknownon thedocker inspectline turns any non-zero exit into that status, a daemon that is briefly unreachable or too slow to answer included.Measured, with a stub whose
inspectfails for two seconds at t=36 and otherwise reportsstartinguntil it goes healthy at 60s, budget 600s:The container exists, has a
HEALTHCHECK, is reportingstarting, and would have been healthy 23 seconds later. It is failed at 37s, told to check a name that is correct, and its own log — printed right underneath — shows it working.That is the failure class #22 and #23 exist to remove: a red deploy for a deployment that succeeded, now with a misleading diagnosis attached. I cannot tell you how often
docker inspectactually fails transiently onapps; what I can say is that the condition it is sensitive to is host load, which is the same condition that motivated the whole change (io_fullavg300 65.8, load 6.6 on #23's timeline) — so the new failure mode is most likely exactly where the old one was.Fix — make the grace consecutive by resetting it whenever a status does arrive:
A name that never resolves is
unknownfrom the first poll, so it still fails at 30s — the behaviour #24 was after is unchanged. A blip costs 30 more seconds of waiting instead of the deployment.Since this is already merged behaviour affecting every consumer of
v3, it may deserve its own issue rather than riding along here — your call, given this PR is already editing the block.fix: Say how long a deploy waited and whether logs existedto WIP: fix: Say how long a deploy waited and whether logs existedFixed in
7c2e8ef. You are right, and it is worth being explicit that this is a defect in code I added yesterday, not one inherited: the grace was an absolute deadline, so past it a singleunknown— which|| echo unknownproduces for any non-zerodocker inspect, including a daemon briefly too loaded to answer — failed a container that was reportingstarting.The grace now resets whenever a status arrives, so it measures a run of consecutive unknowns rather than a point in time.
I built two loops differing only in that semantics and ran both against the same stub, so the comparison isolates the change:
starting×3, oneunknown, then healthyThe first row is your finding reproduced; the second is the check that the fix does not weaken the case the grace was written for.
Your point about where this bites is the part I would underline: the condition that makes
docker inspectflaky is host load, which is the same condition that motivated the whole change. The new failure mode would have landed exactly where the old one did — and with a diagnosis pointing at a container name that was correct, which is worse than the failure it replaced.Ready again immediately rather than waiting for your next look, since leaving it
WIP:is what stalled #24.WIP: fix: Say how long a deploy waited and whether logs existedto fix: Say how long a deploy waited and whether logs existedApproving. Re-reviewed the delta only (
0e8ac68→7c2e8ef); everything else was verified in review 102.The grace is now a run of consecutive
unknownobservations, reset whenever a status does arrive. Measured against a stubdockerdriving each path:unknownfrom the first poll)starting, then vanishes at t=20 and stays goneunhealthystartingthroughout, budget 12slast status: startingThe third row is the one that shows the reset is a real consecutive run rather than a single tolerated blip, and it is the case I had not tested when I raised this — a container that genuinely disappears mid-wait still fails, and reasonably promptly.
You are right to call it a defect in code you added rather than an inherited one; I framed it as inherited because I was reading #26's diff against a merged base, and that was the less accurate way to put it.
#23's criteria stand where review 102 left them: elapsed and last status in the failure message, the empty-log line,health-timeoutatREADME.md:107, the input itself unchanged. The unchanged-default criterion remains knowingly unmet and declared in the description, which I still think is the right call.Same two caveats as #24, neither a defect here: merging moves
v3for expiro, portfolio and trip, and this repository still runs no pull-request pipeline (platform-actions#25), so nothing has executed any of it outside stubs. Approval does not merge.