diff --git a/api/server/router/container/backend.go b/api/server/router/container/backend.go index 0819911d9e..fa1c0dcc54 100644 --- a/api/server/router/container/backend.go +++ b/api/server/router/container/backend.go @@ -32,13 +32,13 @@ type copyBackend interface { // stateBackend includes functions to implement to provide container state lifecycle functionality. type stateBackend interface { - ContainerCreate(ctx context.Context, config types.ContainerCreateConfig) (container.CreateResponse, error) + ContainerCreate(ctx context.Context, config backend.ContainerCreateConfig) (container.CreateResponse, error) ContainerKill(name string, signal string) error ContainerPause(name string) error ContainerRename(oldName, newName string) error ContainerResize(name string, height, width int) error ContainerRestart(ctx context.Context, name string, options container.StopOptions) error - ContainerRm(name string, config *types.ContainerRmConfig) error + ContainerRm(name string, config *backend.ContainerRmConfig) error ContainerStart(ctx context.Context, name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error ContainerStop(ctx context.Context, name string, options container.StopOptions) error ContainerUnpause(name string) error diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go index 0278859751..08a9f3c0ad 100644 --- a/api/server/router/container/container_routes.go +++ b/api/server/router/container/container_routes.go @@ -643,7 +643,7 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo hostConfig.PidsLimit = nil } - ccr, err := s.backend.ContainerCreate(ctx, types.ContainerCreateConfig{ + ccr, err := s.backend.ContainerCreate(ctx, backend.ContainerCreateConfig{ Name: name, Config: config, HostConfig: hostConfig, @@ -712,7 +712,7 @@ func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseW } name := vars["name"] - config := &types.ContainerRmConfig{ + config := &backend.ContainerRmConfig{ ForceRemove: httputils.BoolValue(r, "force"), RemoveVolume: httputils.BoolValue(r, "v"), RemoveLink: httputils.BoolValue(r, "link"), diff --git a/api/types/backend/backend.go b/api/types/backend/backend.go index 45df56fd71..dc0500570c 100644 --- a/api/types/backend/backend.go +++ b/api/types/backend/backend.go @@ -7,8 +7,27 @@ import ( "github.com/distribution/reference" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) +// ContainerCreateConfig is the parameter set to ContainerCreate() +type ContainerCreateConfig struct { + Name string + Config *container.Config + HostConfig *container.HostConfig + NetworkingConfig *network.NetworkingConfig + Platform *ocispec.Platform + AdjustCPUShares bool +} + +// ContainerRmConfig holds arguments for the container remove +// operation. This struct is used to tell the backend what operations +// to perform. +type ContainerRmConfig struct { + ForceRemove, RemoveVolume, RemoveLink bool +} + // ContainerAttachConfig holds the streams to use when connecting to a container to view logs. type ContainerAttachConfig struct { GetStreams func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error) diff --git a/api/types/configs.go b/api/types/configs.go index 7d5930bbeb..07a13afeb0 100644 --- a/api/types/configs.go +++ b/api/types/configs.go @@ -1,32 +1,9 @@ package types // import "github.com/docker/docker/api/types" -import ( - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" -) - // configs holds structs used for internal communication between the // frontend (such as an http server) and the backend (such as the // docker daemon). -// ContainerCreateConfig is the parameter set to ContainerCreate() -type ContainerCreateConfig struct { - Name string - Config *container.Config - HostConfig *container.HostConfig - NetworkingConfig *network.NetworkingConfig - Platform *ocispec.Platform - AdjustCPUShares bool -} - -// ContainerRmConfig holds arguments for the container remove -// operation. This struct is used to tell the backend what operations -// to perform. -type ContainerRmConfig struct { - ForceRemove, RemoveVolume, RemoveLink bool -} - // ExecConfig is a small subset of the Config struct that holds the configuration // for the exec feature of docker. type ExecConfig struct { diff --git a/builder/builder.go b/builder/builder.go index 402b0cdc8f..76c6648463 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -8,7 +8,6 @@ import ( "context" "io" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/container" containerpkg "github.com/docker/docker/container" @@ -60,9 +59,9 @@ type ExecBackend interface { // ContainerAttachRaw attaches to container. ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool, attached chan struct{}) error // ContainerCreateIgnoreImagesArgsEscaped creates a new Docker container and returns potential warnings - ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, config types.ContainerCreateConfig) (container.CreateResponse, error) + ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, config backend.ContainerCreateConfig) (container.CreateResponse, error) // ContainerRm removes a container specified by `id`. - ContainerRm(name string, config *types.ContainerRmConfig) error + ContainerRm(name string, config *backend.ContainerRmConfig) error // ContainerKill stops the container execution abruptly. ContainerKill(containerID string, sig string) error // ContainerStart starts a new container diff --git a/builder/dockerfile/containerbackend.go b/builder/dockerfile/containerbackend.go index 81bb165325..a4a8b08569 100644 --- a/builder/dockerfile/containerbackend.go +++ b/builder/dockerfile/containerbackend.go @@ -6,7 +6,7 @@ import ( "io" "github.com/containerd/log" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/container" "github.com/docker/docker/builder" containerpkg "github.com/docker/docker/container" @@ -29,15 +29,15 @@ func newContainerManager(docker builder.ExecBackend) *containerManager { // Create a container func (c *containerManager) Create(ctx context.Context, runConfig *container.Config, hostConfig *container.HostConfig) (container.CreateResponse, error) { - container, err := c.backend.ContainerCreateIgnoreImagesArgsEscaped(ctx, types.ContainerCreateConfig{ + ctr, err := c.backend.ContainerCreateIgnoreImagesArgsEscaped(ctx, backend.ContainerCreateConfig{ Config: runConfig, HostConfig: hostConfig, }) if err != nil { - return container, err + return ctr, err } - c.tmpContainers[container.ID] = struct{}{} - return container, nil + c.tmpContainers[ctr.ID] = struct{}{} + return ctr, nil } var errCancelled = errors.New("build cancelled") @@ -123,7 +123,7 @@ func (e *statusCodeError) StatusCode() int { } func (c *containerManager) removeContainer(containerID string, stdout io.Writer) error { - rmConfig := &types.ContainerRmConfig{ + rmConfig := &backend.ContainerRmConfig{ ForceRemove: true, RemoveVolume: true, } diff --git a/builder/dockerfile/dispatchers_test.go b/builder/dockerfile/dispatchers_test.go index 01626f3d02..d0edc527d5 100644 --- a/builder/dockerfile/dispatchers_test.go +++ b/builder/dockerfile/dispatchers_test.go @@ -479,7 +479,7 @@ func TestRunWithBuildArgs(t *testing.T) { config: &container.Config{Cmd: origCmd}, }, nil, nil } - mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.CreateResponse, error) { + mockBackend.containerCreateFunc = func(config backend.ContainerCreateConfig) (container.CreateResponse, error) { // Check the runConfig.Cmd sent to create() assert.Check(t, is.DeepEqual(cmdWithShell, config.Config.Cmd)) assert.Check(t, is.Contains(config.Config.Env, "one=two")) @@ -548,7 +548,7 @@ func TestRunIgnoresHealthcheck(t *testing.T) { config: &container.Config{Cmd: origCmd}, }, nil, nil } - mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.CreateResponse, error) { + mockBackend.containerCreateFunc = func(config backend.ContainerCreateConfig) (container.CreateResponse, error) { return container.CreateResponse{ID: "12345"}, nil } mockBackend.commitFunc = func(cfg backend.CommitConfig) (image.ID, error) { @@ -575,7 +575,7 @@ func TestRunIgnoresHealthcheck(t *testing.T) { assert.NilError(t, dispatch(context.TODO(), sb, cmd)) assert.Assert(t, sb.state.runConfig.Healthcheck != nil) - mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.CreateResponse, error) { + mockBackend.containerCreateFunc = func(config backend.ContainerCreateConfig) (container.CreateResponse, error) { // Check the Healthcheck is disabled. assert.Check(t, is.DeepEqual([]string{"NONE"}, config.Config.Healthcheck.Test)) return container.CreateResponse{ID: "123456"}, nil diff --git a/builder/dockerfile/mockbackend_test.go b/builder/dockerfile/mockbackend_test.go index 3e3c28b878..1278ffcf91 100644 --- a/builder/dockerfile/mockbackend_test.go +++ b/builder/dockerfile/mockbackend_test.go @@ -6,7 +6,6 @@ import ( "io" "runtime" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/container" "github.com/docker/docker/builder" @@ -18,7 +17,7 @@ import ( // MockBackend implements the builder.Backend interface for unit testing type MockBackend struct { - containerCreateFunc func(config types.ContainerCreateConfig) (container.CreateResponse, error) + containerCreateFunc func(config backend.ContainerCreateConfig) (container.CreateResponse, error) commitFunc func(backend.CommitConfig) (image.ID, error) getImageFunc func(string) (builder.Image, builder.ROLayer, error) makeImageCacheFunc func(cacheFrom []string) builder.ImageCache @@ -28,14 +27,14 @@ func (m *MockBackend) ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout return nil } -func (m *MockBackend) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, config types.ContainerCreateConfig) (container.CreateResponse, error) { +func (m *MockBackend) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, config backend.ContainerCreateConfig) (container.CreateResponse, error) { if m.containerCreateFunc != nil { return m.containerCreateFunc(config) } return container.CreateResponse{}, nil } -func (m *MockBackend) ContainerRm(name string, config *types.ContainerRmConfig) error { +func (m *MockBackend) ContainerRm(name string, config *backend.ContainerRmConfig) error { return nil } diff --git a/daemon/cluster/executor/backend.go b/daemon/cluster/executor/backend.go index 976a434545..4cd9a2391d 100644 --- a/daemon/cluster/executor/backend.go +++ b/daemon/cluster/executor/backend.go @@ -38,7 +38,7 @@ type Backend interface { FindNetwork(idName string) (*libnetwork.Network, error) SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error) ReleaseIngress() (<-chan struct{}, error) - CreateManagedContainer(ctx context.Context, config types.ContainerCreateConfig) (container.CreateResponse, error) + CreateManagedContainer(ctx context.Context, config backend.ContainerCreateConfig) (container.CreateResponse, error) ContainerStart(ctx context.Context, name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error ContainerStop(ctx context.Context, name string, config container.StopOptions) error ContainerLogs(ctx context.Context, name string, config *container.LogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error) @@ -48,7 +48,7 @@ type Backend interface { UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error ContainerInspectCurrent(ctx context.Context, name string, size bool) (*types.ContainerJSON, error) ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error) - ContainerRm(name string, config *types.ContainerRmConfig) error + ContainerRm(name string, config *backend.ContainerRmConfig) error ContainerKill(name string, sig string) error SetContainerDependencyStore(name string, store exec.DependencyGetter) error SetContainerSecretReferences(name string, refs []*swarm.SecretReference) error diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index 32e1d3dbd0..487d45da7a 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/daemon/cluster/executor/container/adapter.go @@ -293,7 +293,7 @@ func (c *containerAdapter) waitForDetach(ctx context.Context) error { func (c *containerAdapter) create(ctx context.Context) error { var cr containertypes.CreateResponse var err error - if cr, err = c.backend.CreateManagedContainer(ctx, types.ContainerCreateConfig{ + if cr, err = c.backend.CreateManagedContainer(ctx, backend.ContainerCreateConfig{ Name: c.container.name(), Config: c.container.config(), HostConfig: c.container.hostConfig(c.dependencies.Volumes()), @@ -417,7 +417,7 @@ func (c *containerAdapter) terminate(ctx context.Context) error { } func (c *containerAdapter) remove(ctx context.Context) error { - return c.backend.ContainerRm(c.container.name(), &types.ContainerRmConfig{ + return c.backend.ContainerRm(c.container.name(), &backend.ContainerRmConfig{ RemoveVolume: true, ForceRemove: true, }) diff --git a/daemon/cluster/swarm.go b/daemon/cluster/swarm.go index e81c838a48..9562fcc1f1 100644 --- a/daemon/cluster/swarm.go +++ b/daemon/cluster/swarm.go @@ -8,7 +8,7 @@ import ( "time" "github.com/containerd/log" - apitypes "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" types "github.com/docker/docker/api/types/swarm" @@ -414,7 +414,7 @@ func (c *Cluster) Leave(ctx context.Context, force bool) error { return err } for _, id := range nodeContainers { - if err := c.config.Backend.ContainerRm(id, &apitypes.ContainerRmConfig{ForceRemove: true}); err != nil { + if err := c.config.Backend.ContainerRm(id, &backend.ContainerRmConfig{ForceRemove: true}); err != nil { log.G(ctx).Errorf("error removing %v: %v", id, err) } } diff --git a/daemon/create.go b/daemon/create.go index d77d0594d6..757f8b026e 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -9,7 +9,7 @@ import ( "github.com/containerd/containerd/platforms" "github.com/containerd/log" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/events" imagetypes "github.com/docker/docker/api/types/image" @@ -28,13 +28,13 @@ import ( ) type createOpts struct { - params types.ContainerCreateConfig + params backend.ContainerCreateConfig managed bool ignoreImagesArgsEscaped bool } // CreateManagedContainer creates a container that is managed by a Service -func (daemon *Daemon) CreateManagedContainer(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) { +func (daemon *Daemon) CreateManagedContainer(ctx context.Context, params backend.ContainerCreateConfig) (containertypes.CreateResponse, error) { return daemon.containerCreate(ctx, daemon.config(), createOpts{ params: params, managed: true, @@ -42,7 +42,7 @@ func (daemon *Daemon) CreateManagedContainer(ctx context.Context, params types.C } // ContainerCreate creates a regular container -func (daemon *Daemon) ContainerCreate(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) { +func (daemon *Daemon) ContainerCreate(ctx context.Context, params backend.ContainerCreateConfig) (containertypes.CreateResponse, error) { return daemon.containerCreate(ctx, daemon.config(), createOpts{ params: params, }) @@ -50,7 +50,7 @@ func (daemon *Daemon) ContainerCreate(ctx context.Context, params types.Containe // ContainerCreateIgnoreImagesArgsEscaped creates a regular container. This is called from the builder RUN case // and ensures that we do not take the images ArgsEscaped -func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) { +func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, params backend.ContainerCreateConfig) (containertypes.CreateResponse, error) { return daemon.containerCreate(ctx, daemon.config(), createOpts{ params: params, ignoreImagesArgsEscaped: true, @@ -176,7 +176,7 @@ func (daemon *Daemon) create(ctx context.Context, daemonCfg *config.Config, opts } defer func() { if retErr != nil { - err = daemon.cleanupContainer(ctr, types.ContainerRmConfig{ + err = daemon.cleanupContainer(ctr, backend.ContainerRmConfig{ ForceRemove: true, RemoveVolume: true, }) diff --git a/daemon/daemon.go b/daemon/daemon.go index a8febbd3aa..f332b4e77b 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -26,6 +26,7 @@ import ( "github.com/distribution/reference" dist "github.com/docker/distribution" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" containertypes "github.com/docker/docker/api/types/container" imagetypes "github.com/docker/docker/api/types/image" registrytypes "github.com/docker/docker/api/types/registry" @@ -594,7 +595,7 @@ func (daemon *Daemon) restore(cfg *configStore) error { go func(cid string) { _ = sem.Acquire(context.Background(), 1) - if err := daemon.containerRm(&cfg.Config, cid, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil { + if err := daemon.containerRm(&cfg.Config, cid, &backend.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil { log.G(context.TODO()).WithField("container", cid).WithError(err).Error("failed to remove container") } diff --git a/daemon/delete.go b/daemon/delete.go index fd325cf303..39b4f1f34c 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -11,7 +11,7 @@ import ( cerrdefs "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/leases" "github.com/containerd/log" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/events" "github.com/docker/docker/container" @@ -26,11 +26,11 @@ import ( // is returned if the container is not found, or if the remove // fails. If the remove succeeds, the container name is released, and // network links are removed. -func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) error { +func (daemon *Daemon) ContainerRm(name string, config *backend.ContainerRmConfig) error { return daemon.containerRm(&daemon.config().Config, name, config) } -func (daemon *Daemon) containerRm(cfg *config.Config, name string, opts *types.ContainerRmConfig) error { +func (daemon *Daemon) containerRm(cfg *config.Config, name string, opts *backend.ContainerRmConfig) error { start := time.Now() ctr, err := daemon.GetContainer(name) if err != nil { @@ -87,7 +87,7 @@ func (daemon *Daemon) rmLink(cfg *config.Config, container *container.Container, // cleanupContainer unregisters a container from the daemon, stops stats // collection and cleanly removes contents and metadata from the filesystem. -func (daemon *Daemon) cleanupContainer(container *container.Container, config types.ContainerRmConfig) error { +func (daemon *Daemon) cleanupContainer(container *container.Container, config backend.ContainerRmConfig) error { if container.IsRunning() { if !config.ForceRemove { if state := container.StateString(); state == "paused" { diff --git a/daemon/delete_test.go b/daemon/delete_test.go index df7d434212..9733498cac 100644 --- a/daemon/delete_test.go +++ b/daemon/delete_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/errdefs" @@ -74,7 +74,7 @@ func TestContainerDelete(t *testing.T) { defer cleanup() d.containers.Add(c.ID, c) - err := d.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: false}) + err := d.ContainerRm(c.ID, &backend.ContainerRmConfig{ForceRemove: false}) assert.Check(t, is.ErrorType(err, errdefs.IsConflict)) assert.Check(t, is.ErrorContains(err, tc.errMsg)) }) @@ -93,6 +93,6 @@ func TestContainerDoubleDelete(t *testing.T) { // Try to remove the container when its state is removalInProgress. // It should return an error indicating it is under removal progress. - err := d.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: true}) + err := d.ContainerRm(c.ID, &backend.ContainerRmConfig{ForceRemove: true}) assert.Check(t, is.ErrorContains(err, fmt.Sprintf("removal of container %s is already in progress", c.ID))) } diff --git a/daemon/monitor.go b/daemon/monitor.go index 200fd40618..4734fe45e5 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -6,7 +6,7 @@ import ( "time" "github.com/containerd/log" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/events" "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" @@ -306,7 +306,7 @@ func (daemon *Daemon) autoRemove(cfg *config.Config, c *container.Container) { return } - err := daemon.containerRm(cfg, c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}) + err := daemon.containerRm(cfg, c.ID, &backend.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}) if err == nil { return } diff --git a/daemon/prune.go b/daemon/prune.go index 6f1f131e02..e147c2d959 100644 --- a/daemon/prune.go +++ b/daemon/prune.go @@ -9,6 +9,7 @@ import ( "github.com/containerd/log" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" timetypes "github.com/docker/docker/api/types/time" @@ -78,7 +79,7 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters. return nil, err } // TODO: sets RmLink to true? - err = daemon.containerRm(cfg, c.ID, &types.ContainerRmConfig{}) + err = daemon.containerRm(cfg, c.ID, &backend.ContainerRmConfig{}) if err != nil { log.G(ctx).Warnf("failed to prune container %s: %v", c.ID, err) continue diff --git a/daemon/start.go b/daemon/start.go index 5ea8f70ec0..948f458c0e 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -6,7 +6,7 @@ import ( "time" "github.com/containerd/log" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/events" "github.com/docker/docker/container" @@ -142,7 +142,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *configStore // if containers AutoRemove flag is set, remove it after clean up if container.HostConfig.AutoRemove { container.Unlock() - if err := daemon.containerRm(&daemonCfg.Config, container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil { + if err := daemon.containerRm(&daemonCfg.Config, container.ID, &backend.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil { log.G(ctx).Errorf("can't remove container %s: %v", container.ID, err) } container.Lock()