Skip to content

fix(provisioner/terraform): interrupt terraform again before killing it - #31685

Merged
ethanndickson merged 3 commits into
mainfrom
terraform-second-interrupt
Nov 3, 2026
Merged

ethanndickson merged 3 commits into
mainfrom
terraform-second-interrupt

Conversation

@ethanndickson

@ethanndickson ethanndickson commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Relates to #14726
Relates to https://linear.app/codercom/issue/ENG-745
Relates to https://linear.app/codercom/issue/CORE-807

I tracked a run of text file busy failures on dogfood down to a single provisioner pod. A coder-kata build hung creating a kubernetes_pod_v1 and was cancelled. We sent terraform a SIGINT, waited out the 3 minute ExitTimeout, and then SIGKILLed it. The kill skips terraform's plugin cleanup, and providers ignore SIGINT, so the idle terraform-provider-coder it had started just kept running. coder-kata has no lock file, so each terraform init reinstalls the provider into the shared plugin cache, and every coder-kata build on that pod afterwards failed with:

Error: Failed to install provider
Error while installing coder/coder v2.20.0: open /tmp/coder/registry.terraform.io/coder/coder/2.20.0/linux_amd64/terraform-provider-coder_v2.20.0: text file busy

To fix, we'll interrupt terraform a second time instead of killing it straight away. On a second interrupt, terraform cancels the operation and returns within 5 seconds (meta.go), which lets its deferred plugin.CleanupClients() stop the providers (main.go). If terraform is still running 10 seconds later, we kill it like before.

This is the same cmd.Cancel and cmd.WaitDelay approach terraform-exec uses to stop terraform (cmd.go), the only difference being that we've already sent the first interrupt by the time killCtx is done. I checked this against a real terraform plan stuck in a data source: it kept running after the first SIGINT, and exited about 50ms after the second, with the provider gone.

Terraform does warn that data loss may have occurred on a second interrupt, but that's still better than the SIGKILL we send today. This won't help if terraform panics or gets OOM killed (hashicorp/terraform#39399), and Windows is unchanged, since it can't be interrupted.

We already capture a stack trace from the provider when terraform init hits text file busy, but that broke when we started running init with -json, since terraform then reports the error on stdout instead of stderr. I'm fixing that in #31688.

When a canceled Terraform command is still running after ExitTimeout,
the provisioner sent SIGKILL to Terraform. The kill skips the plugin
cleanup of Terraform, so provider plugins kept running. A leftover
provider holds its binary open, and a later "terraform init" that
replaces that binary fails with "text file busy".

Terraform exits at once on a second interrupt and stops its provider
plugins before it exits. Now the provisioner sends a second interrupt
when ExitTimeout expires, and kills Terraform only if it is still
running 10 seconds later (or after ExitTimeout, if that is shorter).
Windows is unchanged because it does not support interrupts.

Refs #14726
@linear-code

linear-code Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

ENG-745

CORE-807

@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-03T14:37:34.067276Z 5db1928 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 4c4eeef9ae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ethanndickson
ethanndickson marked this pull request as ready for review November 3, 2026 03:58

@mafredri mafredri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One correctness concern (inline), plus a few nits. The fix itself works: with real terraform 1.16.2 and hashicorp/external 2.3.5 stuck in a data source at the default 3m ExitTimeout, this PR exits with exit status 1 after "Two interrupts received" and the provider is gone. On main, the run ends with signal: killed and the provider is left running. ExitsOnSecondInterrupt also fails on main and passes here, so it does test the change. Didn't look at Windows beyond reading the code; nothing changes there.

Nits:

  • The forceExitTimeout doc says terraform exits within seconds of a second interrupt. That's only true for plan and apply (RunOperation). init handles only the first interrupt, and show/graph don't handle interrupts at all, so those still get killed, just 10s later than before.
  • ServeOptions.ExitTimeout repeats "10 seconds" as a literal instead of pointing at forceExitTimeout.
  • The HungJobExitTimeout comment in jobreaper still describes 3m as the limit. The real total is now 3m10s, which is still under HungJobDuration.
  • A timed-out cancel now reports terraform plan: exit status 1 instead of signal: killed, which reads like an ordinary terraform failure. No code matches on the old string. Perhaps wrap the error when killCtx.Err() != nil?

🤖 This review was automatically generated with Coder Agents.

Comment thread provisioner/terraform/executor.go Resolved
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 5db19282be

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ethanndickson
ethanndickson merged commit d970150 into main Nov 3, 2026
28 of 29 checks passed
@ethanndickson
ethanndickson deleted the terraform-second-interrupt branch November 3, 2026 14:54
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants