containerd/cmd/ctr/commands/containers/containers.go
Michael Crosby 61d930ad5b Move specific errors to their respective packages
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
2019-07-15 20:21:11 +00:00

286 lines
6.7 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"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/run"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/log"
"github.com/containerd/typeurl"
"github.com/urfave/cli"
)
// 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.SnapshotterFlags, commands.ContainerFlags...),
Action: func(context *cli.Context) error {
var (
id string
ref string
config = context.IsSet("config")
)
if config {
id = context.Args().First()
if context.NArg() > 1 {
return commands.ErrArgConfigFile
}
} else {
id = context.Args().Get(1)
ref = context.Args().First()
if ref == "" {
return commands.ErrUnprovidedImageRef
}
}
if id == "" {
return commands.ErrEmptyContainerID
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
_, err = run.NewContainer(ctx, client, context)
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, q",
Usage: "print only the container id",
},
},
Action: func(context *cli.Context) error {
var (
filters = context.Args()
quiet = context.Bool("quiet")
)
client, ctx, cancel, err := commands.NewClient(context)
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", "rm"},
Flags: []cli.Flag{
cli.BoolFlag{
Name: "keep-snapshot",
Usage: "do not clean up snapshot with container",
},
},
Action: func(context *cli.Context) error {
var exitErr error
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
deleteOpts := []containerd.DeleteOpts{}
if !context.Bool("keep-snapshot") {
deleteOpts = append(deleteOpts, containerd.WithSnapshotCleanup)
}
if context.NArg() == 0 {
return commands.ErrDeleteNoneContainer
}
for _, arg := range context.Args() {
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(context *cli.Context) error {
containerID, labels := commands.ObjectWithLabelArgs(context)
if containerID == "" {
return commands.ErrEmptyContainerID
}
client, ctx, cancel, err := commands.NewClient(context)
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",
Action: func(context *cli.Context) error {
id := context.Args().First()
if id == "" {
return commands.ErrEmptyContainerID
}
client, ctx, cancel, err := commands.NewClient(context)
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 info.Spec != nil && info.Spec.Value != 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
},
}