 dd0542f7c1
			
		
	
	dd0542f7c1
	
	
	
		
			
			Unfortunately, this is a rather large diff, but perhaps worth a one-time "rip off the bandaid" for v2. This patch removes the use of "gocontext" as alias for stdLib's "context", and uses "cliContext" for uses of cli.context. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
			
				
	
	
		
			302 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			302 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
|    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 containers
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 	"text/tabwriter"
 | |
| 
 | |
| 	containerd "github.com/containerd/containerd/v2/client"
 | |
| 	"github.com/containerd/containerd/v2/cmd/ctr/commands"
 | |
| 	"github.com/containerd/containerd/v2/cmd/ctr/commands/run"
 | |
| 	"github.com/containerd/containerd/v2/core/containers"
 | |
| 	"github.com/containerd/containerd/v2/pkg/cio"
 | |
| 	"github.com/containerd/errdefs"
 | |
| 	"github.com/containerd/log"
 | |
| 	"github.com/containerd/typeurl/v2"
 | |
| 	"github.com/urfave/cli/v2"
 | |
| )
 | |
| 
 | |
| // Command is the cli command for managing containers
 | |
| var Command = &cli.Command{
 | |
| 	Name:    "containers",
 | |
| 	Usage:   "Manage containers",
 | |
| 	Aliases: []string{"c", "container"},
 | |
| 	Subcommands: []*cli.Command{
 | |
| 		createCommand,
 | |
| 		deleteCommand,
 | |
| 		infoCommand,
 | |
| 		listCommand,
 | |
| 		setLabelsCommand,
 | |
| 		checkpointCommand,
 | |
| 		restoreCommand,
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var createCommand = &cli.Command{
 | |
| 	Name:      "create",
 | |
| 	Usage:     "Create container",
 | |
| 	ArgsUsage: "[flags] Image|RootFS CONTAINER [COMMAND] [ARG...]",
 | |
| 	Flags:     append(commands.RuntimeFlags, append(append(commands.SnapshotterFlags, []cli.Flag{commands.SnapshotterLabels}...), commands.ContainerFlags...)...),
 | |
| 	Action: func(cliContext *cli.Context) error {
 | |
| 		var (
 | |
| 			id     string
 | |
| 			ref    string
 | |
| 			config = cliContext.IsSet("config")
 | |
| 		)
 | |
| 
 | |
| 		if config {
 | |
| 			id = cliContext.Args().First()
 | |
| 			if cliContext.NArg() > 1 {
 | |
| 				return fmt.Errorf("with spec config file, only container id should be provided: %w", errdefs.ErrInvalidArgument)
 | |
| 			}
 | |
| 		} else {
 | |
| 			id = cliContext.Args().Get(1)
 | |
| 			ref = cliContext.Args().First()
 | |
| 			if ref == "" {
 | |
| 				return fmt.Errorf("image ref must be provided: %w", errdefs.ErrInvalidArgument)
 | |
| 			}
 | |
| 		}
 | |
| 		if id == "" {
 | |
| 			return fmt.Errorf("container id must be provided: %w", errdefs.ErrInvalidArgument)
 | |
| 		}
 | |
| 		client, ctx, cancel, err := commands.NewClient(cliContext)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer cancel()
 | |
| 		_, err = run.NewContainer(ctx, client, cliContext)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		return nil
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var listCommand = &cli.Command{
 | |
| 	Name:      "list",
 | |
| 	Aliases:   []string{"ls"},
 | |
| 	Usage:     "List containers",
 | |
| 	ArgsUsage: "[flags] [<filter>, ...]",
 | |
| 	Flags: []cli.Flag{
 | |
| 		&cli.BoolFlag{
 | |
| 			Name:    "quiet",
 | |
| 			Aliases: []string{"q"},
 | |
| 			Usage:   "Print only the container id",
 | |
| 		},
 | |
| 	},
 | |
| 	Action: func(cliContext *cli.Context) error {
 | |
| 		var (
 | |
| 			filters = cliContext.Args().Slice()
 | |
| 			quiet   = cliContext.Bool("quiet")
 | |
| 		)
 | |
| 		client, ctx, cancel, err := commands.NewClient(cliContext)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer cancel()
 | |
| 		containers, err := client.Containers(ctx, filters...)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if quiet {
 | |
| 			for _, c := range containers {
 | |
| 				fmt.Printf("%s\n", c.ID())
 | |
| 			}
 | |
| 			return nil
 | |
| 		}
 | |
| 		w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
 | |
| 		fmt.Fprintln(w, "CONTAINER\tIMAGE\tRUNTIME\t")
 | |
| 		for _, c := range containers {
 | |
| 			info, err := c.Info(ctx, containerd.WithoutRefreshedMetadata)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			imageName := info.Image
 | |
| 			if imageName == "" {
 | |
| 				imageName = "-"
 | |
| 			}
 | |
| 			if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t\n",
 | |
| 				c.ID(),
 | |
| 				imageName,
 | |
| 				info.Runtime.Name,
 | |
| 			); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 		return w.Flush()
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var deleteCommand = &cli.Command{
 | |
| 	Name:      "delete",
 | |
| 	Usage:     "Delete one or more existing containers",
 | |
| 	ArgsUsage: "[flags] CONTAINER [CONTAINER, ...]",
 | |
| 	Aliases:   []string{"del", "remove", "rm"},
 | |
| 	Flags: []cli.Flag{
 | |
| 		&cli.BoolFlag{
 | |
| 			Name:  "keep-snapshot",
 | |
| 			Usage: "Do not clean up snapshot with container",
 | |
| 		},
 | |
| 	},
 | |
| 	Action: func(cliContext *cli.Context) error {
 | |
| 		var exitErr error
 | |
| 		client, ctx, cancel, err := commands.NewClient(cliContext)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer cancel()
 | |
| 		deleteOpts := []containerd.DeleteOpts{}
 | |
| 		if !cliContext.Bool("keep-snapshot") {
 | |
| 			deleteOpts = append(deleteOpts, containerd.WithSnapshotCleanup)
 | |
| 		}
 | |
| 
 | |
| 		if cliContext.NArg() == 0 {
 | |
| 			return fmt.Errorf("must specify at least one container to delete: %w", errdefs.ErrInvalidArgument)
 | |
| 		}
 | |
| 		for _, arg := range cliContext.Args().Slice() {
 | |
| 			if err := deleteContainer(ctx, client, arg, deleteOpts...); err != nil {
 | |
| 				if exitErr == nil {
 | |
| 					exitErr = err
 | |
| 				}
 | |
| 				log.G(ctx).WithError(err).Errorf("failed to delete container %q", arg)
 | |
| 			}
 | |
| 		}
 | |
| 		return exitErr
 | |
| 	},
 | |
| }
 | |
| 
 | |
| func deleteContainer(ctx context.Context, client *containerd.Client, id string, opts ...containerd.DeleteOpts) error {
 | |
| 	container, err := client.LoadContainer(ctx, id)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	task, err := container.Task(ctx, cio.Load)
 | |
| 	if err != nil {
 | |
| 		return container.Delete(ctx, opts...)
 | |
| 	}
 | |
| 	status, err := task.Status(ctx)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if status.Status == containerd.Stopped || status.Status == containerd.Created {
 | |
| 		if _, err := task.Delete(ctx); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		return container.Delete(ctx, opts...)
 | |
| 	}
 | |
| 	return fmt.Errorf("cannot delete a non stopped container: %v", status)
 | |
| 
 | |
| }
 | |
| 
 | |
| var setLabelsCommand = &cli.Command{
 | |
| 	Name:        "label",
 | |
| 	Usage:       "Set and clear labels for a container",
 | |
| 	ArgsUsage:   "[flags] CONTAINER [<key>=<value>, ...]",
 | |
| 	Description: "set and clear labels for a container",
 | |
| 	Flags:       []cli.Flag{},
 | |
| 	Action: func(cliContext *cli.Context) error {
 | |
| 		containerID, labels := commands.ObjectWithLabelArgs(cliContext)
 | |
| 		if containerID == "" {
 | |
| 			return fmt.Errorf("container id must be provided: %w", errdefs.ErrInvalidArgument)
 | |
| 		}
 | |
| 		client, ctx, cancel, err := commands.NewClient(cliContext)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer cancel()
 | |
| 
 | |
| 		container, err := client.LoadContainer(ctx, containerID)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		setlabels, err := container.SetLabels(ctx, labels)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		var labelStrings []string
 | |
| 		for k, v := range setlabels {
 | |
| 			labelStrings = append(labelStrings, fmt.Sprintf("%s=%s", k, v))
 | |
| 		}
 | |
| 
 | |
| 		fmt.Println(strings.Join(labelStrings, ","))
 | |
| 
 | |
| 		return nil
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var infoCommand = &cli.Command{
 | |
| 	Name:      "info",
 | |
| 	Usage:     "Get info about a container",
 | |
| 	ArgsUsage: "CONTAINER",
 | |
| 	Flags: []cli.Flag{
 | |
| 		&cli.BoolFlag{
 | |
| 			Name:  "spec",
 | |
| 			Usage: "Only display the spec",
 | |
| 		},
 | |
| 	},
 | |
| 	Action: func(cliContext *cli.Context) error {
 | |
| 		id := cliContext.Args().First()
 | |
| 		if id == "" {
 | |
| 			return fmt.Errorf("container id must be provided: %w", errdefs.ErrInvalidArgument)
 | |
| 		}
 | |
| 		client, ctx, cancel, err := commands.NewClient(cliContext)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer cancel()
 | |
| 		container, err := client.LoadContainer(ctx, id)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		info, err := container.Info(ctx, containerd.WithoutRefreshedMetadata)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if cliContext.Bool("spec") {
 | |
| 			v, err := typeurl.UnmarshalAny(info.Spec)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			commands.PrintAsJSON(v)
 | |
| 			return nil
 | |
| 		}
 | |
| 
 | |
| 		if info.Spec != nil && info.Spec.GetValue() != nil {
 | |
| 			v, err := typeurl.UnmarshalAny(info.Spec)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			commands.PrintAsJSON(struct {
 | |
| 				containers.Container
 | |
| 				Spec interface{} `json:"Spec,omitempty"`
 | |
| 			}{
 | |
| 				Container: info,
 | |
| 				Spec:      v,
 | |
| 			})
 | |
| 			return nil
 | |
| 		}
 | |
| 		commands.PrintAsJSON(info)
 | |
| 		return nil
 | |
| 	},
 | |
| }
 |