From dffecf0e00eefb9420ed8636197fd764ad5424a4 Mon Sep 17 00:00:00 2001 From: MD-Mushfiqur123 Date: Sat, 13 Jun 2026 02:03:59 +0000 Subject: [PATCH] nerdctl version: show RootlessKit version when running rootless Signed-off-by: MD-Mushfiqur123 --- cmd/nerdctl/version.go | 2 +- pkg/infoutil/infoutil.go | 49 ++++++++++++++++++++++++++-- pkg/infoutil/infoutil_test.go | 61 +++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/cmd/nerdctl/version.go b/cmd/nerdctl/version.go index 797a0c314cb..59519752e1d 100644 --- a/cmd/nerdctl/version.go +++ b/cmd/nerdctl/version.go @@ -120,7 +120,7 @@ func versionAction(cmd *cobra.Command, args []string) error { // Address can be empty to skip inspecting the server. func versionInfo(cmd *cobra.Command, ns, address string) (dockercompat.VersionInfo, error) { v := dockercompat.VersionInfo{ - Client: infoutil.ClientVersion(), + Client: infoutil.ClientVersion(cmd.Context()), } if address == "" { return v, nil diff --git a/pkg/infoutil/infoutil.go b/pkg/infoutil/infoutil.go index 2dee6f88a85..a5a880a2e8e 100644 --- a/pkg/infoutil/infoutil.go +++ b/pkg/infoutil/infoutil.go @@ -35,7 +35,9 @@ import ( "github.com/containerd/nerdctl/v2/pkg/buildkitutil" "github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat" "github.com/containerd/nerdctl/v2/pkg/inspecttypes/native" + "github.com/containerd/nerdctl/v2/pkg/rootlessutil" "github.com/containerd/nerdctl/v2/pkg/version" + "github.com/rootless-containers/rootlesskit/v3/pkg/api" ) func NativeDaemonInfo(ctx context.Context, client *containerd.Client) (*native.DaemonInfo, error) { @@ -113,8 +115,8 @@ func GetSnapshotterNames(ctx context.Context, introService introspection.Service return names, nil } -func ClientVersion() dockercompat.ClientVersion { - return dockercompat.ClientVersion{ +func ClientVersion(ctx context.Context) dockercompat.ClientVersion { + v := dockercompat.ClientVersion{ Version: version.GetVersion(), GitCommit: version.GetRevision(), GoVersion: runtime.Version(), @@ -124,6 +126,10 @@ func ClientVersion() dockercompat.ClientVersion { buildctlVersion(), }, } + if rk := rootlesskitVersion(ctx); rk != nil { + v.Components = append(v.Components, *rk) + } + return v } func ServerVersion(ctx context.Context, client *containerd.Client) (*dockercompat.ServerVersion, error) { @@ -190,6 +196,45 @@ func parseBuildctlVersion(buildctlVersionStdout []byte) (*dockercompat.Component return v, nil } +// rootlesskitVersion returns the RootlessKit version info. +// It returns nil when not running rootless. +func rootlesskitVersion(ctx context.Context) *dockercompat.ComponentVersion { + if !rootlessutil.IsRootless() { + return nil + } + rlkClient, err := rootlessutil.NewRootlessKitClient() + if err != nil { + log.L.WithError(err).Warnf("unable to determine rootlesskit version") + return &dockercompat.ComponentVersion{Name: "rootlesskit"} + } + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + info, err := rlkClient.Info(ctx) + if err != nil { + log.L.WithError(err).Warnf("unable to determine rootlesskit version") + return &dockercompat.ComponentVersion{Name: "rootlesskit"} + } + return parseRootlesskitVersion(info) +} + +func parseRootlesskitVersion(info *api.Info) *dockercompat.ComponentVersion { + v := &dockercompat.ComponentVersion{ + Name: "rootlesskit", + Version: info.Version, + Details: map[string]string{ + "ApiVersion": info.APIVersion, + "StateDir": info.StateDir, + }, + } + if nd := info.NetworkDriver; nd != nil { + v.Details["NetworkDriver"] = nd.Driver + } + if pd := info.PortDriver; pd != nil { + v.Details["PortDriver"] = pd.Driver + } + return v +} + func runcVersion() dockercompat.ComponentVersion { stdout, err := exec.Command("runc", "--version").Output() if err != nil { diff --git a/pkg/infoutil/infoutil_test.go b/pkg/infoutil/infoutil_test.go index 33cbe6cc0b4..abd1b8e9267 100644 --- a/pkg/infoutil/infoutil_test.go +++ b/pkg/infoutil/infoutil_test.go @@ -22,6 +22,7 @@ import ( "gotest.tools/v3/assert" "github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat" + "github.com/rootless-containers/rootlesskit/v3/pkg/api" ) func TestParseBuildctlVersion(t *testing.T) { @@ -83,3 +84,63 @@ libseccomp: 2.5.1`: { } } } + +func TestParseRootlesskitVersion(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + info *api.Info + want *dockercompat.ComponentVersion + }{ + { + name: "with network and port driver", + info: &api.Info{ + Version: "2.0.0", + APIVersion: "1.1.1", + StateDir: "/run/user/1000/rootlesskit", + NetworkDriver: &api.NetworkDriverInfo{ + Driver: "slirp4netns", + }, + PortDriver: &api.PortDriverInfo{ + Driver: "builtin", + }, + }, + want: &dockercompat.ComponentVersion{ + Name: "rootlesskit", + Version: "2.0.0", + Details: map[string]string{ + "ApiVersion": "1.1.1", + "StateDir": "/run/user/1000/rootlesskit", + "NetworkDriver": "slirp4netns", + "PortDriver": "builtin", + }, + }, + }, + { + name: "without network and port driver", + info: &api.Info{ + Version: "1.0.0", + APIVersion: "1.0.0", + StateDir: "/tmp/rk", + }, + want: &dockercompat.ComponentVersion{ + Name: "rootlesskit", + Version: "1.0.0", + Details: map[string]string{ + "ApiVersion": "1.0.0", + "StateDir": "/tmp/rk", + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := parseRootlesskitVersion(tt.info) + assert.DeepEqual(t, tt.want, got) + }) + } +}