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:
@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user