Merge pull request #631 from erikwilson/update-cri

Update cri & dependencies
pull/634/head v0.7.0-rc6
Erik Wilson 2019-07-12 10:50:43 -07:00 committed by GitHub
commit ba4d920f1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 320 additions and 279 deletions

View File

@ -38,7 +38,7 @@ import:
- package: github.com/containerd/continuity - package: github.com/containerd/continuity
version: bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 version: bd77b46c8352f74eb12c85bdc01f4b90f69d66b4
- package: github.com/containerd/cri - package: github.com/containerd/cri
version: v1.2-k3s.2 version: v1.2.7-k3s1
repo: https://github.com/rancher/cri.git repo: https://github.com/rancher/cri.git
- package: github.com/containerd/fifo - package: github.com/containerd/fifo
version: 3d5202aec260678c48179c56f40e6f38a095738c version: 3d5202aec260678c48179c56f40e6f38a095738c
@ -53,8 +53,8 @@ import:
- package: github.com/containernetworking/cni - package: github.com/containernetworking/cni
version: v0.6.0 version: v0.6.0
- package: github.com/containernetworking/plugins - package: github.com/containernetworking/plugins
version: 9810b7d5137b171c4e07ce59bb18be9feccec557 version: v0.7.5-k3s1
repo: https://github.com/ibuildthecloud/plugins.git repo: https://github.com/rancher/plugins.git
- package: github.com/coreos/etcd - package: github.com/coreos/etcd
version: v3.3.10 version: v3.3.10
- package: github.com/coreos/flannel - package: github.com/coreos/flannel
@ -65,7 +65,7 @@ import:
- package: github.com/coreos/go-semver - package: github.com/coreos/go-semver
version: v0.3.0 version: v0.3.0
- package: github.com/coreos/go-systemd - package: github.com/coreos/go-systemd
version: 48702e0da86bd25e76cfef347e2adeb434a0d0a6 version: v14
- package: github.com/coreos/pkg - package: github.com/coreos/pkg
version: v4 version: v4
- package: github.com/cyphar/filepath-securejoin - package: github.com/cyphar/filepath-securejoin
@ -107,7 +107,7 @@ import:
- package: github.com/go-sql-driver/mysql - package: github.com/go-sql-driver/mysql
version: v1.4.1 version: v1.4.1
- package: github.com/godbus/dbus - package: github.com/godbus/dbus
version: c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f version: v3
- package: github.com/gogo/googleapis - package: github.com/gogo/googleapis
version: 08a7655d27152912db7aaf4f983275eaf8d128ef version: 08a7655d27152912db7aaf4f983275eaf8d128ef
- package: github.com/gogo/protobuf - package: github.com/gogo/protobuf
@ -134,7 +134,7 @@ import:
- package: github.com/gregjones/httpcache - package: github.com/gregjones/httpcache
version: 787624de3eb7bd915c329cba748687a3b22666a6 version: 787624de3eb7bd915c329cba748687a3b22666a6
- package: github.com/grpc-ecosystem/go-grpc-prometheus - package: github.com/grpc-ecosystem/go-grpc-prometheus
version: 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 version: v1.1
- package: github.com/hashicorp/errwrap - package: github.com/hashicorp/errwrap
version: 7554cd9344cec97297fa6649b055a8c98c2a1e55 version: 7554cd9344cec97297fa6649b055a8c98c2a1e55
- package: github.com/hashicorp/go-multierror - package: github.com/hashicorp/go-multierror
@ -209,7 +209,7 @@ import:
- package: github.com/opencontainers/runtime-tools - package: github.com/opencontainers/runtime-tools
version: v0.6.0 version: v0.6.0
- package: github.com/opencontainers/selinux - package: github.com/opencontainers/selinux
version: v1.2.1 version: v1.2.2
- package: github.com/pborman/uuid - package: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4 version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- package: github.com/peterbourgon/diskv - package: github.com/peterbourgon/diskv

View File

@ -30,13 +30,14 @@ golang.org/x/crypto a49355c7e3f8fe157a85be2f77e6e269a0f89602
gopkg.in/freddierice/go-losetup.v1 fc9adea44124401d8bfef3a97eaf61b5d44cc2c6 gopkg.in/freddierice/go-losetup.v1 fc9adea44124401d8bfef3a97eaf61b5d44cc2c6
github.com/urfave/cli 8e01ec4cd3e2d84ab2fe90d8210528ffbb06d8ff github.com/urfave/cli 8e01ec4cd3e2d84ab2fe90d8210528ffbb06d8ff
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/docker/docker c12f09bf99b54f274a5ae241dd154fa74020cbab
# flannel # flannel
github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998 github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998
github.com/coreos/go-iptables 47f22b0dd3355c0ba570ba12b0b8a36bf214c04b github.com/coreos/go-iptables 47f22b0dd3355c0ba570ba12b0b8a36bf214c04b
# cni # cni
github.com/containernetworking/plugins 9810b7d5137b171c4e07ce59bb18be9feccec557 https://github.com/ibuildthecloud/plugins.git github.com/containernetworking/plugins v0.7.5-k3s1 https://github.com/rancher/plugins.git
github.com/j-keck/arping 2cf9dc699c5640a7e2c81403a44127bf28033600 github.com/j-keck/arping 2cf9dc699c5640a7e2c81403a44127bf28033600
github.com/alexflint/go-filemutex 72bdc8eae2aef913234599b837f5dda445ca9bd9 github.com/alexflint/go-filemutex 72bdc8eae2aef913234599b837f5dda445ca9bd9
@ -52,11 +53,11 @@ github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244 github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244
github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4
github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6 github.com/coreos/go-systemd v14
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.3 #github.com/docker/go-units v0.3.3
github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f github.com/godbus/dbus v3
#github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823 #github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823
#github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c #github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
#github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563 #github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563
@ -69,16 +70,16 @@ 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 v1.0.0-rc8 github.com/opencontainers/runc v1.0.0-rc8
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
google.golang.org/grpc v1.12.0 google.golang.org/grpc v1.12.0
github.com/pkg/errors v0.8.0 github.com/pkg/errors v0.8.0
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7 github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
golang.org/x/sys 41f3e6584952bb034a481797859f6ab34b6803bd https://github.com/golang/sys golang.org/x/sys 41f3e6584952bb034a481797859f6ab34b6803bd https://github.com/golang/sys
github.com/opencontainers/image-spec v1.0.1 github.com/opencontainers/image-spec v1.0.1
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c #golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 github.com/grpc-ecosystem/go-grpc-prometheus v1.1
github.com/Microsoft/go-winio v0.4.12 github.com/Microsoft/go-winio v0.4.12
github.com/Microsoft/hcsshim v0.8.6 github.com/Microsoft/hcsshim v0.8.6
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
@ -93,14 +94,14 @@ go.etcd.io/bbolt v1.3.1-etcd.8
github.com/kubernetes-sigs/cri-tools v1.14.0-k3s1 https://github.com/rancher/cri-tools.git github.com/kubernetes-sigs/cri-tools v1.14.0-k3s1 https://github.com/rancher/cri-tools.git
# cri dependencies # cri dependencies
github.com/containerd/cri v1.2-k3s.2 https://github.com/rancher/cri.git github.com/containerd/cri v1.2.7-k3s1 https://github.com/rancher/cri.git
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
#github.com/containernetworking/plugins v0.7.5 #github.com/containernetworking/plugins v0.7.5
github.com/davecgh/go-spew v1.1.0 github.com/davecgh/go-spew v1.1.0
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580 github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
github.com/docker/docker c12f09bf99b54f274a5ae241dd154fa74020cbab #github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/emicklei/go-restful v2.2.1 github.com/emicklei/go-restful v2.2.1
github.com/ghodss/yaml v1.0.0 github.com/ghodss/yaml v1.0.0
@ -112,13 +113,13 @@ github.com/json-iterator/go 1.1.5
github.com/modern-go/reflect2 1.0.1 github.com/modern-go/reflect2 1.0.1
github.com/modern-go/concurrent 1.0.3 github.com/modern-go/concurrent 1.0.3
github.com/opencontainers/runtime-tools v0.6.0 github.com/opencontainers/runtime-tools v0.6.0
github.com/opencontainers/selinux v1.2.1 github.com/opencontainers/selinux v1.2.2
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
github.com/tchap/go-patricia v2.2.6 github.com/tchap/go-patricia v2.2.6
github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6 github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6
github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b
github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874
golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 #golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067
golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4 golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4

View File

@ -131,7 +131,7 @@ func (c *criService) execInContainer(ctx context.Context, id string, opts execOp
defer func() { defer func() {
deferCtx, deferCancel := ctrdutil.DeferContext() deferCtx, deferCancel := ctrdutil.DeferContext()
defer deferCancel() defer deferCancel()
if _, err := process.Delete(deferCtx); err != nil { if _, err := process.Delete(deferCtx, containerd.WithProcessKill); err != nil {
logrus.WithError(err).Errorf("Failed to delete exec process %q for container %q", execID, id) logrus.WithError(err).Errorf("Failed to delete exec process %q for container %q", execID, id)
} }
}() }()

View File

@ -105,6 +105,9 @@ func setContainerRemoving(container containerstore.Container) error {
if status.State() == runtime.ContainerState_CONTAINER_UNKNOWN { if status.State() == runtime.ContainerState_CONTAINER_UNKNOWN {
return status, errors.New("container state is unknown, to stop first") return status, errors.New("container state is unknown, to stop first")
} }
if status.Starting {
return status, errors.New("container is in starting state, can't be removed")
}
if status.Removing { if status.Removing {
return status, errors.New("container is already in removing state") return status, errors.New("container is already in removing state")
} }

View File

@ -38,64 +38,48 @@ import (
// StartContainer starts the container. // StartContainer starts the container.
func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (retRes *runtime.StartContainerResponse, retErr error) { func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContainerRequest) (retRes *runtime.StartContainerResponse, retErr error) {
container, err := c.containerStore.Get(r.GetContainerId()) cntr, err := c.containerStore.Get(r.GetContainerId())
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId()) return nil, errors.Wrapf(err, "an error occurred when try to find container %q", r.GetContainerId())
} }
var startErr error
// update container status in one transaction to avoid race with event monitor.
if err := container.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
// Always apply status change no matter startContainer fails or not. Because startContainer
// may change container state no matter it fails or succeeds.
startErr = c.startContainer(ctx, container, &status)
return status, nil
}); startErr != nil {
return nil, startErr
} else if err != nil {
return nil, errors.Wrapf(err, "failed to update container %q metadata", container.ID)
}
return &runtime.StartContainerResponse{}, nil
}
// startContainer actually starts the container. The function needs to be run in one transaction. Any updates
// to the status passed in will be applied no matter the function returns error or not.
func (c *criService) startContainer(ctx context.Context,
cntr containerstore.Container,
status *containerstore.Status) (retErr error) {
id := cntr.ID id := cntr.ID
meta := cntr.Metadata meta := cntr.Metadata
container := cntr.Container container := cntr.Container
config := meta.Config config := meta.Config
// Return error if container is not in created state. // Set starting state to prevent other start/remove operations against this container
if status.State() != runtime.ContainerState_CONTAINER_CREATED { // while it's being started.
return errors.Errorf("container %q is in %s state", id, criContainerStateToString(status.State())) if err := setContainerStarting(cntr); err != nil {
return nil, errors.Wrapf(err, "failed to set starting state for container %q", id)
} }
// Do not start the container when there is a removal in progress.
if status.Removing {
return errors.Errorf("container %q is in removing state", id)
}
defer func() { defer func() {
if retErr != nil { if retErr != nil {
// Set container to exited if fail to start. // Set container to exited if fail to start.
status.Pid = 0 if err := cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
status.FinishedAt = time.Now().UnixNano() status.Pid = 0
status.ExitCode = errorStartExitCode status.FinishedAt = time.Now().UnixNano()
status.Reason = errorStartReason status.ExitCode = errorStartExitCode
status.Message = retErr.Error() status.Reason = errorStartReason
status.Message = retErr.Error()
return status, nil
}); err != nil {
logrus.WithError(err).Errorf("failed to set start failure state for container %q", id)
}
}
if err := resetContainerStarting(cntr); err != nil {
logrus.WithError(err).Errorf("failed to reset starting state for container %q", id)
} }
}() }()
// Get sandbox config from sandbox store. // Get sandbox config from sandbox store.
sandbox, err := c.sandboxStore.Get(meta.SandboxID) sandbox, err := c.sandboxStore.Get(meta.SandboxID)
if err != nil { if err != nil {
return errors.Wrapf(err, "sandbox %q not found", meta.SandboxID) return nil, errors.Wrapf(err, "sandbox %q not found", meta.SandboxID)
} }
sandboxID := meta.SandboxID sandboxID := meta.SandboxID
if sandbox.Status.Get().State != sandboxstore.StateReady { if sandbox.Status.Get().State != sandboxstore.StateReady {
return errors.Errorf("sandbox container %q is not running", sandboxID) return nil, errors.Errorf("sandbox container %q is not running", sandboxID)
} }
ioCreation := func(id string) (_ containerdio.IO, err error) { ioCreation := func(id string) (_ containerdio.IO, err error) {
@ -110,7 +94,7 @@ func (c *criService) startContainer(ctx context.Context,
ctrInfo, err := container.Info(ctx) ctrInfo, err := container.Info(ctx)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to get container info") return nil, errors.Wrap(err, "failed to get container info")
} }
var taskOpts []containerd.NewTaskOpts var taskOpts []containerd.NewTaskOpts
@ -120,7 +104,7 @@ func (c *criService) startContainer(ctx context.Context,
} }
task, err := container.NewTask(ctx, ioCreation, taskOpts...) task, err := container.NewTask(ctx, ioCreation, taskOpts...)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to create containerd task") return nil, errors.Wrap(err, "failed to create containerd task")
} }
defer func() { defer func() {
if retErr != nil { if retErr != nil {
@ -133,15 +117,61 @@ func (c *criService) startContainer(ctx context.Context,
} }
}() }()
// wait is a long running background request, no timeout needed.
exitCh, err := task.Wait(ctrdutil.NamespacedContext())
if err != nil {
return nil, errors.Wrap(err, "failed to wait for containerd task")
}
// Start containerd task. // Start containerd task.
if err := task.Start(ctx); err != nil { if err := task.Start(ctx); err != nil {
return errors.Wrapf(err, "failed to start containerd task %q", id) return nil, errors.Wrapf(err, "failed to start containerd task %q", id)
} }
// Update container start timestamp. // Update container start timestamp.
status.Pid = task.Pid() if err := cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
status.StartedAt = time.Now().UnixNano() status.Pid = task.Pid()
return nil status.StartedAt = time.Now().UnixNano()
return status, nil
}); err != nil {
return nil, errors.Wrapf(err, "failed to update container %q state", id)
}
// start the monitor after updating container state, this ensures that
// event monitor receives the TaskExit event and update container state
// after this.
c.eventMonitor.startExitMonitor(context.Background(), id, task.Pid(), exitCh)
return &runtime.StartContainerResponse{}, nil
}
// setContainerStarting sets the container into starting state. In starting state, the
// container will not be removed or started again.
func setContainerStarting(container containerstore.Container) error {
return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
// Return error if container is not in created state.
if status.State() != runtime.ContainerState_CONTAINER_CREATED {
return status, errors.Errorf("container is in %s state", criContainerStateToString(status.State()))
}
// Do not start the container when there is a removal in progress.
if status.Removing {
return status, errors.New("container is in removing state, can't be started")
}
if status.Starting {
return status, errors.New("container is already in starting state")
}
status.Starting = true
return status, nil
})
}
// resetContainerStarting resets the container starting state on start failure. So
// that we could remove the container later.
func resetContainerStarting(container containerstore.Container) error {
return container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
status.Starting = false
return status, nil
})
} }
// createContainerLoggers creates container loggers and return write closer for stdout and stderr. // createContainerLoggers creates container loggers and return write closer for stdout and stderr.

