1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #34085 from cpuguy83/tests_use_locally_built_plugin

Use local plugins, not from hub for tests
This commit is contained in:
Vincent Demeester 2017-07-18 10:47:58 +02:00 committed by GitHub
commit f909119582
6 changed files with 338 additions and 207 deletions

View file

@ -5,21 +5,26 @@ import (
"net/http/httptest"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"sync"
"syscall"
"testing"
"time"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/cli/config"
"github.com/docker/docker/integration-cli/checker"
"github.com/docker/docker/integration-cli/cli"
"github.com/docker/docker/integration-cli/cli/build/fakestorage"
"github.com/docker/docker/integration-cli/daemon"
"github.com/docker/docker/integration-cli/environment"
"github.com/docker/docker/integration-cli/fixtures/plugin"
"github.com/docker/docker/integration-cli/registry"
"github.com/docker/docker/pkg/reexec"
"github.com/go-check/check"
"golang.org/x/net/context"
)
const (
@ -442,3 +447,50 @@ func (s *DockerTrustedSwarmSuite) TearDownTest(c *check.C) {
func (s *DockerTrustedSwarmSuite) OnTimeout(c *check.C) {
s.swarmSuite.OnTimeout(c)
}
func init() {
check.Suite(&DockerPluginSuite{
ds: &DockerSuite{},
})
}
type DockerPluginSuite struct {
ds *DockerSuite
registry *registry.V2
}
func (ps *DockerPluginSuite) registryHost() string {
return privateRegistryURL
}
func (ps *DockerPluginSuite) getPluginRepo() string {
return path.Join(ps.registryHost(), "plugin", "basic")
}
func (ps *DockerPluginSuite) getPluginRepoWithTag() string {
return ps.getPluginRepo() + ":" + "latest"
}
func (ps *DockerPluginSuite) SetUpSuite(c *check.C) {
testRequires(c, DaemonIsLinux)
ps.registry = setupRegistry(c, false, "", "")
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
err := plugin.CreateInRegistry(ctx, ps.getPluginRepo(), nil)
c.Assert(err, checker.IsNil, check.Commentf("failed to create plugin"))
}
func (ps *DockerPluginSuite) TearDownSuite(c *check.C) {
if ps.registry != nil {
ps.registry.Close()
}
}
func (ps *DockerPluginSuite) TearDownTest(c *check.C) {
ps.ds.TearDownTest(c)
}
func (ps *DockerPluginSuite) OnTimeout(c *check.C) {
ps.ds.OnTimeout(c)
}

View file

@ -5,14 +5,20 @@ import (
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/integration-cli/checker"
"github.com/docker/docker/integration-cli/cli"
"github.com/docker/docker/integration-cli/daemon"
"github.com/docker/docker/integration-cli/fixtures/plugin"
"github.com/docker/docker/integration-cli/request"
icmd "github.com/docker/docker/pkg/testutil/cmd"
"github.com/go-check/check"
"golang.org/x/net/context"
)
var (
@ -24,31 +30,30 @@ var (
npNameWithTag = npName + ":" + pTag
)
func (s *DockerSuite) TestPluginBasicOps(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
func (ps *DockerPluginSuite) TestPluginBasicOps(c *check.C) {
plugin := ps.getPluginRepoWithTag()
_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", plugin)
c.Assert(err, checker.IsNil)
out, _, err := dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, pName)
c.Assert(out, checker.Contains, pTag)
c.Assert(out, checker.Contains, plugin)
c.Assert(out, checker.Contains, "true")
id, _, err := dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pNameWithTag)
id, _, err := dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", plugin)
id = strings.TrimSpace(id)
c.Assert(err, checker.IsNil)
out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
out, _, err = dockerCmdWithError("plugin", "remove", plugin)
c.Assert(err, checker.NotNil)
c.Assert(out, checker.Contains, "is enabled")
_, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
_, _, err = dockerCmdWithError("plugin", "disable", plugin)
c.Assert(err, checker.IsNil)
out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
out, _, err = dockerCmdWithError("plugin", "remove", plugin)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, pNameWithTag)
c.Assert(out, checker.Contains, plugin)
_, err = os.Stat(filepath.Join(testEnv.DockerBasePath(), "plugins", id))
if !os.IsNotExist(err) {
@ -56,8 +61,9 @@ func (s *DockerSuite) TestPluginBasicOps(c *check.C) {
}
}
func (s *DockerSuite) TestPluginForceRemove(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
func (ps *DockerPluginSuite) TestPluginForceRemove(c *check.C) {
pNameWithTag := ps.getPluginRepoWithTag()
out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
c.Assert(err, checker.IsNil)
@ -71,6 +77,7 @@ func (s *DockerSuite) TestPluginForceRemove(c *check.C) {
func (s *DockerSuite) TestPluginActive(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
c.Assert(err, checker.IsNil)
@ -118,8 +125,9 @@ func (s *DockerSuite) TestPluginActiveNetwork(c *check.C) {
c.Assert(out, checker.Contains, npNameWithTag)
}
func (s *DockerSuite) TestPluginInstallDisable(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
func (ps *DockerPluginSuite) TestPluginInstallDisable(c *check.C) {
pName := ps.getPluginRepoWithTag()
out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", "--disable", pName)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Contains, pName)
@ -150,22 +158,39 @@ func (s *DockerSuite) TestPluginInstallDisableVolumeLs(c *check.C) {
dockerCmd(c, "volume", "ls")
}
func (s *DockerSuite) TestPluginSet(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
out, _ := dockerCmd(c, "plugin", "install", "--grant-all-permissions", "--disable", pName)
c.Assert(strings.TrimSpace(out), checker.Contains, pName)
func (ps *DockerPluginSuite) TestPluginSet(c *check.C) {
// Create a new plugin with extra settings
client, err := request.NewClient()
c.Assert(err, checker.IsNil, check.Commentf("failed to create test client"))
env, _ := dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", pName)
name := "test"
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
initialValue := "0"
err = plugin.Create(ctx, client, name, func(cfg *plugin.Config) {
cfg.Env = []types.PluginEnv{{Name: "DEBUG", Value: &initialValue, Settable: []string{"value"}}}
})
c.Assert(err, checker.IsNil, check.Commentf("failed to create test plugin"))
env, _ := dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", name)
c.Assert(strings.TrimSpace(env), checker.Equals, "[DEBUG=0]")
dockerCmd(c, "plugin", "set", pName, "DEBUG=1")
dockerCmd(c, "plugin", "set", name, "DEBUG=1")
env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", pName)
env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", name)
c.Assert(strings.TrimSpace(env), checker.Equals, "[DEBUG=1]")
}
func (s *DockerSuite) TestPluginInstallArgs(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
func (ps *DockerPluginSuite) TestPluginInstallArgs(c *check.C) {
pName := path.Join(ps.registryHost(), "plugin", "testplugininstallwithargs")
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
plugin.CreateInRegistry(ctx, pName, nil, func(cfg *plugin.Config) {
cfg.Env = []types.PluginEnv{{Name: "DEBUG", Settable: []string{"value"}}}
})
out, _ := dockerCmd(c, "plugin", "install", "--grant-all-permissions", "--disable", pName, "DEBUG=1")
c.Assert(strings.TrimSpace(out), checker.Contains, pName)
@ -173,8 +198,8 @@ func (s *DockerSuite) TestPluginInstallArgs(c *check.C) {
c.Assert(strings.TrimSpace(env), checker.Equals, "[DEBUG=1]")
}
func (s *DockerRegistrySuite) TestPluginInstallImage(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64)
func (ps *DockerPluginSuite) TestPluginInstallImage(c *check.C) {
testRequires(c, IsAmd64)
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
// tag the image to upload it to the private registry
@ -187,8 +212,9 @@ func (s *DockerRegistrySuite) TestPluginInstallImage(c *check.C) {
c.Assert(out, checker.Contains, `Encountered remote "application/vnd.docker.container.image.v1+json"(image) when fetching`)
}
func (s *DockerSuite) TestPluginEnableDisableNegative(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
func (ps *DockerPluginSuite) TestPluginEnableDisableNegative(c *check.C) {
pName := ps.getPluginRepoWithTag()
out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pName)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Contains, pName)
@ -208,9 +234,7 @@ func (s *DockerSuite) TestPluginEnableDisableNegative(c *check.C) {
c.Assert(err, checker.IsNil)
}
func (s *DockerSuite) TestPluginCreate(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
func (ps *DockerPluginSuite) TestPluginCreate(c *check.C) {
name := "foo/bar-driver"
temp, err := ioutil.TempDir("", "foo")
c.Assert(err, checker.IsNil)
@ -242,15 +266,15 @@ func (s *DockerSuite) TestPluginCreate(c *check.C) {
c.Assert(len(strings.Split(strings.TrimSpace(out), "\n")), checker.Equals, 2)
}
func (s *DockerSuite) TestPluginInspect(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
func (ps *DockerPluginSuite) TestPluginInspect(c *check.C) {
pNameWithTag := ps.getPluginRepoWithTag()
_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
c.Assert(err, checker.IsNil)
out, _, err := dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, pName)
c.Assert(out, checker.Contains, pTag)
c.Assert(out, checker.Contains, pNameWithTag)
c.Assert(out, checker.Contains, "true")
// Find the ID first
@ -275,7 +299,7 @@ func (s *DockerSuite) TestPluginInspect(c *check.C) {
c.Assert(strings.TrimSpace(out), checker.Equals, id)
// Name without tag form
out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pName)
out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", ps.getPluginRepo())
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Equals, id)
@ -347,21 +371,29 @@ func (s *DockerTrustSuite) TestPluginUntrustedInstall(c *check.C) {
})
}
func (s *DockerSuite) TestPluginIDPrefix(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
_, _, err := dockerCmdWithError("plugin", "install", "--disable", "--grant-all-permissions", pNameWithTag)
c.Assert(err, checker.IsNil)
func (ps *DockerPluginSuite) TestPluginIDPrefix(c *check.C) {
name := "test"
client, err := request.NewClient()
c.Assert(err, checker.IsNil, check.Commentf("error creating test client"))
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
initialValue := "0"
err = plugin.Create(ctx, client, name, func(cfg *plugin.Config) {
cfg.Env = []types.PluginEnv{{Name: "DEBUG", Value: &initialValue, Settable: []string{"value"}}}
})
cancel()
c.Assert(err, checker.IsNil, check.Commentf("failed to create test plugin"))
// Find ID first
id, _, err := dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pNameWithTag)
id, _, err := dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", name)
id = strings.TrimSpace(id)
c.Assert(err, checker.IsNil)
// List current state
out, _, err := dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, pName)
c.Assert(out, checker.Contains, pTag)
c.Assert(out, checker.Contains, name)
c.Assert(out, checker.Contains, "false")
env, _ := dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", id[:5])
@ -377,8 +409,7 @@ func (s *DockerSuite) TestPluginIDPrefix(c *check.C) {
c.Assert(err, checker.IsNil)
out, _, err = dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, pName)
c.Assert(out, checker.Contains, pTag)
c.Assert(out, checker.Contains, name)
c.Assert(out, checker.Contains, "true")
// Disable
@ -386,8 +417,7 @@ func (s *DockerSuite) TestPluginIDPrefix(c *check.C) {
c.Assert(err, checker.IsNil)
out, _, err = dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, pName)
c.Assert(out, checker.Contains, pTag)
c.Assert(out, checker.Contains, name)
c.Assert(out, checker.Contains, "false")
// Remove
@ -396,13 +426,10 @@ func (s *DockerSuite) TestPluginIDPrefix(c *check.C) {
// List returns none
out, _, err = dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Not(checker.Contains), pName)
c.Assert(out, checker.Not(checker.Contains), pTag)
c.Assert(out, checker.Not(checker.Contains), name)
}
func (s *DockerSuite) TestPluginListDefaultFormat(c *check.C) {
testRequires(c, DaemonIsLinux, Network, IsAmd64)
func (ps *DockerPluginSuite) TestPluginListDefaultFormat(c *check.C) {
config, err := ioutil.TempDir("", "config-file-")
c.Assert(err, check.IsNil)
defer os.RemoveAll(config)
@ -410,17 +437,25 @@ func (s *DockerSuite) TestPluginListDefaultFormat(c *check.C) {
err = ioutil.WriteFile(filepath.Join(config, "config.json"), []byte(`{"pluginsFormat": "raw"}`), 0644)
c.Assert(err, check.IsNil)
out, _ := dockerCmd(c, "plugin", "install", "--grant-all-permissions", pName)
c.Assert(strings.TrimSpace(out), checker.Contains, pName)
name := "test:latest"
client, err := request.NewClient()
c.Assert(err, checker.IsNil, check.Commentf("error creating test client"))
out, _ = dockerCmd(c, "plugin", "inspect", "--format", "{{.ID}}", pNameWithTag)
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
err = plugin.Create(ctx, client, name, func(cfg *plugin.Config) {
cfg.Description = "test plugin"
})
c.Assert(err, checker.IsNil, check.Commentf("failed to create test plugin"))
out, _ := dockerCmd(c, "plugin", "inspect", "--format", "{{.ID}}", name)
id := strings.TrimSpace(out)
// We expect the format to be in `raw + --no-trunc`
expectedOutput := fmt.Sprintf(`plugin_id: %s
name: %s
description: A sample volume plugin for Docker
enabled: true`, id, pNameWithTag)
description: test plugin
enabled: false`, id, name)
out, _ = dockerCmd(c, "--config", config, "plugin", "ls", "--no-trunc")
c.Assert(strings.TrimSpace(out), checker.Contains, expectedOutput)

View file

@ -1,20 +1,9 @@
package plugin
import (
"encoding/json"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/libcontainerd"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/plugin"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
@ -43,141 +32,3 @@ func WithBinary(bin string) CreateOpt {
type CreateClient interface {
PluginCreate(context.Context, io.Reader, types.PluginCreateOptions) error
}
// Create creates a new plugin with the specified name
func Create(ctx context.Context, c CreateClient, name string, opts ...CreateOpt) error {
tmpDir, err := ioutil.TempDir("", "create-test-plugin")
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
tar, err := makePluginBundle(tmpDir, opts...)
if err != nil {
return err
}
defer tar.Close()
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
return c.PluginCreate(ctx, tar, types.PluginCreateOptions{RepoName: name})
}
// TODO(@cpuguy83): we really shouldn't have to do this...
// The manager panics on init when `Executor` is not set.
type dummyExecutor struct{}
func (dummyExecutor) Client(libcontainerd.Backend) (libcontainerd.Client, error) { return nil, nil }
func (dummyExecutor) Cleanup() {}
func (dummyExecutor) UpdateOptions(...libcontainerd.RemoteOption) error { return nil }
// CreateInRegistry makes a plugin (locally) and pushes it to a registry.
// This does not use a dockerd instance to create or push the plugin.
// If you just want to create a plugin in some daemon, use `Create`.
//
// This can be useful when testing plugins on swarm where you don't really want
// the plugin to exist on any of the daemons (immediately) and there needs to be
// some way to distribute the plugin.
func CreateInRegistry(ctx context.Context, repo string, auth *types.AuthConfig, opts ...CreateOpt) error {
tmpDir, err := ioutil.TempDir("", "create-test-plugin-local")
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
inPath := filepath.Join(tmpDir, "plugin")
if err := os.MkdirAll(inPath, 0755); err != nil {
return errors.Wrap(err, "error creating plugin root")
}
tar, err := makePluginBundle(inPath, opts...)
if err != nil {
return err
}
defer tar.Close()
managerConfig := plugin.ManagerConfig{
Store: plugin.NewStore(),
RegistryService: registry.NewService(registry.ServiceOptions{V2Only: true}),
Root: filepath.Join(tmpDir, "root"),
ExecRoot: "/run/docker", // manager init fails if not set
Executor: dummyExecutor{},
LogPluginEvent: func(id, name, action string) {}, // panics when not set
}
manager, err := plugin.NewManager(managerConfig)
if err != nil {
return errors.Wrap(err, "error creating plugin manager")
}
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := manager.CreateFromContext(ctx, tar, &types.PluginCreateOptions{RepoName: repo}); err != nil {
return err
}
if auth == nil {
auth = &types.AuthConfig{}
}
err = manager.Push(ctx, repo, nil, auth, ioutil.Discard)
return errors.Wrap(err, "error pushing plugin")
}
func makePluginBundle(inPath string, opts ...CreateOpt) (io.ReadCloser, error) {
p := &types.PluginConfig{
Interface: types.PluginConfigInterface{
Socket: "basic.sock",
Types: []types.PluginInterfaceType{{Capability: "docker.dummy/1.0"}},
},
Entrypoint: []string{"/basic"},
}
cfg := &Config{
PluginConfig: p,
}
for _, o := range opts {
o(cfg)
}
if cfg.binPath == "" {
binPath, err := ensureBasicPluginBin()
if err != nil {
return nil, err
}
cfg.binPath = binPath
}
configJSON, err := json.Marshal(p)
if err != nil {
return nil, err
}
if err := ioutil.WriteFile(filepath.Join(inPath, "config.json"), configJSON, 0644); err != nil {
return nil, err
}
if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(p.Entrypoint[0])), 0755); err != nil {
return nil, errors.Wrap(err, "error creating plugin rootfs dir")
}
if err := archive.NewDefaultArchiver().CopyFileWithTar(cfg.binPath, filepath.Join(inPath, "rootfs", p.Entrypoint[0])); err != nil {
return nil, errors.Wrap(err, "error copying plugin binary to rootfs path")
}
tar, err := archive.Tar(inPath, archive.Uncompressed)
return tar, errors.Wrap(err, "error making plugin archive")
}
func ensureBasicPluginBin() (string, error) {
name := "docker-basic-plugin"
p, err := exec.LookPath(name)
if err == nil {
return p, nil
}
goBin, err := exec.LookPath("go")
if err != nil {
return "", err
}
installPath := filepath.Join(os.Getenv("GOPATH"), "bin", name)
cmd := exec.Command(goBin, "build", "-o", installPath, "./"+filepath.Join("fixtures", "plugin", "basic"))
cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
if out, err := cmd.CombinedOutput(); err != nil {
return "", errors.Wrapf(err, "error building basic plugin bin: %s", string(out))
}
return installPath, nil
}

View file

@ -0,0 +1,157 @@
package plugin
import (
"encoding/json"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/libcontainerd"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/plugin"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
// Create creates a new plugin with the specified name
func Create(ctx context.Context, c CreateClient, name string, opts ...CreateOpt) error {
tmpDir, err := ioutil.TempDir("", "create-test-plugin")
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
tar, err := makePluginBundle(tmpDir, opts...)
if err != nil {
return err
}
defer tar.Close()
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
return c.PluginCreate(ctx, tar, types.PluginCreateOptions{RepoName: name})
}
// TODO(@cpuguy83): we really shouldn't have to do this...
// The manager panics on init when `Executor` is not set.
type dummyExecutor struct{}
func (dummyExecutor) Client(libcontainerd.Backend) (libcontainerd.Client, error) { return nil, nil }
func (dummyExecutor) Cleanup() {}
func (dummyExecutor) UpdateOptions(...libcontainerd.RemoteOption) error { return nil }
// CreateInRegistry makes a plugin (locally) and pushes it to a registry.
// This does not use a dockerd instance to create or push the plugin.
// If you just want to create a plugin in some daemon, use `Create`.
//
// This can be useful when testing plugins on swarm where you don't really want
// the plugin to exist on any of the daemons (immediately) and there needs to be
// some way to distribute the plugin.
func CreateInRegistry(ctx context.Context, repo string, auth *types.AuthConfig, opts ...CreateOpt) error {
tmpDir, err := ioutil.TempDir("", "create-test-plugin-local")
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
inPath := filepath.Join(tmpDir, "plugin")
if err := os.MkdirAll(inPath, 0755); err != nil {
return errors.Wrap(err, "error creating plugin root")
}
tar, err := makePluginBundle(inPath, opts...)
if err != nil {
return err
}
defer tar.Close()
managerConfig := plugin.ManagerConfig{
Store: plugin.NewStore(),
RegistryService: registry.NewService(registry.ServiceOptions{V2Only: true}),
Root: filepath.Join(tmpDir, "root"),
ExecRoot: "/run/docker", // manager init fails if not set
Executor: dummyExecutor{},
LogPluginEvent: func(id, name, action string) {}, // panics when not set
}
manager, err := plugin.NewManager(managerConfig)
if err != nil {
return errors.Wrap(err, "error creating plugin manager")
}
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := manager.CreateFromContext(ctx, tar, &types.PluginCreateOptions{RepoName: repo}); err != nil {
return err
}
if auth == nil {
auth = &types.AuthConfig{}
}
err = manager.Push(ctx, repo, nil, auth, ioutil.Discard)
return errors.Wrap(err, "error pushing plugin")
}
func makePluginBundle(inPath string, opts ...CreateOpt) (io.ReadCloser, error) {
p := &types.PluginConfig{
Interface: types.PluginConfigInterface{
Socket: "basic.sock",
Types: []types.PluginInterfaceType{{Capability: "docker.dummy/1.0"}},
},
Entrypoint: []string{"/basic"},
}
cfg := &Config{
PluginConfig: p,
}
for _, o := range opts {
o(cfg)
}
if cfg.binPath == "" {
binPath, err := ensureBasicPluginBin()
if err != nil {
return nil, err
}
cfg.binPath = binPath
}
configJSON, err := json.Marshal(p)
if err != nil {
return nil, err
}
if err := ioutil.WriteFile(filepath.Join(inPath, "config.json"), configJSON, 0644); err != nil {
return nil, err
}
if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(p.Entrypoint[0])), 0755); err != nil {
return nil, errors.Wrap(err, "error creating plugin rootfs dir")
}
if err := archive.NewDefaultArchiver().CopyFileWithTar(cfg.binPath, filepath.Join(inPath, "rootfs", p.Entrypoint[0])); err != nil {
return nil, errors.Wrap(err, "error copying plugin binary to rootfs path")
}
tar, err := archive.Tar(inPath, archive.Uncompressed)
return tar, errors.Wrap(err, "error making plugin archive")
}
func ensureBasicPluginBin() (string, error) {
name := "docker-basic-plugin"
p, err := exec.LookPath(name)
if err == nil {
return p, nil
}
goBin, err := exec.LookPath("go")
if err != nil {
return "", err
}
installPath := filepath.Join(os.Getenv("GOPATH"), "bin", name)
cmd := exec.Command(goBin, "build", "-o", installPath, "./"+filepath.Join("fixtures", "plugin", "basic"))
cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
if out, err := cmd.CombinedOutput(); err != nil {
return "", errors.Wrapf(err, "error building basic plugin bin: %s", string(out))
}
return installPath, nil
}

View file

@ -0,0 +1,19 @@
// +build !linux
package plugin
import (
"github.com/docker/docker/api/types"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
// Create is not supported on this platform
func Create(ctx context.Context, c CreateClient, name string, opts ...CreateOpt) error {
return errors.New("not supported on this platform")
}
// CreateInRegistry is not supported on this platform
func CreateInRegistry(ctx context.Context, repo string, auth *types.AuthConfig, opts ...CreateOpt) error {
return errors.New("not supported on this platform")
}

View file

@ -1,6 +1,7 @@
package main
import (
"context"
"fmt"
"io/ioutil"
"net"
@ -11,9 +12,12 @@ import (
"strings"
"time"
"github.com/docker/docker/api/types"
cliconfig "github.com/docker/docker/cli/config"
"github.com/docker/docker/integration-cli/checker"
"github.com/docker/docker/integration-cli/cli"
"github.com/docker/docker/integration-cli/fixtures/plugin"
"github.com/docker/docker/integration-cli/request"
icmd "github.com/docker/docker/pkg/testutil/cmd"
"github.com/docker/go-connections/tlsconfig"
"github.com/go-check/check"
@ -225,10 +229,23 @@ func (s *DockerTrustSuite) setupTrustedImage(c *check.C, name string) string {
func (s *DockerTrustSuite) setupTrustedplugin(c *check.C, source, name string) string {
repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name)
client, err := request.NewClient()
c.Assert(err, checker.IsNil, check.Commentf("could not create test client"))
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
err = plugin.Create(ctx, client, repoName)
cancel()
c.Assert(err, checker.IsNil, check.Commentf("could not create test plugin"))
// tag the image and upload it to the private registry
cli.DockerCmd(c, "plugin", "install", "--grant-all-permissions", "--alias", repoName, source)
// TODO: shouldn't need to use the CLI to do trust
cli.Docker(cli.Args("plugin", "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
cli.DockerCmd(c, "plugin", "rm", "-f", repoName)
ctx, cancel = context.WithTimeout(context.Background(), 60*time.Second)
err = client.PluginRemove(ctx, repoName, types.PluginRemoveOptions{Force: true})
cancel()
c.Assert(err, checker.IsNil, check.Commentf("failed to cleanup test plugin for trust suite"))
return repoName
}