Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 0 additions & 97 deletions cmd/nerdctl/namespace/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,9 @@
package namespace

import (
"fmt"
"sort"
"strings"
"text/tabwriter"

"github.com/spf13/cobra"

"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/log"

"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
"github.com/containerd/nerdctl/v2/pkg/clientutil"
"github.com/containerd/nerdctl/v2/pkg/mountutil/volumestore"
)

func Command() *cobra.Command {
Expand All @@ -50,90 +40,3 @@ func Command() *cobra.Command {
cmd.AddCommand(inspectCommand())
return cmd
}

func listCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "ls",
Aliases: []string{"list"},
Short: "List containerd namespaces",
RunE: listAction,
SilenceUsage: true,
SilenceErrors: true,
}
cmd.Flags().BoolP("quiet", "q", false, "Only display names")
return cmd
}

func listAction(cmd *cobra.Command, args []string) error {
globalOptions, err := helpers.ProcessRootCmdFlags(cmd)
if err != nil {
return err
}
client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), globalOptions.Namespace, globalOptions.Address)
if err != nil {
return err
}
defer cancel()

nsService := client.NamespaceService()
nsList, err := nsService.List(ctx)
if err != nil {
return err
}
quiet, err := cmd.Flags().GetBool("quiet")
if err != nil {
return err
}
if quiet {
for _, ns := range nsList {
fmt.Fprintln(cmd.OutOrStdout(), ns)
}
return nil
}
dataStore, err := clientutil.DataStore(globalOptions.DataRoot, globalOptions.Address)
if err != nil {
return err
}

w := tabwriter.NewWriter(cmd.OutOrStdout(), 4, 8, 4, ' ', 0)
// no "NETWORKS", because networks are global objects
fmt.Fprintln(w, "NAME\tCONTAINERS\tIMAGES\tVOLUMES\tLABELS")
for _, ns := range nsList {
ctx = namespaces.WithNamespace(ctx, ns)
var numContainers, numImages, numVolumes int
var labelStrings []string

containers, err := client.Containers(ctx)
if err != nil {
log.L.Warn(err)
}
numContainers = len(containers)

images, err := client.ImageService().List(ctx)
if err != nil {
log.L.Warn(err)
}
numImages = len(images)

volStore, err := volumestore.New(dataStore, ns)
if err != nil {
log.L.Warn(err)
} else {
numVolumes, err = volStore.Count()
if err != nil {
log.L.Warn(err)
}
}

labels, err := client.NamespaceService().Labels(ctx, ns)
if err != nil {
return err
}
for k, v := range labels {
labelStrings = append(labelStrings, strings.Join([]string{k, v}, "="))
}
sort.Strings(labelStrings)
fmt.Fprintf(w, "%s\t%d\t%d\t%d\t%v\t\n", ns, numContainers, numImages, numVolumes, strings.Join(labelStrings, ","))
}
return w.Flush()
}
1 change: 0 additions & 1 deletion cmd/nerdctl/namespace/namespace_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,5 @@ func inspectAction(cmd *cobra.Command, args []string) error {
}

func namespaceInspectShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// show namespace names
return completion.NamespaceNames(cmd, args, toComplete)
}
76 changes: 76 additions & 0 deletions cmd/nerdctl/namespace/namespace_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright The containerd Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package namespace

import (
"github.com/spf13/cobra"

"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
"github.com/containerd/nerdctl/v2/pkg/api/types"
"github.com/containerd/nerdctl/v2/pkg/clientutil"
"github.com/containerd/nerdctl/v2/pkg/cmd/namespace"
)

func listCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "ls",
Aliases: []string{"list"},
Short: "List containerd namespaces",
RunE: listAction,
SilenceUsage: true,
SilenceErrors: true,
}
cmd.Flags().BoolP("quiet", "q", false, "Only display names")
cmd.Flags().StringP("format", "f", "", "Format the output using the given Go template, e.g, '{{json .}}'")
return cmd
}

func listOptions(cmd *cobra.Command) (types.NamespaceListOptions, error) {
globalOptions, err := helpers.ProcessRootCmdFlags(cmd)
if err != nil {
return types.NamespaceListOptions{}, err
}
format, err := cmd.Flags().GetString("format")
if err != nil {
return types.NamespaceListOptions{}, err
}
quiet, err := cmd.Flags().GetBool("quiet")
if err != nil {
return types.NamespaceListOptions{}, err
}
return types.NamespaceListOptions{
GOptions: globalOptions,
Format: format,
Quiet: quiet,
Stdout: cmd.OutOrStdout(),
}, nil
}

func listAction(cmd *cobra.Command, args []string) error {
options, err := listOptions(cmd)
if err != nil {
return err
}

client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), options.GOptions.Namespace, options.GOptions.Address)
if err != nil {
return err
}
defer cancel()

return namespace.List(ctx, client, options)
}
1 change: 0 additions & 1 deletion cmd/nerdctl/namespace/namespace_remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,5 @@ func removeAction(cmd *cobra.Command, args []string) error {
}

func namespaceRemoveShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// show namespace names
return completion.NamespaceNames(cmd, args, toComplete)
}
1 change: 0 additions & 1 deletion cmd/nerdctl/namespace/namespace_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,5 @@ func updateAction(cmd *cobra.Command, args []string) error {
}

func namespaceUpdateShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// show namespace names
return completion.NamespaceNames(cmd, args, toComplete)
}
1 change: 1 addition & 0 deletions docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,7 @@ Usage: `nerdctl namespace ls [OPTIONS]`
Flags:

- `-q, --quiet`: Only display namespace names
- `-f, --format`: Format the output using the given Go template, e.g, `{{json .}}`

### :nerd_face: :blue_square: nerdctl namespace remove

Expand Down
10 changes: 10 additions & 0 deletions pkg/api/types/namespace_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,13 @@ type NamespaceInspectOptions struct {
// Format the output using the given Go template, e.g, '{{json .}}'
Format string
}

// NamespaceListOptions specifies options for `nerdctl namespace ls`.
type NamespaceListOptions struct {
Stdout io.Writer
GOptions GlobalCommandOptions
// Format the output using the given Go template, e.g, '{{json .}}'
Format string
// Quiet suppresses extra information and only prints namespace names
Quiet bool
}
153 changes: 153 additions & 0 deletions pkg/cmd/namespace/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
Copyright The containerd Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package namespace

import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"strings"
"text/tabwriter"
"text/template"

containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/log"

"github.com/containerd/nerdctl/v2/pkg/api/types"
"github.com/containerd/nerdctl/v2/pkg/clientutil"
"github.com/containerd/nerdctl/v2/pkg/formatter"
"github.com/containerd/nerdctl/v2/pkg/mountutil/volumestore"
)

func List(ctx context.Context, client *containerd.Client, options types.NamespaceListOptions) error {
nsStore := client.NamespaceService()
nsList, err := nsStore.List(ctx)
if err != nil {
return err
}

dataStore, err := clientutil.DataStore(options.GOptions.DataRoot, options.GOptions.Address)
if err != nil {
return err
}

w := options.Stdout
var tmpl *template.Template
namespaceList := []namespace{}
for _, ns := range nsList {
ctx = namespaces.WithNamespace(ctx, ns)
var numContainers, numImages, numVolumes int

containers, err := client.Containers(ctx)
if err != nil {
log.L.Warn(err)
}
numContainers = len(containers)

images, err := client.ImageService().List(ctx)
if err != nil {
log.L.Warn(err)
}
numImages = len(images)

volStore, err := volumestore.New(dataStore, ns)
if err != nil {
log.L.Warn(err)
} else {
numVolumes, err = volStore.Count()
if err != nil {
log.L.Warn(err)
}
}

labels, err := client.NamespaceService().Labels(ctx, ns)
if err != nil {
return err
}
namespaceList = append(namespaceList, namespace{
Name: ns,
Containers: numContainers,
Images: numImages,
Volumes: numVolumes,
Labels: labels,
})
}

switch options.Format {
case "", "table", "wide":
if !options.Quiet {
w = tabwriter.NewWriter(w, 4, 8, 4, ' ', 0)
// no "NETWORKS", because networks are global objects
fmt.Fprintln(w, "NAME\tCONTAINERS\tIMAGES\tVOLUMES\tLABELS")
}
case "raw":
return errors.New("unsupported format: \"raw\"")
default:
if options.Quiet {
return errors.New("format and quiet must not be specified together")
}
var err error
tmpl, err = formatter.ParseTemplate(options.Format)
if err != nil {
return err
}
}

for _, namespace := range namespaceList {
if tmpl != nil {
var b bytes.Buffer
if err := tmpl.Execute(&b, namespace); err != nil {
return err
}
if _, err := fmt.Fprintln(w, b.String()); err != nil {
return err
}
} else if options.Quiet {
if _, err := fmt.Fprintln(w, namespace.Name); err != nil {
return err
}
} else {
format := "%s\t%d\t%d\t%d\t%v\t\n"
var labelStrings []string
for k, v := range namespace.Labels {
labelStrings = append(labelStrings, strings.Join([]string{k, v}, "="))
}
sort.Strings(labelStrings)
args := []interface{}{}
args = append(args, namespace.Name, namespace.Containers, namespace.Images, namespace.Volumes, strings.Join(labelStrings, ","))
if _, err := fmt.Fprintf(w, format, args...); err != nil {
return err
}
}
}

if f, ok := w.(formatter.Flusher); ok {
return f.Flush()
}
return nil
}

type namespace struct {
Name string
Containers int
Images int
Volumes int
Labels map[string]string
}