Make plugin removes more resilient to failure

Before this patch, if the plugin's `config.json` is successfully removed
but the main plugin state dir could not be removed for some reason (e.g.
leaked mount), it will prevent the daemon from being able to be
restarted.

This patches changes this to atomically remove the plugin such that on
daemon restart we can detect that there was an error and re-try. It also
changes the logic so that it only logs errors on restore rather than
erroring out the daemon.

This also removes some code which is now duplicated elsewhere.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 11cf394e5ea964636294a219872b188fe5bdf4dd)
Signed-off-by: Andrew Hsu <andrewhsu@docker.com>
This commit is contained in:
Brian Goff
2017-06-26 14:54:14 -04:00
committed by Andrew Hsu
parent 2036e34692
commit 73b8fa7bc6
4 changed files with 81 additions and 29 deletions

View File

@ -5,6 +5,7 @@ package main
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"io"
@ -25,6 +26,9 @@ import (
"crypto/x509"
"github.com/cloudflare/cfssl/helpers"
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/docker/docker/integration-cli/checker"
"github.com/docker/docker/integration-cli/cli"
"github.com/docker/docker/integration-cli/daemon"
@ -2980,3 +2984,45 @@ func (s *DockerDaemonSuite) TestShmSizeReload(c *check.C) {
c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
}
// TestFailedPluginRemove makes sure that a failed plugin remove does not block
// the daemon from starting
func (s *DockerDaemonSuite) TestFailedPluginRemove(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, SameHostDaemon)
d := daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{})
d.Start(c)
cli, err := client.NewClient(d.Sock(), api.DefaultVersion, nil, nil)
c.Assert(err, checker.IsNil)
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second)
defer cancel()
name := "test-plugin-rm-fail"
out, err := cli.PluginInstall(ctx, name, types.PluginInstallOptions{
Disabled: true,
AcceptAllPermissions: true,
RemoteRef: "cpuguy83/docker-logdriver-test",
})
c.Assert(err, checker.IsNil)
defer out.Close()
io.Copy(ioutil.Discard, out)
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
p, _, err := cli.PluginInspectWithRaw(ctx, name)
c.Assert(err, checker.IsNil)
// simulate a bad/partial removal by removing the plugin config.
configPath := filepath.Join(d.Root, "plugins", p.ID, "config.json")
c.Assert(os.Remove(configPath), checker.IsNil)
d.Restart(c)
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = cli.Ping(ctx)
c.Assert(err, checker.IsNil)
_, _, err = cli.PluginInspectWithRaw(ctx, name)
// plugin should be gone since the config.json is gone
c.Assert(err, checker.NotNil)
}