Repository: cilium/cilium
Pull request #38743: shell: extract from daemon and reuse it for Cilium Operator
Author: login: mhofstetter
Labels: name: ready-to-merge
description: This PR has passed all tests and received consensus from code owners to merge.
name: area/cli
description: Impacts the command line interface of any command in the repository.
name: area/operator
description: Impacts the cilium-operator component
name: release-note/misc
description: This PR makes changes that have no direct user impact.
Created: 2025-04-04T09:35:21Z | Merged: 2025-04-16T08:18:20Z

This PR extracts the Cilium debug shell cell from the daemon and reuses it for the Cilium Operator. In addition it adds the `shell` command (to connect to the debug shell)  to the Cilium Agent & Operator binaries.

Although there are currently not many shell commands available for the Cilium operator (more or less only accessing statedb tables - with health being the only table), this could serve as a starting point for using the shell to check the operator runtime (by adding more shell commands in the future).

**Example (with local kind cluster)**

Operator

```
❯ kubectl -n kube-system exec -it cilium-operator-86649d6bc8-sfp9v -- cilium-operator-generic shell
time="2025-04-07T13:18:09.263802882Z" level=debug msg="Skipped reading configuration file" error="Config File \"cilium-operators\" Not Found in \"[/]\"" subsys=config
    /¯¯\
 /¯¯\__/¯¯\
 \__/¯¯\__/  Cilium 1.18.0-dev d4f2da37d4 2025-04-07T14:28:01+02:00 go version go1.24.2 linux/amd64
 /¯¯\__/¯¯\  Welcome to the Cilium Shell! Type 'help' for list of commands.
 \__/¯¯\__/
    \__/

kind-worker>
```

Agent

```
❯ kubectl -n kube-system exec -it cilium-hgnpr  -- cilium-agent shell
time="2025-04-07T13:18:24.45746434Z" level=info msg="Memory available for map entries (0.250% of 32338743296B): 80846858B" subsys=config
time="2025-04-07T13:18:24.457598437Z" level=info msg="option bpf-ct-global-tcp-max set by dynamic sizing to 283673" subsys=config
time="2025-04-07T13:18:24.457608005Z" level=info msg="option bpf-ct-global-any-max set by dynamic sizing to 141836" subsys=config
time="2025-04-07T13:18:24.45761904Z" level=info msg="option bpf-nat-global-max set by dynamic sizing to 283673" subsys=config
time="2025-04-07T13:18:24.457631333Z" level=info msg="option bpf-neigh-global-max set by dynamic sizing to 283673" subsys=config
time="2025-04-07T13:18:24.457644323Z" level=info msg="option bpf-sock-rev-map-max set by dynamic sizing to 141836" subsys=config
time="2025-04-07T13:18:24.457753137Z" level=warning msg="Running Cilium with \"kvstore\"=\"\" requires identity allocation via CRDs. Changing identity-allocation-mode to \"crd\"" subsys=config
    /¯¯\
 /¯¯\__/¯¯\
 \__/¯¯\__/  Cilium 1.18.0-dev d4f2da37d4 2025-04-07T14:28:01+02:00 go version go1.24.2 linux/amd64
 /¯¯\__/¯¯\  Welcome to the Cilium Shell! Type 'help' for list of commands.
 \__/¯¯\__/
    \__/

kind-control-plane>
```

Unfortunately depending on the config triggers some log messages - depending on the log level (`debug` in above case). But i think these are fine for the moment - and should be eventually eliminated by further reducing the global config.

Note:

- It still uses a UNIX domain socket as shell socket -> I thought about using a tcp socket on the loopback interface and accessing it via `kubectl port-forward` functionality. This way we could implement the shell client functionality within the Cilium CLI.
- This PR also fixes that stopped shell sessions are listed in the health table (by properly closing the health scope).
- Currently one needs to know the name of the binary (e.g. `cilium-operator-generic`)


Diff:
diff --git a/CODEOWNERS b/CODEOWNERS
index f4be7ace63413..742e8987f0699 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -619,6 +619,7 @@ Makefile* @cilium/build
 /pkg/safeio @cilium/sig-agent
 /pkg/safetime/ @cilium/sig-agent
 /pkg/service @cilium/sig-lb
+/pkg/shell @cilium/sig-foundations
 /pkg/shortener @cilium/sig-foundations @cilium/sig-k8s
 /pkg/signal @cilium/sig-datapath
 /pkg/slices @cilium/sig-foundations
diff --git a/Documentation/cmdref/cilium-agent.md b/Documentation/cmdref/cilium-agent.md
index c94a22502d845..45963497a9d13 100644
--- a/Documentation/cmdref/cilium-agent.md
+++ b/Documentation/cmdref/cilium-agent.md
@@ -405,4 +405,5 @@ cilium-agent [flags]
 
 * [cilium-agent completion](cilium-agent_completion.md)	 - Generate the autocompletion script for the specified shell
 * [cilium-agent hive](cilium-agent_hive.md)	 - Inspect the hive
+* [cilium-agent shell](cilium-agent_shell.md)	 - Connect to the Cilium shell
 
diff --git a/Documentation/cmdref/cilium-agent_shell.md b/Documentation/cmdref/cilium-agent_shell.md
new file mode 100644
index 0000000000000..63125ab520c37
--- /dev/null
+++ b/Documentation/cmdref/cilium-agent_shell.md
@@ -0,0 +1,20 @@
+<!-- This file was autogenerated via cilium-agent cmdref, do not edit manually-->
+
+## cilium-agent shell
+
+Connect to the Cilium shell
+
+```
+cilium-agent shell [command] [args]... [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for shell
+```
+
+### SEE ALSO
+
+* [cilium-agent](cilium-agent.md)	 - Run the cilium agent
+
diff --git a/Documentation/cmdref/cilium-operator-alibabacloud.md b/Documentation/cmdref/cilium-operator-alibabacloud.md
index 60e757192ec9c..d3539e1b6890a 100644
--- a/Documentation/cmdref/cilium-operator-alibabacloud.md
+++ b/Documentation/cmdref/cilium-operator-alibabacloud.md
@@ -143,6 +143,7 @@ cilium-operator-alibabacloud [flags]
 * [cilium-operator-alibabacloud completion](cilium-operator-alibabacloud_completion.md)	 - Generate the autocompletion script for the specified shell
 * [cilium-operator-alibabacloud hive](cilium-operator-alibabacloud_hive.md)	 - Inspect the hive
 * [cilium-operator-alibabacloud metrics](cilium-operator-alibabacloud_metrics.md)	 - Access metric status of the operator
+* [cilium-operator-alibabacloud shell](cilium-operator-alibabacloud_shell.md)	 - Connect to the Cilium shell
 * [cilium-operator-alibabacloud status](cilium-operator-alibabacloud_status.md)	 - Display status of operator
 * [cilium-operator-alibabacloud troubleshoot](cilium-operator-alibabacloud_troubleshoot.md)	 - Run troubleshooting utilities to check control-plane connectivity
 
diff --git a/Documentation/cmdref/cilium-operator-alibabacloud_shell.md b/Documentation/cmdref/cilium-operator-alibabacloud_shell.md
new file mode 100644
index 0000000000000..207935da76666
--- /dev/null
+++ b/Documentation/cmdref/cilium-operator-alibabacloud_shell.md
@@ -0,0 +1,20 @@
+<!-- This file was autogenerated via cilium-operator-alibabacloud cmdref, do not edit manually-->
+
+## cilium-operator-alibabacloud shell
+
+Connect to the Cilium shell
+
+```
+cilium-operator-alibabacloud shell [command] [args]... [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for shell
+```
+
+### SEE ALSO
+
+* [cilium-operator-alibabacloud](cilium-operator-alibabacloud.md)	 - Run cilium-operator-alibabacloud
+
diff --git a/Documentation/cmdref/cilium-operator-aws.md b/Documentation/cmdref/cilium-operator-aws.md
index 24a6cfd95039a..b97281ac2b009 100644
--- a/Documentation/cmdref/cilium-operator-aws.md
+++ b/Documentation/cmdref/cilium-operator-aws.md
@@ -150,6 +150,7 @@ cilium-operator-aws [flags]
 * [cilium-operator-aws completion](cilium-operator-aws_completion.md)	 - Generate the autocompletion script for the specified shell
 * [cilium-operator-aws hive](cilium-operator-aws_hive.md)	 - Inspect the hive
 * [cilium-operator-aws metrics](cilium-operator-aws_metrics.md)	 - Access metric status of the operator