View File

@ -19,7 +19,6 @@ package server
import ( import (
"time" "time"
"github.com/containerd/containerd"
eventtypes "github.com/containerd/containerd/api/events" eventtypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/signal"
@ -29,6 +28,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
"github.com/containerd/cri/pkg/store" "github.com/containerd/cri/pkg/store"
containerstore "github.com/containerd/cri/pkg/store/container" containerstore "github.com/containerd/cri/pkg/store/container"
) )
@ -75,36 +75,34 @@ func (c *criService) stopContainer(ctx context.Context, container containerstore
return errors.Wrapf(err, "failed to get task for container %q", id) return errors.Wrapf(err, "failed to get task for container %q", id)
} }
// Don't return for unknown state, some cleanup needs to be done. // Don't return for unknown state, some cleanup needs to be done.
if state != runtime.ContainerState_CONTAINER_UNKNOWN { if state == runtime.ContainerState_CONTAINER_UNKNOWN {
return nil return cleanupUnknownContainer(ctx, id, container)
} }
// Task is an interface, explicitly set it to nil just in case. return nil
task = nil
} }
// Handle unknown state. // Handle unknown state.
if state == runtime.ContainerState_CONTAINER_UNKNOWN { if state == runtime.ContainerState_CONTAINER_UNKNOWN {
status, err := getTaskStatus(ctx, task) // Start an exit handler for containers in unknown state.
waitCtx, waitCancel := context.WithCancel(ctrdutil.NamespacedContext())
defer waitCancel()
exitCh, err := task.Wait(waitCtx)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to get task status for %q", id) if !errdefs.IsNotFound(err) {
} return errors.Wrapf(err, "failed to wait for task for %q", id)
switch status.Status { }
case containerd.Running, containerd.Created: return cleanupUnknownContainer(ctx, id, container)
// The task is still running, continue stopping the task.
case containerd.Stopped:
// The task has exited. If the task exited after containerd
// started, the event monitor will receive its exit event; if it
// exited before containerd started, the event monitor will never
// receive its exit event.
// However, we can't tell that because the task state was not
// successfully loaded during containerd start (container is
// in UNKNOWN state).
// So always do cleanup here, just in case that we've missed the
// exit event.
return cleanupUnknownContainer(ctx, id, status, container)
default:
return errors.Wrapf(err, "unsupported task status %q", status.Status)
} }
exitCtx, exitCancel := context.WithCancel(context.Background())
stopCh := c.eventMonitor.startExitMonitor(exitCtx, id, task.Pid(), exitCh)
defer func() {
exitCancel()
// This ensures that exit monitor is stopped before
// `Wait` is cancelled, so no exit event is generated
// because of the `Wait` cancellation.
<-stopCh
}()
} }
// We only need to kill the task. The event handler will Delete the // We only need to kill the task. The event handler will Delete the
@ -177,19 +175,13 @@ func (c *criService) waitContainerStop(ctx context.Context, container containers
} }
// cleanupUnknownContainer cleanup stopped container in unknown state. // cleanupUnknownContainer cleanup stopped container in unknown state.
func cleanupUnknownContainer(ctx context.Context, id string, status containerd.Status, func cleanupUnknownContainer(ctx context.Context, id string, cntr containerstore.Container) error {
cntr containerstore.Container) error {
// Reuse handleContainerExit to do the cleanup. // Reuse handleContainerExit to do the cleanup.
// NOTE(random-liu): If the task did exit after containerd started, both
// the event monitor and the cleanup function would update the container
// state. The final container state will be whatever being updated first.
// There is no way to completely avoid this race condition, and for best
// effort unknown state container cleanup, this seems acceptable.
return handleContainerExit(ctx, &eventtypes.TaskExit{ return handleContainerExit(ctx, &eventtypes.TaskExit{
ContainerID: id, ContainerID: id,
ID: id, ID: id,
Pid: 0, Pid: 0,
ExitStatus: status.ExitStatus, ExitStatus: unknownExitCode,
ExitedAt: status.ExitTime, ExitedAt: time.Now(),
}, cntr) }, cntr)
} }

