Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

render: interrupting with Ctrl+C (SIGINT/SIGTERM) leaks Function containers, the render container, and the network

未关闭
#400 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 3 天内回复

@jcogilvie 已经在做这个了。

开始于 2026年10月1日。

  • #406 来自 @jcogilvie —— 未关闭

评估

难度
3/5
预计耗时
1-2 天
新手友好度
68/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
docker, go
领域
cli, devops

调研方向

Read the deferred cleanup paths in xr/cmd.go and op/cmd.go, then inspect the signal handling in internal/terminal/spinner.go and the render command entry points. Reproduce the issue with the supplied hanging Function and Ctrl+C, and verify that the existing containers, render container, and temporary network are removed before the command exits.

由索引模型根据 Issue 内容生成。

描述

bug
What happened?

Interrupting crossplane render with Ctrl+C while a render is in progress leaves every Function container it
started, the crossplane internal render engine container, and the temporary crossplane-render-* network
all still running. This happens with the default runtime-docker-cleanup policy (Remove), with or
without a TTY.

All cleanup in the render commands is defer-based
(xr/cmd.go L270-L281,
op/cmd.go L210-L221),
and nothing turns SIGINT/SIGTERM into context cancellation, so the process exits before any deferred
cleanup runs. The only signal handler in the CLI is the terminal spinner's
(internal/terminal/spinner.go L291-L300),
which calls os.Exit(130) — and that skips deferred cleanup too.

Expected: an interrupted render cleans up what it created before exiting.

How can we reproduce it?

Use a Function that accepts a connection but never responds, so the render blocks long enough to interrupt:

# tagged fnlc-hang:test
FROM alpine:3.20
ENTRYPOINT ["sh","-c","exec nc -lk -p 9443 -e cat","hang"]

xr.yaml:

apiVersion: example.org/v1
kind: XThing
metadata:
  name: test-xr
spec:
  coolField: hello

comp-hang.yaml:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xthings.example.org
spec:
  compositeTypeRef: {apiVersion: example.org/v1, kind: XThing}
  mode: Pipeline
  pipeline:
  - step: templating
    functionRef: {name: function-go-templating}
    input:
      apiVersion: gotemplating.fn.crossplane.io/v1beta1
      kind: GoTemplate
      source: Inline
      inline:
        template: |
          apiVersion: nop.crossplane.io/v1alpha1
          kind: NopResource
          metadata:
            annotations: {gotemplating.fn.crossplane.io/composition-resource-name: nop}
          spec: {forProvider: {}}
  - step: hang
    functionRef: {name: function-hang}

functions-hang.yaml (names set only to make leftovers easy to find):

apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
  name: function-go-templating
  annotations: {render.crossplane.io/runtime-docker-name: fnlc-sigint-gotmpl}
spec: {package: xpkg.crossplane.io/crossplane-contrib/function-go-templating:v0.11.0}
---
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
  name: function-hang
  annotations:
    render.crossplane.io/runtime-docker-name: fnlc-sigint-hang
    render.crossplane.io/runtime-docker-image: fnlc-hang:test
    render.crossplane.io/runtime-docker-pull-policy: Never
spec: {package: example.org/unused:v0.0.0}

Run it, press Ctrl+C after a few seconds, then look:

$ crossplane render xr.yaml comp-hang.yaml functions-hang.yaml
^C
$ echo $?
130
$ docker ps --filter name=fnlc-sigint --format '{{.Names}} {{.Status}}'
fnlc-sigint-hang Up 7 seconds
fnlc-sigint-gotmpl Up 8 seconds
$ docker network inspect crossplane-render-wxjbqjcj --format '{{range .Containers}}{{.Name}} {{end}}'
musing_lamport
$ docker inspect musing_lamport --format '{{.Config.Image}} {{.State.Status}} cmd={{json .Config.Cmd}}'
xpkg.crossplane.io/crossplane/crossplane:stable running cmd=["internal","render"]

(Transcript lightly abridged: the reproduction was scripted, sending SIGINT 6s after start. It gives the
same result under a pseudo-TTY, where the spinner's handler is the one that exits.)

Possible fix: wrap the command context in signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) so an
interrupt cancels in-flight work and lets the existing deferred cleanup run, and have the spinner cooperate
with that instead of calling os.Exit. Cleanup already runs on a detached context.Background(), so it
still works after cancellation. A second signal could force-exit immediately, as is conventional.

Some interruptions (kill -9, a crash, a CI job timeout) can never be handled in-process; #401 proposes
labels so leftovers from those can be found and swept.

What environment did it happen in?
  • Crossplane CLI version: built from main @ 29316fea54f2ede9d2c039d9c54f0c29cbad4b65
  • Platform (e.g., linux/amd64): darwin/arm64, Docker Engine 29.5.3 (Rancher Desktop)
  • Crossplane version (if applicable): render engine image xpkg.crossplane.io/crossplane/crossplane:stable
主要语言
Go
星标
19
派生
33
平均合并
3 天 10 小时
30 天内合并 PR
24

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

crossplane/cli 的其他 Issue

查看 crossplane/cli 的全部 Issue

相似的 Issue

更多 Go Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。