+* [cilium-operator-aws shell](cilium-operator-aws_shell.md)	 - Connect to the Cilium shell
 * [cilium-operator-aws status](cilium-operator-aws_status.md)	 - Display status of operator
 * [cilium-operator-aws troubleshoot](cilium-operator-aws_troubleshoot.md)	 - Run troubleshooting utilities to check control-plane connectivity
 
diff --git a/Documentation/cmdref/cilium-operator-aws_shell.md b/Documentation/cmdref/cilium-operator-aws_shell.md
new file mode 100644
index 0000000000000..57971ac2bcfd5
--- /dev/null
+++ b/Documentation/cmdref/cilium-operator-aws_shell.md
@@ -0,0 +1,20 @@
+<!-- This file was autogenerated via cilium-operator-aws cmdref, do not edit manually-->
+
+## cilium-operator-aws shell
+
+Connect to the Cilium shell
+
+```
+cilium-operator-aws shell [command] [args]... [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for shell
+```
+
+### SEE ALSO
+
+* [cilium-operator-aws](cilium-operator-aws.md)	 - Run cilium-operator-aws
+
diff --git a/Documentation/cmdref/cilium-operator-azure.md b/Documentation/cmdref/cilium-operator-azure.md
index 48d9e25a38d69..d239d0df7c395 100644
--- a/Documentation/cmdref/cilium-operator-azure.md
+++ b/Documentation/cmdref/cilium-operator-azure.md
@@ -146,6 +146,7 @@ cilium-operator-azure [flags]
 * [cilium-operator-azure completion](cilium-operator-azure_completion.md)	 - Generate the autocompletion script for the specified shell
 * [cilium-operator-azure hive](cilium-operator-azure_hive.md)	 - Inspect the hive
 * [cilium-operator-azure metrics](cilium-operator-azure_metrics.md)	 - Access metric status of the operator
+* [cilium-operator-azure shell](cilium-operator-azure_shell.md)	 - Connect to the Cilium shell
 * [cilium-operator-azure status](cilium-operator-azure_status.md)	 - Display status of operator
 * [cilium-operator-azure troubleshoot](cilium-operator-azure_troubleshoot.md)	 - Run troubleshooting utilities to check control-plane connectivity
 
diff --git a/Documentation/cmdref/cilium-operator-azure_shell.md b/Documentation/cmdref/cilium-operator-azure_shell.md
new file mode 100644
index 0000000000000..b6c85faad9e2e
--- /dev/null
+++ b/Documentation/cmdref/cilium-operator-azure_shell.md
@@ -0,0 +1,20 @@
+<!-- This file was autogenerated via cilium-operator-azure cmdref, do not edit manually-->
+
+## cilium-operator-azure shell
+
+Connect to the Cilium shell
+
+```
+cilium-operator-azure shell [command] [args]... [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for shell
+```
+
+### SEE ALSO
+
+* [cilium-operator-azure](cilium-operator-azure.md)	 - Run cilium-operator-azure
+
diff --git a/Documentation/cmdref/cilium-operator-generic.md b/Documentation/cmdref/cilium-operator-generic.md
index 8540b1c0fa149..c5da524a32faf 100644
--- a/Documentation/cmdref/cilium-operator-generic.md
+++ b/Documentation/cmdref/cilium-operator-generic.md
@@ -142,6 +142,7 @@ cilium-operator-generic [flags]
 * [cilium-operator-generic completion](cilium-operator-generic_completion.md)	 - Generate the autocompletion script for the specified shell
 * [cilium-operator-generic hive](cilium-operator-generic_hive.md)	 - Inspect the hive
 * [cilium-operator-generic metrics](cilium-operator-generic_metrics.md)	 - Access metric status of the operator
+* [cilium-operator-generic shell](cilium-operator-generic_shell.md)	 - Connect to the Cilium shell
 * [cilium-operator-generic status](cilium-operator-generic_status.md)	 - Display status of operator
 * [cilium-operator-generic troubleshoot](cilium-operator-generic_troubleshoot.md)	 - Run troubleshooting utilities to check control-plane connectivity
 