View File

@ -50,13 +50,17 @@ const (
// Add a timeout for each event handling, events that timeout will be requeued and // Add a timeout for each event handling, events that timeout will be requeued and
// handled again in the future. // handled again in the future.
handleEventTimeout = 10 * time.Second handleEventTimeout = 10 * time.Second
exitChannelSize = 1024
) )
// eventMonitor monitors containerd event and updates internal state correspondingly. // eventMonitor monitors containerd event and updates internal state correspondingly.
// TODO(random-liu): Handle event for each container in a separate goroutine. // TODO(random-liu): Handle event for each container in a separate goroutine.
type eventMonitor struct { type eventMonitor struct {
c *criService c *criService
ch <-chan *events.Envelope ch <-chan *events.Envelope
// exitCh receives container/sandbox exit events from exit monitors.
exitCh chan *eventtypes.TaskExit
errCh <-chan error errCh <-chan error
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
@ -89,6 +93,7 @@ func newEventMonitor(c *criService) *eventMonitor {
c: c, c: c,
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
exitCh: make(chan *eventtypes.TaskExit, exitChannelSize),
backOff: newBackOff(), backOff: newBackOff(),
} }
} }
@ -98,13 +103,38 @@ func (em *eventMonitor) subscribe(subscriber events.Subscriber) {
// note: filters are any match, if you want any match but not in namespace foo // note: filters are any match, if you want any match but not in namespace foo
// then you have to manually filter namespace foo // then you have to manually filter namespace foo
filters := []string{ filters := []string{
`topic=="/tasks/exit"`,
`topic=="/tasks/oom"`, `topic=="/tasks/oom"`,
`topic~="/images/"`, `topic~="/images/"`,
} }
em.ch, em.errCh = subscriber.Subscribe(em.ctx, filters...) em.ch, em.errCh = subscriber.Subscribe(em.ctx, filters...)
} }
// startExitMonitor starts an exit monitor for a given container/sandbox.
func (em *eventMonitor) startExitMonitor(ctx context.Context, id string, pid uint32, exitCh <-chan containerd.ExitStatus) <-chan struct{} {
stopCh := make(chan struct{})
go func() {
defer close(stopCh)
select {
case exitRes := <-exitCh:
exitStatus, exitedAt, err := exitRes.Result()
if err != nil {
logrus.WithError(err).Errorf("Failed to get task exit status for %q", id)
exitStatus = unknownExitCode
exitedAt = time.Now()
}
em.exitCh <- &eventtypes.TaskExit{
ContainerID: id,
ID: id,
Pid: pid,
ExitStatus: exitStatus,
ExitedAt: exitedAt,
}
case <-ctx.Done():
}
}()
return stopCh
}
func convertEvent(e *gogotypes.Any) (string, interface{}, error) { func convertEvent(e *gogotypes.Any) (string, interface{}, error) {
id := "" id := ""
evt, err := typeurl.UnmarshalAny(e) evt, err := typeurl.UnmarshalAny(e)
@ -113,8 +143,6 @@ func convertEvent(e *gogotypes.Any) (string, interface{}, error) {
} }
switch e := evt.(type) { switch e := evt.(type) {
case *eventtypes.TaskExit:
id = e.ContainerID
case *eventtypes.TaskOOM: case *eventtypes.TaskOOM:
id = e.ContainerID id = e.ContainerID
case *eventtypes.ImageCreate: case *eventtypes.ImageCreate:
@ -142,6 +170,18 @@ func (em *eventMonitor) start() <-chan error {
defer close(errCh) defer close(errCh)
for { for {
select { select {
case e := <-em.exitCh:
logrus.Debugf("Received exit event %+v", e)
id := e.ID
if em.backOff.isInBackOff(id) {
logrus.Infof("Events for %q is in backoff, enqueue event %+v", id, e)
em.backOff.enBackOff(id, e)
break
}
if err := em.handleEvent(e); err != nil {
logrus.WithError(err).Errorf("Failed to handle exit event %+v for %s", e, id)
em.backOff.enBackOff(id, e)
}
case e := <-em.ch: case e := <-em.ch:
logrus.Debugf("Received containerd event timestamp - %v, namespace - %q, topic - %q", e.Timestamp, e.Namespace, e.Topic) logrus.Debugf("Received containerd event timestamp - %v, namespace - %q, topic - %q", e.Timestamp, e.Namespace, e.Topic)
if e.Namespace != constants.K8sContainerdNamespace { if e.Namespace != constants.K8sContainerdNamespace {
@ -217,8 +257,7 @@ func (em *eventMonitor) handleEvent(any interface{}) error {
} else if err != store.ErrNotExist { } else if err != store.ErrNotExist {
return errors.Wrap(err, "can't find container for TaskExit event") return errors.Wrap(err, "can't find container for TaskExit event")
} }
// Use GetAll to include sandbox in init state. sb, err := em.c.sandboxStore.Get(e.ID)
sb, err := em.c.sandboxStore.GetAll(e.ID)
if err == nil { if err == nil {
if err := handleSandboxExit(ctx, e, sb); err != nil { if err := handleSandboxExit(ctx, e, sb); err != nil {
return errors.Wrap(err, "failed to handle sandbox TaskExit event") return errors.Wrap(err, "failed to handle sandbox TaskExit event")
@ -326,15 +365,7 @@ func handleSandboxExit(ctx context.Context, e *eventtypes.TaskExit, sb sandboxst
} }
} }
err = sb.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) { err = sb.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) {
// NOTE(random-liu): We SHOULD NOT change INIT state here. status.State = sandboxstore.StateNotReady
// If sandbox state is INIT when event monitor receives an TaskExit event,
// it means that sandbox start has failed. In that case, `RunPodSandbox` will
// cleanup everything immediately.
// Once sandbox state goes out of INIT, it becomes visable to the user, which
// is not what we want.
if status.State != sandboxstore.StateInit {
status.State = sandboxstore.StateNotReady
}
status.Pid = 0 status.Pid = 0
return status, nil return status, nil
}) })

View File

@ -25,12 +25,9 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/runtime/linux/runctypes" "github.com/containerd/containerd/runtime/linux/runctypes"
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options" runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
@ -580,31 +577,6 @@ func unknownSandboxStatus() sandboxstore.Status {
} }
} }
// unknownExitStatus generates containerd.Status for container exited with unknown exit code.
func unknownExitStatus() containerd.Status {
return containerd.Status{
Status: containerd.Stopped,
ExitStatus: unknownExitCode,
ExitTime: time.Now(),
}
}
// getTaskStatus returns status for a given task. It returns unknown exit status if
// the task is nil or not found.
func getTaskStatus(ctx context.Context, task containerd.Task) (containerd.Status, error) {
if task == nil {
return unknownExitStatus(), nil
}
status, err := task.Status(ctx)
if err != nil {
if !errdefs.IsNotFound(err) {
return containerd.Status{}, err
}
return unknownExitStatus(), nil
}
return status, nil
}
func getCurrentOOMScoreAdj() (int, error) { func getCurrentOOMScoreAdj() (int, error) {
b, err := ioutil.ReadFile("/proc/self/oom_score_adj") b, err := ioutil.ReadFile("/proc/self/oom_score_adj")
if err != nil { if err != nil {

View File

@ -102,6 +102,7 @@ func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest)
containerd.WithResolver(resolver), containerd.WithResolver(resolver),
containerd.WithPullSnapshotter(c.config.ContainerdConfig.Snapshotter), containerd.WithPullSnapshotter(c.config.ContainerdConfig.Snapshotter),
containerd.WithPullUnpack, containerd.WithPullUnpack,
containerd.WithPullLabel(imageLabelKey, imageLabelValue),
) )
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to pull and unpack image %q", ref) return nil, errors.Wrapf(err, "failed to pull and unpack image %q", ref)

View File

@ -34,6 +34,7 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
"github.com/containerd/cri/pkg/netns" "github.com/containerd/cri/pkg/netns"
cio "github.com/containerd/cri/pkg/server/io" cio "github.com/containerd/cri/pkg/server/io"
containerstore "github.com/containerd/cri/pkg/store/container" containerstore "github.com/containerd/cri/pkg/store/container"
@ -57,7 +58,7 @@ func (c *criService) recover(ctx context.Context) error {
return errors.Wrap(err, "failed to list sandbox containers") return errors.Wrap(err, "failed to list sandbox containers")
} }
for _, sandbox := range sandboxes { for _, sandbox := range sandboxes {
sb, err := loadSandbox(ctx, sandbox) sb, err := c.loadSandbox(ctx, sandbox)
if err != nil { if err != nil {
logrus.WithError(err).Errorf("Failed to load sandbox %q", sandbox.ID()) logrus.WithError(err).Errorf("Failed to load sandbox %q", sandbox.ID())
continue continue
@ -275,6 +276,22 @@ func (c *criService) loadContainer(ctx context.Context, cntr containerd.Containe
status.StartedAt = time.Now().UnixNano() status.StartedAt = time.Now().UnixNano()
status.Pid = t.Pid() status.Pid = t.Pid()
} }
// Wait for the task for exit monitor.
// wait is a long running background request, no timeout needed.
exitCh, err := t.Wait(ctrdutil.NamespacedContext())
if err != nil {
if !errdefs.IsNotFound(err) {
return errors.Wrap(err, "failed to wait for task")
}
// Container was in running state, but its task has been deleted,
// set unknown exited state.
status.FinishedAt = time.Now().UnixNano()
status.ExitCode = unknownExitCode
status.Reason = unknownExitReason
} else {
// Start exit monitor.
c.eventMonitor.startExitMonitor(context.Background(), id, status.Pid, exitCh)
}
case containerd.Stopped: case containerd.Stopped:
// Task is stopped. Updata status and delete the task. // Task is stopped. Updata status and delete the task.
if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) { if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
@ -304,7 +321,7 @@ func (c *criService) loadContainer(ctx context.Context, cntr containerd.Containe
} }
// loadSandbox loads sandbox from containerd. // loadSandbox loads sandbox from containerd.
func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.Sandbox, error) { func (c *criService) loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.Sandbox, error) {
ctx, cancel := context.WithTimeout(ctx, loadContainerTimeout) ctx, cancel := context.WithTimeout(ctx, loadContainerTimeout)
defer cancel() defer cancel()
var sandbox sandboxstore.Sandbox var sandbox sandboxstore.Sandbox
@ -358,9 +375,20 @@ func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.S
status.State = sandboxstore.StateNotReady status.State = sandboxstore.StateNotReady
} else { } else {
if taskStatus.Status == containerd.Running { if taskStatus.Status == containerd.Running {
// Task is running, set sandbox state as READY. // Wait for the task for sandbox monitor.
status.State = sandboxstore.StateReady // wait is a long running background request, no timeout needed.
status.Pid = t.Pid() exitCh, err := t.Wait(ctrdutil.NamespacedContext())
if err != nil {
if !errdefs.IsNotFound(err) {
return status, errors.Wrap(err, "failed to wait for task")
}
status.State = sandboxstore.StateNotReady
} else {
// Task is running, set sandbox state as READY.
status.State = sandboxstore.StateReady
status.Pid = t.Pid()
c.eventMonitor.startExitMonitor(context.Background(), meta.ID, status.Pid, exitCh)
}
} else { } else {
// Task is not running. Delete the task and set sandbox state as NOTREADY. // Task is not running. Delete the task and set sandbox state as NOTREADY.
if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) { if _, err := t.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {

View File

@ -82,7 +82,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
RuntimeHandler: r.GetRuntimeHandler(), RuntimeHandler: r.GetRuntimeHandler(),
}, },
sandboxstore.Status{ sandboxstore.Status{
State: sandboxstore.StateInit, State: sandboxstore.StateUnknown,
}, },
) )
@ -247,88 +247,65 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to get sandbox container info") return nil, errors.Wrap(err, "failed to get sandbox container info")
} }
// Create sandbox task in containerd.
log.Tracef("Create sandbox container (id=%q, name=%q).",
id, name)
var taskOpts []containerd.NewTaskOpts
// TODO(random-liu): Remove this after shim v1 is deprecated.
if c.config.NoPivot && ociRuntime.Type == linuxRuntime {
taskOpts = append(taskOpts, containerd.WithNoPivotRoot)
}
// We don't need stdio for sandbox container.
task, err := container.NewTask(ctx, containerdio.NullIO, taskOpts...)
if err != nil {
return nil, errors.Wrap(err, "failed to create containerd task")
}
defer func() {
if retErr != nil {
deferCtx, deferCancel := ctrdutil.DeferContext()
defer deferCancel()
// Cleanup the sandbox container if an error is returned.
if _, err := task.Delete(deferCtx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
logrus.WithError(err).Errorf("Failed to delete sandbox container %q", id)
}
}
}()
// wait is a long running background request, no timeout needed.
exitCh, err := task.Wait(ctrdutil.NamespacedContext())
if err != nil {
return nil, errors.Wrap(err, "failed to wait for sandbox container task")
}
if err := task.Start(ctx); err != nil {
return nil, errors.Wrapf(err, "failed to start sandbox container task %q", id)
}
if err := sandbox.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) { if err := sandbox.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) {
// Set the pod sandbox as ready after successfully start sandbox container.
status.Pid = task.Pid()
status.State = sandboxstore.StateReady
status.CreatedAt = info.CreatedAt status.CreatedAt = info.CreatedAt
return status, nil return status, nil
}); err != nil { }); err != nil {
return nil, errors.Wrap(err, "failed to update sandbox created timestamp") return nil, errors.Wrap(err, "failed to update sandbox status")
} }
// Add sandbox into sandbox store in INIT state. // Add sandbox into sandbox store in INIT state.
sandbox.Container = container sandbox.Container = container
if err := c.sandboxStore.Add(sandbox); err != nil { if err := c.sandboxStore.Add(sandbox); err != nil {
return nil, errors.Wrapf(err, "failed to add sandbox %+v into store", sandbox) return nil, errors.Wrapf(err, "failed to add sandbox %+v into store", sandbox)
} }
defer func() {
// Delete sandbox from sandbox store if there is an error. // start the monitor after adding sandbox into the store, this ensures
if retErr != nil { // that sandbox is in the store, when event monitor receives the TaskExit event.
c.sandboxStore.Delete(id)
}
}()
// NOTE(random-liu): Sandbox state only stay in INIT state after this point
// and before the end of this function.
// * If `Update` succeeds, sandbox state will become READY in one transaction.
// * If `Update` fails, sandbox will be removed from the store in the defer above.
// * If containerd stops at any point before `Update` finishes, because sandbox
// state is not checkpointed, it will be recovered from corresponding containerd task
// status during restart:
// * If the task is running, sandbox state will be READY,
// * Or else, sandbox state will be NOTREADY.
// //
// In any case, sandbox will leave INIT state, so it's safe to ignore sandbox // TaskOOM from containerd may come before sandbox is added to store,
// in INIT state in other functions. // but we don't care about sandbox TaskOOM right now, so it is fine.
c.eventMonitor.startExitMonitor(context.Background(), id, task.Pid(), exitCh)
// Start sandbox container in one transaction to avoid race condition with
// event monitor.
if err := sandbox.Status.Update(func(status sandboxstore.Status) (_ sandboxstore.Status, retErr error) {
// NOTE(random-liu): We should not change the sandbox state to NOTREADY
// if `Update` fails.
//
// If `Update` fails, the sandbox will be cleaned up by all the defers
// above. We should not let user see this sandbox, or else they will
// see the sandbox disappear after the defer clean up, which may confuse
// them.
//
// Given so, we should keep the sandbox in INIT state if `Update` fails,
// and ignore sandbox in INIT state in all the inspection functions.
// Create sandbox task in containerd.
log.Tracef("Create sandbox container (id=%q, name=%q).",
id, name)
var taskOpts []containerd.NewTaskOpts
// TODO(random-liu): Remove this after shim v1 is deprecated.
if c.config.NoPivot && ociRuntime.Type == linuxRuntime {
taskOpts = append(taskOpts, containerd.WithNoPivotRoot)
}
// We don't need stdio for sandbox container.
task, err := container.NewTask(ctx, containerdio.NullIO, taskOpts...)
if err != nil {
return status, errors.Wrap(err, "failed to create containerd task")
}
defer func() {
if retErr != nil {
deferCtx, deferCancel := ctrdutil.DeferContext()
defer deferCancel()
// Cleanup the sandbox container if an error is returned.
// It's possible that task is deleted by event monitor.
if _, err := task.Delete(deferCtx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
logrus.WithError(err).Errorf("Failed to delete sandbox container %q", id)
}
}
}()
if err := task.Start(ctx); err != nil {
return status, errors.Wrapf(err, "failed to start sandbox container task %q", id)
}
// Set the pod sandbox as ready after successfully start sandbox container.
status.Pid = task.Pid()
status.State = sandboxstore.StateReady
return status, nil
}); err != nil {
return nil, errors.Wrap(err, "failed to start sandbox container")
}
return &runtime.RunPodSandboxResponse{PodSandboxId: id}, nil return &runtime.RunPodSandboxResponse{PodSandboxId: id}, nil
} }

View File

@ -19,7 +19,6 @@ package server
import ( import (
"time" "time"
"github.com/containerd/containerd"
eventtypes "github.com/containerd/containerd/api/events" eventtypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
cni "github.com/containerd/go-cni" cni "github.com/containerd/go-cni"
@ -29,6 +28,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
sandboxstore "github.com/containerd/cri/pkg/store/sandbox" sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
) )
@ -97,6 +97,7 @@ func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandb
// `task.Delete` is not called here because it will be called when // `task.Delete` is not called here because it will be called when
// the event monitor handles the `TaskExit` event. // the event monitor handles the `TaskExit` event.
func (c *criService) stopSandboxContainer(ctx context.Context, sandbox sandboxstore.Sandbox) error { func (c *criService) stopSandboxContainer(ctx context.Context, sandbox sandboxstore.Sandbox) error {
id := sandbox.ID
container := sandbox.Container container := sandbox.Container
state := sandbox.Status.Get().State state := sandbox.Status.Get().State
task, err := container.Task(ctx, nil) task, err := container.Task(ctx, nil)
@ -105,29 +106,35 @@ func (c *criService) stopSandboxContainer(ctx context.Context, sandbox sandboxst
return errors.Wrap(err, "failed to get sandbox container") return errors.Wrap(err, "failed to get sandbox container")
} }
// Don't return for unknown state, some cleanup needs to be done. // Don't return for unknown state, some cleanup needs to be done.
if state != sandboxstore.StateUnknown { if state == sandboxstore.StateUnknown {
return nil return cleanupUnknownSandbox(ctx, id, sandbox)
} }
// Task is an interface, explicitly set it to nil just in case. return nil
task = nil
} }
// Handle unknown state. // Handle unknown state.
// The cleanup logic is the same with container unknown state. // The cleanup logic is the same with container unknown state.
if state == sandboxstore.StateUnknown { if state == sandboxstore.StateUnknown {
status, err := getTaskStatus(ctx, task) // Start an exit handler for containers in unknown state.
waitCtx, waitCancel := context.WithCancel(ctrdutil.NamespacedContext())
defer waitCancel()
exitCh, err := task.Wait(waitCtx)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to get task status for %q", sandbox.ID) if !errdefs.IsNotFound(err) {
} return errors.Wrap(err, "failed to wait for task")
switch status.Status { }
case containerd.Running, containerd.Created: return cleanupUnknownSandbox(ctx, id, sandbox)
// The task is still running, continue stopping the task.
case containerd.Stopped:
// The task has exited, explicitly cleanup.
return cleanupUnknownSandbox(ctx, sandbox.ID, status, sandbox)
default:
return errors.Wrapf(err, "unsupported task status %q", status.Status)
} }
exitCtx, exitCancel := context.WithCancel(context.Background())
stopCh := c.eventMonitor.startExitMonitor(exitCtx, id, task.Pid(), exitCh)
defer func() {
exitCancel()
// This ensures that exit monitor is stopped before
// `Wait` is cancelled, so no exit event is generated
// because of the `Wait` cancellation.
<-stopCh
}()
} }
// Kill the sandbox container. // Kill the sandbox container.
@ -166,14 +173,13 @@ func (c *criService) teardownPod(id string, path string, config *runtime.PodSand
} }
// cleanupUnknownSandbox cleanup stopped sandbox in unknown state. // cleanupUnknownSandbox cleanup stopped sandbox in unknown state.
func cleanupUnknownSandbox(ctx context.Context, id string, status containerd.Status, func cleanupUnknownSandbox(ctx context.Context, id string, sandbox sandboxstore.Sandbox) error {
sandbox sandboxstore.Sandbox) error {
// Reuse handleSandboxExit to do the cleanup. // Reuse handleSandboxExit to do the cleanup.
return handleSandboxExit(ctx, &eventtypes.TaskExit{ return handleSandboxExit(ctx, &eventtypes.TaskExit{
ContainerID: id, ContainerID: id,
ID: id, ID: id,
Pid: 0, Pid: 0,
ExitStatus: status.ExitStatus, ExitStatus: unknownExitCode,
ExitedAt: status.ExitTime, ExitedAt: time.Now(),
}, sandbox) }, sandbox)
} }

View File

@ -88,6 +88,10 @@ type Status struct {
// Human-readable message indicating details about why container is in its // Human-readable message indicating details about why container is in its
// current state. // current state.
Message string Message string
// Starting indicates that the container is in starting state.
// This field doesn't need to be checkpointed.
// TODO(now): Add unit test.
Starting bool `json:"-"`
// Removing indicates that the container is in removing state. // Removing indicates that the container is in removing state.
// This field doesn't need to be checkpointed. // This field doesn't need to be checkpointed.
Removing bool `json:"-"` Removing bool `json:"-"`

View File

@ -86,22 +86,9 @@ func (s *Store) Add(sb Sandbox) error {
return nil return nil
} }
// Get returns the sandbox with specified id. Returns store.ErrNotExist // Get returns the sandbox with specified id.
// if the sandbox doesn't exist. // Returns store.ErrNotExist if the sandbox doesn't exist.
func (s *Store) Get(id string) (Sandbox, error) { func (s *Store) Get(id string) (Sandbox, error) {
sb, err := s.GetAll(id)
if err != nil {
return sb, err
}
if sb.Status.Get().State == StateInit {
return Sandbox{}, store.ErrNotExist
}
return sb, nil
}
// GetAll returns the sandbox with specified id, including sandbox in unknown
// state. Returns store.ErrNotExist if the sandbox doesn't exist.
func (s *Store) GetAll(id string) (Sandbox, error) {
s.lock.RLock() s.lock.RLock()
defer s.lock.RUnlock() defer s.lock.RUnlock()
id, err := s.idIndex.Get(id) id, err := s.idIndex.Get(id)
@ -123,9 +110,6 @@ func (s *Store) List() []Sandbox {
defer s.lock.RUnlock() defer s.lock.RUnlock()
var sandboxes []Sandbox var sandboxes []Sandbox
for _, sb := range s.sandboxes { for _, sb := range s.sandboxes {
if sb.Status.Get().State == StateInit {
continue
}
sandboxes = append(sandboxes, sb) sandboxes = append(sandboxes, sb)
} }
return sandboxes return sandboxes

View File

@ -26,11 +26,11 @@ import (
// | | // | |
// | Create(Run) | Load // | Create(Run) | Load
// | | // | |
// Start +----v----+ | // Start | |
// (failed) | | | // (failed) | |
// +-------------+ INIT | +-----------+ // +------------------+ +-----------+
// | | | | | // | | | |
// | +----+----+ | | // | | | |
// | | | | // | | | |
// | | Start(Run) | | // | | Start(Run) | |
// | | | | // | | | |
@ -53,23 +53,17 @@ import (
// +-------------> DELETED // +-------------> DELETED
// State is the sandbox state we use in containerd/cri. // State is the sandbox state we use in containerd/cri.
// It includes init and unknown, which are internal states not defined in CRI. // It includes unknown, which is internal states not defined in CRI.
// The state mapping from internal states to CRI states: // The state mapping from internal states to CRI states:
// * ready -> ready // * ready -> ready
// * not ready -> not ready // * not ready -> not ready
// * init -> not exist
// * unknown -> not ready // * unknown -> not ready
type State uint32 type State uint32
const ( const (
// StateInit is init state of sandbox. Sandbox
// is in init state before its corresponding sandbox container
// is created. Sandbox in init state should be ignored by most
// functions, unless the caller needs to update sandbox state.
StateInit State = iota
// StateReady is ready state, it means sandbox container // StateReady is ready state, it means sandbox container
// is running. // is running.
StateReady StateReady = iota
// StateNotReady is notready state, it ONLY means sandbox // StateNotReady is notready state, it ONLY means sandbox
// container is not running. // container is not running.
// StopPodSandbox should still be called for NOTREADY sandbox to // StopPodSandbox should still be called for NOTREADY sandbox to

View File

@ -1,14 +1,14 @@
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
github.com/blang/semver v3.1.0 github.com/blang/semver v3.1.0
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/containerd/cgroups dbea6f2bd41658b84b00417ceefa416b979cbf10 github.com/containerd/cgroups 4994991857f9b0ae8dc439551e8bebdbb4bf66c1
github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23
github.com/containerd/containerd v1.2.5 github.com/containerd/containerd v1.2.7
github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/go-cni 40bcf8ec8acd7372be1d77031d585d5d8e561c90 github.com/containerd/go-cni 40bcf8ec8acd7372be1d77031d585d5d8e561c90
github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3
github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a github.com/containerd/ttrpc f82148331ad2181edea8f3f649a1f7add6c3f9c2
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containernetworking/cni v0.6.0 github.com/containernetworking/cni v0.6.0
github.com/containernetworking/plugins v0.7.5 github.com/containernetworking/plugins v0.7.5
@ -39,10 +39,10 @@ github.com/modern-go/concurrent 1.0.3
github.com/modern-go/reflect2 1.0.1 github.com/modern-go/reflect2 1.0.1
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7 github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
github.com/opencontainers/image-spec v1.0.1 github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc 2b18fe1d885ee5083ef9f0838fee39b62d653e30 github.com/opencontainers/runc v1.0.0-rc8
github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353
github.com/opencontainers/runtime-tools v0.6.0 github.com/opencontainers/runtime-tools v0.6.0
github.com/opencontainers/selinux v1.2.1 github.com/opencontainers/selinux v1.2.2
github.com/pkg/errors v0.8.0 github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823 github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823

View File

@ -29,6 +29,8 @@ type chain struct {
entryRules [][]string // the rules that "point" to this chain entryRules [][]string // the rules that "point" to this chain
rules [][]string // the rules this chain contains rules [][]string // the rules this chain contains
prependEntry bool // whether or not the entry rules should be prepended
} }
// setup idempotently creates the chain. It will not error if the chain exists. // setup idempotently creates the chain. It will not error if the chain exists.
@ -45,19 +47,19 @@ func (c *chain) setup(ipt *iptables.IPTables) error {
} }
// Add the rules to the chain // Add the rules to the chain
for i := len(c.rules) - 1; i >= 0; i-- { for _, rule := range c.rules {
if err := prependUnique(ipt, c.table, c.name, c.rules[i]); err != nil { if err := insertUnique(ipt, c.table, c.name, false, rule); err != nil {
return err return err
} }
} }
// Add the entry rules to the entry chains // Add the entry rules to the entry chains
for _, entryChain := range c.entryChains { for _, entryChain := range c.entryChains {
for i := len(c.entryRules) - 1; i >= 0; i-- { for _, rule := range c.entryRules {
r := []string{} r := []string{}
r = append(r, c.entryRules[i]...) r = append(r, rule...)
r = append(r, "-j", c.name) r = append(r, "-j", c.name)
if err := prependUnique(ipt, c.table, entryChain, r); err != nil { if err := insertUnique(ipt, c.table, entryChain, c.prependEntry, r); err != nil {
return err return err
} }
} }
@ -105,8 +107,9 @@ func (c *chain) teardown(ipt *iptables.IPTables) error {
return nil return nil
} }
// prependUnique will prepend a rule to a chain, if it does not already exist // insertUnique will add a rule to a chain if it does not already exist.
func prependUnique(ipt *iptables.IPTables, table, chain string, rule []string) error { // By default the rule is appended, unless prepend is true.
func insertUnique(ipt *iptables.IPTables, table, chain string, prepend bool, rule []string) error {
exists, err := ipt.Exists(table, chain, rule...) exists, err := ipt.Exists(table, chain, rule...)
if err != nil { if err != nil {
return err return err
@ -115,7 +118,11 @@ func prependUnique(ipt *iptables.IPTables, table, chain string, rule []string) e
return nil return nil
} }
return ipt.Insert(table, chain, 1, rule...) if prepend {
return ipt.Insert(table, chain, 1, rule...)
} else {
return ipt.Append(table, chain, rule...)
}
} }
func chainExists(ipt *iptables.IPTables, tableName, chainName string) (bool, error) { func chainExists(ipt *iptables.IPTables, tableName, chainName string) (bool, error) {

View File

@ -255,6 +255,10 @@ func genMarkMasqChain(markBit int) chain {
table: "nat", table: "nat",
name: MarkMasqChainName, name: MarkMasqChainName,
entryChains: []string{"POSTROUTING"}, entryChains: []string{"POSTROUTING"},
// Only this entry chain needs to be prepended, because otherwise it is
// stomped on by the masquerading rules created by the CNI ptp and bridge
// plugins.
prependEntry: true,
entryRules: [][]string{{ entryRules: [][]string{{
"-m", "comment", "-m", "comment",
"--comment", "CNI portfwd requiring masquerade", "--comment", "CNI portfwd requiring masquerade",

View File

@ -1 +1 @@
1.3.0-dev 1.2.2

View File

@ -406,7 +406,14 @@ func SocketLabel() (string, error) {
// SetKeyLabel takes a process label and tells the kernel to assign the // SetKeyLabel takes a process label and tells the kernel to assign the
// label to the next kernel keyring that gets created // label to the next kernel keyring that gets created
func SetKeyLabel(label string) error { func SetKeyLabel(label string) error {
return writeCon("/proc/self/attr/keycreate", label) err := writeCon("/proc/self/attr/keycreate", label)
if os.IsNotExist(err) {
return nil
}
if label == "" && os.IsPermission(err) && !GetEnabled() {
return nil
}
return err
} }
// KeyLabel retrieves the current kernel keyring label setting // KeyLabel retrieves the current kernel keyring label setting