{"record":{"id":"e61a4a1da4cbe91d","repo":"kubernetes/kops","slug":"serving-readiness-probe-w","errorCode":null,"errorMessage":"serving readiness probe: %w","messagePattern":"serving readiness probe: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"channels/pkg/cmd/apply_channel.go","lineNumber":130,"sourceCode":"\t\t}\n\t}\n\tif err := RunApplyChannel(ctx, f, out, options, args); err != nil {\n\t\tmerr = multierr.Append(merr, err)\n\t}\n\treturn merr\n}\n\n// runApplyChannelLoop reconciles repeatedly until ctx is cancelled. A fresh\n// ChannelsFactory per iteration drops cached REST configs and the discovery\n// cache, picking up cert rotation and new CRDs without a restart.\nfunc runApplyChannelLoop(ctx context.Context, out io.Writer, options *ApplyChannelOptions, args []string) error {\n\t// In daemon mode kops-channels runs as a system-node-critical static pod; serve a\n\t// readiness probe reporting the last apply outcome, so a persistent failure surfaces\n\t// as NotReady (failing `kops validate cluster`, which gates rolling updates) instead\n\t// of only being logged. Starts NotReady until the first successful apply.\n\treadiness, err := serveReadiness(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"serving readiness probe: %w\", err)\n\t}\n\n\t// Retry quickly until the first success: the apiserver is usually\n\t// unreachable while the control plane is still coming up.\n\tconst startupRetryInterval = 5 * time.Second\n\n\tsettled := false\n\tfor {\n\t\tinterval := options.Interval\n\t\terr := runApplyChannelIteration(ctx, NewChannelsFactory(), out, options, args)\n\t\treadiness.recordApplyResult(err)\n\t\tif err != nil {\n\t\t\tif !settled {\n\t\t\t\tinterval = min(startupRetryInterval, options.Interval)\n\t\t\t}\n\t\t\tklog.Warningf(\"error in apply iteration (will retry in %s): %v\", interval, err)\n\t\t} else {\n\t\t\tsettled = true","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/channels/pkg/cmd/apply_channel.go#L112-L148","documentation":"In daemon mode, runApplyChannelLoop first calls serveReadiness to start an HTTP readiness endpoint reporting the last apply outcome. If that server cannot be created (e.g. it cannot bind its listen address), the loop aborts immediately with this wrapped error instead of retrying.","triggerScenarios":"serveReadiness fails to bind its TCP port: the readiness port is already in use by another process, the address is not assignable in the network namespace, or insufficient permissions to listen.","commonSituations":"Two kops-channels daemon instances running concurrently (e.g. leftover process during pod restart); a hostPort/port collision in the static pod spec; restricted environment blocking listen().","solutions":["Check for and kill any existing kops-channels process holding the readiness port (ss -ltnp / lsof)","Fix the static pod spec so the readiness port is unique and not conflicting with hostPort mappings","Restart the pod/container so the network namespace is clean","If serveReadiness supports an address option, bind to a free port"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// check the readiness port is free before starting the daemon\nln, err := net.Listen(\"tcp\", readinessAddr)\nif err != nil {\n\treturn fmt.Errorf(\"readiness port %s unavailable: %w\", readinessAddr, err)\n}\nln.Close()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run only one kops-channels daemon per pod/network namespace","Choose a dedicated, unused readiness port in the static pod spec","Avoid hostPort conflicts in the pod manifest","Use a liveness/startup probe to detect port-binding failures early"],"tags":["http-server","port-conflict","daemon","readiness-probe"],"backgroundTag":"port-already-in-use","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}