diff --git a/Documentation/cmdref/cilium-operator-generic_shell.md b/Documentation/cmdref/cilium-operator-generic_shell.md
new file mode 100644
index 0000000000000..6a22abe5bce2f
--- /dev/null
+++ b/Documentation/cmdref/cilium-operator-generic_shell.md
@@ -0,0 +1,20 @@
+<!-- This file was autogenerated via cilium-operator-generic cmdref, do not edit manually-->
+
+## cilium-operator-generic shell
+
+Connect to the Cilium shell
+
+```
+cilium-operator-generic shell [command] [args]... [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for shell
+```
+
+### SEE ALSO
+
+* [cilium-operator-generic](cilium-operator-generic.md)	 - Run cilium-operator-generic
+
diff --git a/Documentation/cmdref/cilium-operator.md b/Documentation/cmdref/cilium-operator.md
index 6b730e6c276b5..8cfff85ecc6df 100644
--- a/Documentation/cmdref/cilium-operator.md
+++ b/Documentation/cmdref/cilium-operator.md
@@ -155,6 +155,7 @@ cilium-operator [flags]
 * [cilium-operator completion](cilium-operator_completion.md)	 - Generate the autocompletion script for the specified shell
 * [cilium-operator hive](cilium-operator_hive.md)	 - Inspect the hive
 * [cilium-operator metrics](cilium-operator_metrics.md)	 - Access metric status of the operator
+* [cilium-operator shell](cilium-operator_shell.md)	 - Connect to the Cilium shell
 * [cilium-operator status](cilium-operator_status.md)	 - Display status of operator
 * [cilium-operator troubleshoot](cilium-operator_troubleshoot.md)	 - Run troubleshooting utilities to check control-plane connectivity
 
diff --git a/Documentation/cmdref/cilium-operator_shell.md b/Documentation/cmdref/cilium-operator_shell.md
new file mode 100644
index 0000000000000..548400cf6f18b
--- /dev/null
+++ b/Documentation/cmdref/cilium-operator_shell.md
@@ -0,0 +1,20 @@
+<!-- This file was autogenerated via cilium-operator cmdref, do not edit manually-->
+
+## cilium-operator shell
+
+Connect to the Cilium shell
+
+```
+cilium-operator shell [command] [args]... [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for shell
+```
+
+### SEE ALSO
+
+* [cilium-operator](cilium-operator.md)	 - Run cilium-operator
+
diff --git a/cilium-dbg/cmd/metrics_list.go b/cilium-dbg/cmd/metrics_list.go
index 18ca970561770..d835312c8efd4 100644
--- a/cilium-dbg/cmd/metrics_list.go
+++ b/cilium-dbg/cmd/metrics_list.go
@@ -10,6 +10,7 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/cilium/cilium/pkg/command"
+	shell "github.com/cilium/cilium/pkg/shell/client"
 )
 
 var matchPattern string
@@ -23,7 +24,7 @@ var MetricsListCmd = &cobra.Command{
 		if command.OutputOption() {
 			format = strings.ToLower(command.OutputOptionString())
 		}
-		shellExchange(os.Stdout, "metrics --format=%s '%s'", format, matchPattern)
+		shell.ShellExchange(os.Stdout, "metrics --format=%s '%s'", format, matchPattern)
 	},
 }
 
diff --git a/cilium-dbg/cmd/root.go b/cilium-dbg/cmd/root.go
index e6f4a8e7cc133..f5c6fa895bc5e 100644
--- a/cilium-dbg/cmd/root.go
+++ b/cilium-dbg/cmd/root.go
@@ -18,6 +18,7 @@ import (
 	"github.com/cilium/cilium/pkg/logging"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/option"
+	shell "github.com/cilium/cilium/pkg/shell/client"
 )
 
 var (
@@ -61,6 +62,7 @@ func init() {
 	RootCmd.AddCommand(cmdref.NewCmd(RootCmd))
 	RootCmd.AddCommand(newCmdCompletion(os.Stdout))
 	RootCmd.AddCommand(troubleshoot.Cmd)
+	RootCmd.AddCommand(shell.ShellCmd)
 	RootCmd.SetOut(os.Stdout)
 	RootCmd.SetErr(os.Stderr)
 }
diff --git a/daemon/cmd/cells.go b/daemon/cmd/cells.go
index 129a46960d0e9..e1f728a90d9ba 100644
--- a/daemon/cmd/cells.go
+++ b/daemon/cmd/cells.go
@@ -70,6 +70,7 @@ import (
 	"github.com/cilium/cilium/pkg/recorder"
 	"github.com/cilium/cilium/pkg/redirectpolicy"
 	"github.com/cilium/cilium/pkg/service"
+	shell "github.com/cilium/cilium/pkg/shell/server"
 	"github.com/cilium/cilium/pkg/signal"
 	"github.com/cilium/cilium/pkg/source"
 )
@@ -139,7 +140,7 @@ var (
 		k8sSynced.CRDSyncCell,
 
 		// Shell for inspecting the agent. Listens on the 'shell.sock' UNIX socket.
-		shellCell,
+		shell.Cell,
 
 		// DNSProxy provides the DefaultDNSProxy singleton which is used by different
 		// packages.
diff --git a/daemon/cmd/root.go b/daemon/cmd/root.go
index 39667f18c5754..c1a6f22e072d2 100644
--- a/daemon/cmd/root.go
+++ b/daemon/cmd/root.go
@@ -14,6 +14,7 @@ import (
 	"github.com/cilium/cilium/pkg/hive"
 	"github.com/cilium/cilium/pkg/logging"
 	"github.com/cilium/cilium/pkg/option"
+	shell "github.com/cilium/cilium/pkg/shell/client"
 	"github.com/cilium/cilium/pkg/time"
 	"github.com/cilium/cilium/pkg/version"
 )
@@ -51,6 +52,7 @@ func NewAgentCmd(hfn func() *hive.Hive) *cobra.Command {
 
 	rootCmd.AddCommand(
 		cmdref.NewCmd(rootCmd),
+		shell.ShellCmd,
 		h.Command(),
 	)
 
@@ -68,6 +70,7 @@ func NewAgentCmd(hfn func() *hive.Hive) *cobra.Command {
 
 	return rootCmd
 }
+
 func Execute(cmd *cobra.Command) {
 	if err := cmd.Execute(); err != nil {
 		fmt.Println(err)
diff --git a/operator/cmd/root.go b/operator/cmd/root.go
index 9b96701026983..565ff20ddd698 100644
--- a/operator/cmd/root.go
+++ b/operator/cmd/root.go
@@ -71,6 +71,8 @@ import (
 	features "github.com/cilium/cilium/pkg/metrics/features/operator"
 	"github.com/cilium/cilium/pkg/option"
 	"github.com/cilium/cilium/pkg/pprof"
+	shellclient "github.com/cilium/cilium/pkg/shell/client"
+	shell "github.com/cilium/cilium/pkg/shell/server"
 	"github.com/cilium/cilium/pkg/version"
 )
 
@@ -122,6 +124,9 @@ var (
 				EnableMetrics: operatorCfg.EnableMetrics,
 			}
 		}),
+
+		// Shell for inspecting the operator. Listens on the 'shell.sock' UNIX socket.
+		shell.Cell,
 	)
 
 	// ControlPlane implements the control functions.
@@ -317,6 +322,7 @@ func NewOperatorCmd(h *hive.Hive) *cobra.Command {
 		MetricsCmd,
 		StatusCmd,
 		troubleshoot.Cmd,
+		shellclient.ShellCmd,
 		h.Command(),
 	)
 
diff --git a/cilium-dbg/cmd/shell.go b/pkg/shell/client/shell_client.go
similarity index 90%
rename from cilium-dbg/cmd/shell.go
rename to pkg/shell/client/shell_client.go
index 92441eb047cc2..7f3dd9210703e 100644
--- a/cilium-dbg/cmd/shell.go
+++ b/pkg/shell/client/shell_client.go
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 // Copyright Authors of Cilium
 
-package cmd
+package shell
 
 import (
 	"bufio"
@@ -12,20 +12,20 @@ import (
 	"os"
 	"os/signal"
 	"strings"
-	"time"
 
 	"github.com/cilium/hive/script"
 	"github.com/spf13/cobra"
 	"golang.org/x/term"
 
 	"github.com/cilium/cilium/pkg/defaults"
+	"github.com/cilium/cilium/pkg/time"
 	"github.com/cilium/cilium/pkg/version"
 )
 
-var shellCmd = &cobra.Command{
+var ShellCmd = &cobra.Command{
 	Use:   "shell [command] [args]...",
 	Short: "Connect to the Cilium shell",
-	Run:   shell,
+	Run:   executeShell,
 }
 
 var stdReadWriter = struct {
@@ -36,10 +36,6 @@ var stdReadWriter = struct {
 	Writer: os.Stdout,
 }
 
-func init() {
-	RootCmd.AddCommand(shellCmd)
-}
-
 func dialShell(w io.Writer) (net.Conn, error) {
 	var conn net.Conn
 	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
@@ -51,7 +47,7 @@ func dialShell(w io.Writer) (net.Conn, error) {
 		if err == nil {
 			break
 		}
-		// Dialing failed. Agent might not be fully up yet. Wait a bit and retry.
+		// Dialing failed. Server might not be fully up yet. Wait a bit and retry.
 		select {
 		case <-ctx.Done():
 			return nil, fmt.Errorf("dialing timed out: %w", err)
@@ -62,7 +58,7 @@ func dialShell(w io.Writer) (net.Conn, error) {
 	return conn, nil
 }
 
-func shellExchange(w io.Writer, format string, args ...any) error {
+func ShellExchange(w io.Writer, format string, args ...any) error {
 	conn, err := dialShell(os.Stderr)
 	if err != nil {
 		return err
@@ -101,9 +97,9 @@ func shellExchange(w io.Writer, format string, args ...any) error {
 	}
 }
 
-func shell(_ *cobra.Command, args []string) {
+func executeShell(_ *cobra.Command, args []string) {
 	if len(args) > 0 {
-		err := shellExchange(os.Stdout, "%s", strings.Join(args, " "))
+		err := ShellExchange(os.Stdout, "%s", strings.Join(args, " "))
 		if err != nil {
 			fmt.Fprintf(os.Stdout, "error: %s\n", err)
 		}
@@ -141,9 +137,9 @@ func interactiveShell() {
 	printShellGreeting(console)
 
 	for {
-		// Try to dial the shell.sock. Since it takes a moment for the agent to come up and this
+		// Try to dial the shell.sock. Since it takes a moment for the server to come up and this
 		// is meant for interactive use we'll try to be helpful and retry the dialing until
-		// agent comes up.
+		// server comes up.
 		conn, err := dialShell(console)
 		if err != nil {
 			fmt.Fprintf(os.Stderr, "Error: %s\n", err)
@@ -163,7 +159,7 @@ func interactiveShell() {
 
 		bio := bufio.NewReader(conn)
 
-		// Read commands from the console and send them to the agent for execution.
+		// Read commands from the console and send them to the server for execution.
 		// Stop on errors reading from the connection and redial. This allows interrupting
 		// long running commands with ctrl-c.
 		closed := false
@@ -174,7 +170,7 @@ func interactiveShell() {
 				return
 			}
 
-			// Send the command to the agent.
+			// Send the command to the server.
 			if _, err = fmt.Fprintln(conn, line); err != nil {
 				close(stop)
 				return
diff --git a/daemon/cmd/shell.go b/pkg/shell/server/shell_server.go
similarity index 85%
rename from daemon/cmd/shell.go
rename to pkg/shell/server/shell_server.go
index 1cdad04c757a3..18afd3d4d8240 100644
--- a/daemon/cmd/shell.go
+++ b/pkg/shell/server/shell_server.go
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 // Copyright Authors of Cilium
 
-package cmd
+package shell
 
 import (
 	"bufio"
@@ -13,7 +13,7 @@ import (
 	"runtime"
 	"sync"
 
-	upstreamHive "github.com/cilium/hive"
+	"github.com/cilium/hive"
 	"github.com/cilium/hive/cell"
 	"github.com/cilium/hive/job"
 	"github.com/cilium/hive/script"
@@ -22,7 +22,7 @@ import (
 	"github.com/cilium/cilium/pkg/logging/logfields"
 )
 
-var shellCell = cell.Module(
+var Cell = cell.Module(
 	"shell",
 	"Cilium debug shell",
 	cell.Invoke(registerShell),
@@ -35,7 +35,7 @@ var defaultCmdsToInclude = []string{
 	"cat", "exec", "help",
 }
 
-func registerShell(in upstreamHive.ScriptCmds, log *slog.Logger, jg job.Group) {
+func registerShell(in hive.ScriptCmds, log *slog.Logger, jg job.Group) {
 	cmds := in.Map()
 	defCmds := script.DefaultCmds()
 	for _, name := range defaultCmdsToInclude {
@@ -55,9 +55,15 @@ type shell struct {
 }
 
 func (sh shell) listener(ctx context.Context, health cell.Health) error {
-	// Remove any old UNIX sock file from previous agent run.
+	// Remove any old UNIX sock file from previous runs.
 	os.Remove(defaults.ShellSockPath)
 
+	if _, err := os.Stat(defaults.RuntimePath); os.IsNotExist(err) {
+		if err := os.MkdirAll(defaults.RuntimePath, defaults.RuntimePathRights); err != nil {
+			return fmt.Errorf("could not create default runtime directory: %w", err)
+		}
+	}
+
 	var lc net.ListenConfig
 	l, err := lc.Listen(ctx, "unix", defaults.ShellSockPath)
 	if err != nil {
@@ -82,8 +88,9 @@ func (sh shell) listener(ctx context.Context, health cell.Health) error {
 		}
 		sh.jg.Add(job.OneShot(
 			fmt.Sprintf("shell-%d", connCount),
-			func(ctx context.Context, _ cell.Health) error {
+			func(ctx context.Context, h cell.Health) error {
 				sh.handleConn(ctx, conn)
+				h.Close() // remove from health list
 				sh.log.Info("exited")
 				return nil
 			}))
@@ -109,7 +116,7 @@ func (sh shell) handleConn(ctx context.Context, conn net.Conn) {
 	defer wg.Wait()
 	defer cancel()
 
-	// Catch panics to make sure the script commands can't bring the agent down.
+	// Catch panics to make sure the script commands can't bring the runtime down.
 	defer func() {
 		if err := recover(); err != nil {
 			// Log the panic and also write it to cilium-dbg. We keep processing


Reviews:
user: login: joamaki
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:47:00Z
user: login: joamaki
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:48:44Z
user: login: joamaki
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:49:58Z
user: login: mhofstetter
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T09:02:20Z
user: login: mhofstetter
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T09:04:07Z
user: login: mhofstetter
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T09:07:07Z
user: login: mhofstetter
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T13:23:14Z
user: login: mhofstetter
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T13:23:39Z
user: login: mhofstetter
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T13:23:59Z
user: login: joamaki
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T14:29:14Z
user: login: tommyp1ckles
body: Nice, this will be very useful.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T05:03:03Z
user: login: squeed
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T08:53:24Z
user: login: bimmlerd
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-16T07:54:17Z

Review comments:
diff_hunk: @@ -309,6 +309,10 @@ spec:
           mountPath: /var/run/cilium/envoy/sockets
           readOnly: false
         {{- end }}
+        # accessing debug shell socket from k8s debug containers
+        - name: shell-sockets
+          mountPath: /var/run/cilium/shell/sockets
path: install/kubernetes/cilium/templates/cilium-agent/daemonset.yaml
user: login: joamaki
body: Would it make sense to just have `/var/run/cilium/sockets` that'd have both `shell.sock` and whatever envoy puts there?
created_at: 2025-04-07T08:47:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,9 @@
+# Can be used to connect to the Cilium debug shell from a k8s debugging container
+# e.g. for the Cilium Operator
+#
+# kubectl -n kube-system debug cilium-operator-7dcff8d46c-79sp6 -it --image=localhost:5000/cilium/cilium-dev:local --custom=contrib/testing/debug-mount-shell.yaml --profile=general -- bash
path: contrib/testing/debug-mount-shell.yaml
user: login: joamaki
body: Could we instead of exporting the volume mount etc. just have `cilium-operator shell` command that'd do something like `cilium-dbg/cmd/shell.go`? This is what I'd eventually like to happen for the agent as well (e.g. cilium-dbg binary goes away, you only have `cilium-agent` that has a `shell` sub-command).
created_at: 2025-04-07T08:48:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
path: pkg/shell/shell.go
user: login: joamaki
body: Hmm I do wonder if this should move into `cilium/hive` instead. I don't see a reason why not to tightly couple it into it. Could provide the socket path via `hive.Options` and the hive cobra commands (`command.go`) could have a `shell` sub-command implementing basically what is currently in `cilium-dbg/cmd/shell.go`. Then `cilium-dbg shell` would just exec `cilium-agent hive shell`.
created_at: 2025-04-07T08:49:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -309,6 +309,10 @@ spec:
           mountPath: /var/run/cilium/envoy/sockets
           readOnly: false
         {{- end }}
+        # accessing debug shell socket from k8s debug containers
+        - name: shell-sockets
+          mountPath: /var/run/cilium/shell/sockets
path: install/kubernetes/cilium/templates/cilium-agent/daemonset.yaml
user: login: mhofstetter
body: I thought about this, but envoys sockets are shared via hostmount volumemount (because these UNIX domain sockets need to shared between Pods (agent <-> envoy (in its own daemonset)). IMO it's better to handle the shell sockets separately - also to avoid confusion if agent & operator run on the same k8s node.
created_at: 2025-04-07T09:02:19Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,9 @@
+# Can be used to connect to the Cilium debug shell from a k8s debugging container
+# e.g. for the Cilium Operator
+#
+# kubectl -n kube-system debug cilium-operator-7dcff8d46c-79sp6 -it --image=localhost:5000/cilium/cilium-dev:local --custom=contrib/testing/debug-mount-shell.yaml --profile=general -- bash
path: contrib/testing/debug-mount-shell.yaml
user: login: mhofstetter
body: haven't thought about this. has it's pros and cons - easier to use - but not as easy to pipe into other linux tooling (except one does it on the client)
created_at: 2025-04-07T09:04:06Z
author_association: MEMBER
diff_hunk: @@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
path: pkg/shell/shell.go
user: login: mhofstetter
body: Honestly - I was surprised to see that `cilium/hive` already provides built-in shell commands. Even though it makes sense to interactively play with the hive cell - i wonder whether we should add even more "Cilium related" "runtime" aspects to this repo - as it's actual "just" provides a dependency injection framework :thinking: Personally I'd keep it in the `cilium/cilium` repo for the time being.
created_at: 2025-04-07T09:07:07Z
author_association: MEMBER
diff_hunk: @@ -309,6 +309,10 @@ spec:
           mountPath: /var/run/cilium/envoy/sockets
           readOnly: false
         {{- end }}
+        # accessing debug shell socket from k8s debug containers
+        - name: shell-sockets
+          mountPath: /var/run/cilium/shell/sockets
path: install/kubernetes/cilium/templates/cilium-agent/daemonset.yaml
user: login: mhofstetter
body: current implementation no longer uses volumemounts
created_at: 2025-04-07T13:23:14Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,9 @@
+# Can be used to connect to the Cilium debug shell from a k8s debugging container
+# e.g. for the Cilium Operator
+#
+# kubectl -n kube-system debug cilium-operator-7dcff8d46c-79sp6 -it --image=localhost:5000/cilium/cilium-dev:local --custom=contrib/testing/debug-mount-shell.yaml --profile=general -- bash
path: contrib/testing/debug-mount-shell.yaml
user: login: mhofstetter
body: Refactored this so that `cilium-agent` & `cilium-operator` binaries have the `shell` subcommand
created_at: 2025-04-07T13:23:39Z
author_association: MEMBER
diff_hunk: @@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
path: pkg/shell/shell.go
user: login: mhofstetter
body: as discussed offline - let's keep this in `cilium/cilium` for the time being.
created_at: 2025-04-07T13:23:59Z
author_association: MEMBER

PR comments:
user: login: mhofstetter
created_at: 2025-04-04T13:31:18Z
body: /test
author_association: MEMBER
user: login: mhofstetter
created_at: 2025-04-07T13:40:39Z
body: /test
author_association: MEMBER