Skip to content

panic: close of closed channel when staticManifestPath is set — SetupSignalHandler called twice #669

Description

@fastly-ffej

Description

When staticManifestPath is configured (i.e. staticManifestMode is not "off"), main() calls both staticRun() and run() sequentially. Both functions call ctrl.SetupSignalHandler(). The controller-runtime signal handler uses a package-level sync.Once and panics on the second call with panic: close of closed channel.

Reproduction

Configure spire-controller-manager with staticManifestPath set (any non-empty value). After the static reconcilers complete, the manager starts briefly then panics:

panic: close of closed channel

goroutine 1 [running]:
sigs.k8s.io/controller-runtime/pkg/manager/signals.SetupSignalHandler()
        .../controller-runtime@v0.22.4/pkg/manager/signals/signal.go:31 +0x1e
main.run(...)
        /workspace/cmd/main.go:274 +0xa5
main.main()
        /workspace/cmd/main.go:107 +0xfd

Root Cause

In cmd/main.go:

// line 101
if mainConfig.ctrlConfig.StaticManifestPath != nil {
    if err := staticRun(mainConfig); err != nil {  // calls SetupSignalHandler() at line 495
        os.Exit(1)
    }
}
if err := run(mainConfig); err != nil {            // calls SetupSignalHandler() again at line 274
    os.Exit(1)
}

staticRun completes its full reconciliation and returns, then run is called and hits SetupSignalHandler a second time.

Potential Fix

Since staticRun and run appear to be mutually exclusive lifecycle paths, main() could exit after staticRun returns rather than falling through to run():

if mainConfig.ctrlConfig.StaticManifestPath != nil {
    if err := staticRun(mainConfig); err != nil {
        os.Exit(1)
    }
    os.Exit(0)
}

Happy to submit a PR if this approach looks reasonable to maintainers.

Observed Behaviour

  • Controller-manager reconciles all static entries successfully
  • Manager starts, briefly binds metrics (:8082) and health (:8083) ports
  • Immediately panics, process exits with code 2
  • Pod enters a crash loop (every ~3 minutes in our environment with ~10k entries)

Version

v0.6.4 (latest). Same code present in main branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions