Commit 76a19184 authored by Darren Shepherd's avatar Darren Shepherd

Update vendor

parent 6c294dab
apiVersion: k3s.cattle.io/v1
kind: HelmChart
metadata:
name: nginx-ingress
namespace: kube-system
spec:
chart: stable/nginx-ingress
...@@ -9,9 +9,9 @@ package=github.com/opencontainers/runc/libcontainer/nsenter ...@@ -9,9 +9,9 @@ package=github.com/opencontainers/runc/libcontainer/nsenter
package=github.com/opencontainers/runc/libcontainer/specconv package=github.com/opencontainers/runc/libcontainer/specconv
package=github.com/opencontainers/runc/contrib/cmd/recvtty package=github.com/opencontainers/runc/contrib/cmd/recvtty
k8s.io/kubernetes v1.13.3-klp1 file:///home/darren/src/kuberlite/.git transitive=true,staging=true k8s.io/kubernetes v1.13.3-k3s2 https://github.com/ibuildthecloud/k3s.git transitive=true,staging=true
github.com/rancher/norman d640c72bf09d9fbff596dead44338453bba718cc https://github.com/ibuildthecloud/norman.git github.com/rancher/norman e4f12a1656cb04b4b2a25c571fc05283ec25f946 https://github.com/ibuildthecloud/norman.git
github.com/coreos/flannel 823afe66b2266bf71f5bec24e6e28b26d70cfc7c https://github.com/ibuildthecloud/flannel.git github.com/coreos/flannel 823afe66b2266bf71f5bec24e6e28b26d70cfc7c https://github.com/ibuildthecloud/flannel.git
github.com/natefinch/lumberjack aee4629129445bbdfb69aa565537dcfa16544311 github.com/natefinch/lumberjack aee4629129445bbdfb69aa565537dcfa16544311
github.com/gorilla/mux v1.6.2 github.com/gorilla/mux v1.6.2
...@@ -36,7 +36,7 @@ github.com/alexflint/go-filemutex 72bdc8eae2aef913234599b837f5dda445ca9bd9 ...@@ -36,7 +36,7 @@ github.com/alexflint/go-filemutex 72bdc8eae2aef913234599b837f5dda445ca9bd9
github.com/opencontainers/runtime-spec 5684b8af48c1ac3b1451fa499724e30e3c20a294 github.com/opencontainers/runtime-spec 5684b8af48c1ac3b1451fa499724e30e3c20a294
# containerd # containerd
github.com/containerd/containerd v1.2.2-k3s2 https://github.com/ibuildthecloud/containerd github.com/containerd/containerd v1.2.3-k3s1 https://github.com/ibuildthecloud/containerd
github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3
github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
...@@ -47,7 +47,7 @@ github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 ...@@ -47,7 +47,7 @@ github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4
github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6 github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098 github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/docker/go-units v0.3.1 #github.com/docker/go-units v0.3.3
github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f
#github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823 #github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823
#github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c #github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
...@@ -59,7 +59,7 @@ github.com/gogo/protobuf v1.0.0 ...@@ -59,7 +59,7 @@ github.com/gogo/protobuf v1.0.0
github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef
github.com/golang/protobuf v1.1.0 github.com/golang/protobuf v1.1.0
#github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 # v1.0.1-45-geba862d #github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 # v1.0.1-45-geba862d
github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb github.com/opencontainers/runc 12f6a991201fdb8f82579582d5e00e28fba06d0a
github.com/sirupsen/logrus v1.0.3 github.com/sirupsen/logrus v1.0.3
github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
...@@ -81,8 +81,11 @@ gotest.tools v2.1.0 ...@@ -81,8 +81,11 @@ gotest.tools v2.1.0
github.com/google/go-cmp v0.1.0 github.com/google/go-cmp v0.1.0
go.etcd.io/bbolt v1.3.1-etcd.8 go.etcd.io/bbolt v1.3.1-etcd.8
# crictl
github.com/kubernetes-sigs/cri-tools c465773e3ad8c941d1756c0a467d550b04a8f65b https://github.com/ibuildthecloud/cri-tools.git
# cri dependencies # cri dependencies
github.com/containerd/cri 0d5cabd006cb5319dc965046067b8432d9fa5ef8 # release/1.2 branch github.com/containerd/cri c3cf754321fc38c6af5dfd2552fdde0ad192b31d # release/1.2 branch
github.com/containerd/go-cni 40bcf8ec8acd7372be1d77031d585d5d8e561c90 github.com/containerd/go-cni 40bcf8ec8acd7372be1d77031d585d5d8e561c90
github.com/blang/semver v3.1.0 github.com/blang/semver v3.1.0
github.com/containernetworking/cni v0.6.0 github.com/containernetworking/cni v0.6.0
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
"Enable": [ "Enable": [
"structcheck", "structcheck",
"unused",
"varcheck", "varcheck",
"staticcheck", "staticcheck",
"unconvert", "unconvert",
......
...@@ -24,3 +24,5 @@ Jian Liao <jliao@alauda.io> liaojian <liaojian@Dabllo.local> ...@@ -24,3 +24,5 @@ Jian Liao <jliao@alauda.io> liaojian <liaojian@Dabllo.local>
Rui Cao <ruicao@alauda.io> ruicao <ruicao@alauda.io> Rui Cao <ruicao@alauda.io> ruicao <ruicao@alauda.io>
Xuean Yan <yan.xuean@zte.com.cn> yanxuean <yan.xuean@zte.com.cn> Xuean Yan <yan.xuean@zte.com.cn> yanxuean <yan.xuean@zte.com.cn>
Mike Brown <brownwm@us.ibm.com> Mike Brown <mikebrow@users.noreply.github.com> Mike Brown <brownwm@us.ibm.com> Mike Brown <mikebrow@users.noreply.github.com>
Ace-Tang <aceapril@126.com>
Wei Fu <fuweid89@gmail.com>
...@@ -100,7 +100,7 @@ const ( ...@@ -100,7 +100,7 @@ const (
// readdir calls to this directory do not follow to lower layers. // readdir calls to this directory do not follow to lower layers.
whiteoutOpaqueDir = whiteoutMetaPrefix + ".opq" whiteoutOpaqueDir = whiteoutMetaPrefix + ".opq"
paxSchilyXattr = "SCHILY.xattrs." paxSchilyXattr = "SCHILY.xattr."
) )
// Apply applies a tar stream of an OCI style diff tar. // Apply applies a tar stream of an OCI style diff tar.
...@@ -295,7 +295,7 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO ...@@ -295,7 +295,7 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO
linkBasename := filepath.Base(hdr.Linkname) linkBasename := filepath.Base(hdr.Linkname)
srcHdr = aufsHardlinks[linkBasename] srcHdr = aufsHardlinks[linkBasename]
if srcHdr == nil { if srcHdr == nil {
return 0, fmt.Errorf("Invalid aufs hardlink") return 0, fmt.Errorf("invalid aufs hardlink")
} }
p, err := fs.RootPath(aufsTempdir, linkBasename) p, err := fs.RootPath(aufsTempdir, linkBasename)
if err != nil { if err != nil {
......
...@@ -74,7 +74,7 @@ func tarName(p string) (string, error) { ...@@ -74,7 +74,7 @@ func tarName(p string) (string, error) {
// in file names, it is mostly safe to replace however we must // in file names, it is mostly safe to replace however we must
// check just in case // check just in case
if strings.Contains(p, "/") { if strings.Contains(p, "/") {
return "", fmt.Errorf("Windows path contains forward slash: %s", p) return "", fmt.Errorf("windows path contains forward slash: %s", p)
} }
return strings.Replace(p, string(os.PathSeparator), "/", -1), nil return strings.Replace(p, string(os.PathSeparator), "/", -1), nil
...@@ -130,11 +130,7 @@ func skipFile(hdr *tar.Header) bool { ...@@ -130,11 +130,7 @@ func skipFile(hdr *tar.Header) bool {
// specific or Linux-specific, this warning should be changed to an error // specific or Linux-specific, this warning should be changed to an error
// to cater for the situation where someone does manage to upload a Linux // to cater for the situation where someone does manage to upload a Linux
// image but have it tagged as Windows inadvertently. // image but have it tagged as Windows inadvertently.
if strings.Contains(hdr.Name, ":") { return strings.Contains(hdr.Name, ":")
return true
}
return false
} }
// handleTarTypeBlockCharFifo is an OS-specific helper function used by // handleTarTypeBlockCharFifo is an OS-specific helper function used by
......
...@@ -338,7 +338,8 @@ func (c *Client) Fetch(ctx context.Context, ref string, opts ...RemoteOpt) (imag ...@@ -338,7 +338,8 @@ func (c *Client) Fetch(ctx context.Context, ref string, opts ...RemoteOpt) (imag
} }
defer done(ctx) defer done(ctx)
return c.fetch(ctx, fetchCtx, ref, 0) img, _, err := c.fetch(ctx, fetchCtx, ref, 0)
return img, err
} }
// Pull downloads the provided content into containerd's content store // Pull downloads the provided content into containerd's content store
...@@ -372,12 +373,12 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image ...@@ -372,12 +373,12 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
} }
defer done(ctx) defer done(ctx)
img, err := c.fetch(ctx, pullCtx, ref, 1) img, imagePlatforms, err := c.fetch(ctx, pullCtx, ref, 1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
i := NewImageWithPlatform(c, img, pullCtx.PlatformMatcher) i := NewImageWithPlatform(c, img, imagePlatforms)
if pullCtx.Unpack { if pullCtx.Unpack {
if err := i.Unpack(ctx, pullCtx.Snapshotter); err != nil { if err := i.Unpack(ctx, pullCtx.Snapshotter); err != nil {
...@@ -388,25 +389,36 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image ...@@ -388,25 +389,36 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
return i, nil return i, nil
} }
func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, limit int) (images.Image, error) { func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, limit int) (images.Image, platforms.MatchComparer, error) {
platformCapture := images.NewPlatformCapture(rCtx.PlatformMatcher)
store := c.ContentStore() store := c.ContentStore()
name, desc, err := rCtx.Resolver.Resolve(ctx, ref) name, desc, err := rCtx.Resolver.Resolve(ctx, ref)
if err != nil { if err != nil {
return images.Image{}, errors.Wrapf(err, "failed to resolve reference %q", ref) return images.Image{}, nil, errors.Wrapf(err, "failed to resolve reference %q", ref)
} }
fetcher, err := rCtx.Resolver.Fetcher(ctx, name) fetcher, err := rCtx.Resolver.Fetcher(ctx, name)
if err != nil { if err != nil {
return images.Image{}, errors.Wrapf(err, "failed to get fetcher for %q", name) return images.Image{}, nil, errors.Wrapf(err, "failed to get fetcher for %q", name)
} }
var ( var (
schema1Converter *schema1.Converter handler images.Handler
handler images.Handler
isConvertible bool
converterFunc func(context.Context, ocispec.Descriptor) (ocispec.Descriptor, error)
) )
if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 { if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 {
schema1Converter = schema1.NewConverter(store, fetcher) schema1Converter := schema1.NewConverter(store, fetcher)
handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...) handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...)
isConvertible = true
converterFunc = func(ctx context.Context, _ ocispec.Descriptor) (ocispec.Descriptor, error) {
return schema1Converter.Convert(ctx)
}
} else { } else {
// Get all the children for a descriptor // Get all the children for a descriptor
childrenHandler := images.ChildrenHandler(store) childrenHandler := images.ChildrenHandler(store)
...@@ -419,19 +431,38 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim ...@@ -419,19 +431,38 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim
childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit) childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit)
} }
// Save the platforms that match the PlatformMatcher
childrenHandler = platformCapture.Handler(childrenHandler)
// set isConvertible to true if there is application/octet-stream media type
convertibleHandler := images.HandlerFunc(
func(_ context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if desc.MediaType == docker.LegacyConfigMediaType {
isConvertible = true
}
return []ocispec.Descriptor{}, nil
},
)
handler = images.Handlers(append(rCtx.BaseHandlers, handler = images.Handlers(append(rCtx.BaseHandlers,
remotes.FetchHandler(store, fetcher), remotes.FetchHandler(store, fetcher),
convertibleHandler,
childrenHandler, childrenHandler,
)...) )...)
converterFunc = func(ctx context.Context, desc ocispec.Descriptor) (ocispec.Descriptor, error) {
return docker.ConvertManifest(ctx, store, desc)
}
} }
if err := images.Dispatch(ctx, handler, desc); err != nil { if err := images.Dispatch(ctx, handler, desc); err != nil {
return images.Image{}, err return images.Image{}, nil, err
} }
if schema1Converter != nil {
desc, err = schema1Converter.Convert(ctx) if isConvertible {
if err != nil { if desc, err = converterFunc(ctx, desc); err != nil {
return images.Image{}, err return images.Image{}, nil, err
} }
} }
...@@ -445,7 +476,7 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim ...@@ -445,7 +476,7 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim
for { for {
if created, err := is.Create(ctx, img); err != nil { if created, err := is.Create(ctx, img); err != nil {
if !errdefs.IsAlreadyExists(err) { if !errdefs.IsAlreadyExists(err) {
return images.Image{}, err return images.Image{}, nil, err
} }
updated, err := is.Update(ctx, img) updated, err := is.Update(ctx, img)
...@@ -454,7 +485,7 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim ...@@ -454,7 +485,7 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim
if errdefs.IsNotFound(err) { if errdefs.IsNotFound(err) {
continue continue
} }
return images.Image{}, err return images.Image{}, nil, err
} }
img = updated img = updated
...@@ -462,7 +493,7 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim ...@@ -462,7 +493,7 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim
img = created img = created
} }
return img, nil return img, platformCapture.MatchComparer(), nil
} }
} }
......
/*
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 app
import (
"fmt"
"io/ioutil"
"github.com/containerd/containerd/cmd/ctr/commands/containers"
"github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/cmd/ctr/commands/events"
"github.com/containerd/containerd/cmd/ctr/commands/images"
"github.com/containerd/containerd/cmd/ctr/commands/install"
"github.com/containerd/containerd/cmd/ctr/commands/leases"
namespacesCmd "github.com/containerd/containerd/cmd/ctr/commands/namespaces"
"github.com/containerd/containerd/cmd/ctr/commands/plugins"
"github.com/containerd/containerd/cmd/ctr/commands/pprof"
"github.com/containerd/containerd/cmd/ctr/commands/run"
"github.com/containerd/containerd/cmd/ctr/commands/snapshots"
"github.com/containerd/containerd/cmd/ctr/commands/tasks"
versionCmd "github.com/containerd/containerd/cmd/ctr/commands/version"
"github.com/containerd/containerd/defaults"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/version"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"google.golang.org/grpc/grpclog"
)
var extraCmds = []cli.Command{}
func init() {
// Discard grpc logs so that they don't mess with our stdio
grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard))
cli.VersionPrinter = func(c *cli.Context) {
fmt.Println(c.App.Name, version.Package, c.App.Version)
}
}
// New returns a *cli.App instance.
func New() *cli.App {
app := cli.NewApp()
app.Name = "ctr"
app.Version = version.Version
app.Usage = `
__
_____/ /______
/ ___/ __/ ___/
/ /__/ /_/ /
\___/\__/_/
containerd CLI
`
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "debug",
Usage: "enable debug output in logs",
},
cli.StringFlag{
Name: "address, a",
Usage: "address for containerd's GRPC server",
Value: defaults.DefaultAddress,
},
cli.DurationFlag{
Name: "timeout",
Usage: "total timeout for ctr commands",
},
cli.DurationFlag{
Name: "connect-timeout",
Usage: "timeout for connecting to containerd",
},
cli.StringFlag{
Name: "namespace, n",
Usage: "namespace to use with commands",
Value: namespaces.Default,
EnvVar: namespaces.NamespaceEnvVar,
},
}
app.Commands = append([]cli.Command{
plugins.Command,
versionCmd.Command,
containers.Command,
content.Command,
events.Command,
images.Command,
leases.Command,
namespacesCmd.Command,
pprof.Command,
run.Command,
snapshots.Command,
tasks.Command,
install.Command,
}, extraCmds...)
app.Before = func(context *cli.Context) error {
if context.GlobalBool("debug") {
logrus.SetLevel(logrus.DebugLevel)
}
return nil
}
return app
}
// +build !windows
/*
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 app
import "github.com/containerd/containerd/cmd/ctr/commands/shim"
func init() {
extraCmds = append(extraCmds, shim.Command)
}
/*
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 commands
import (
gocontext "context"
"github.com/containerd/containerd"
"github.com/containerd/containerd/namespaces"
"github.com/urfave/cli"
)
// AppContext returns the context for a command. Should only be called once per
// command, near the start.
//
// This will ensure the namespace is picked up and set the timeout, if one is
// defined.
func AppContext(context *cli.Context) (gocontext.Context, gocontext.CancelFunc) {
var (
ctx = gocontext.Background()
timeout = context.GlobalDuration("timeout")
namespace = context.GlobalString("namespace")
cancel gocontext.CancelFunc
)
ctx = namespaces.WithNamespace(ctx, namespace)
if timeout > 0 {
ctx, cancel = gocontext.WithTimeout(ctx, timeout)
} else {
ctx, cancel = gocontext.WithCancel(ctx)
}
return ctx, cancel
}
// NewClient returns a new containerd client
func NewClient(context *cli.Context, opts ...containerd.ClientOpt) (*containerd.Client, gocontext.Context, gocontext.CancelFunc, error) {
client, err := containerd.New(context.GlobalString("address"), opts...)
if err != nil {
return nil, nil, nil, err
}
ctx, cancel := AppContext(context)
return client, ctx, cancel, nil
}
/*
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 commands
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/containerd/containerd"
"github.com/urfave/cli"
)
var (
// SnapshotterFlags are cli flags specifying snapshotter names
SnapshotterFlags = []cli.Flag{
cli.StringFlag{
Name: "snapshotter",
Usage: "snapshotter name. Empty value stands for the default value.",
Value: containerd.DefaultSnapshotter,
EnvVar: "CONTAINERD_SNAPSHOTTER",
},
}
// LabelFlag is a cli flag specifying labels
LabelFlag = cli.StringSliceFlag{
Name: "label",
Usage: "labels to attach to the image",
}
// RegistryFlags are cli flags specifying registry options
RegistryFlags = []cli.Flag{
cli.BoolFlag{
Name: "skip-verify,k",
Usage: "skip SSL certificate validation",
},
cli.BoolFlag{
Name: "plain-http",
Usage: "allow connections using plain HTTP",
},
cli.StringFlag{
Name: "user,u",
Usage: "user[:password] Registry user and password",
},
cli.StringFlag{
Name: "refresh",
Usage: "refresh token for authorization server",
},
}
// ContainerFlags are cli flags specifying container options
ContainerFlags = []cli.Flag{
cli.StringFlag{
Name: "config,c",
Usage: "path to the runtime-specific spec config file",
},
cli.StringFlag{
Name: "checkpoint",
Usage: "provide the checkpoint digest to restore the container",
},
cli.StringFlag{
Name: "cwd",
Usage: "specify the working directory of the process",
},
cli.StringSliceFlag{
Name: "env",
Usage: "specify additional container environment variables (i.e. FOO=bar)",
},
cli.StringSliceFlag{
Name: "label",
Usage: "specify additional labels (i.e. foo=bar)",
},
cli.StringSliceFlag{
Name: "mount",
Usage: "specify additional container mount (ex: type=bind,src=/tmp,dst=/host,options=rbind:ro)",
},
cli.BoolFlag{
Name: "net-host",
Usage: "enable host networking for the container",
},
cli.BoolFlag{
Name: "privileged",
Usage: "run privileged container",
},
cli.BoolFlag{
Name: "read-only",
Usage: "set the containers filesystem as readonly",
},
cli.StringFlag{
Name: "runtime",
Usage: "runtime name",
Value: fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS),
},
cli.BoolFlag{
Name: "tty,t",
Usage: "allocate a TTY for the container",
},
cli.StringSliceFlag{
Name: "with-ns",
Usage: "specify existing Linux namespaces to join at container runtime (format '<nstype>:<path>')",
},
cli.StringFlag{
Name: "pid-file",
Usage: "file path to write the task's pid",
},
cli.IntFlag{
Name: "gpus",
Usage: "add gpus to the container",
},
cli.BoolFlag{
Name: "allow-new-privs",
Usage: "turn off OCI spec's NoNewPrivileges feature flag",
},
}
)
// ObjectWithLabelArgs returns the first arg and a LabelArgs object
func ObjectWithLabelArgs(clicontext *cli.Context) (string, map[string]string) {
var (
first = clicontext.Args().First()
labelStrings = clicontext.Args().Tail()
)
return first, LabelArgs(labelStrings)
}
// LabelArgs returns a map of label key,value pairs
func LabelArgs(labelStrings []string) map[string]string {
labels := make(map[string]string, len(labelStrings))
for _, label := range labelStrings {
parts := strings.SplitN(label, "=", 2)
key := parts[0]
value := "true"
if len(parts) > 1 {
value = parts[1]
}
labels[key] = value
}
return labels
}
// PrintAsJSON prints input in JSON format
func PrintAsJSON(x interface{}) {
b, err := json.MarshalIndent(x, "", " ")
if err != nil {
fmt.Fprintf(os.Stderr, "can't marshal %+v as a JSON string: %v\n", x, err)
}
fmt.Println(string(b))
}
// WritePidFile writes the pid atomically to a file
func WritePidFile(path string, pid int) error {
path, err := filepath.Abs(path)
if err != nil {
return err
}
tempPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path)))
f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666)
if err != nil {
return err
}
_, err = fmt.Fprintf(f, "%d", pid)
f.Close()
if err != nil {
return err
}
return os.Rename(tempPath, path)
}
// +build !windows
/*
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 commands
import (
"github.com/urfave/cli"
)
func init() {
ContainerFlags = append(ContainerFlags, cli.BoolFlag{
Name: "rootfs",
Usage: "use custom rootfs that is not managed by containerd snapshotter",
}, cli.BoolFlag{
Name: "no-pivot",
Usage: "disable use of pivot-root (linux only)",
})
}
/*
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"
"errors"
"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,
},
}
var createCommand = cli.Command{
Name: "create",
Usage: "create container",
ArgsUsage: "[flags] Image|RootFS CONTAINER",
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 errors.New("with spec config file, only container id should be provided")
}
} else {
id = context.Args().Get(1)
ref = context.Args().First()
if ref == "" {
return errors.New("image ref must be provided")
}
}
if id == "" {
return errors.New("container id must be provided")
}
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)
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 errors.New("must specify at least one container to delete")
}
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 errors.New("container id must be provided")
}
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 errors.New("container id must be provided")
}
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)
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
},
}
/*
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 events
import (
"encoding/json"
"fmt"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/events"
"github.com/containerd/typeurl"
"github.com/urfave/cli"
// Register grpc event types
_ "github.com/containerd/containerd/api/events"
)
// Command is the cli command for displaying containerd events
var Command = cli.Command{
Name: "events",
Aliases: []string{"event"},
Usage: "display containerd events",
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
eventsClient := client.EventService()
eventsCh, errCh := eventsClient.Subscribe(ctx, context.Args()...)
open := true
for open {
var e *events.Envelope
select {
case e = <-eventsCh:
case err, open = <-errCh:
return err
}
if e != nil {
var out []byte
if e.Event != nil {
v, err := typeurl.UnmarshalAny(e.Event)
if err != nil {
return err
}
out, err = json.Marshal(v)
if err != nil {
return err
}
}
if _, err := fmt.Println(
e.Timestamp,
e.Namespace,
e.Topic,
string(out),
); err != nil {
return err
}
}
}
return nil
},
}
/*
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 images
import (
"io"
"os"
"github.com/containerd/containerd/cmd/ctr/commands"
oci "github.com/containerd/containerd/images/oci"
"github.com/containerd/containerd/reference"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var exportCommand = cli.Command{
Name: "export",
Usage: "export an image",
ArgsUsage: "[flags] <out> <image>",
Description: `Export an image to a tar stream.
Currently, only OCI format is supported.
`,
Flags: []cli.Flag{
// TODO(AkihiroSuda): make this map[string]string as in moby/moby#33355?
cli.StringFlag{
Name: "oci-ref-name",
Value: "",
Usage: "override org.opencontainers.image.ref.name annotation",
},
cli.StringFlag{
Name: "manifest",
Usage: "digest of manifest",
},
cli.StringFlag{
Name: "manifest-type",
Usage: "media type of manifest digest",
Value: ocispec.MediaTypeImageManifest,
},
},
Action: func(context *cli.Context) error {
var (
out = context.Args().First()
local = context.Args().Get(1)
desc ocispec.Descriptor
)
if out == "" || local == "" {
return errors.New("please provide both an output filename and an image reference to export")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
if manifest := context.String("manifest"); manifest != "" {
desc.Digest, err = digest.Parse(manifest)
if err != nil {
return errors.Wrap(err, "invalid manifest digest")
}
desc.MediaType = context.String("manifest-type")
} else {
img, err := client.ImageService().Get(ctx, local)
if err != nil {
return errors.Wrap(err, "unable to resolve image to manifest")
}
desc = img.Target
}
if desc.Annotations == nil {
desc.Annotations = make(map[string]string)
}
if s, ok := desc.Annotations[ocispec.AnnotationRefName]; !ok || s == "" {
if ociRefName := determineOCIRefName(local); ociRefName != "" {
desc.Annotations[ocispec.AnnotationRefName] = ociRefName
}
if ociRefName := context.String("oci-ref-name"); ociRefName != "" {
desc.Annotations[ocispec.AnnotationRefName] = ociRefName
}
}
var w io.WriteCloser
if out == "-" {
w = os.Stdout
} else {
w, err = os.Create(out)
if err != nil {
return nil
}
}
r, err := client.Export(ctx, &oci.V1Exporter{}, desc)
if err != nil {
return err
}
if _, err := io.Copy(w, r); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
return r.Close()
},
}
func determineOCIRefName(local string) string {
refspec, err := reference.Parse(local)
if err != nil {
return ""
}
tag, _ := reference.SplitObject(refspec.Object)
return tag
}
/*
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 images
import (
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/pkg/progress"
"github.com/containerd/containerd/platforms"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// Command is the cli command for managing images
var Command = cli.Command{
Name: "images",
Aliases: []string{"image", "i"},
Usage: "manage images",
Subcommands: cli.Commands{
checkCommand,
exportCommand,
importCommand,
listCommand,
pullCommand,
pushCommand,
removeCommand,
setLabelsCommand,
},
}
var listCommand = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "list images known to containerd",
ArgsUsage: "[flags] <ref>",
Description: "list images registered with containerd",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the image refs",
},
},
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()
var (
imageStore = client.ImageService()
cs = client.ContentStore()
)
imageList, err := imageStore.List(ctx, filters...)
if err != nil {
return errors.Wrap(err, "failed to list images")
}
if quiet {
for _, image := range imageList {
fmt.Println(image.Name)
}
return nil
}
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
fmt.Fprintln(tw, "REF\tTYPE\tDIGEST\tSIZE\tPLATFORMS\tLABELS\t")
for _, image := range imageList {
size, err := image.Size(ctx, cs, platforms.Default())
if err != nil {
log.G(ctx).WithError(err).Errorf("failed calculating size for image %s", image.Name)
}
platformColumn := "-"
specs, err := images.Platforms(ctx, cs, image.Target)
if err != nil {
log.G(ctx).WithError(err).Errorf("failed resolving platform for image %s", image.Name)
} else if len(specs) > 0 {
psm := map[string]struct{}{}
for _, p := range specs {
psm[platforms.Format(p)] = struct{}{}
}
var ps []string
for p := range psm {
ps = append(ps, p)
}
sort.Stable(sort.StringSlice(ps))
platformColumn = strings.Join(ps, ",")
}
labels := "-"
if len(image.Labels) > 0 {
var pairs []string
for k, v := range image.Labels {
pairs = append(pairs, fmt.Sprintf("%v=%v", k, v))
}
sort.Strings(pairs)
labels = strings.Join(pairs, ",")
}
fmt.Fprintf(tw, "%v\t%v\t%v\t%v\t%v\t%s\t\n",
image.Name,
image.Target.MediaType,
image.Target.Digest,
progress.Bytes(size),
platformColumn,
labels)
}
return tw.Flush()
},
}
var setLabelsCommand = cli.Command{
Name: "label",
Usage: "set and clear labels for an image",
ArgsUsage: "[flags] <name> [<key>=<value>, ...]",
Description: "set and clear labels for an image",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "replace-all, r",
Usage: "replace all labels",
},
},
Action: func(context *cli.Context) error {
var (
replaceAll = context.Bool("replace-all")
name, labels = commands.ObjectWithLabelArgs(context)
)
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
if name == "" {
return errors.New("please specify an image")
}
var (
is = client.ImageService()
fieldpaths []string
)
for k := range labels {
if replaceAll {
fieldpaths = append(fieldpaths, "labels")
} else {
fieldpaths = append(fieldpaths, strings.Join([]string{"labels", k}, "."))
}
}
image := images.Image{
Name: name,
Labels: labels,
}
updated, err := is.Update(ctx, image, fieldpaths...)
if err != nil {
return err
}
var labelStrings []string
for k, v := range updated.Labels {
labelStrings = append(labelStrings, fmt.Sprintf("%s=%s", k, v))
}
fmt.Println(strings.Join(labelStrings, ","))
return nil
},
}
var checkCommand = cli.Command{
Name: "check",
Usage: "check that an image has all content available locally",
ArgsUsage: "[flags] <ref> [<ref>, ...]",
Description: "check that an image has all content available locally",
Flags: commands.SnapshotterFlags,
Action: func(context *cli.Context) error {
var (
exitErr error
)
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
var (
contentStore = client.ContentStore()
tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
)
fmt.Fprintln(tw, "REF\tTYPE\tDIGEST\tSTATUS\tSIZE\tUNPACKED\t")
args := []string(context.Args())
imageList, err := client.ListImages(ctx, args...)
if err != nil {
return errors.Wrap(err, "failed listing images")
}
for _, image := range imageList {
var (
status string = "complete"
size string
requiredSize int64
presentSize int64
)
available, required, present, missing, err := images.Check(ctx, contentStore, image.Target(), platforms.Default())
if err != nil {
if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to check %v", image.Name())
}
log.G(ctx).WithError(err).Errorf("unable to check %v", image.Name())
status = "error"
}
if status != "error" {
for _, d := range required {
requiredSize += d.Size
}
for _, d := range present {
presentSize += d.Size
}
if len(missing) > 0 {
status = "incomplete"
}
if available {
status += fmt.Sprintf(" (%v/%v)", len(present), len(required))
size = fmt.Sprintf("%v/%v", progress.Bytes(presentSize), progress.Bytes(requiredSize))
} else {
status = fmt.Sprintf("unavailable (%v/?)", len(present))
size = fmt.Sprintf("%v/?", progress.Bytes(presentSize))
}
} else {
size = "-"
}
unpacked, err := image.IsUnpacked(ctx, context.String("snapshotter"))
if err != nil {
if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to check unpack for %v", image.Name())
}
log.G(ctx).WithError(err).Errorf("unable to check unpack for %v", image.Name())
}
fmt.Fprintf(tw, "%v\t%v\t%v\t%v\t%v\t%t\n",
image.Name(),
image.Target().MediaType,
image.Target().Digest,
status,
size,
unpacked)
}
tw.Flush()
return exitErr
},
}
var removeCommand = cli.Command{
Name: "remove",
Aliases: []string{"rm"},
Usage: "remove one or more images by reference",
ArgsUsage: "[flags] <ref> [<ref>, ...]",
Description: "remove one or more images by reference",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "sync",
Usage: "Synchronously remove image and all associated resources",
},
},
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
var (
exitErr error
imageStore = client.ImageService()
)
for i, target := range context.Args() {
var opts []images.DeleteOpt
if context.Bool("sync") && i == context.NArg()-1 {
opts = append(opts, images.SynchronousDelete())
}
if err := imageStore.Delete(ctx, target, opts...); err != nil {
if !errdefs.IsNotFound(err) {
if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to delete %v", target)
}
log.G(ctx).WithError(err).Errorf("unable to delete %v", target)
continue
}
// image ref not found in metadata store; log not found condition
log.G(ctx).Warnf("%v: image not found", target)
} else {
fmt.Println(target)
}
}
return exitErr
},
}
/*
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 images
import (
"fmt"
"io"
"os"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/images/archive"
"github.com/containerd/containerd/log"
"github.com/urfave/cli"
)
var importCommand = cli.Command{
Name: "import",
Usage: "import images",
ArgsUsage: "[flags] <in>",
Description: `Import images from a tar stream.
Implemented formats:
- oci.v1
- docker.v1.1
- docker.v1.2
For OCI v1, you may need to specify --base-name because an OCI archive may
contain only partial image references (tags without the base image name).
If no base image name is provided, a name will be generated as "import-%{yyyy-MM-dd}".
e.g.
$ ctr images import --base-name foo/bar foobar.tar
If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadbeef", the command will create
"foo/bar:latest" and "foo/bar@sha256:deadbeef" images in the containerd store.
`,
Flags: append([]cli.Flag{
cli.StringFlag{
Name: "base-name",
Value: "",
Usage: "base image name for added images, when provided only images with this name prefix are imported",
},
cli.BoolFlag{
Name: "digests",
Usage: "whether to create digest images (default: false)",
},
cli.StringFlag{
Name: "index-name",
Usage: "image name to keep index as, by default index is discarded",
},
}, commands.SnapshotterFlags...),
Action: func(context *cli.Context) error {
var (
in = context.Args().First()
opts []containerd.ImportOpt
)
prefix := context.String("base-name")
if prefix == "" {
prefix = fmt.Sprintf("import-%s", time.Now().Format("2006-01-02"))
opts = append(opts, containerd.WithImageRefTranslator(archive.AddRefPrefix(prefix)))
} else {
// When provided, filter out references which do not match
opts = append(opts, containerd.WithImageRefTranslator(archive.FilterRefPrefix(prefix)))
}
if context.Bool("digests") {
opts = append(opts, containerd.WithDigestRef(archive.DigestTranslator(prefix)))
}
if idxName := context.String("index-name"); idxName != "" {
opts = append(opts, containerd.WithIndexName(idxName))
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
var r io.ReadCloser
if in == "-" {
r = os.Stdin
} else {
r, err = os.Open(in)
if err != nil {
return err
}
}
imgs, err := client.Import(ctx, r, opts...)
closeErr := r.Close()
if err != nil {
return err
}
if closeErr != nil {
return closeErr
}
log.G(ctx).Debugf("unpacking %d images", len(imgs))
for _, img := range imgs {
// TODO: Allow configuration of the platform
image := containerd.NewImage(client, img)
// TODO: Show unpack status
fmt.Printf("unpacking %s (%s)...", img.Name, img.Target.Digest)
err = image.Unpack(ctx, context.String("snapshotter"))
if err != nil {
return err
}
fmt.Println("done")
}
return nil
},
}
/*
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 images
import (
"fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/platforms"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var pullCommand = cli.Command{
Name: "pull",
Usage: "pull an image from a remote",
ArgsUsage: "[flags] <ref>",
Description: `Fetch and prepare an image for use in containerd.
After pulling an image, it should be ready to use the same reference in a run
command. As part of this process, we do the following:
1. Fetch all resources into containerd.
2. Prepare the snapshot filesystem with the pulled resources.
3. Register metadata for the image.
`,
Flags: append(append(commands.RegistryFlags, append(commands.SnapshotterFlags, commands.LabelFlag)...),
cli.StringSliceFlag{
Name: "platform",
Usage: "Pull content from a specific platform",
Value: &cli.StringSlice{},
},
cli.BoolFlag{
Name: "all-platforms",
Usage: "pull content from all platforms",
},
),
Action: func(context *cli.Context) error {
var (
ref = context.Args().First()
)
if ref == "" {
return fmt.Errorf("please provide an image reference to pull")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
ctx, done, err := client.WithLease(ctx)
if err != nil {
return err
}
defer done(ctx)
config, err := content.NewFetchConfig(ctx, context)
if err != nil {
return err
}
img, err := content.Fetch(ctx, client, ref, config)
if err != nil {
return err
}
log.G(ctx).WithField("image", ref).Debug("unpacking")
// TODO: Show unpack status
var p []ocispec.Platform
if context.Bool("all-platforms") {
p, err = images.Platforms(ctx, client.ContentStore(), img.Target)
if err != nil {
return errors.Wrap(err, "unable to resolve image platforms")
}
} else {
for _, s := range context.StringSlice("platform") {
ps, err := platforms.Parse(s)
if err != nil {
return errors.Wrapf(err, "unable to parse platform %s", s)
}
p = append(p, ps)
}
}
if len(p) == 0 {
p = append(p, platforms.DefaultSpec())
}
for _, platform := range p {
fmt.Printf("unpacking %s %s...\n", platforms.Format(platform), img.Target.Digest)
i := containerd.NewImageWithPlatform(client, img, platforms.Any(platform))
err = i.Unpack(ctx, context.String("snapshotter"))
if err != nil {
return err
}
}
fmt.Println("done")
return nil
},
}
/*
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 images
import (
gocontext "context"
"os"
"sync"
"text/tabwriter"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/pkg/progress"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/urfave/cli"
"golang.org/x/sync/errgroup"
)
var pushCommand = cli.Command{
Name: "push",
Usage: "push an image to a remote",
ArgsUsage: "[flags] <remote> [<local>]",
Description: `Pushes an image reference from containerd.
All resources associated with the manifest reference will be pushed.
The ref is used to resolve to a locally existing image manifest.
The image manifest must exist before push. Creating a new image
manifest can be done through calculating the diff for layers,
creating the associated configuration, and creating the manifest
which references those resources.
`,
Flags: append(commands.RegistryFlags, cli.StringFlag{
Name: "manifest",
Usage: "digest of manifest",
}, cli.StringFlag{
Name: "manifest-type",
Usage: "media type of manifest digest",
Value: ocispec.MediaTypeImageManifest,
}),
Action: func(context *cli.Context) error {
var (
ref = context.Args().First()
local = context.Args().Get(1)
desc ocispec.Descriptor
)
if ref == "" {
return errors.New("please provide a remote image reference to push")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
if manifest := context.String("manifest"); manifest != "" {
desc.Digest, err = digest.Parse(manifest)
if err != nil {
return errors.Wrap(err, "invalid manifest digest")
}
desc.MediaType = context.String("manifest-type")
} else {
if local == "" {
local = ref
}
img, err := client.ImageService().Get(ctx, local)
if err != nil {
return errors.Wrap(err, "unable to resolve image to manifest")
}
desc = img.Target
}
resolver, err := commands.GetResolver(ctx, context)
if err != nil {
return err
}
ongoing := newPushJobs(commands.PushTracker)
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
log.G(ctx).WithField("image", ref).WithField("digest", desc.Digest).Debug("pushing")
jobHandler := images.HandlerFunc(func(ctx gocontext.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
ongoing.add(remotes.MakeRefKey(ctx, desc))
return nil, nil
})
return client.Push(ctx, ref, desc,
containerd.WithResolver(resolver),
containerd.WithImageHandler(jobHandler),
)
})
errs := make(chan error)
go func() {
defer close(errs)
errs <- eg.Wait()
}()
var (
ticker = time.NewTicker(100 * time.Millisecond)
fw = progress.NewWriter(os.Stdout)
start = time.Now()
done bool
)
defer ticker.Stop()
for {
select {
case <-ticker.C:
fw.Flush()
tw := tabwriter.NewWriter(fw, 1, 8, 1, ' ', 0)
content.Display(tw, ongoing.status(), start)
tw.Flush()
if done {
fw.Flush()
return nil
}
case err := <-errs:
if err != nil {
return err
}
done = true
case <-ctx.Done():
done = true // allow ui to update once more
}
}
},
}
type pushjobs struct {
jobs map[string]struct{}
ordered []string
tracker docker.StatusTracker
mu sync.Mutex
}
func newPushJobs(tracker docker.StatusTracker) *pushjobs {
return &pushjobs{
jobs: make(map[string]struct{}),
tracker: tracker,
}
}
func (j *pushjobs) add(ref string) {
j.mu.Lock()
defer j.mu.Unlock()
if _, ok := j.jobs[ref]; ok {
return
}
j.ordered = append(j.ordered, ref)
j.jobs[ref] = struct{}{}
}
func (j *pushjobs) status() []content.StatusInfo {
j.mu.Lock()
defer j.mu.Unlock()
statuses := make([]content.StatusInfo, 0, len(j.jobs))
for _, name := range j.ordered {
si := content.StatusInfo{
Ref: name,
}
status, err := j.tracker.GetStatus(name)
if err != nil {
si.Status = "waiting"
} else {
si.Offset = status.Offset
si.Total = status.Total
si.StartedAt = status.StartedAt
si.UpdatedAt = status.UpdatedAt
if status.Offset >= status.Total {
if status.UploadUUID == "" {
si.Status = "done"
} else {
si.Status = "committing"
}
} else {
si.Status = "uploading"
}
}
statuses = append(statuses, si)
}
return statuses
}
/*
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 install
import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli"
)
// Command to install binary packages
var Command = cli.Command{
Name: "install",
Usage: "install a new package",
ArgsUsage: "<ref>",
Description: "install a new package",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "libs,l",
Usage: "install libs from the image",
},
cli.BoolFlag{
Name: "replace,r",
Usage: "replace any binaries or libs in the opt directory",
},
cli.StringFlag{
Name: "path",
Usage: "set an optional install path other than the managed opt directory",
},
},
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
ref := context.Args().First()
image, err := client.GetImage(ctx, ref)
if err != nil {
return err
}
var opts []containerd.InstallOpts
if context.Bool("libs") {
opts = append(opts, containerd.WithInstallLibs)
}
if context.Bool("replace") {
opts = append(opts, containerd.WithInstallReplace)
}
if path := context.String("path"); path != "" {
opts = append(opts, containerd.WithInstallPath(path))
}
return client.Install(ctx, image, opts...)
},
}
/*
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 leases
import (
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/leases"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// Command is the cli command for managing content
var Command = cli.Command{
Name: "leases",
Usage: "manage leases",
Subcommands: cli.Commands{
listCommand,
createCommand,
deleteCommand,
},
}
var listCommand = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "list all active leases",
ArgsUsage: "[flags] <filter>",
Description: "list active leases by containerd",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the blob digest",
},
},
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()
ls := client.LeasesService()
leaseList, err := ls.List(ctx, filters...)
if err != nil {
return errors.Wrap(err, "failed to list leases")
}
if quiet {
for _, l := range leaseList {
fmt.Println(l.ID)
}
return nil
}
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
fmt.Fprintln(tw, "ID\tCREATED AT\tLABELS\t")
for _, l := range leaseList {
labels := "-"
if len(l.Labels) > 0 {
var pairs []string
for k, v := range l.Labels {
pairs = append(pairs, fmt.Sprintf("%v=%v", k, v))
}
sort.Strings(pairs)
labels = strings.Join(pairs, ",")
}
fmt.Fprintf(tw, "%v\t%v\t%s\t\n",
l.ID,
l.CreatedAt.Local().Format(time.RFC3339),
labels)
}
return tw.Flush()
},
}
var createCommand = cli.Command{
Name: "create",
Usage: "create lease",
ArgsUsage: "[flags] <label>=<value> ...",
Description: "create a new lease",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "set the id for the lease, will be generated by default",
},
cli.DurationFlag{
Name: "expires, x",
Usage: "expiration of lease (0 value will not expire)",
Value: 24 * time.Hour,
},
},
Action: func(context *cli.Context) error {
var labelstr = context.Args()
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
ls := client.LeasesService()
opts := []leases.Opt{}
if len(labelstr) > 0 {
labels := map[string]string{}
for _, lstr := range labelstr {
l := strings.SplitN(lstr, "=", 2)
if len(l) == 1 {
labels[l[0]] = ""
} else {
labels[l[0]] = l[1]
}
}
opts = append(opts, leases.WithLabels(labels))
}
if id := context.String("id"); id != "" {
opts = append(opts, leases.WithID(id))
}
if exp := context.Duration("expires"); exp > 0 {
opts = append(opts, leases.WithExpiration(exp))
}
l, err := ls.Create(ctx, opts...)
if err != nil {
return err
}
fmt.Println(l.ID)
return nil
},
}
var deleteCommand = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "delete a lease",
ArgsUsage: "[flags] <lease id> ...",
Description: "delete a lease",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "sync",
Usage: "Synchronously remove leases and all unreferenced resources",
},
},
Action: func(context *cli.Context) error {
var lids = context.Args()
if len(lids) == 0 {
return cli.ShowSubcommandHelp(context)
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
ls := client.LeasesService()
sync := context.Bool("sync")
for i, lid := range lids {
var opts []leases.DeleteOpt
if sync && i == len(lids)-1 {
opts = append(opts, leases.SynchronousDelete)
}
lease := leases.Lease{
ID: lid,
}
if err := ls.Delete(ctx, lease, opts...); err != nil {
return err
}
fmt.Println(lid)
}
return nil
},
}
/*
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 namespaces
import (
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// Command is the cli command for managing namespaces
var Command = cli.Command{
Name: "namespaces",
Aliases: []string{"namespace"},
Usage: "manage namespaces",
Subcommands: cli.Commands{
createCommand,
listCommand,
removeCommand,
setLabelsCommand,
},
}
var createCommand = cli.Command{
Name: "create",
Usage: "create a new namespace",
ArgsUsage: "<name> [<key>=<value]",
Description: "create a new namespace. it must be unique",
Action: func(context *cli.Context) error {
namespace, labels := commands.ObjectWithLabelArgs(context)
if namespace == "" {
return errors.New("please specify a namespace")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
namespaces := client.NamespaceService()
return namespaces.Create(ctx, namespace, labels)
},
}
var setLabelsCommand = cli.Command{
Name: "label",
Usage: "set and clear labels for a namespace",
ArgsUsage: "<name> [<key>=<value>, ...]",
Description: "set and clear labels for a namespace",
Action: func(context *cli.Context) error {
namespace, labels := commands.ObjectWithLabelArgs(context)
if namespace == "" {
return errors.New("please specify a namespace")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
namespaces := client.NamespaceService()
for k, v := range labels {
if err := namespaces.SetLabel(ctx, namespace, k, v); err != nil {
return err
}
}
return nil
},
}
var listCommand = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "list namespaces",
ArgsUsage: "[flags]",
Description: "list namespaces",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the namespace name",
},
},
Action: func(context *cli.Context) error {
quiet := context.Bool("quiet")
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
namespaces := client.NamespaceService()
nss, err := namespaces.List(ctx)
if err != nil {
return err
}
if quiet {
for _, ns := range nss {
fmt.Println(ns)
}
return nil
}
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
fmt.Fprintln(tw, "NAME\tLABELS\t")
for _, ns := range nss {
labels, err := namespaces.Labels(ctx, ns)
if err != nil {
return err
}
var labelStrings []string
for k, v := range labels {
labelStrings = append(labelStrings, strings.Join([]string{k, v}, "="))
}
sort.Strings(labelStrings)
fmt.Fprintf(tw, "%v\t%v\t\n", ns, strings.Join(labelStrings, ","))
}
return tw.Flush()
},
}
var removeCommand = cli.Command{
Name: "remove",
Aliases: []string{"rm"},
Usage: "remove one or more namespaces",
ArgsUsage: "<name> [<name>, ...]",
Description: "remove one or more namespaces. for now, the namespace must be empty",
Action: func(context *cli.Context) error {
var exitErr error
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
namespaces := client.NamespaceService()
for _, target := range context.Args() {
if err := namespaces.Delete(ctx, target); err != nil {
if !errdefs.IsNotFound(err) {
if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to delete %v", target)
}
log.G(ctx).WithError(err).Errorf("unable to delete %v", target)
continue
}
}
fmt.Println(target)
}
return exitErr
},
}
/*
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 plugins
import (
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
introspection "github.com/containerd/containerd/api/services/introspection/v1"
"github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/platforms"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli"
"google.golang.org/grpc/codes"
)
// Command is a cli command that outputs plugin information
var Command = cli.Command{
Name: "plugins",
Aliases: []string{"plugin"},
Usage: "provides information about containerd plugins",
Subcommands: []cli.Command{
listCommand,
},
}
var listCommand = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "lists containerd plugins",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet,q",
Usage: "print only the plugin ids",
},
cli.BoolFlag{
Name: "detailed,d",
Usage: "print detailed information about each plugin",
},
},
Action: func(context *cli.Context) error {
var (
quiet = context.Bool("quiet")
detailed = context.Bool("detailed")
)
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
ps := client.IntrospectionService()
response, err := ps.Plugins(ctx, &introspection.PluginsRequest{
Filters: context.Args(),
})
if err != nil {
return err
}
if quiet {
for _, plugin := range response.Plugins {
fmt.Println(plugin.ID)
}
return nil
}
w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
if detailed {
first := true
for _, plugin := range response.Plugins {
if !first {
fmt.Fprintln(w, "\t\t\t")
}
first = false
fmt.Fprintln(w, "Type:\t", plugin.Type)
fmt.Fprintln(w, "ID:\t", plugin.ID)
if len(plugin.Requires) > 0 {
fmt.Fprintln(w, "Requires:\t")
for _, r := range plugin.Requires {
fmt.Fprintln(w, "\t", r)
}
}
if len(plugin.Platforms) > 0 {
fmt.Fprintln(w, "Platforms:\t", prettyPlatforms(plugin.Platforms))
}
if len(plugin.Exports) > 0 {
fmt.Fprintln(w, "Exports:\t")
for k, v := range plugin.Exports {
fmt.Fprintln(w, "\t", k, "\t", v)
}
}
if len(plugin.Capabilities) > 0 {
fmt.Fprintln(w, "Capabilities:\t", strings.Join(plugin.Capabilities, ","))
}
if plugin.InitErr != nil {
fmt.Fprintln(w, "Error:\t")
fmt.Fprintln(w, "\t Code:\t", codes.Code(plugin.InitErr.Code))
fmt.Fprintln(w, "\t Message:\t", plugin.InitErr.Message)
}
}
return w.Flush()
}
fmt.Fprintln(w, "TYPE\tID\tPLATFORMS\tSTATUS\t")
for _, plugin := range response.Plugins {
status := "ok"
if plugin.InitErr != nil {
status = "error"
}
var platformColumn = "-"
if len(plugin.Platforms) > 0 {
platformColumn = prettyPlatforms(plugin.Platforms)
}
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n",
plugin.Type,
plugin.ID,
platformColumn,
status,
); err != nil {
return err
}
}
return w.Flush()
},
}
func prettyPlatforms(pspb []types.Platform) string {
psm := map[string]struct{}{}
for _, p := range pspb {
psm[platforms.Format(v1.Platform{
OS: p.OS,
Architecture: p.Architecture,
Variant: p.Variant,
})] = struct{}{}
}
var ps []string
for p := range psm {
ps = append(ps, p)
}
sort.Stable(sort.StringSlice(ps))
return strings.Join(ps, ",")
}
/*
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 pprof
import (
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/containerd/containerd/defaults"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
type pprofDialer struct {
proto string
addr string
}
// Command is the cli command for providing golang pprof outputs for containerd
var Command = cli.Command{
Name: "pprof",
Usage: "provide golang pprof outputs for containerd",
Flags: []cli.Flag{
cli.StringFlag{
Name: "debug-socket, d",
Usage: "socket path for containerd's debug server",
Value: defaults.DefaultDebugAddress,
},
},
Subcommands: []cli.Command{
pprofBlockCommand,
pprofGoroutinesCommand,
pprofHeapCommand,
pprofProfileCommand,
pprofThreadcreateCommand,
pprofTraceCommand,
},
}
var pprofGoroutinesCommand = cli.Command{
Name: "goroutines",
Usage: "dump goroutine stack dump",
Action: func(context *cli.Context) error {
client := getPProfClient(context)
output, err := httpGetRequest(client, "/debug/pprof/goroutine?debug=2")
if err != nil {
return err
}
defer output.Close()
_, err = io.Copy(os.Stdout, output)
return err
},
}
var pprofHeapCommand = cli.Command{
Name: "heap",
Usage: "dump heap profile",
Action: func(context *cli.Context) error {
client := getPProfClient(context)
output, err := httpGetRequest(client, "/debug/pprof/heap")
if err != nil {
return err
}
defer output.Close()
_, err = io.Copy(os.Stdout, output)
return err
},
}
var pprofProfileCommand = cli.Command{
Name: "profile",
Usage: "CPU profile",
Flags: []cli.Flag{
cli.DurationFlag{
Name: "seconds,s",
Usage: "duration for collection (seconds)",
Value: 30 * time.Second,
},
},
Action: func(context *cli.Context) error {
client := getPProfClient(context)
seconds := context.Duration("seconds").Seconds()
output, err := httpGetRequest(client, fmt.Sprintf("/debug/pprof/profile?seconds=%v", seconds))
if err != nil {
return err
}
defer output.Close()
_, err = io.Copy(os.Stdout, output)
return err
},
}
var pprofTraceCommand = cli.Command{
Name: "trace",
Usage: "collect execution trace",
Flags: []cli.Flag{
cli.DurationFlag{
Name: "seconds,s",
Usage: "trace time (seconds)",
Value: 5 * time.Second,
},
},
Action: func(context *cli.Context) error {
client := getPProfClient(context)
seconds := context.Duration("seconds").Seconds()
uri := fmt.Sprintf("/debug/pprof/trace?seconds=%v", seconds)
output, err := httpGetRequest(client, uri)
if err != nil {
return err
}
defer output.Close()
_, err = io.Copy(os.Stdout, output)
return err
},
}
var pprofBlockCommand = cli.Command{
Name: "block",
Usage: "goroutine blocking profile",
Action: func(context *cli.Context) error {
client := getPProfClient(context)
output, err := httpGetRequest(client, "/debug/pprof/block")
if err != nil {
return err
}
defer output.Close()
_, err = io.Copy(os.Stdout, output)
return err
},
}
var pprofThreadcreateCommand = cli.Command{
Name: "threadcreate",
Usage: "goroutine thread creating profile",
Action: func(context *cli.Context) error {
client := getPProfClient(context)
output, err := httpGetRequest(client, "/debug/pprof/threadcreate")
if err != nil {
return err
}
defer output.Close()
_, err = io.Copy(os.Stdout, output)
return err
},
}
func getPProfClient(context *cli.Context) *http.Client {
dialer := getPProfDialer(context.GlobalString("debug-socket"))
tr := &http.Transport{
Dial: dialer.pprofDial,
}
client := &http.Client{Transport: tr}
return client
}
func httpGetRequest(client *http.Client, request string) (io.ReadCloser, error) {
resp, err := client.Get("http://." + request)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, errors.Errorf("http get failed with status: %s", resp.Status)
}
return resp.Body, nil
}
// +build !windows
/*
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 pprof
import "net"
func (d *pprofDialer) pprofDial(proto, addr string) (conn net.Conn, err error) {
return net.Dial(d.proto, d.addr)
}
func getPProfDialer(addr string) *pprofDialer {
return &pprofDialer{"unix", addr}
}
/*
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 pprof
import (
"net"
winio "github.com/Microsoft/go-winio"
)
func (d *pprofDialer) pprofDial(proto, addr string) (conn net.Conn, err error) {
return winio.DialPipe(d.addr, nil)
}
func getPProfDialer(addr string) *pprofDialer {
return &pprofDialer{"winpipe", addr}
}
/*
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 commands
import (
"bufio"
gocontext "context"
"crypto/tls"
"fmt"
"net"
"net/http"
"strings"
"time"
"github.com/containerd/console"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// PushTracker returns a new InMemoryTracker which tracks the ref status
var PushTracker = docker.NewInMemoryTracker()
func passwordPrompt() (string, error) {
c := console.Current()
defer c.Reset()
if err := c.DisableEcho(); err != nil {
return "", errors.Wrap(err, "failed to disable echo")
}
line, _, err := bufio.NewReader(c).ReadLine()
if err != nil {
return "", errors.Wrap(err, "failed to read line")
}
return string(line), nil
}
// GetResolver prepares the resolver from the environment and options
func GetResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolver, error) {
username := clicontext.String("user")
var secret string
if i := strings.IndexByte(username, ':'); i > 0 {
secret = username[i+1:]
username = username[0:i]
}
options := docker.ResolverOptions{
PlainHTTP: clicontext.Bool("plain-http"),
Tracker: PushTracker,
}
if username != "" {
if secret == "" {
fmt.Printf("Password: ")
var err error
secret, err = passwordPrompt()
if err != nil {
return nil, err
}
fmt.Print("\n")
}
} else if rt := clicontext.String("refresh"); rt != "" {
secret = rt
}
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: clicontext.Bool("insecure"),
},
ExpectContinueTimeout: 5 * time.Second,
}
options.Client = &http.Client{
Transport: tr,
}
credentials := func(host string) (string, string, error) {
// Only one host
return username, secret, nil
}
options.Authorizer = docker.NewAuthorizer(options.Client, credentials)
return docker.NewResolver(options), nil
}
/*
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 run
import (
gocontext "context"
"encoding/csv"
"fmt"
"strings"
"github.com/containerd/console"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/tasks"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/oci"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func withMounts(context *cli.Context) oci.SpecOpts {
return func(ctx gocontext.Context, client oci.Client, container *containers.Container, s *specs.Spec) error {
mounts := make([]specs.Mount, 0)
for _, mount := range context.StringSlice("mount") {
m, err := parseMountFlag(mount)
if err != nil {
return err
}
mounts = append(mounts, m)
}
return oci.WithMounts(mounts)(ctx, client, container, s)
}
}
// parseMountFlag parses a mount string in the form "type=foo,source=/path,destination=/target,options=rbind:rw"
func parseMountFlag(m string) (specs.Mount, error) {
mount := specs.Mount{}
r := csv.NewReader(strings.NewReader(m))
fields, err := r.Read()
if err != nil {
return mount, err
}
for _, field := range fields {
v := strings.Split(field, "=")
if len(v) != 2 {
return mount, fmt.Errorf("invalid mount specification: expected key=val")
}
key := v[0]
val := v[1]
switch key {
case "type":
mount.Type = val
case "source", "src":
mount.Source = val
case "destination", "dst":
mount.Destination = val
case "options":
mount.Options = strings.Split(val, ":")
default:
return mount, fmt.Errorf("mount option %q not supported", key)
}
}
return mount, nil
}
// Command runs a container
var Command = cli.Command{
Name: "run",
Usage: "run a container",
ArgsUsage: "[flags] Image|RootFS ID [COMMAND] [ARG...]",
Flags: append([]cli.Flag{
cli.BoolFlag{
Name: "rm",
Usage: "remove the container after running",
},
cli.BoolFlag{
Name: "null-io",
Usage: "send all IO to /dev/null",
},
cli.BoolFlag{
Name: "detach,d",
Usage: "detach from the task after it has started execution",
},
cli.StringFlag{
Name: "fifo-dir",
Usage: "directory used for storing IO FIFOs",
},
cli.BoolFlag{
Name: "isolated",
Usage: "run the container with vm isolation",
},
}, append(commands.SnapshotterFlags, commands.ContainerFlags...)...),
Action: func(context *cli.Context) error {
var (
err error
id string
ref string
tty = context.Bool("tty")
detach = context.Bool("detach")
config = context.IsSet("config")
)
if config {
id = context.Args().First()
if context.NArg() > 1 {
return errors.New("with spec config file, only container id should be provided")
}
} else {
id = context.Args().Get(1)
ref = context.Args().First()
if ref == "" {
return errors.New("image ref must be provided")
}
}
if id == "" {
return errors.New("container id must be provided")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := NewContainer(ctx, client, context)
if err != nil {
return err
}
if context.Bool("rm") && !detach {
defer container.Delete(ctx, containerd.WithSnapshotCleanup)
}
var con console.Console
if tty {
con = console.Current()
defer con.Reset()
if err := con.SetRaw(); err != nil {
return err
}
}
opts := getNewTaskOpts(context)
ioOpts := []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))}
task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), con, context.Bool("null-io"), ioOpts, opts...)
if err != nil {
return err
}
var statusC <-chan containerd.ExitStatus
if !detach {
defer task.Delete(ctx)
if statusC, err = task.Wait(ctx); err != nil {
return err
}
}
if context.IsSet("pid-file") {
if err := commands.WritePidFile(context.String("pid-file"), int(task.Pid())); err != nil {
return err
}
}
if err := task.Start(ctx); err != nil {
return err
}
if detach {
return nil
}
if tty {
if err := tasks.HandleConsoleResize(ctx, task, con); err != nil {
logrus.WithError(err).Error("console resize")
}
} else {
sigc := commands.ForwardAllSignals(ctx, task)
defer commands.StopCatch(sigc)
}
status := <-statusC
code, _, err := status.Result()
if err != nil {
return err
}
if _, err := task.Delete(ctx); err != nil {
return err
}
if code != 0 {
return cli.NewExitError("", int(code))
}
return nil
},
}
// +build !windows
/*
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 run
import (
gocontext "context"
"path/filepath"
"strings"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/contrib/nvidia"
"github.com/containerd/containerd/oci"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// NewContainer creates a new container
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
var (
id string
config = context.IsSet("config")
)
if config {
id = context.Args().First()
} else {
id = context.Args().Get(1)
}
if raw := context.String("checkpoint"); raw != "" {
im, err := client.GetImage(ctx, raw)
if err != nil {
return nil, err
}
return client.NewContainer(ctx, id, containerd.WithCheckpoint(im, id), containerd.WithRuntime(context.String("runtime"), nil))
}
var (
opts []oci.SpecOpts
cOpts []containerd.NewContainerOpts
spec containerd.NewContainerOpts
)
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
if config {
opts = append(opts, oci.WithSpecFromFile(context.String("config")))
} else {
var (
ref = context.Args().First()
//for container's id is Args[1]
args = context.Args()[2:]
)
opts = append(opts, oci.WithDefaultSpec(), oci.WithDefaultUnixDevices)
opts = append(opts, oci.WithEnv(context.StringSlice("env")))
opts = append(opts, withMounts(context))
if context.Bool("rootfs") {
rootfs, err := filepath.Abs(ref)
if err != nil {
return nil, err
}
opts = append(opts, oci.WithRootFSPath(rootfs))
} else {
snapshotter := context.String("snapshotter")
image, err := client.GetImage(ctx, ref)
if err != nil {
return nil, err
}
unpacked, err := image.IsUnpacked(ctx, snapshotter)
if err != nil {
return nil, err
}
if !unpacked {
if err := image.Unpack(ctx, snapshotter); err != nil {
return nil, err
}
}
opts = append(opts, oci.WithImageConfig(image))
cOpts = append(cOpts,
containerd.WithImage(image),
containerd.WithSnapshotter(snapshotter),
// Even when "readonly" is set, we don't use KindView snapshot here. (#1495)
// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
// after creating some mount points on demand.
containerd.WithNewSnapshot(id, image),
containerd.WithImageStopSignal(image, "SIGTERM"))
}
if context.Bool("readonly") {
opts = append(opts, oci.WithRootFSReadonly())
}
if len(args) > 0 {
opts = append(opts, oci.WithProcessArgs(args...))
}
if cwd := context.String("cwd"); cwd != "" {
opts = append(opts, oci.WithProcessCwd(cwd))
}
if context.Bool("tty") {
opts = append(opts, oci.WithTTY)
}
if context.Bool("privileged") {
opts = append(opts, oci.WithPrivileged)
}
if context.Bool("net-host") {
opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf)
}
joinNs := context.StringSlice("with-ns")
for _, ns := range joinNs {
parts := strings.Split(ns, ":")
if len(parts) != 2 {
return nil, errors.New("joining a Linux namespace using --with-ns requires the format 'nstype:path'")
}
if !validNamespace(parts[0]) {
return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + parts[0])
}
opts = append(opts, oci.WithLinuxNamespace(specs.LinuxNamespace{
Type: specs.LinuxNamespaceType(parts[0]),
Path: parts[1],
}))
}
if context.IsSet("gpus") {
opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities))
}
if context.IsSet("allow-new-privs") {
opts = append(opts, oci.WithNewPrivileges)
}
}
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
var s specs.Spec
spec = containerd.WithSpec(&s, opts...)
cOpts = append(cOpts, spec)
// oci.WithImageConfig (WithUsername, WithUserID) depends on access to rootfs for resolving via
// the /etc/{passwd,group} files. So cOpts needs to have precedence over opts.
return client.NewContainer(ctx, id, cOpts...)
}
func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
if context.Bool("no-pivot") {
return []containerd.NewTaskOpts{containerd.WithNoPivotRoot}
}
return nil
}
func validNamespace(ns string) bool {
linuxNs := specs.LinuxNamespaceType(ns)
switch linuxNs {
case specs.PIDNamespace,
specs.NetworkNamespace,
specs.UTSNamespace,
specs.MountNamespace,
specs.UserNamespace,
specs.IPCNamespace,
specs.CgroupNamespace:
return true
default:
return false
}
}
/*
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 run
import (
gocontext "context"
"github.com/containerd/console"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/oci"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
// NewContainer creates a new container
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
var (
id string
opts []oci.SpecOpts
cOpts []containerd.NewContainerOpts
spec containerd.NewContainerOpts
config = context.IsSet("config")
)
if config {
id = context.Args().First()
opts = append(opts, oci.WithSpecFromFile(context.String("config")))
} else {
var (
ref = context.Args().First()
args = context.Args()[2:]
)
id = context.Args().Get(1)
snapshotter := context.String("snapshotter")
if snapshotter == "windows-lcow" {
opts = append(opts, oci.WithDefaultSpecForPlatform("linux/amd64"))
// Clear the rootfs section.
opts = append(opts, oci.WithRootFSPath(""))
} else {
opts = append(opts, oci.WithDefaultSpec())
}
opts = append(opts, oci.WithEnv(context.StringSlice("env")))
opts = append(opts, withMounts(context))
image, err := client.GetImage(ctx, ref)
if err != nil {
return nil, err
}
unpacked, err := image.IsUnpacked(ctx, snapshotter)
if err != nil {
return nil, err
}
if !unpacked {
if err := image.Unpack(ctx, snapshotter); err != nil {
return nil, err
}
}
opts = append(opts, oci.WithImageConfig(image))
cOpts = append(cOpts, containerd.WithImage(image))
cOpts = append(cOpts, containerd.WithSnapshotter(snapshotter))
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
if len(args) > 0 {
opts = append(opts, oci.WithProcessArgs(args...))
}
if cwd := context.String("cwd"); cwd != "" {
opts = append(opts, oci.WithProcessCwd(cwd))
}
if context.Bool("tty") {
opts = append(opts, oci.WithTTY)
con := console.Current()
size, err := con.Size()
if err != nil {
logrus.WithError(err).Error("console size")
}
opts = append(opts, oci.WithTTYSize(int(size.Width), int(size.Height)))
}
if context.Bool("isolated") {
opts = append(opts, oci.WithWindowsHyperV)
}
}
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
var s specs.Spec
spec = containerd.WithSpec(&s, opts...)
cOpts = append(cOpts, spec)
return client.NewContainer(ctx, id, cOpts...)
}
func getNewTaskOpts(_ *cli.Context) []containerd.NewTaskOpts {
return nil
}
// +build !windows
/*
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 shim
import (
gocontext "context"
"io"
"os"
"sync"
"github.com/containerd/fifo"
"golang.org/x/sys/unix"
)
var bufPool = sync.Pool{
New: func() interface{} {
buffer := make([]byte, 32<<10)
return &buffer
},
}
func prepareStdio(stdin, stdout, stderr string, console bool) (wg *sync.WaitGroup, err error) {
wg = &sync.WaitGroup{}
ctx := gocontext.Background()
f, err := fifo.OpenFifo(ctx, stdin, unix.O_WRONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700)
if err != nil {
return nil, err
}
defer func(c io.Closer) {
if err != nil {
c.Close()
}
}(f)
go func(w io.WriteCloser) {
p := bufPool.Get().(*[]byte)
defer bufPool.Put(p)
io.CopyBuffer(w, os.Stdin, *p)
w.Close()
}(f)
f, err = fifo.OpenFifo(ctx, stdout, unix.O_RDONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700)
if err != nil {
return nil, err
}
defer func(c io.Closer) {
if err != nil {
c.Close()
}
}(f)
wg.Add(1)
go func(r io.ReadCloser) {
io.Copy(os.Stdout, r)
r.Close()
wg.Done()
}(f)
f, err = fifo.OpenFifo(ctx, stderr, unix.O_RDONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700)
if err != nil {
return nil, err
}
defer func(c io.Closer) {
if err != nil {
c.Close()
}
}(f)
if !console {
wg.Add(1)
go func(r io.ReadCloser) {
io.Copy(os.Stderr, r)
r.Close()
wg.Done()
}(f)
}
return wg, nil
}
// +build !windows
/*
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 shim
import (
gocontext "context"
"fmt"
"io/ioutil"
"net"
"github.com/containerd/console"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/runtime/v2/task"
"github.com/containerd/ttrpc"
"github.com/containerd/typeurl"
ptypes "github.com/gogo/protobuf/types"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var fifoFlags = []cli.Flag{
cli.StringFlag{
Name: "stdin",
Usage: "specify the path to the stdin fifo",
},
cli.StringFlag{
Name: "stdout",
Usage: "specify the path to the stdout fifo",
},
cli.StringFlag{
Name: "stderr",
Usage: "specify the path to the stderr fifo",
},
cli.BoolFlag{
Name: "tty,t",
Usage: "enable tty support",
},
}
// Command is the cli command for interacting with a task
var Command = cli.Command{
Name: "shim",
Usage: "interact with a shim directly",
Flags: []cli.Flag{
cli.StringFlag{
Name: "socket",
Usage: "socket on which to connect to the shim",
},
},
Subcommands: []cli.Command{
deleteCommand,
execCommand,
startCommand,
stateCommand,
},
}
var startCommand = cli.Command{
Name: "start",
Usage: "start a container with a task",
Action: func(context *cli.Context) error {
service, err := getTaskService(context)
if err != nil {
return err
}
_, err = service.Start(gocontext.Background(), &task.StartRequest{
ID: context.Args().First(),
})
return err
},
}
var deleteCommand = cli.Command{
Name: "delete",
Usage: "delete a container with a task",
Action: func(context *cli.Context) error {
service, err := getTaskService(context)
if err != nil {
return err
}
r, err := service.Delete(gocontext.Background(), &task.DeleteRequest{
ID: context.Args().First(),
})
if err != nil {
return err
}
fmt.Printf("container deleted and returned exit status %d\n", r.ExitStatus)
return nil
},
}
var stateCommand = cli.Command{
Name: "state",
Usage: "get the state of all the processes of the task",
Action: func(context *cli.Context) error {
service, err := getTaskService(context)
if err != nil {
return err
}
r, err := service.State(gocontext.Background(), &task.StateRequest{
ID: context.Args().First(),
})
if err != nil {
return err
}
commands.PrintAsJSON(r)
return nil
},
}
var execCommand = cli.Command{
Name: "exec",
Usage: "exec a new process in the task's container",
Flags: append(fifoFlags,
cli.BoolFlag{
Name: "attach,a",
Usage: "stay attached to the container and open the fifos",
},
cli.StringSliceFlag{
Name: "env,e",
Usage: "add environment vars",
Value: &cli.StringSlice{},
},
cli.StringFlag{
Name: "cwd",
Usage: "current working directory",
},
cli.StringFlag{
Name: "spec",
Usage: "runtime spec",
},
),
Action: func(context *cli.Context) error {
service, err := getTaskService(context)
if err != nil {
return err
}
var (
id = context.Args().First()
ctx = gocontext.Background()
)
if id == "" {
return errors.New("exec id must be provided")
}
tty := context.Bool("tty")
wg, err := prepareStdio(context.String("stdin"), context.String("stdout"), context.String("stderr"), tty)
if err != nil {
return err
}
// read spec file and extract Any object
spec, err := ioutil.ReadFile(context.String("spec"))
if err != nil {
return err
}
url, err := typeurl.TypeURL(specs.Process{})
if err != nil {
return err
}
rq := &task.ExecProcessRequest{
ID: id,
Spec: &ptypes.Any{
TypeUrl: url,
Value: spec,
},
Stdin: context.String("stdin"),
Stdout: context.String("stdout"),
Stderr: context.String("stderr"),
Terminal: tty,
}
if _, err := service.Exec(ctx, rq); err != nil {
return err
}
r, err := service.Start(ctx, &task.StartRequest{
ID: id,
})
if err != nil {
return err
}
fmt.Printf("exec running with pid %d\n", r.Pid)
if context.Bool("attach") {
logrus.Info("attaching")
if tty {
current := console.Current()
defer current.Reset()
if err := current.SetRaw(); err != nil {
return err
}
size, err := current.Size()
if err != nil {
return err
}
if _, err := service.ResizePty(ctx, &task.ResizePtyRequest{
ID: id,
Width: uint32(size.Width),
Height: uint32(size.Height),
}); err != nil {
return err
}
}
wg.Wait()
}
return nil
},
}
func getTaskService(context *cli.Context) (task.TaskService, error) {
bindSocket := context.GlobalString("socket")
if bindSocket == "" {
return nil, errors.New("socket path must be specified")
}
conn, err := net.Dial("unix", "\x00"+bindSocket)
if err != nil {
return nil, err
}
client := ttrpc.NewClient(conn)
// TODO(stevvooe): This actually leaks the connection. We were leaking it
// before, so may not be a huge deal.
return task.NewTaskClient(client), nil
}
/*
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 commands
import (
gocontext "context"
"os"
"os/signal"
"syscall"
"github.com/containerd/containerd"
"github.com/sirupsen/logrus"
)
type killer interface {
Kill(gocontext.Context, syscall.Signal, ...containerd.KillOpts) error
}
// ForwardAllSignals forwards signals
func ForwardAllSignals(ctx gocontext.Context, task killer) chan os.Signal {
sigc := make(chan os.Signal, 128)
signal.Notify(sigc)
go func() {
for s := range sigc {
logrus.Debug("forwarding signal ", s)
if err := task.Kill(ctx, s.(syscall.Signal)); err != nil {
logrus.WithError(err).Errorf("forward signal %s", s)
}
}
}()
return sigc
}
// StopCatch stops and closes a channel
func StopCatch(sigc chan os.Signal) {
signal.Stop(sigc)
close(sigc)
}
/*
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 tasks
import (
"github.com/containerd/console"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var attachCommand = cli.Command{
Name: "attach",
Usage: "attach to the IO of a running container",
ArgsUsage: "CONTAINER",
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, context.Args().First())
if err != nil {
return err
}
spec, err := container.Spec(ctx)
if err != nil {
return err
}
var (
con console.Console
tty = spec.Process.Terminal
)
if tty {
con = console.Current()
defer con.Reset()
if err := con.SetRaw(); err != nil {
return err
}
}
task, err := container.Task(ctx, cio.NewAttach(cio.WithStdio))
if err != nil {
return err
}
defer task.Delete(ctx)
statusC, err := task.Wait(ctx)
if err != nil {
return err
}
if tty {
if err := HandleConsoleResize(ctx, task, con); err != nil {
logrus.WithError(err).Error("console resize")
}
} else {
sigc := commands.ForwardAllSignals(ctx, task)
defer commands.StopCatch(sigc)
}
ec := <-statusC
code, _, err := ec.Result()
if err != nil {
return err
}
if code != 0 {
return cli.NewExitError("", int(code))
}
return nil
},
}
/*
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 tasks
import (
"fmt"
"runtime"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var checkpointCommand = cli.Command{
Name: "checkpoint",
Usage: "checkpoint a container",
ArgsUsage: "[flags] CONTAINER",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "exit",
Usage: "stop the container after the checkpoint",
},
cli.StringFlag{
Name: "runtime",
Usage: "runtime name",
Value: fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS),
},
},
Action: func(context *cli.Context) error {
id := context.Args().First()
if id == "" {
return errors.New("container id must be provided")
}
client, ctx, cancel, err := commands.NewClient(context, containerd.WithDefaultRuntime(context.String("runtime")))
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, id)
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
var opts []containerd.CheckpointTaskOpts
if context.Bool("exit") {
opts = append(opts, withExit(context.String("runtime")))
}
checkpoint, err := task.Checkpoint(ctx, opts...)
if err != nil {
return err
}
fmt.Println(checkpoint.Name())
return nil
},
}
func withExit(rt string) containerd.CheckpointTaskOpts {
return func(r *containerd.CheckpointTaskInfo) error {
switch rt {
case "io.containerd.runc.v1":
if r.Options == nil {
r.Options = &options.CheckpointOptions{
Exit: true,
}
} else {
opts, _ := r.Options.(*options.CheckpointOptions)
opts.Exit = true
}
default:
if r.Options == nil {
r.Options = &runctypes.CheckpointOptions{
Exit: true,
}
} else {
opts, _ := r.Options.(*runctypes.CheckpointOptions)
opts.Exit = true
}
}
return nil
}
}
/*
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 tasks
import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli"
)
var deleteCommand = cli.Command{
Name: "delete",
Usage: "[flags] delete a task",
ArgsUsage: "CONTAINER",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "force, f",
Usage: "force delete task process",
},
},
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, context.Args().First())
if err != nil {
return err
}
task, err := container.Task(ctx, cio.Load)
if err != nil {
return err
}
var opts []containerd.ProcessDeleteOpts
if context.Bool("force") {
opts = append(opts, containerd.WithProcessKill)
}
status, err := task.Delete(ctx, opts...)
if err != nil {
return err
}
if ec := status.ExitCode(); ec != 0 {
return cli.NewExitError("", int(ec))
}
return nil
},
}
/*
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 tasks
import (
"errors"
"github.com/containerd/console"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
//TODO:(jessvalarezo) exec-id is optional here, update to required arg
var execCommand = cli.Command{
Name: "exec",
Usage: "execute additional processes in an existing container",
ArgsUsage: "[flags] CONTAINER CMD [ARG...]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "cwd",
Usage: "working directory of the new process",
},
cli.BoolFlag{
Name: "tty,t",
Usage: "allocate a TTY for the container",
},
cli.StringFlag{
Name: "exec-id",
Usage: "exec specific id for the process",
},
cli.StringFlag{
Name: "fifo-dir",
Usage: "directory used for storing IO FIFOs",
},
},
Action: func(context *cli.Context) error {
var (
id = context.Args().First()
args = context.Args().Tail()
tty = context.Bool("tty")
)
if id == "" {
return errors.New("container id must be provided")
}
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
}
spec, err := container.Spec(ctx)
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
pspec := spec.Process
pspec.Terminal = tty
pspec.Args = args
cioOpts := []cio.Opt{cio.WithStdio, cio.WithFIFODir(context.String("fifo-dir"))}
if tty {
cioOpts = append(cioOpts, cio.WithTerminal)
}
ioCreator := cio.NewCreator(cioOpts...)
process, err := task.Exec(ctx, context.String("exec-id"), pspec, ioCreator)
if err != nil {
return err
}
defer process.Delete(ctx)
statusC, err := process.Wait(ctx)
if err != nil {
return err
}
var con console.Console
if tty {
con = console.Current()
defer con.Reset()
if err := con.SetRaw(); err != nil {
return err
}
}
if tty {
if err := HandleConsoleResize(ctx, process, con); err != nil {
logrus.WithError(err).Error("console resize")
}
} else {
sigc := commands.ForwardAllSignals(ctx, process)
defer commands.StopCatch(sigc)
}
if err := process.Start(ctx); err != nil {
return err
}
status := <-statusC
code, _, err := status.Result()
if err != nil {
return err
}
if code != 0 {
return cli.NewExitError("", int(code))
}
return nil
},
}
/*
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 tasks
import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
const defaultSignal = "SIGTERM"
var killCommand = cli.Command{
Name: "kill",
Usage: "signal a container (default: SIGTERM)",
ArgsUsage: "[flags] CONTAINER",
Flags: []cli.Flag{
cli.StringFlag{
Name: "signal, s",
Value: "",
Usage: "signal to send to the container",
},
cli.StringFlag{
Name: "exec-id",
Usage: "process ID to kill",
},
cli.BoolFlag{
Name: "all, a",
Usage: "send signal to all processes inside the container",
},
},
Action: func(context *cli.Context) error {
id := context.Args().First()
if id == "" {
return errors.New("container id must be provided")
}
signal, err := containerd.ParseSignal(defaultSignal)
if err != nil {
return err
}
var (
all = context.Bool("all")
execID = context.String("exec-id")
opts []containerd.KillOpts
)
if all && execID != "" {
return errors.New("specify an exec-id or all; not both")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
if all {
opts = append(opts, containerd.WithKillAll)
}
if execID != "" {
opts = append(opts, containerd.WithKillExecID(execID))
}
container, err := client.LoadContainer(ctx, id)
if err != nil {
return err
}
if context.String("signal") != "" {
signal, err = containerd.ParseSignal(context.String("signal"))
if err != nil {
return err
}
} else {
signal, err = containerd.GetStopSignal(ctx, container, signal)
if err != nil {
return err
}
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
return task.Kill(ctx, signal, opts...)
},
}
/*
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 tasks
import (
"fmt"
"os"
"text/tabwriter"
tasks "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli"
)
var listCommand = cli.Command{
Name: "list",
Usage: "list tasks",
Aliases: []string{"ls"},
ArgsUsage: "[flags]",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the task id",
},
},
Action: func(context *cli.Context) error {
quiet := context.Bool("quiet")
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
s := client.TaskService()
response, err := s.List(ctx, &tasks.ListTasksRequest{})
if err != nil {
return err
}
if quiet {
for _, task := range response.Tasks {
fmt.Println(task.ID)
}
return nil
}
w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
fmt.Fprintln(w, "TASK\tPID\tSTATUS\t")
for _, task := range response.Tasks {
if _, err := fmt.Fprintf(w, "%s\t%d\t%s\n",
task.ID,
task.Pid,
task.Status.String(),
); err != nil {
return err
}
}
return w.Flush()
},
}
// +build linux
/*
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 tasks
import (
"encoding/json"
"errors"
"fmt"
"os"
"text/tabwriter"
"github.com/containerd/cgroups"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/typeurl"
"github.com/urfave/cli"
)
func init() {
// metricsCommand is only added on Linux as github.com/containerd/cgroups
// does not compile on darwin or windows
Command.Subcommands = append(Command.Subcommands, metricsCommand)
}
const (
formatFlag = "format"
formatTable = "table"
formatJSON = "json"
)
var metricsCommand = cli.Command{
Name: "metrics",
Usage: "get a single data point of metrics for a task with the built-in Linux runtime",
ArgsUsage: "CONTAINER",
Aliases: []string{"metric"},
Flags: []cli.Flag{
cli.StringFlag{
Name: formatFlag,
Usage: `"table" or "json"`,
Value: formatTable,
},
},
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, context.Args().First())
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
metric, err := task.Metrics(ctx)
if err != nil {
return nil
}
anydata, err := typeurl.UnmarshalAny(metric.Data)
if err != nil {
return err
}
data, ok := anydata.(*cgroups.Metrics)
if !ok {
return errors.New("cannot convert metric data to cgroups.Metrics")
}
switch context.String(formatFlag) {
case formatTable:
w := tabwriter.NewWriter(os.Stdout, 1, 8, 4, ' ', 0)
fmt.Fprintf(w, "ID\tTIMESTAMP\t\n")
fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp)
fmt.Fprintf(w, "METRIC\tVALUE\t\n")
if data.Memory != nil {
fmt.Fprintf(w, "memory.usage_in_bytes\t%d\t\n", data.Memory.Usage.Usage)
fmt.Fprintf(w, "memory.limit_in_bytes\t%d\t\n", data.Memory.Usage.Limit)
fmt.Fprintf(w, "memory.stat.cache\t%d\t\n", data.Memory.TotalCache)
}
if data.CPU != nil {
fmt.Fprintf(w, "cpuacct.usage\t%d\t\n", data.CPU.Usage.Total)
fmt.Fprintf(w, "cpuacct.usage_percpu\t%v\t\n", data.CPU.Usage.PerCPU)
}
if data.Pids != nil {
fmt.Fprintf(w, "pids.current\t%v\t\n", data.Pids.Current)
fmt.Fprintf(w, "pids.limit\t%v\t\n", data.Pids.Limit)
}
return w.Flush()
case formatJSON:
marshaledJSON, err := json.MarshalIndent(data, "", " ")
if err != nil {
return err
}
fmt.Println(string(marshaledJSON))
return nil
default:
return errors.New("format must be table or json")
}
},
}
/*
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 tasks
import (
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli"
)
var pauseCommand = cli.Command{
Name: "pause",
Usage: "pause an existing container",
ArgsUsage: "CONTAINER",
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, context.Args().First())
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
return task.Pause(ctx)
},
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment