Problem
The Containerization framework provides LinuxPod.execInContainer(_:processID:configure:)
to spawn a new process inside a running container, but exposes no mechanism
to re-attach an interactive PTY (stdin + stdout + stderr) to the primary
process that was started with startContainer(). Once the initial stdout/stderr
streams are consumed at start time, there is no way to reconnect.
This means the Kubernetes CRI Attach RPC — which connects a client's terminal
to the container's existing entrypoint process — cannot be implemented on
the pod-shared path.
Use case
kubectl attach -it <pod> expects the CRI runtime to open a bidirectional
byte stream to the container's primary process. On the pod-shared path
(podvmd), the CRI runtime dispatches a JSON-RPC pod.attach call to the
daemon. The daemon must then re-attach to the process that was started at
container boot.
Without this primitive, the only option is to exec a new shell, which is
semantically different (new PID, new process group, does not share the
entrypoint's stdin history or signal handling).
Reproducer
In our podvmd daemon (tools/podvmd/Sources/podvmd/PodRegistry.swift,
line ~449):
// MARK: - pod.attach
//
// Apple Containerization framework (as of 0.12.x) does not expose a
// re-attach primitive for the primary container process.
func attach(_ params: PodAttachParams) async throws -> PodAttachResult {
_ = try podEntry(params.handle)
throw PodVMError(
.methodNotFound,
"pod.attach not supported by Apple Containerization framework on this host"
)
}
The Go CRI layer (darwin-cri) calls this method and receives JSON-RPC error
code -32601 (method not found), which it maps to gRPC codes.Unimplemented:
Attach: pod-shared path: pod.attach returned method_not_found
(-32601: pod.attach not supported by Apple Containerization
framework on this host)
A kubectl attach -it against a pod-shared pod produces:
error: unable to upgrade connection: container attach not supported
Proposed API shape
Option A — method on LinuxPod:
/// Attach stdin/stdout/stderr to an already-running container process.
/// Returns when the client disconnects or the process exits.
func attach(
containerID: String,
processID: String?, // nil = primary process
stdin: (any ReaderStream)?,
stdout: (any Writer)?,
stderr: (any Writer)?,
tty: Bool
) async throws
Option B — method on LinuxProcess:
/// Re-open the stdio streams for an existing process.
func openTTY() async throws -> (stdin: FileHandle, stdout: FileHandle)
Either shape would unblock the CRI Attach path.
Workarounds we've tried
- Exec a new
/bin/sh — not semantically equivalent; creates a new
process rather than attaching to the existing one.
- Capture stdout/stderr at start time into files, then tail — provides
read-only output but no stdin, and misses data written before the tail
starts.
- Hold the initial
Writer references open — works for log streaming
but does not provide a way to inject stdin after the fact, and the
Writer protocol is unidirectional.
Problem
The Containerization framework provides
LinuxPod.execInContainer(_:processID:configure:)to spawn a new process inside a running container, but exposes no mechanism
to re-attach an interactive PTY (stdin + stdout + stderr) to the primary
process that was started with
startContainer(). Once the initial stdout/stderrstreams are consumed at start time, there is no way to reconnect.
This means the Kubernetes CRI
AttachRPC — which connects a client's terminalto the container's existing entrypoint process — cannot be implemented on
the pod-shared path.
Use case
kubectl attach -it <pod>expects the CRI runtime to open a bidirectionalbyte stream to the container's primary process. On the pod-shared path
(
podvmd), the CRI runtime dispatches a JSON-RPCpod.attachcall to thedaemon. The daemon must then re-attach to the process that was started at
container boot.
Without this primitive, the only option is to
execa new shell, which issemantically different (new PID, new process group, does not share the
entrypoint's stdin history or signal handling).
Reproducer
In our
podvmddaemon (tools/podvmd/Sources/podvmd/PodRegistry.swift,line ~449):
The Go CRI layer (
darwin-cri) calls this method and receives JSON-RPC errorcode
-32601(method not found), which it maps to gRPCcodes.Unimplemented:A
kubectl attach -itagainst a pod-shared pod produces:Proposed API shape
Option A — method on
LinuxPod:Option B — method on
LinuxProcess:Either shape would unblock the CRI
Attachpath.Workarounds we've tried
/bin/sh— not semantically equivalent; creates a newprocess rather than attaching to the existing one.
read-only output but no stdin, and misses data written before the tail
starts.
Writerreferences open — works for log streamingbut does not provide a way to inject stdin after the fact, and the
Writerprotocol is unidirectional.