Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

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

オープン
#400 コメント 0 件 リアクション 0 件 担当者 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時間
マージ済み PR(30日)
24

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

crossplane/cli のほかの issue

crossplane/cli の issue をすべて見る

似ている issue

Go の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。