Merge component 'engine' from git@github.com:moby/moby master
This commit is contained in:
@@ -293,7 +293,6 @@ func (daemon *Daemon) createSecretsDir(c *container.Container) error {
|
||||
if err := mount.Mount("tmpfs", dir, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil {
|
||||
return errors.Wrap(err, "unable to setup secret mount")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -400,15 +399,5 @@ func (daemon *Daemon) setupContainerMountsRoot(c *container.Container) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idtools.MkdirAllAndChown(p, 0700, daemon.idMappings.RootPair()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mount.MakeUnbindable(p); err != nil {
|
||||
// Setting unbindable is a precaution and is not neccessary for correct operation.
|
||||
// Do not error out if this fails.
|
||||
logrus.WithError(err).WithField("resource", p).WithField("container", c.ID).Warn("Error setting container resource mounts to unbindable, this may cause mount leakages, preventing removal of this container.")
|
||||
}
|
||||
return nil
|
||||
return idtools.MkdirAllAndChown(p, 0700, daemon.idMappings.RootPair())
|
||||
}
|
||||
|
||||
@@ -103,6 +103,12 @@ if [ ! "$GOPATH" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Adds $1_$2 to DOCKER_BUILDTAGS unless it already
|
||||
# contains a word starting from $1_
|
||||
add_buildtag() {
|
||||
[[ " $DOCKER_BUILDTAGS" == *" $1_"* ]] || DOCKER_BUILDTAGS+=" $1_$2"
|
||||
}
|
||||
|
||||
if ${PKG_CONFIG} 'libsystemd >= 209' 2> /dev/null ; then
|
||||
DOCKER_BUILDTAGS+=" journald"
|
||||
elif ${PKG_CONFIG} 'libsystemd-journal' 2> /dev/null ; then
|
||||
@@ -118,12 +124,14 @@ if \
|
||||
fi
|
||||
|
||||
# test whether "libdevmapper.h" is new enough to support deferred remove
|
||||
# functionality.
|
||||
# functionality. We favour libdm_dlsym_deferred_remove over
|
||||
# libdm_no_deferred_remove in dynamic cases because the binary could be shipped
|
||||
# with a newer libdevmapper than the one it was built wih.
|
||||
if \
|
||||
command -v gcc &> /dev/null \
|
||||
&& ! ( echo -e '#include <libdevmapper.h>\nint main() { dm_task_deferred_remove(NULL); }'| gcc -xc - -o /dev/null $(pkg-config --libs devmapper) &> /dev/null ) \
|
||||
; then
|
||||
DOCKER_BUILDTAGS+=' libdm_no_deferred_remove'
|
||||
add_buildtag libdm dlsym_deferred_remove
|
||||
fi
|
||||
|
||||
# Use these flags when compiling the tests and final binary
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/docker/docker/integration-cli/environment"
|
||||
"github.com/docker/docker/integration-cli/fixtures/plugin"
|
||||
"github.com/docker/docker/integration-cli/registry"
|
||||
testdaemon "github.com/docker/docker/internal/test/daemon"
|
||||
ienv "github.com/docker/docker/internal/test/environment"
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
"github.com/go-check/check"
|
||||
@@ -100,7 +101,7 @@ func (s *DockerSuite) OnTimeout(c *check.C) {
|
||||
|
||||
daemonPid := int(rawPid)
|
||||
if daemonPid > 0 {
|
||||
daemon.SignalDaemonDump(daemonPid)
|
||||
testdaemon.SignalDaemonDump(daemonPid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +286,7 @@ func (s *DockerDaemonSuite) TearDownTest(c *check.C) {
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TearDownSuite(c *check.C) {
|
||||
filepath.Walk(daemon.SockRoot, func(path string, fi os.FileInfo, err error) error {
|
||||
filepath.Walk(testdaemon.SockRoot, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
// ignore errors here
|
||||
// not cleaning up sockets is not really an error
|
||||
@@ -296,7 +297,7 @@ func (s *DockerDaemonSuite) TearDownSuite(c *check.C) {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
os.RemoveAll(daemon.SockRoot)
|
||||
os.RemoveAll(testdaemon.SockRoot)
|
||||
}
|
||||
|
||||
const defaultSwarmPort = 2477
|
||||
@@ -310,7 +311,7 @@ func init() {
|
||||
type DockerSwarmSuite struct {
|
||||
server *httptest.Server
|
||||
ds *DockerSuite
|
||||
daemons []*daemon.Swarm
|
||||
daemons []*daemon.Daemon
|
||||
daemonsLock sync.Mutex // protect access to daemons
|
||||
portIndex int
|
||||
}
|
||||
@@ -327,14 +328,10 @@ func (s *DockerSwarmSuite) SetUpTest(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, SameHostDaemon)
|
||||
}
|
||||
|
||||
func (s *DockerSwarmSuite) AddDaemon(c *check.C, joinSwarm, manager bool) *daemon.Swarm {
|
||||
d := &daemon.Swarm{
|
||||
Daemon: daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
|
||||
}),
|
||||
Port: defaultSwarmPort + s.portIndex,
|
||||
}
|
||||
d.ListenAddr = fmt.Sprintf("0.0.0.0:%d", d.Port)
|
||||
func (s *DockerSwarmSuite) AddDaemon(c *check.C, joinSwarm, manager bool) *daemon.Daemon {
|
||||
d := daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
|
||||
}, testdaemon.WithSwarmPort(defaultSwarmPort+s.portIndex))
|
||||
args := []string{"--iptables=false", "--swarm-default-advertise-addr=lo"} // avoid networking conflicts
|
||||
d.StartWithBusybox(c, args...)
|
||||
|
||||
@@ -345,12 +342,12 @@ func (s *DockerSwarmSuite) AddDaemon(c *check.C, joinSwarm, manager bool) *daemo
|
||||
if manager {
|
||||
token = tokens.Manager
|
||||
}
|
||||
c.Assert(d.Join(swarm.JoinRequest{
|
||||
RemoteAddrs: []string{s.daemons[0].ListenAddr},
|
||||
d.SwarmJoin(c, swarm.JoinRequest{
|
||||
RemoteAddrs: []string{s.daemons[0].SwarmListenAddr()},
|
||||
JoinToken: token,
|
||||
}), check.IsNil)
|
||||
})
|
||||
} else {
|
||||
c.Assert(d.Init(swarm.InitRequest{}), check.IsNil)
|
||||
d.SwarmInit(c, swarm.InitRequest{})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +1,17 @@
|
||||
package daemon // import "github.com/docker/docker/integration-cli/daemon"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/request"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/go-check/check"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/icmd"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type testingT interface {
|
||||
@@ -40,32 +24,10 @@ type logT interface {
|
||||
Logf(string, ...interface{})
|
||||
}
|
||||
|
||||
// SockRoot holds the path of the default docker integration daemon socket
|
||||
var SockRoot = filepath.Join(os.TempDir(), "docker-integration")
|
||||
|
||||
var errDaemonNotStarted = errors.New("daemon not started")
|
||||
|
||||
// Daemon represents a Docker daemon for the testing framework.
|
||||
type Daemon struct {
|
||||
GlobalFlags []string
|
||||
Root string
|
||||
Folder string
|
||||
Wait chan error
|
||||
UseDefaultHost bool
|
||||
UseDefaultTLSHost bool
|
||||
|
||||
id string
|
||||
logFile *os.File
|
||||
stdin io.WriteCloser
|
||||
stdout, stderr io.ReadCloser
|
||||
cmd *exec.Cmd
|
||||
storageDriver string
|
||||
userlandProxy bool
|
||||
execRoot string
|
||||
experimental bool
|
||||
dockerBinary string
|
||||
dockerdBinary string
|
||||
log logT
|
||||
*daemon.Daemon
|
||||
dockerBinary string
|
||||
}
|
||||
|
||||
// Config holds docker daemon integration configuration
|
||||
@@ -73,504 +35,22 @@ type Config struct {
|
||||
Experimental bool
|
||||
}
|
||||
|
||||
type clientConfig struct {
|
||||
transport *http.Transport
|
||||
scheme string
|
||||
addr string
|
||||
}
|
||||
|
||||
// New returns a Daemon instance to be used for testing.
|
||||
// This will create a directory such as d123456789 in the folder specified by $DOCKER_INTEGRATION_DAEMON_DEST or $DEST.
|
||||
// The daemon will not automatically start.
|
||||
func New(t testingT, dockerBinary string, dockerdBinary string, config Config) *Daemon {
|
||||
dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST")
|
||||
if dest == "" {
|
||||
dest = os.Getenv("DEST")
|
||||
}
|
||||
if dest == "" {
|
||||
t.Fatalf("Please set the DOCKER_INTEGRATION_DAEMON_DEST or the DEST environment variable")
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(SockRoot, 0700); err != nil {
|
||||
t.Fatalf("could not create daemon socket root")
|
||||
}
|
||||
|
||||
id := fmt.Sprintf("d%s", stringid.TruncateID(stringid.GenerateRandomID()))
|
||||
dir := filepath.Join(dest, id)
|
||||
daemonFolder, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not make %q an absolute path", dir)
|
||||
}
|
||||
daemonRoot := filepath.Join(daemonFolder, "root")
|
||||
|
||||
if err := os.MkdirAll(daemonRoot, 0755); err != nil {
|
||||
t.Fatalf("Could not create daemon root %q", dir)
|
||||
}
|
||||
|
||||
userlandProxy := true
|
||||
if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" {
|
||||
if val, err := strconv.ParseBool(env); err != nil {
|
||||
userlandProxy = val
|
||||
}
|
||||
func New(t testingT, dockerBinary string, dockerdBinary string, config Config, ops ...func(*daemon.Daemon)) *Daemon {
|
||||
ops = append(ops, daemon.WithDockerdBinary(dockerdBinary))
|
||||
if config.Experimental {
|
||||
ops = append(ops, daemon.WithExperimental)
|
||||
}
|
||||
d := daemon.New(t, ops...)
|
||||
|
||||
return &Daemon{
|
||||
id: id,
|
||||
Folder: daemonFolder,
|
||||
Root: daemonRoot,
|
||||
storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"),
|
||||
userlandProxy: userlandProxy,
|
||||
execRoot: filepath.Join(os.TempDir(), "docker-execroot", id),
|
||||
dockerBinary: dockerBinary,
|
||||
dockerdBinary: dockerdBinary,
|
||||
experimental: config.Experimental,
|
||||
log: t,
|
||||
Daemon: d,
|
||||
dockerBinary: dockerBinary,
|
||||
}
|
||||
}
|
||||
|
||||
// RootDir returns the root directory of the daemon.
|
||||
func (d *Daemon) RootDir() string {
|
||||
return d.Root
|
||||
}
|
||||
|
||||
// ID returns the generated id of the daemon
|
||||
func (d *Daemon) ID() string {
|
||||
return d.id
|
||||
}
|
||||
|
||||
// StorageDriver returns the configured storage driver of the daemon
|
||||
func (d *Daemon) StorageDriver() string {
|
||||
return d.storageDriver
|
||||
}
|
||||
|
||||
// CleanupExecRoot cleans the daemon exec root (network namespaces, ...)
|
||||
func (d *Daemon) CleanupExecRoot(c *check.C) {
|
||||
cleanupExecRoot(c, d.execRoot)
|
||||
}
|
||||
|
||||
func (d *Daemon) getClientConfig() (*clientConfig, error) {
|
||||
var (
|
||||
transport *http.Transport
|
||||
scheme string
|
||||
addr string
|
||||
proto string
|
||||
)
|
||||
if d.UseDefaultTLSHost {
|
||||
option := &tlsconfig.Options{
|
||||
CAFile: "fixtures/https/ca.pem",
|
||||
CertFile: "fixtures/https/client-cert.pem",
|
||||
KeyFile: "fixtures/https/client-key.pem",
|
||||
}
|
||||
tlsConfig, err := tlsconfig.Client(*option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transport = &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
addr = fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort)
|
||||
scheme = "https"
|
||||
proto = "tcp"
|
||||
} else if d.UseDefaultHost {
|
||||
addr = opts.DefaultUnixSocket
|
||||
proto = "unix"
|
||||
scheme = "http"
|
||||
transport = &http.Transport{}
|
||||
} else {
|
||||
addr = d.sockPath()
|
||||
proto = "unix"
|
||||
scheme = "http"
|
||||
transport = &http.Transport{}
|
||||
}
|
||||
|
||||
if err := sockets.ConfigureTransport(transport, proto, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transport.DisableKeepAlives = true
|
||||
|
||||
return &clientConfig{
|
||||
transport: transport,
|
||||
scheme: scheme,
|
||||
addr: addr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start starts the daemon and return once it is ready to receive requests.
|
||||
func (d *Daemon) Start(t testingT, args ...string) {
|
||||
if err := d.StartWithError(args...); err != nil {
|
||||
t.Fatalf("Error starting daemon with arguments: %v", args)
|
||||
}
|
||||
}
|
||||
|
||||
// StartWithError starts the daemon and return once it is ready to receive requests.
|
||||
// It returns an error in case it couldn't start.
|
||||
func (d *Daemon) StartWithError(args ...string) error {
|
||||
logFile, err := os.OpenFile(filepath.Join(d.Folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[%s] Could not create %s/docker.log", d.id, d.Folder)
|
||||
}
|
||||
|
||||
return d.StartWithLogFile(logFile, args...)
|
||||
}
|
||||
|
||||
// StartWithLogFile will start the daemon and attach its streams to a given file.
|
||||
func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
|
||||
dockerdBinary, err := exec.LookPath(d.dockerdBinary)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[%s] could not find docker binary in $PATH", d.id)
|
||||
}
|
||||
args := append(d.GlobalFlags,
|
||||
"--containerd", "/var/run/docker/containerd/docker-containerd.sock",
|
||||
"--data-root", d.Root,
|
||||
"--exec-root", d.execRoot,
|
||||
"--pidfile", fmt.Sprintf("%s/docker.pid", d.Folder),
|
||||
fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
|
||||
)
|
||||
if d.experimental {
|
||||
args = append(args, "--experimental", "--init")
|
||||
}
|
||||
if !(d.UseDefaultHost || d.UseDefaultTLSHost) {
|
||||
args = append(args, []string{"--host", d.Sock()}...)
|
||||
}
|
||||
if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
|
||||
args = append(args, []string{"--userns-remap", root}...)
|
||||
}
|
||||
|
||||
// If we don't explicitly set the log-level or debug flag(-D) then
|
||||
// turn on debug mode
|
||||
foundLog := false
|
||||
foundSd := false
|
||||
for _, a := range providedArgs {
|
||||
if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") {
|
||||
foundLog = true
|
||||
}
|
||||
if strings.Contains(a, "--storage-driver") {
|
||||
foundSd = true
|
||||
}
|
||||
}
|
||||
if !foundLog {
|
||||
args = append(args, "--debug")
|
||||
}
|
||||
if d.storageDriver != "" && !foundSd {
|
||||
args = append(args, "--storage-driver", d.storageDriver)
|
||||
}
|
||||
|
||||
args = append(args, providedArgs...)
|
||||
d.cmd = exec.Command(dockerdBinary, args...)
|
||||
d.cmd.Env = append(os.Environ(), "DOCKER_SERVICE_PREFER_OFFLINE_IMAGE=1")
|
||||
d.cmd.Stdout = out
|
||||
d.cmd.Stderr = out
|
||||
d.logFile = out
|
||||
|
||||
if err := d.cmd.Start(); err != nil {
|
||||
return errors.Errorf("[%s] could not start daemon container: %v", d.id, err)
|
||||
}
|
||||
|
||||
wait := make(chan error)
|
||||
|
||||
go func() {
|
||||
wait <- d.cmd.Wait()
|
||||
d.log.Logf("[%s] exiting daemon", d.id)
|
||||
close(wait)
|
||||
}()
|
||||
|
||||
d.Wait = wait
|
||||
|
||||
tick := time.Tick(500 * time.Millisecond)
|
||||
// make sure daemon is ready to receive requests
|
||||
startTime := time.Now().Unix()
|
||||
for {
|
||||
d.log.Logf("[%s] waiting for daemon to start", d.id)
|
||||
if time.Now().Unix()-startTime > 5 {
|
||||
// After 5 seconds, give up
|
||||
return errors.Errorf("[%s] Daemon exited and never started", d.id)
|
||||
}
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
return errors.Errorf("[%s] timeout: daemon does not respond", d.id)
|
||||
case <-tick:
|
||||
clientConfig, err := d.getClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: clientConfig.transport,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "/_ping", nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[%s] could not create new request", d.id)
|
||||
}
|
||||
req.URL.Host = clientConfig.addr
|
||||
req.URL.Scheme = clientConfig.scheme
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
d.log.Logf("[%s] received status != 200 OK: %s\n", d.id, resp.Status)
|
||||
}
|
||||
d.log.Logf("[%s] daemon started\n", d.id)
|
||||
d.Root, err = d.queryRootDir()
|
||||
if err != nil {
|
||||
return errors.Errorf("[%s] error querying daemon for root directory: %v", d.id, err)
|
||||
}
|
||||
return nil
|
||||
case <-d.Wait:
|
||||
return errors.Errorf("[%s] Daemon exited during startup", d.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StartWithBusybox will first start the daemon with Daemon.Start()
|
||||
// then save the busybox image from the main daemon and load it into this Daemon instance.
|
||||
func (d *Daemon) StartWithBusybox(t testingT, arg ...string) {
|
||||
d.Start(t, arg...)
|
||||
d.LoadBusybox(t)
|
||||
}
|
||||
|
||||
// Kill will send a SIGKILL to the daemon
|
||||
func (d *Daemon) Kill() error {
|
||||
if d.cmd == nil || d.Wait == nil {
|
||||
return errDaemonNotStarted
|
||||
}
|
||||
|
||||
defer func() {
|
||||
d.logFile.Close()
|
||||
d.cmd = nil
|
||||
}()
|
||||
|
||||
if err := d.cmd.Process.Kill(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder))
|
||||
}
|
||||
|
||||
// Pid returns the pid of the daemon
|
||||
func (d *Daemon) Pid() int {
|
||||
return d.cmd.Process.Pid
|
||||
}
|
||||
|
||||
// Interrupt stops the daemon by sending it an Interrupt signal
|
||||
func (d *Daemon) Interrupt() error {
|
||||
return d.Signal(os.Interrupt)
|
||||
}
|
||||
|
||||
// Signal sends the specified signal to the daemon if running
|
||||
func (d *Daemon) Signal(signal os.Signal) error {
|
||||
if d.cmd == nil || d.Wait == nil {
|
||||
return errDaemonNotStarted
|
||||
}
|
||||
return d.cmd.Process.Signal(signal)
|
||||
}
|
||||
|
||||
// DumpStackAndQuit sends SIGQUIT to the daemon, which triggers it to dump its
|
||||
// stack to its log file and exit
|
||||
// This is used primarily for gathering debug information on test timeout
|
||||
func (d *Daemon) DumpStackAndQuit() {
|
||||
if d.cmd == nil || d.cmd.Process == nil {
|
||||
return
|
||||
}
|
||||
SignalDaemonDump(d.cmd.Process.Pid)
|
||||
}
|
||||
|
||||
// Stop will send a SIGINT every second and wait for the daemon to stop.
|
||||
// If it times out, a SIGKILL is sent.
|
||||
// Stop will not delete the daemon directory. If a purged daemon is needed,
|
||||
// instantiate a new one with NewDaemon.
|
||||
// If an error occurs while starting the daemon, the test will fail.
|
||||
func (d *Daemon) Stop(t testingT) {
|
||||
err := d.StopWithError()
|
||||
if err != nil {
|
||||
if err != errDaemonNotStarted {
|
||||
t.Fatalf("Error while stopping the daemon %s : %v", d.id, err)
|
||||
} else {
|
||||
t.Logf("Daemon %s is not started", d.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StopWithError will send a SIGINT every second and wait for the daemon to stop.
|
||||
// If it timeouts, a SIGKILL is sent.
|
||||
// Stop will not delete the daemon directory. If a purged daemon is needed,
|
||||
// instantiate a new one with NewDaemon.
|
||||
func (d *Daemon) StopWithError() error {
|
||||
if d.cmd == nil || d.Wait == nil {
|
||||
return errDaemonNotStarted
|
||||
}
|
||||
|
||||
defer func() {
|
||||
d.logFile.Close()
|
||||
d.cmd = nil
|
||||
}()
|
||||
|
||||
i := 1
|
||||
tick := time.Tick(time.Second)
|
||||
|
||||
if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
|
||||
if strings.Contains(err.Error(), "os: process already finished") {
|
||||
return errDaemonNotStarted
|
||||
}
|
||||
return errors.Errorf("could not send signal: %v", err)
|
||||
}
|
||||
out1:
|
||||
for {
|
||||
select {
|
||||
case err := <-d.Wait:
|
||||
return err
|
||||
case <-time.After(20 * time.Second):
|
||||
// time for stopping jobs and run onShutdown hooks
|
||||
d.log.Logf("[%s] daemon started", d.id)
|
||||
break out1
|
||||
}
|
||||
}
|
||||
|
||||
out2:
|
||||
for {
|
||||
select {
|
||||
case err := <-d.Wait:
|
||||
return err
|
||||
case <-tick:
|
||||
i++
|
||||
if i > 5 {
|
||||
d.log.Logf("tried to interrupt daemon for %d times, now try to kill it", i)
|
||||
break out2
|
||||
}
|
||||
d.log.Logf("Attempt #%d: daemon is still running with pid %d", i, d.cmd.Process.Pid)
|
||||
if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
|
||||
return errors.Errorf("could not send signal: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := d.cmd.Process.Kill(); err != nil {
|
||||
d.log.Logf("Could not kill daemon: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
d.cmd.Wait()
|
||||
|
||||
return os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder))
|
||||
}
|
||||
|
||||
// Restart will restart the daemon by first stopping it and the starting it.
|
||||
// If an error occurs while starting the daemon, the test will fail.
|
||||
func (d *Daemon) Restart(t testingT, args ...string) {
|
||||
d.Stop(t)
|
||||
d.handleUserns()
|
||||
d.Start(t, args...)
|
||||
}
|
||||
|
||||
// RestartWithError will restart the daemon by first stopping it and then starting it.
|
||||
func (d *Daemon) RestartWithError(arg ...string) error {
|
||||
if err := d.StopWithError(); err != nil {
|
||||
return err
|
||||
}
|
||||
d.handleUserns()
|
||||
return d.StartWithError(arg...)
|
||||
}
|
||||
|
||||
func (d *Daemon) handleUserns() {
|
||||
// in the case of tests running a user namespace-enabled daemon, we have resolved
|
||||
// d.Root to be the actual final path of the graph dir after the "uid.gid" of
|
||||
// remapped root is added--we need to subtract it from the path before calling
|
||||
// start or else we will continue making subdirectories rather than truly restarting
|
||||
// with the same location/root:
|
||||
if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
|
||||
d.Root = filepath.Dir(d.Root)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadBusybox image into the daemon
|
||||
func (d *Daemon) LoadBusybox(t testingT) {
|
||||
clientHost, err := client.NewEnvClient()
|
||||
assert.NilError(t, err, "failed to create client")
|
||||
defer clientHost.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
reader, err := clientHost.ImageSave(ctx, []string{"busybox:latest"})
|
||||
assert.NilError(t, err, "failed to download busybox")
|
||||
defer reader.Close()
|
||||
|
||||
client, err := d.NewClient()
|
||||
assert.NilError(t, err, "failed to create client")
|
||||
defer client.Close()
|
||||
|
||||
resp, err := client.ImageLoad(ctx, reader, true)
|
||||
assert.NilError(t, err, "failed to load busybox")
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
func (d *Daemon) queryRootDir() (string, error) {
|
||||
// update daemon root by asking /info endpoint (to support user
|
||||
// namespaced daemon with root remapped uid.gid directory)
|
||||
clientConfig, err := d.getClientConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: clientConfig.transport,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "/info", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.URL.Host = clientConfig.addr
|
||||
req.URL.Scheme = clientConfig.scheme
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
|
||||
return resp.Body.Close()
|
||||
})
|
||||
|
||||
type Info struct {
|
||||
DockerRootDir string
|
||||
}
|
||||
var b []byte
|
||||
var i Info
|
||||
b, err = request.ReadBody(body)
|
||||
if err == nil && resp.StatusCode == http.StatusOK {
|
||||
// read the docker root dir
|
||||
if err = json.Unmarshal(b, &i); err == nil {
|
||||
return i.DockerRootDir, nil
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Sock returns the socket path of the daemon
|
||||
func (d *Daemon) Sock() string {
|
||||
return fmt.Sprintf("unix://" + d.sockPath())
|
||||
}
|
||||
|
||||
func (d *Daemon) sockPath() string {
|
||||
return filepath.Join(SockRoot, d.id+".sock")
|
||||
}
|
||||
|
||||
// WaitRun waits for a container to be running for 10s
|
||||
func (d *Daemon) WaitRun(contID string) error {
|
||||
args := []string{"--host", d.Sock()}
|
||||
return WaitInspectWithArgs(d.dockerBinary, contID, "{{.State.Running}}", "true", 10*time.Second, args...)
|
||||
}
|
||||
|
||||
// Info returns the info struct for this daemon
|
||||
func (d *Daemon) Info(t assert.TestingT) types.Info {
|
||||
apiclient, err := client.NewClientWithOpts(client.WithHost((d.Sock())))
|
||||
assert.NilError(t, err)
|
||||
info, err := apiclient.Info(context.Background())
|
||||
assert.NilError(t, err)
|
||||
return info
|
||||
}
|
||||
|
||||
// Cmd executes a docker CLI command against this daemon.
|
||||
// Example: d.Cmd("version") will run docker -H unix://path/to/unix.sock version
|
||||
func (d *Daemon) Cmd(args ...string) (string, error) {
|
||||
@@ -594,11 +74,6 @@ func (d *Daemon) PrependHostArg(args []string) []string {
|
||||
return append([]string{"--host", d.Sock()}, args...)
|
||||
}
|
||||
|
||||
// LogFileName returns the path the daemon's log file
|
||||
func (d *Daemon) LogFileName() string {
|
||||
return d.logFile.Name()
|
||||
}
|
||||
|
||||
// GetIDByName returns the ID of an object (container, volume, …) given its name
|
||||
func (d *Daemon) GetIDByName(name string) (string, error) {
|
||||
return d.inspectFieldWithError(name, "Id")
|
||||
@@ -616,11 +91,6 @@ func (d *Daemon) ActiveContainers() (ids []string) {
|
||||
return
|
||||
}
|
||||
|
||||
// ReadLogFile returns the content of the daemon log file
|
||||
func (d *Daemon) ReadLogFile() ([]byte, error) {
|
||||
return ioutil.ReadFile(d.logFile.Name())
|
||||
}
|
||||
|
||||
// InspectField returns the field filter by 'filter'
|
||||
func (d *Daemon) InspectField(name, filter string) (string, error) {
|
||||
return d.inspectFilter(name, filter)
|
||||
@@ -672,59 +142,25 @@ func (d *Daemon) CheckActiveContainerCount(c *check.C) (interface{}, check.Comme
|
||||
return len(strings.Split(strings.TrimSpace(out), "\n")), check.Commentf("output: %q", string(out))
|
||||
}
|
||||
|
||||
// ReloadConfig asks the daemon to reload its configuration
|
||||
func (d *Daemon) ReloadConfig() error {
|
||||
if d.cmd == nil || d.cmd.Process == nil {
|
||||
return errors.New("daemon is not running")
|
||||
}
|
||||
|
||||
errCh := make(chan error)
|
||||
started := make(chan struct{})
|
||||
go func() {
|
||||
_, body, err := request.DoOnHost(d.Sock(), "/events", request.Method(http.MethodGet))
|
||||
close(started)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
defer body.Close()
|
||||
dec := json.NewDecoder(body)
|
||||
for {
|
||||
var e events.Message
|
||||
if err := dec.Decode(&e); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if e.Type != events.DaemonEventType {
|
||||
continue
|
||||
}
|
||||
if e.Action != "reload" {
|
||||
continue
|
||||
}
|
||||
close(errCh) // notify that we are done
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
<-started
|
||||
if err := signalDaemonReload(d.cmd.Process.Pid); err != nil {
|
||||
return errors.Errorf("error signaling daemon reload: %v", err)
|
||||
}
|
||||
select {
|
||||
case err := <-errCh:
|
||||
if err != nil {
|
||||
return errors.Errorf("error waiting for daemon reload event: %v", err)
|
||||
}
|
||||
case <-time.After(30 * time.Second):
|
||||
return errors.New("timeout waiting for daemon reload event")
|
||||
}
|
||||
return nil
|
||||
// WaitRun waits for a container to be running for 10s
|
||||
func (d *Daemon) WaitRun(contID string) error {
|
||||
args := []string{"--host", d.Sock()}
|
||||
return WaitInspectWithArgs(d.dockerBinary, contID, "{{.State.Running}}", "true", 10*time.Second, args...)
|
||||
}
|
||||
|
||||
// NewClient creates new client based on daemon's socket path
|
||||
func (d *Daemon) NewClient() (*client.Client, error) {
|
||||
return client.NewClientWithOpts(
|
||||
client.FromEnv,
|
||||
client.WithHost(d.Sock()))
|
||||
// CmdRetryOutOfSequence tries the specified command against the current daemon for 10 times
|
||||
func (d *Daemon) CmdRetryOutOfSequence(args ...string) (string, error) {
|
||||
for i := 0; ; i++ {
|
||||
out, err := d.Cmd(args...)
|
||||
if err != nil {
|
||||
if strings.Contains(out, "update out of sequence") {
|
||||
if i < 10 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
|
||||
// WaitInspectWithArgs waits for the specified expression to be equals to the specified expected string in the given time.
|
||||
|
||||
@@ -3,7 +3,6 @@ package daemon // import "github.com/docker/docker/integration-cli/daemon"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
@@ -12,178 +11,12 @@ import (
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/go-check/check"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Swarm is a test daemon with helpers for participating in a swarm.
|
||||
type Swarm struct {
|
||||
*Daemon
|
||||
swarm.Info
|
||||
Port int
|
||||
ListenAddr string
|
||||
}
|
||||
|
||||
// Init initializes a new swarm cluster.
|
||||
func (d *Swarm) Init(req swarm.InitRequest) error {
|
||||
if req.ListenAddr == "" {
|
||||
req.ListenAddr = d.ListenAddr
|
||||
}
|
||||
cli, err := d.NewClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing swarm: failed to create client %v", err)
|
||||
}
|
||||
defer cli.Close()
|
||||
_, err = cli.SwarmInit(context.Background(), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing swarm: %v", err)
|
||||
}
|
||||
info, err := d.SwarmInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Info = info
|
||||
return nil
|
||||
}
|
||||
|
||||
// Join joins a daemon to an existing cluster.
|
||||
func (d *Swarm) Join(req swarm.JoinRequest) error {
|
||||
if req.ListenAddr == "" {
|
||||
req.ListenAddr = d.ListenAddr
|
||||
}
|
||||
cli, err := d.NewClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("joining swarm: failed to create client %v", err)
|
||||
}
|
||||
defer cli.Close()
|
||||
err = cli.SwarmJoin(context.Background(), req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("joining swarm: %v", err)
|
||||
}
|
||||
info, err := d.SwarmInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Info = info
|
||||
return nil
|
||||
}
|
||||
|
||||
// Leave forces daemon to leave current cluster.
|
||||
func (d *Swarm) Leave(force bool) error {
|
||||
cli, err := d.NewClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("leaving swarm: failed to create client %v", err)
|
||||
}
|
||||
defer cli.Close()
|
||||
err = cli.SwarmLeave(context.Background(), force)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("leaving swarm: %v", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SwarmInfo returns the swarm information of the daemon
|
||||
func (d *Swarm) SwarmInfo() (swarm.Info, error) {
|
||||
cli, err := d.NewClient()
|
||||
if err != nil {
|
||||
return swarm.Info{}, fmt.Errorf("get swarm info: %v", err)
|
||||
}
|
||||
|
||||
info, err := cli.Info(context.Background())
|
||||
if err != nil {
|
||||
return swarm.Info{}, fmt.Errorf("get swarm info: %v", err)
|
||||
}
|
||||
|
||||
return info.Swarm, nil
|
||||
}
|
||||
|
||||
// Unlock tries to unlock a locked swarm
|
||||
func (d *Swarm) Unlock(req swarm.UnlockRequest) error {
|
||||
cli, err := d.NewClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unlocking swarm: failed to create client %v", err)
|
||||
}
|
||||
defer cli.Close()
|
||||
err = cli.SwarmUnlock(context.Background(), req)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unlocking swarm")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ServiceConstructor defines a swarm service constructor function
|
||||
type ServiceConstructor func(*swarm.Service)
|
||||
|
||||
// NodeConstructor defines a swarm node constructor
|
||||
type NodeConstructor func(*swarm.Node)
|
||||
|
||||
// SecretConstructor defines a swarm secret constructor
|
||||
type SecretConstructor func(*swarm.Secret)
|
||||
|
||||
// ConfigConstructor defines a swarm config constructor
|
||||
type ConfigConstructor func(*swarm.Config)
|
||||
|
||||
// SpecConstructor defines a swarm spec constructor
|
||||
type SpecConstructor func(*swarm.Spec)
|
||||
|
||||
// CreateServiceWithOptions creates a swarm service given the specified service constructors
|
||||
// and auth config
|
||||
func (d *Swarm) CreateServiceWithOptions(c *check.C, opts types.ServiceCreateOptions, f ...ServiceConstructor) string {
|
||||
var service swarm.Service
|
||||
for _, fn := range f {
|
||||
fn(&service)
|
||||
}
|
||||
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := cli.ServiceCreate(ctx, service.Spec, opts)
|
||||
c.Assert(err, checker.IsNil)
|
||||
return res.ID
|
||||
}
|
||||
|
||||
// CreateService creates a swarm service given the specified service constructor
|
||||
func (d *Swarm) CreateService(c *check.C, f ...ServiceConstructor) string {
|
||||
return d.CreateServiceWithOptions(c, types.ServiceCreateOptions{}, f...)
|
||||
}
|
||||
|
||||
// GetService returns the swarm service corresponding to the specified id
|
||||
func (d *Swarm) GetService(c *check.C, id string) *swarm.Service {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
service, _, err := cli.ServiceInspectWithRaw(context.Background(), id, types.ServiceInspectOptions{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
return &service
|
||||
}
|
||||
|
||||
// GetServiceTasks returns the swarm tasks for the specified service
|
||||
func (d *Swarm) GetServiceTasks(c *check.C, service string) []swarm.Task {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
filterArgs := filters.NewArgs()
|
||||
filterArgs.Add("desired-state", "running")
|
||||
filterArgs.Add("service", service)
|
||||
|
||||
options := types.TaskListOptions{
|
||||
Filters: filterArgs,
|
||||
}
|
||||
|
||||
tasks, err := cli.TaskList(context.Background(), options)
|
||||
c.Assert(err, checker.IsNil)
|
||||
return tasks
|
||||
}
|
||||
|
||||
// CheckServiceTasksInState returns the number of tasks with a matching state,
|
||||
// and optional message substring.
|
||||
func (d *Swarm) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
return func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
tasks := d.GetServiceTasks(c, service)
|
||||
var count int
|
||||
@@ -200,7 +33,7 @@ func (d *Swarm) CheckServiceTasksInState(service string, state swarm.TaskState,
|
||||
|
||||
// CheckServiceTasksInStateWithError returns the number of tasks with a matching state,
|
||||
// and optional message substring.
|
||||
func (d *Swarm) CheckServiceTasksInStateWithError(service string, state swarm.TaskState, errorMessage string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckServiceTasksInStateWithError(service string, state swarm.TaskState, errorMessage string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
return func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
tasks := d.GetServiceTasks(c, service)
|
||||
var count int
|
||||
@@ -216,12 +49,12 @@ func (d *Swarm) CheckServiceTasksInStateWithError(service string, state swarm.Ta
|
||||
}
|
||||
|
||||
// CheckServiceRunningTasks returns the number of running tasks for the specified service
|
||||
func (d *Swarm) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
return d.CheckServiceTasksInState(service, swarm.TaskStateRunning, "")
|
||||
}
|
||||
|
||||
// CheckServiceUpdateState returns the current update state for the specified service
|
||||
func (d *Swarm) CheckServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
return func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
service := d.GetService(c, service)
|
||||
if service.UpdateStatus == nil {
|
||||
@@ -232,7 +65,7 @@ func (d *Swarm) CheckServiceUpdateState(service string) func(*check.C) (interfac
|
||||
}
|
||||
|
||||
// CheckPluginRunning returns the runtime state of the plugin
|
||||
func (d *Swarm) CheckPluginRunning(plugin string) func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckPluginRunning(plugin string) func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
return func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
apiclient, err := d.NewClient()
|
||||
assert.NilError(c, err)
|
||||
@@ -246,7 +79,7 @@ func (d *Swarm) CheckPluginRunning(plugin string) func(c *check.C) (interface{},
|
||||
}
|
||||
|
||||
// CheckPluginImage returns the runtime state of the plugin
|
||||
func (d *Swarm) CheckPluginImage(plugin string) func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckPluginImage(plugin string) func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
return func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
apiclient, err := d.NewClient()
|
||||
assert.NilError(c, err)
|
||||
@@ -260,7 +93,7 @@ func (d *Swarm) CheckPluginImage(plugin string) func(c *check.C) (interface{}, c
|
||||
}
|
||||
|
||||
// CheckServiceTasks returns the number of tasks for the specified service
|
||||
func (d *Swarm) CheckServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
return func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
tasks := d.GetServiceTasks(c, service)
|
||||
return len(tasks), nil
|
||||
@@ -268,7 +101,7 @@ func (d *Swarm) CheckServiceTasks(service string) func(*check.C) (interface{}, c
|
||||
}
|
||||
|
||||
// CheckRunningTaskNetworks returns the number of times each network is referenced from a task.
|
||||
func (d *Swarm) CheckRunningTaskNetworks(c *check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckRunningTaskNetworks(c *check.C) (interface{}, check.CommentInterface) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
@@ -293,7 +126,7 @@ func (d *Swarm) CheckRunningTaskNetworks(c *check.C) (interface{}, check.Comment
|
||||
}
|
||||
|
||||
// CheckRunningTaskImages returns the times each image is running as a task.
|
||||
func (d *Swarm) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
@@ -318,7 +151,7 @@ func (d *Swarm) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentIn
|
||||
}
|
||||
|
||||
// CheckNodeReadyCount returns the number of ready node on the swarm
|
||||
func (d *Swarm) CheckNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) {
|
||||
nodes := d.ListNodes(c)
|
||||
var readyCount int
|
||||
for _, node := range nodes {
|
||||
@@ -329,303 +162,21 @@ func (d *Swarm) CheckNodeReadyCount(c *check.C) (interface{}, check.CommentInter
|
||||
return readyCount, nil
|
||||
}
|
||||
|
||||
// GetTask returns the swarm task identified by the specified id
|
||||
func (d *Swarm) GetTask(c *check.C, id string) swarm.Task {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
task, _, err := cli.TaskInspectWithRaw(context.Background(), id)
|
||||
c.Assert(err, checker.IsNil)
|
||||
return task
|
||||
}
|
||||
|
||||
// UpdateService updates a swarm service with the specified service constructor
|
||||
func (d *Swarm) UpdateService(c *check.C, service *swarm.Service, f ...ServiceConstructor) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
for _, fn := range f {
|
||||
fn(service)
|
||||
}
|
||||
|
||||
_, err = cli.ServiceUpdate(context.Background(), service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// RemoveService removes the specified service
|
||||
func (d *Swarm) RemoveService(c *check.C, id string) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
err = cli.ServiceRemove(context.Background(), id)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// GetNode returns a swarm node identified by the specified id
|
||||
func (d *Swarm) GetNode(c *check.C, id string) *swarm.Node {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
node, _, err := cli.NodeInspectWithRaw(context.Background(), id)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(node.ID, checker.Equals, id)
|
||||
return &node
|
||||
}
|
||||
|
||||
// RemoveNode removes the specified node
|
||||
func (d *Swarm) RemoveNode(c *check.C, id string, force bool) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
options := types.NodeRemoveOptions{
|
||||
Force: force,
|
||||
}
|
||||
err = cli.NodeRemove(context.Background(), id, options)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// UpdateNode updates a swarm node with the specified node constructor
|
||||
func (d *Swarm) UpdateNode(c *check.C, id string, f ...NodeConstructor) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
for i := 0; ; i++ {
|
||||
node := d.GetNode(c, id)
|
||||
for _, fn := range f {
|
||||
fn(node)
|
||||
}
|
||||
|
||||
err = cli.NodeUpdate(context.Background(), node.ID, node.Version, node.Spec)
|
||||
if i < 10 && err != nil && strings.Contains(err.Error(), "update out of sequence") {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
c.Assert(err, checker.IsNil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ListNodes returns the list of the current swarm nodes
|
||||
func (d *Swarm) ListNodes(c *check.C) []swarm.Node {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
nodes, err := cli.NodeList(context.Background(), types.NodeListOptions{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
return nodes
|
||||
}
|
||||
|
||||
// ListServices returns the list of the current swarm services
|
||||
func (d *Swarm) ListServices(c *check.C) []swarm.Service {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
services, err := cli.ServiceList(context.Background(), types.ServiceListOptions{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
return services
|
||||
}
|
||||
|
||||
// CreateSecret creates a secret given the specified spec
|
||||
func (d *Swarm) CreateSecret(c *check.C, secretSpec swarm.SecretSpec) string {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
scr, err := cli.SecretCreate(context.Background(), secretSpec)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
return scr.ID
|
||||
}
|
||||
|
||||
// ListSecrets returns the list of the current swarm secrets
|
||||
func (d *Swarm) ListSecrets(c *check.C) []swarm.Secret {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
secrets, err := cli.SecretList(context.Background(), types.SecretListOptions{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
return secrets
|
||||
}
|
||||
|
||||
// GetSecret returns a swarm secret identified by the specified id
|
||||
func (d *Swarm) GetSecret(c *check.C, id string) *swarm.Secret {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
secret, _, err := cli.SecretInspectWithRaw(context.Background(), id)
|
||||
c.Assert(err, checker.IsNil)
|
||||
return &secret
|
||||
}
|
||||
|
||||
// DeleteSecret removes the swarm secret identified by the specified id
|
||||
func (d *Swarm) DeleteSecret(c *check.C, id string) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
err = cli.SecretRemove(context.Background(), id)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// UpdateSecret updates the swarm secret identified by the specified id
|
||||
// Currently, only label update is supported.
|
||||
func (d *Swarm) UpdateSecret(c *check.C, id string, f ...SecretConstructor) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
secret := d.GetSecret(c, id)
|
||||
for _, fn := range f {
|
||||
fn(secret)
|
||||
}
|
||||
|
||||
err = cli.SecretUpdate(context.Background(), secret.ID, secret.Version, secret.Spec)
|
||||
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// CreateConfig creates a config given the specified spec
|
||||
func (d *Swarm) CreateConfig(c *check.C, configSpec swarm.ConfigSpec) string {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
scr, err := cli.ConfigCreate(context.Background(), configSpec)
|
||||
c.Assert(err, checker.IsNil)
|
||||
return scr.ID
|
||||
}
|
||||
|
||||
// ListConfigs returns the list of the current swarm configs
|
||||
func (d *Swarm) ListConfigs(c *check.C) []swarm.Config {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
configs, err := cli.ConfigList(context.Background(), types.ConfigListOptions{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
return configs
|
||||
}
|
||||
|
||||
// GetConfig returns a swarm config identified by the specified id
|
||||
func (d *Swarm) GetConfig(c *check.C, id string) *swarm.Config {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
config, _, err := cli.ConfigInspectWithRaw(context.Background(), id)
|
||||
c.Assert(err, checker.IsNil)
|
||||
return &config
|
||||
}
|
||||
|
||||
// DeleteConfig removes the swarm config identified by the specified id
|
||||
func (d *Swarm) DeleteConfig(c *check.C, id string) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
err = cli.ConfigRemove(context.Background(), id)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// UpdateConfig updates the swarm config identified by the specified id
|
||||
// Currently, only label update is supported.
|
||||
func (d *Swarm) UpdateConfig(c *check.C, id string, f ...ConfigConstructor) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
config := d.GetConfig(c, id)
|
||||
for _, fn := range f {
|
||||
fn(config)
|
||||
}
|
||||
|
||||
err = cli.ConfigUpdate(context.Background(), config.ID, config.Version, config.Spec)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// GetSwarm returns the current swarm object
|
||||
func (d *Swarm) GetSwarm(c *check.C) swarm.Swarm {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
sw, err := cli.SwarmInspect(context.Background())
|
||||
c.Assert(err, checker.IsNil)
|
||||
return sw
|
||||
}
|
||||
|
||||
// UpdateSwarm updates the current swarm object with the specified spec constructors
|
||||
func (d *Swarm) UpdateSwarm(c *check.C, f ...SpecConstructor) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
sw := d.GetSwarm(c)
|
||||
for _, fn := range f {
|
||||
fn(&sw.Spec)
|
||||
}
|
||||
|
||||
err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, swarm.UpdateFlags{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// RotateTokens update the swarm to rotate tokens
|
||||
func (d *Swarm) RotateTokens(c *check.C) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
sw, err := cli.SwarmInspect(context.Background())
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
flags := swarm.UpdateFlags{
|
||||
RotateManagerToken: true,
|
||||
RotateWorkerToken: true,
|
||||
}
|
||||
|
||||
err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, flags)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// JoinTokens returns the current swarm join tokens
|
||||
func (d *Swarm) JoinTokens(c *check.C) swarm.JoinTokens {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
|
||||
sw, err := cli.SwarmInspect(context.Background())
|
||||
c.Assert(err, checker.IsNil)
|
||||
return sw.JoinTokens
|
||||
}
|
||||
|
||||
// CheckLocalNodeState returns the current swarm node state
|
||||
func (d *Swarm) CheckLocalNodeState(c *check.C) (interface{}, check.CommentInterface) {
|
||||
info, err := d.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
func (d *Daemon) CheckLocalNodeState(c *check.C) (interface{}, check.CommentInterface) {
|
||||
info := d.SwarmInfo(c)
|
||||
return info.LocalNodeState, nil
|
||||
}
|
||||
|
||||
// CheckControlAvailable returns the current swarm control available
|
||||
func (d *Swarm) CheckControlAvailable(c *check.C) (interface{}, check.CommentInterface) {
|
||||
info, err := d.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
func (d *Daemon) CheckControlAvailable(c *check.C) (interface{}, check.CommentInterface) {
|
||||
info := d.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
return info.ControlAvailable, nil
|
||||
}
|
||||
|
||||
// CheckLeader returns whether there is a leader on the swarm or not
|
||||
func (d *Swarm) CheckLeader(c *check.C) (interface{}, check.CommentInterface) {
|
||||
func (d *Daemon) CheckLeader(c *check.C) (interface{}, check.CommentInterface) {
|
||||
cli, err := d.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cli.Close()
|
||||
@@ -644,18 +195,3 @@ func (d *Swarm) CheckLeader(c *check.C) (interface{}, check.CommentInterface) {
|
||||
}
|
||||
return fmt.Errorf("no leader"), check.Commentf("could not find leader")
|
||||
}
|
||||
|
||||
// CmdRetryOutOfSequence tries the specified command against the current daemon for 10 times
|
||||
func (d *Swarm) CmdRetryOutOfSequence(args ...string) (string, error) {
|
||||
for i := 0; ; i++ {
|
||||
out, err := d.Cmd(args...)
|
||||
if err != nil {
|
||||
if strings.Contains(out, "update out of sequence") {
|
||||
if i < 10 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
func (s *DockerSwarmSuite) getDaemon(c *check.C, nodeID string) *daemon.Swarm {
|
||||
func (s *DockerSwarmSuite) getDaemon(c *check.C, nodeID string) *daemon.Daemon {
|
||||
s.daemonsLock.Lock()
|
||||
defer s.daemonsLock.Unlock()
|
||||
for _, d := range s.daemons {
|
||||
if d.NodeID == nodeID {
|
||||
if d.NodeID() == nodeID {
|
||||
return d
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ func (s *DockerSwarmSuite) TestAPISwarmListNodes(c *check.C) {
|
||||
|
||||
loop0:
|
||||
for _, n := range nodes {
|
||||
for _, d := range []*daemon.Swarm{d1, d2, d3} {
|
||||
if n.ID == d.NodeID {
|
||||
for _, d := range []*daemon.Daemon{d1, d2, d3} {
|
||||
if n.ID == d.NodeID() {
|
||||
continue loop0
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,7 @@ func (s *DockerSwarmSuite) TestAPISwarmNodeRemove(c *check.C) {
|
||||
c.Assert(len(nodes), checker.Equals, 3, check.Commentf("nodes: %#v", nodes))
|
||||
|
||||
// Getting the info so we can take the NodeID
|
||||
d2Info, err := d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
d2Info := d2.SwarmInfo(c)
|
||||
|
||||
// forceful removal of d2 should work
|
||||
d1.RemoveNode(c, d2Info.NodeID, true)
|
||||
@@ -88,14 +87,14 @@ func (s *DockerSwarmSuite) TestAPISwarmNodeDrainPause(c *check.C) {
|
||||
waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals, instances)
|
||||
|
||||
// drain d2, all containers should move to d1
|
||||
d1.UpdateNode(c, d2.NodeID, func(n *swarm.Node) {
|
||||
d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
|
||||
n.Spec.Availability = swarm.NodeAvailabilityDrain
|
||||
})
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.Equals, instances)
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.Equals, 0)
|
||||
|
||||
// set d2 back to active
|
||||
d1.UpdateNode(c, d2.NodeID, func(n *swarm.Node) {
|
||||
d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
|
||||
n.Spec.Availability = swarm.NodeAvailabilityActive
|
||||
})
|
||||
|
||||
@@ -115,7 +114,7 @@ func (s *DockerSwarmSuite) TestAPISwarmNodeDrainPause(c *check.C) {
|
||||
d2ContainerCount := len(d2.ActiveContainers())
|
||||
|
||||
// set d2 to paused, scale service up, only d1 gets new tasks
|
||||
d1.UpdateNode(c, d2.NodeID, func(n *swarm.Node) {
|
||||
d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
|
||||
n.Spec.Availability = swarm.NodeAvailabilityPause
|
||||
})
|
||||
|
||||
|
||||
@@ -15,12 +15,13 @@ import (
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration-cli/fixtures/plugin"
|
||||
testdaemon "github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/go-check/check"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func setPortConfig(portConfig []swarm.PortConfig) daemon.ServiceConstructor {
|
||||
func setPortConfig(portConfig []swarm.PortConfig) testdaemon.ServiceConstructor {
|
||||
return func(s *swarm.Service) {
|
||||
if s.Spec.EndpointSpec == nil {
|
||||
s.Spec.EndpointSpec = &swarm.EndpointSpec{}
|
||||
@@ -140,7 +141,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesCreateGlobal(c *check.C) {
|
||||
|
||||
func (s *DockerSwarmSuite) TestAPISwarmServicesUpdate(c *check.C) {
|
||||
const nodeCount = 3
|
||||
var daemons [nodeCount]*daemon.Swarm
|
||||
var daemons [nodeCount]*daemon.Daemon
|
||||
for i := 0; i < nodeCount; i++ {
|
||||
daemons[i] = s.AddDaemon(c, true, i == 0)
|
||||
}
|
||||
@@ -309,7 +310,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *check.C) {
|
||||
|
||||
func (s *DockerSwarmSuite) TestAPISwarmServicesFailedUpdate(c *check.C) {
|
||||
const nodeCount = 3
|
||||
var daemons [nodeCount]*daemon.Swarm
|
||||
var daemons [nodeCount]*daemon.Daemon
|
||||
for i := 0; i < nodeCount; i++ {
|
||||
daemons[i] = s.AddDaemon(c, true, i == 0)
|
||||
}
|
||||
@@ -349,7 +350,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesFailedUpdate(c *check.C) {
|
||||
|
||||
func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintRole(c *check.C) {
|
||||
const nodeCount = 3
|
||||
var daemons [nodeCount]*daemon.Swarm
|
||||
var daemons [nodeCount]*daemon.Daemon
|
||||
for i := 0; i < nodeCount; i++ {
|
||||
daemons[i] = s.AddDaemon(c, true, i == 0)
|
||||
}
|
||||
@@ -401,7 +402,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintRole(c *check.C) {
|
||||
|
||||
func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintLabel(c *check.C) {
|
||||
const nodeCount = 3
|
||||
var daemons [nodeCount]*daemon.Swarm
|
||||
var daemons [nodeCount]*daemon.Daemon
|
||||
for i := 0; i < nodeCount; i++ {
|
||||
daemons[i] = s.AddDaemon(c, true, i == 0)
|
||||
}
|
||||
@@ -496,7 +497,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintLabel(c *check.C) {
|
||||
|
||||
func (s *DockerSwarmSuite) TestAPISwarmServicePlacementPrefs(c *check.C) {
|
||||
const nodeCount = 3
|
||||
var daemons [nodeCount]*daemon.Swarm
|
||||
var daemons [nodeCount]*daemon.Daemon
|
||||
for i := 0; i < nodeCount; i++ {
|
||||
daemons[i] = s.AddDaemon(c, true, i == 0)
|
||||
}
|
||||
@@ -551,9 +552,9 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *check.C) {
|
||||
|
||||
waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals, instances)
|
||||
|
||||
getContainers := func() map[string]*daemon.Swarm {
|
||||
m := make(map[string]*daemon.Swarm)
|
||||
for _, d := range []*daemon.Swarm{d1, d2, d3} {
|
||||
getContainers := func() map[string]*daemon.Daemon {
|
||||
m := make(map[string]*daemon.Daemon)
|
||||
for _, d := range []*daemon.Daemon{d1, d2, d3} {
|
||||
for _, id := range d.ActiveContainers() {
|
||||
m[id] = d
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration-cli/request"
|
||||
testdaemon "github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/docker/swarmkit/ca"
|
||||
"github.com/go-check/check"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
@@ -35,30 +36,30 @@ var defaultReconciliationTimeout = 30 * time.Second
|
||||
func (s *DockerSwarmSuite) TestAPISwarmInit(c *check.C) {
|
||||
// todo: should find a better way to verify that components are running than /info
|
||||
d1 := s.AddDaemon(c, true, true)
|
||||
info, err := d1.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := d1.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.True)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
c.Assert(info.Cluster.RootRotationInProgress, checker.False)
|
||||
|
||||
d2 := s.AddDaemon(c, true, false)
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.False)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
|
||||
// Leaving cluster
|
||||
c.Assert(d2.Leave(false), checker.IsNil)
|
||||
c.Assert(d2.SwarmLeave(false), checker.IsNil)
|
||||
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.False)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
|
||||
c.Assert(d2.Join(swarm.JoinRequest{JoinToken: d1.JoinTokens(c).Worker, RemoteAddrs: []string{d1.ListenAddr}}), checker.IsNil)
|
||||
d2.SwarmJoin(c, swarm.JoinRequest{
|
||||
ListenAddr: d1.SwarmListenAddr(),
|
||||
JoinToken: d1.JoinTokens(c).Worker,
|
||||
RemoteAddrs: []string{d1.SwarmListenAddr()},
|
||||
})
|
||||
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.False)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
|
||||
@@ -69,93 +70,100 @@ func (s *DockerSwarmSuite) TestAPISwarmInit(c *check.C) {
|
||||
d1.Start(c)
|
||||
d2.Start(c)
|
||||
|
||||
info, err = d1.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d1.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.True)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.False)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
}
|
||||
|
||||
func (s *DockerSwarmSuite) TestAPISwarmJoinToken(c *check.C) {
|
||||
d1 := s.AddDaemon(c, false, false)
|
||||
c.Assert(d1.Init(swarm.InitRequest{}), checker.IsNil)
|
||||
d1.SwarmInit(c, swarm.InitRequest{})
|
||||
|
||||
// todo: error message differs depending if some components of token are valid
|
||||
|
||||
d2 := s.AddDaemon(c, false, false)
|
||||
err := d2.Join(swarm.JoinRequest{RemoteAddrs: []string{d1.ListenAddr}})
|
||||
c2 := d2.NewClientT(c)
|
||||
err := c2.SwarmJoin(context.Background(), swarm.JoinRequest{
|
||||
ListenAddr: d2.SwarmListenAddr(),
|
||||
RemoteAddrs: []string{d1.SwarmListenAddr()},
|
||||
})
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "join token is necessary")
|
||||
info, err := d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
|
||||
err = d2.Join(swarm.JoinRequest{JoinToken: "foobaz", RemoteAddrs: []string{d1.ListenAddr}})
|
||||
err = c2.SwarmJoin(context.Background(), swarm.JoinRequest{
|
||||
ListenAddr: d2.SwarmListenAddr(),
|
||||
JoinToken: "foobaz",
|
||||
RemoteAddrs: []string{d1.SwarmListenAddr()},
|
||||
})
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "invalid join token")
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
|
||||
workerToken := d1.JoinTokens(c).Worker
|
||||
|
||||
c.Assert(d2.Join(swarm.JoinRequest{JoinToken: workerToken, RemoteAddrs: []string{d1.ListenAddr}}), checker.IsNil)
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
d2.SwarmJoin(c, swarm.JoinRequest{
|
||||
ListenAddr: d2.SwarmListenAddr(),
|
||||
JoinToken: workerToken,
|
||||
RemoteAddrs: []string{d1.SwarmListenAddr()},
|
||||
})
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
c.Assert(d2.Leave(false), checker.IsNil)
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(d2.SwarmLeave(false), checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
|
||||
// change tokens
|
||||
d1.RotateTokens(c)
|
||||
|
||||
err = d2.Join(swarm.JoinRequest{JoinToken: workerToken, RemoteAddrs: []string{d1.ListenAddr}})
|
||||
err = c2.SwarmJoin(context.Background(), swarm.JoinRequest{
|
||||
ListenAddr: d2.SwarmListenAddr(),
|
||||
JoinToken: workerToken,
|
||||
RemoteAddrs: []string{d1.SwarmListenAddr()},
|
||||
})
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "join token is necessary")
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
|
||||
workerToken = d1.JoinTokens(c).Worker
|
||||
|
||||
c.Assert(d2.Join(swarm.JoinRequest{JoinToken: workerToken, RemoteAddrs: []string{d1.ListenAddr}}), checker.IsNil)
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
d2.SwarmJoin(c, swarm.JoinRequest{JoinToken: workerToken, RemoteAddrs: []string{d1.SwarmListenAddr()}})
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
c.Assert(d2.Leave(false), checker.IsNil)
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(d2.SwarmLeave(false), checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
|
||||
// change spec, don't change tokens
|
||||
d1.UpdateSwarm(c, func(s *swarm.Spec) {})
|
||||
|
||||
err = d2.Join(swarm.JoinRequest{RemoteAddrs: []string{d1.ListenAddr}})
|
||||
err = c2.SwarmJoin(context.Background(), swarm.JoinRequest{
|
||||
ListenAddr: d2.SwarmListenAddr(),
|
||||
RemoteAddrs: []string{d1.SwarmListenAddr()},
|
||||
})
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "join token is necessary")
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
|
||||
c.Assert(d2.Join(swarm.JoinRequest{JoinToken: workerToken, RemoteAddrs: []string{d1.ListenAddr}}), checker.IsNil)
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
d2.SwarmJoin(c, swarm.JoinRequest{JoinToken: workerToken, RemoteAddrs: []string{d1.SwarmListenAddr()}})
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
c.Assert(d2.Leave(false), checker.IsNil)
|
||||
info, err = d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(d2.SwarmLeave(false), checker.IsNil)
|
||||
info = d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
}
|
||||
|
||||
func (s *DockerSwarmSuite) TestUpdateSwarmAddExternalCA(c *check.C) {
|
||||
d1 := s.AddDaemon(c, false, false)
|
||||
c.Assert(d1.Init(swarm.InitRequest{}), checker.IsNil)
|
||||
d1.SwarmInit(c, swarm.InitRequest{})
|
||||
d1.UpdateSwarm(c, func(s *swarm.Spec) {
|
||||
s.CAConfig.ExternalCAs = []*swarm.ExternalCA{
|
||||
{
|
||||
@@ -169,8 +177,7 @@ func (s *DockerSwarmSuite) TestUpdateSwarmAddExternalCA(c *check.C) {
|
||||
},
|
||||
}
|
||||
})
|
||||
info, err := d1.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := d1.SwarmInfo(c)
|
||||
c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs, checker.HasLen, 2)
|
||||
c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
|
||||
c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, "cacert")
|
||||
@@ -182,28 +189,32 @@ func (s *DockerSwarmSuite) TestAPISwarmCAHash(c *check.C) {
|
||||
splitToken := strings.Split(d1.JoinTokens(c).Worker, "-")
|
||||
splitToken[2] = "1kxftv4ofnc6mt30lmgipg6ngf9luhwqopfk1tz6bdmnkubg0e"
|
||||
replacementToken := strings.Join(splitToken, "-")
|
||||
err := d2.Join(swarm.JoinRequest{JoinToken: replacementToken, RemoteAddrs: []string{d1.ListenAddr}})
|
||||
c2 := d2.NewClientT(c)
|
||||
err := c2.SwarmJoin(context.Background(), swarm.JoinRequest{
|
||||
ListenAddr: d2.SwarmListenAddr(),
|
||||
JoinToken: replacementToken,
|
||||
RemoteAddrs: []string{d1.SwarmListenAddr()},
|
||||
})
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "remote CA does not match fingerprint")
|
||||
}
|
||||
|
||||
func (s *DockerSwarmSuite) TestAPISwarmPromoteDemote(c *check.C) {
|
||||
d1 := s.AddDaemon(c, false, false)
|
||||
c.Assert(d1.Init(swarm.InitRequest{}), checker.IsNil)
|
||||
d1.SwarmInit(c, swarm.InitRequest{})
|
||||
d2 := s.AddDaemon(c, true, false)
|
||||
|
||||
info, err := d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := d2.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.False)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
|
||||
d1.UpdateNode(c, d2.NodeID, func(n *swarm.Node) {
|
||||
d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
|
||||
n.Spec.Role = swarm.NodeRoleManager
|
||||
})
|
||||
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d2.CheckControlAvailable, checker.True)
|
||||
|
||||
d1.UpdateNode(c, d2.NodeID, func(n *swarm.Node) {
|
||||
d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
|
||||
n.Spec.Role = swarm.NodeRoleWorker
|
||||
})
|
||||
|
||||
@@ -228,7 +239,7 @@ func (s *DockerSwarmSuite) TestAPISwarmPromoteDemote(c *check.C) {
|
||||
}, checker.Equals, "swarm-worker")
|
||||
|
||||
// Demoting last node should fail
|
||||
node := d1.GetNode(c, d1.NodeID)
|
||||
node := d1.GetNode(c, d1.NodeID())
|
||||
node.Spec.Role = swarm.NodeRoleWorker
|
||||
url := fmt.Sprintf("/nodes/%s/update?version=%d", node.ID, node.Version.Index)
|
||||
res, body, err := request.DoOnHost(d1.Sock(), url, request.Method("POST"), request.JSONBody(node.Spec))
|
||||
@@ -246,13 +257,12 @@ func (s *DockerSwarmSuite) TestAPISwarmPromoteDemote(c *check.C) {
|
||||
if !strings.Contains(string(b), "last manager of the swarm") {
|
||||
c.Assert(string(b), checker.Contains, "this would result in a loss of quorum")
|
||||
}
|
||||
info, err = d1.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d1.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
c.Assert(info.ControlAvailable, checker.True)
|
||||
|
||||
// Promote already demoted node
|
||||
d1.UpdateNode(c, d2.NodeID, func(n *swarm.Node) {
|
||||
d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
|
||||
n.Spec.Role = swarm.NodeRoleManager
|
||||
})
|
||||
|
||||
@@ -278,7 +288,7 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaderProxy(c *check.C) {
|
||||
|
||||
// 3 services should be started now, because the requests were proxied to leader
|
||||
// query each node and make sure it returns 3 services
|
||||
for _, d := range []*daemon.Swarm{d1, d2, d3} {
|
||||
for _, d := range []*daemon.Daemon{d1, d2, d3} {
|
||||
services := d.ListServices(c)
|
||||
c.Assert(services, checker.HasLen, 3)
|
||||
}
|
||||
@@ -291,23 +301,23 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaderElection(c *check.C) {
|
||||
d3 := s.AddDaemon(c, true, true)
|
||||
|
||||
// assert that the first node we made is the leader, and the other two are followers
|
||||
c.Assert(d1.GetNode(c, d1.NodeID).ManagerStatus.Leader, checker.True)
|
||||
c.Assert(d1.GetNode(c, d2.NodeID).ManagerStatus.Leader, checker.False)
|
||||
c.Assert(d1.GetNode(c, d3.NodeID).ManagerStatus.Leader, checker.False)
|
||||
c.Assert(d1.GetNode(c, d1.NodeID()).ManagerStatus.Leader, checker.True)
|
||||
c.Assert(d1.GetNode(c, d2.NodeID()).ManagerStatus.Leader, checker.False)
|
||||
c.Assert(d1.GetNode(c, d3.NodeID()).ManagerStatus.Leader, checker.False)
|
||||
|
||||
d1.Stop(c)
|
||||
|
||||
var (
|
||||
leader *daemon.Swarm // keep track of leader
|
||||
followers []*daemon.Swarm // keep track of followers
|
||||
leader *daemon.Daemon // keep track of leader
|
||||
followers []*daemon.Daemon // keep track of followers
|
||||
)
|
||||
checkLeader := func(nodes ...*daemon.Swarm) checkF {
|
||||
checkLeader := func(nodes ...*daemon.Daemon) checkF {
|
||||
return func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
// clear these out before each run
|
||||
leader = nil
|
||||
followers = nil
|
||||
for _, d := range nodes {
|
||||
if d.GetNode(c, d.NodeID).ManagerStatus.Leader {
|
||||
if d.GetNode(c, d.NodeID()).ManagerStatus.Leader {
|
||||
leader = d
|
||||
} else {
|
||||
followers = append(followers, d)
|
||||
@@ -344,7 +354,7 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaderElection(c *check.C) {
|
||||
c.Assert(leader, checker.NotNil)
|
||||
c.Assert(followers, checker.HasLen, 2)
|
||||
// and that after we added d1 back, the leader hasn't changed
|
||||
c.Assert(leader.NodeID, checker.Equals, stableleader.NodeID)
|
||||
c.Assert(leader.NodeID(), checker.Equals, stableleader.NodeID())
|
||||
}
|
||||
|
||||
func (s *DockerSwarmSuite) TestAPISwarmRaftQuorum(c *check.C) {
|
||||
@@ -400,8 +410,8 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaveRemovesContainer(c *check.C) {
|
||||
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances+1)
|
||||
|
||||
c.Assert(d.Leave(false), checker.NotNil)
|
||||
c.Assert(d.Leave(true), checker.IsNil)
|
||||
c.Assert(d.SwarmLeave(false), checker.NotNil)
|
||||
c.Assert(d.SwarmLeave(true), checker.IsNil)
|
||||
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
|
||||
|
||||
@@ -420,17 +430,18 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaveOnPendingJoin(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
id = strings.TrimSpace(id)
|
||||
|
||||
err = d2.Join(swarm.JoinRequest{
|
||||
c2 := d2.NewClientT(c)
|
||||
err = c2.SwarmJoin(context.Background(), swarm.JoinRequest{
|
||||
ListenAddr: d2.SwarmListenAddr(),
|
||||
RemoteAddrs: []string{"123.123.123.123:1234"},
|
||||
})
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "Timeout was reached")
|
||||
|
||||
info, err := d2.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := d2.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStatePending)
|
||||
|
||||
c.Assert(d2.Leave(true), checker.IsNil)
|
||||
c.Assert(d2.SwarmLeave(true), checker.IsNil)
|
||||
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.Equals, 1)
|
||||
|
||||
@@ -443,7 +454,9 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaveOnPendingJoin(c *check.C) {
|
||||
func (s *DockerSwarmSuite) TestAPISwarmRestoreOnPendingJoin(c *check.C) {
|
||||
testRequires(c, Network)
|
||||
d := s.AddDaemon(c, false, false)
|
||||
err := d.Join(swarm.JoinRequest{
|
||||
client := d.NewClientT(c)
|
||||
err := client.SwarmJoin(context.Background(), swarm.JoinRequest{
|
||||
ListenAddr: d.SwarmListenAddr(),
|
||||
RemoteAddrs: []string{"123.123.123.123:1234"},
|
||||
})
|
||||
c.Assert(err, check.NotNil)
|
||||
@@ -454,8 +467,7 @@ func (s *DockerSwarmSuite) TestAPISwarmRestoreOnPendingJoin(c *check.C) {
|
||||
d.Stop(c)
|
||||
d.Start(c)
|
||||
|
||||
info, err := d.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := d.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
}
|
||||
|
||||
@@ -539,7 +551,7 @@ func (s *DockerSwarmSuite) TestAPISwarmForceNewCluster(c *check.C) {
|
||||
waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals, instances)
|
||||
|
||||
// drain d2, all containers should move to d1
|
||||
d1.UpdateNode(c, d2.NodeID, func(n *swarm.Node) {
|
||||
d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
|
||||
n.Spec.Availability = swarm.NodeAvailabilityDrain
|
||||
})
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.Equals, instances)
|
||||
@@ -547,16 +559,15 @@ func (s *DockerSwarmSuite) TestAPISwarmForceNewCluster(c *check.C) {
|
||||
|
||||
d2.Stop(c)
|
||||
|
||||
c.Assert(d1.Init(swarm.InitRequest{
|
||||
d1.SwarmInit(c, swarm.InitRequest{
|
||||
ForceNewCluster: true,
|
||||
Spec: swarm.Spec{},
|
||||
}), checker.IsNil)
|
||||
})
|
||||
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.Equals, instances)
|
||||
|
||||
d3 := s.AddDaemon(c, true, true)
|
||||
info, err := d3.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := d3.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.True)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
|
||||
@@ -622,7 +633,7 @@ func serviceForUpdate(s *swarm.Service) {
|
||||
s.Spec.Name = "updatetest"
|
||||
}
|
||||
|
||||
func setInstances(replicas int) daemon.ServiceConstructor {
|
||||
func setInstances(replicas int) testdaemon.ServiceConstructor {
|
||||
ureplicas := uint64(replicas)
|
||||
return func(s *swarm.Service) {
|
||||
s.Spec.Mode = swarm.ServiceMode{
|
||||
@@ -633,7 +644,7 @@ func setInstances(replicas int) daemon.ServiceConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
func setUpdateOrder(order string) daemon.ServiceConstructor {
|
||||
func setUpdateOrder(order string) testdaemon.ServiceConstructor {
|
||||
return func(s *swarm.Service) {
|
||||
if s.Spec.UpdateConfig == nil {
|
||||
s.Spec.UpdateConfig = &swarm.UpdateConfig{}
|
||||
@@ -642,7 +653,7 @@ func setUpdateOrder(order string) daemon.ServiceConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
func setRollbackOrder(order string) daemon.ServiceConstructor {
|
||||
func setRollbackOrder(order string) testdaemon.ServiceConstructor {
|
||||
return func(s *swarm.Service) {
|
||||
if s.Spec.RollbackConfig == nil {
|
||||
s.Spec.RollbackConfig = &swarm.UpdateConfig{}
|
||||
@@ -651,7 +662,7 @@ func setRollbackOrder(order string) daemon.ServiceConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
func setImage(image string) daemon.ServiceConstructor {
|
||||
func setImage(image string) testdaemon.ServiceConstructor {
|
||||
return func(s *swarm.Service) {
|
||||
if s.Spec.TaskTemplate.ContainerSpec == nil {
|
||||
s.Spec.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
|
||||
@@ -660,25 +671,25 @@ func setImage(image string) daemon.ServiceConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
func setFailureAction(failureAction string) daemon.ServiceConstructor {
|
||||
func setFailureAction(failureAction string) testdaemon.ServiceConstructor {
|
||||
return func(s *swarm.Service) {
|
||||
s.Spec.UpdateConfig.FailureAction = failureAction
|
||||
}
|
||||
}
|
||||
|
||||
func setMaxFailureRatio(maxFailureRatio float32) daemon.ServiceConstructor {
|
||||
func setMaxFailureRatio(maxFailureRatio float32) testdaemon.ServiceConstructor {
|
||||
return func(s *swarm.Service) {
|
||||
s.Spec.UpdateConfig.MaxFailureRatio = maxFailureRatio
|
||||
}
|
||||
}
|
||||
|
||||
func setParallelism(parallelism uint64) daemon.ServiceConstructor {
|
||||
func setParallelism(parallelism uint64) testdaemon.ServiceConstructor {
|
||||
return func(s *swarm.Service) {
|
||||
s.Spec.UpdateConfig.Parallelism = parallelism
|
||||
}
|
||||
}
|
||||
|
||||
func setConstraints(constraints []string) daemon.ServiceConstructor {
|
||||
func setConstraints(constraints []string) testdaemon.ServiceConstructor {
|
||||
return func(s *swarm.Service) {
|
||||
if s.Spec.TaskTemplate.Placement == nil {
|
||||
s.Spec.TaskTemplate.Placement = &swarm.Placement{}
|
||||
@@ -687,7 +698,7 @@ func setConstraints(constraints []string) daemon.ServiceConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
func setPlacementPrefs(prefs []swarm.PlacementPreference) daemon.ServiceConstructor {
|
||||
func setPlacementPrefs(prefs []swarm.PlacementPreference) testdaemon.ServiceConstructor {
|
||||
return func(s *swarm.Service) {
|
||||
if s.Spec.TaskTemplate.Placement == nil {
|
||||
s.Spec.TaskTemplate.Placement = &swarm.Placement{}
|
||||
@@ -702,18 +713,19 @@ func setGlobalMode(s *swarm.Service) {
|
||||
}
|
||||
}
|
||||
|
||||
func checkClusterHealth(c *check.C, cl []*daemon.Swarm, managerCount, workerCount int) {
|
||||
func checkClusterHealth(c *check.C, cl []*daemon.Daemon, managerCount, workerCount int) {
|
||||
var totalMCount, totalWCount int
|
||||
|
||||
for _, d := range cl {
|
||||
var (
|
||||
info swarm.Info
|
||||
err error
|
||||
)
|
||||
|
||||
// check info in a waitAndAssert, because if the cluster doesn't have a leader, `info` will return an error
|
||||
checkInfo := func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
info, err = d.SwarmInfo()
|
||||
client := d.NewClientT(c)
|
||||
daemonInfo, err := client.Info(context.Background())
|
||||
info = daemonInfo.Swarm
|
||||
return err, check.Commentf("cluster not ready in time")
|
||||
}
|
||||
waitAndAssert(c, defaultReconciliationTimeout, checkInfo, checker.IsNil)
|
||||
@@ -733,7 +745,7 @@ func checkClusterHealth(c *check.C, cl []*daemon.Swarm, managerCount, workerCoun
|
||||
}
|
||||
nn := d.GetNode(c, n.ID)
|
||||
n = *nn
|
||||
return n.Status.State == swarm.NodeStateReady, check.Commentf("state of node %s, reported by %s", n.ID, d.Info.NodeID)
|
||||
return n.Status.State == swarm.NodeStateReady, check.Commentf("state of node %s, reported by %s", n.ID, d.NodeID())
|
||||
}
|
||||
waitAndAssert(c, defaultReconciliationTimeout, waitReady, checker.True)
|
||||
|
||||
@@ -743,18 +755,18 @@ func checkClusterHealth(c *check.C, cl []*daemon.Swarm, managerCount, workerCoun
|
||||
}
|
||||
nn := d.GetNode(c, n.ID)
|
||||
n = *nn
|
||||
return n.Spec.Availability == swarm.NodeAvailabilityActive, check.Commentf("availability of node %s, reported by %s", n.ID, d.Info.NodeID)
|
||||
return n.Spec.Availability == swarm.NodeAvailabilityActive, check.Commentf("availability of node %s, reported by %s", n.ID, d.NodeID())
|
||||
}
|
||||
waitAndAssert(c, defaultReconciliationTimeout, waitActive, checker.True)
|
||||
|
||||
if n.Spec.Role == swarm.NodeRoleManager {
|
||||
c.Assert(n.ManagerStatus, checker.NotNil, check.Commentf("manager status of node %s (manager), reported by %s", n.ID, d.Info.NodeID))
|
||||
c.Assert(n.ManagerStatus, checker.NotNil, check.Commentf("manager status of node %s (manager), reported by %s", n.ID, d.NodeID()))
|
||||
if n.ManagerStatus.Leader {
|
||||
leaderFound = true
|
||||
}
|
||||
mCount++
|
||||
} else {
|
||||
c.Assert(n.ManagerStatus, checker.IsNil, check.Commentf("manager status of node %s (worker), reported by %s", n.ID, d.Info.NodeID))
|
||||
c.Assert(n.ManagerStatus, checker.IsNil, check.Commentf("manager status of node %s (worker), reported by %s", n.ID, d.NodeID()))
|
||||
wCount++
|
||||
}
|
||||
}
|
||||
@@ -769,11 +781,10 @@ func checkClusterHealth(c *check.C, cl []*daemon.Swarm, managerCount, workerCoun
|
||||
func (s *DockerSwarmSuite) TestAPISwarmRestartCluster(c *check.C) {
|
||||
mCount, wCount := 5, 1
|
||||
|
||||
var nodes []*daemon.Swarm
|
||||
var nodes []*daemon.Daemon
|
||||
for i := 0; i < mCount; i++ {
|
||||
manager := s.AddDaemon(c, true, true)
|
||||
info, err := manager.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := manager.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.True)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
nodes = append(nodes, manager)
|
||||
@@ -781,8 +792,7 @@ func (s *DockerSwarmSuite) TestAPISwarmRestartCluster(c *check.C) {
|
||||
|
||||
for i := 0; i < wCount; i++ {
|
||||
worker := s.AddDaemon(c, true, false)
|
||||
info, err := worker.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := worker.SwarmInfo(c)
|
||||
c.Assert(info.ControlAvailable, checker.False)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
nodes = append(nodes, worker)
|
||||
@@ -795,7 +805,7 @@ func (s *DockerSwarmSuite) TestAPISwarmRestartCluster(c *check.C) {
|
||||
errs := make(chan error, len(nodes))
|
||||
|
||||
for _, d := range nodes {
|
||||
go func(daemon *daemon.Swarm) {
|
||||
go func(daemon *daemon.Daemon) {
|
||||
defer wg.Done()
|
||||
if err := daemon.StopWithError(); err != nil {
|
||||
errs <- err
|
||||
@@ -820,7 +830,7 @@ func (s *DockerSwarmSuite) TestAPISwarmRestartCluster(c *check.C) {
|
||||
errs := make(chan error, len(nodes))
|
||||
|
||||
for _, d := range nodes {
|
||||
go func(daemon *daemon.Swarm) {
|
||||
go func(daemon *daemon.Daemon) {
|
||||
defer wg.Done()
|
||||
if err := daemon.StartWithError("--iptables=false"); err != nil {
|
||||
errs <- err
|
||||
@@ -859,7 +869,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateWithName(c *check.C) {
|
||||
// Unlocking an unlocked swarm results in an error
|
||||
func (s *DockerSwarmSuite) TestAPISwarmUnlockNotLocked(c *check.C) {
|
||||
d := s.AddDaemon(c, true, true)
|
||||
err := d.Unlock(swarm.UnlockRequest{UnlockKey: "wrong-key"})
|
||||
err := d.SwarmUnlock(swarm.UnlockRequest{UnlockKey: "wrong-key"})
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "swarm is not locked")
|
||||
}
|
||||
@@ -870,7 +880,10 @@ func (s *DockerSwarmSuite) TestAPISwarmErrorHandling(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer ln.Close()
|
||||
d := s.AddDaemon(c, false, false)
|
||||
err = d.Init(swarm.InitRequest{})
|
||||
client := d.NewClientT(c)
|
||||
_, err = client.SwarmInit(context.Background(), swarm.InitRequest{
|
||||
ListenAddr: d.SwarmListenAddr(),
|
||||
})
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "address already in use")
|
||||
}
|
||||
@@ -940,13 +953,13 @@ func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
|
||||
m := s.AddDaemon(c, true, true)
|
||||
w := s.AddDaemon(c, true, false)
|
||||
|
||||
info, err := m.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := m.SwarmInfo(c)
|
||||
|
||||
currentTrustRoot := info.Cluster.TLSInfo.TrustRoot
|
||||
|
||||
// rotate multiple times
|
||||
for i := 0; i < 4; i++ {
|
||||
var err error
|
||||
var cert, key []byte
|
||||
if i%2 != 0 {
|
||||
cert, _, key, err = initca.New(&csr.CertificateRequest{
|
||||
@@ -966,8 +979,7 @@ func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
|
||||
// poll to make sure update succeeds
|
||||
var clusterTLSInfo swarm.TLSInfo
|
||||
for j := 0; j < 18; j++ {
|
||||
info, err := m.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := m.SwarmInfo(c)
|
||||
|
||||
// the desired CA cert and key is always redacted
|
||||
c.Assert(info.Cluster.Spec.CAConfig.SigningCAKey, checker.Equals, "")
|
||||
@@ -989,8 +1001,8 @@ func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
|
||||
// could take another second or two for the nodes to trust the new roots after they've all gotten
|
||||
// new TLS certificates
|
||||
for j := 0; j < 18; j++ {
|
||||
mInfo := m.GetNode(c, m.NodeID).Description.TLSInfo
|
||||
wInfo := m.GetNode(c, w.NodeID).Description.TLSInfo
|
||||
mInfo := m.GetNode(c, m.NodeID()).Description.TLSInfo
|
||||
wInfo := m.GetNode(c, w.NodeID()).Description.TLSInfo
|
||||
|
||||
if mInfo.TrustRoot == clusterTLSInfo.TrustRoot && wInfo.TrustRoot == clusterTLSInfo.TrustRoot {
|
||||
break
|
||||
@@ -1000,8 +1012,8 @@ func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
}
|
||||
|
||||
c.Assert(m.GetNode(c, m.NodeID).Description.TLSInfo, checker.DeepEquals, clusterTLSInfo)
|
||||
c.Assert(m.GetNode(c, w.NodeID).Description.TLSInfo, checker.DeepEquals, clusterTLSInfo)
|
||||
c.Assert(m.GetNode(c, m.NodeID()).Description.TLSInfo, checker.DeepEquals, clusterTLSInfo)
|
||||
c.Assert(m.GetNode(c, w.NodeID()).Description.TLSInfo, checker.DeepEquals, clusterTLSInfo)
|
||||
currentTrustRoot = clusterTLSInfo.TrustRoot
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
func pruneNetworkAndVerify(c *check.C, d *daemon.Swarm, kept, pruned []string) {
|
||||
func pruneNetworkAndVerify(c *check.C, d *daemon.Daemon, kept, pruned []string) {
|
||||
_, err := d.Cmd("network", "prune", "--force")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ func (s *DockerSwarmSuite) TestServiceLogs(c *check.C) {
|
||||
// countLogLines returns a closure that can be used with waitAndAssert to
|
||||
// verify that a minimum number of expected container log messages have been
|
||||
// output.
|
||||
func countLogLines(d *daemon.Swarm, name string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
func countLogLines(d *daemon.Daemon, name string) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
return func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
result := icmd.RunCmd(d.Command("service", "logs", "-t", "--raw", name))
|
||||
result.Assert(c, icmd.Expected{})
|
||||
|
||||
@@ -69,7 +69,7 @@ func (s *DockerSwarmSuite) TestServiceUpdateSecrets(c *check.C) {
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
|
||||
// add secret
|
||||
out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "test", "--secret-add", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
|
||||
out, err = d.Cmd("service", "update", "--detach", "test", "--secret-add", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
|
||||
out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
|
||||
@@ -84,7 +84,7 @@ func (s *DockerSwarmSuite) TestServiceUpdateSecrets(c *check.C) {
|
||||
c.Assert(refs[0].File.Name, checker.Equals, testTarget)
|
||||
|
||||
// remove
|
||||
out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "test", "--secret-rm", testName)
|
||||
out, err = d.Cmd("service", "update", "--detach", "test", "--secret-rm", testName)
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
|
||||
out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
|
||||
@@ -111,7 +111,7 @@ func (s *DockerSwarmSuite) TestServiceUpdateConfigs(c *check.C) {
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
|
||||
// add config
|
||||
out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "test", "--config-add", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
|
||||
out, err = d.Cmd("service", "update", "--detach", "test", "--config-add", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
|
||||
out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
|
||||
@@ -126,7 +126,7 @@ func (s *DockerSwarmSuite) TestServiceUpdateConfigs(c *check.C) {
|
||||
c.Assert(refs[0].File.Name, checker.Equals, testTarget)
|
||||
|
||||
// remove
|
||||
out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "test", "--config-rm", testName)
|
||||
out, err = d.Cmd("service", "update", "--detach", "test", "--config-rm", testName)
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
|
||||
out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
|
||||
|
||||
@@ -57,7 +57,7 @@ func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
|
||||
// passing an external CA (this is without starting a root rotation) does not fail
|
||||
cli.Docker(cli.Args("swarm", "update", "--external-ca", "protocol=cfssl,url=https://something.org",
|
||||
"--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"),
|
||||
cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
|
||||
cli.Daemon(d)).Assert(c, icmd.Success)
|
||||
|
||||
expected, err := ioutil.ReadFile("fixtures/https/ca.pem")
|
||||
c.Assert(err, checker.IsNil)
|
||||
@@ -73,7 +73,7 @@ func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
|
||||
|
||||
result := cli.Docker(cli.Args("swarm", "update",
|
||||
"--external-ca", fmt.Sprintf("protocol=cfssl,url=https://something.org,cacert=%s", tempFile.Path())),
|
||||
cli.Daemon(d.Daemon))
|
||||
cli.Daemon(d))
|
||||
result.Assert(c, icmd.Expected{
|
||||
ExitCode: 125,
|
||||
Err: "must be in PEM format",
|
||||
@@ -94,7 +94,7 @@ func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
|
||||
|
||||
result := cli.Docker(cli.Args("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s",
|
||||
"--external-ca", fmt.Sprintf("protocol=cfssl,url=https://somethingelse.org,cacert=%s", tempFile.Path())),
|
||||
cli.Daemon(d.Daemon))
|
||||
cli.Daemon(d))
|
||||
result.Assert(c, icmd.Expected{
|
||||
ExitCode: 125,
|
||||
Err: "must be in PEM format",
|
||||
@@ -103,7 +103,7 @@ func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
|
||||
cli.Docker(cli.Args("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s",
|
||||
"--external-ca", "protocol=cfssl,url=https://something.org",
|
||||
"--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"),
|
||||
cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
|
||||
cli.Daemon(d)).Assert(c, icmd.Success)
|
||||
|
||||
expected, err := ioutil.ReadFile("fixtures/https/ca.pem")
|
||||
c.Assert(err, checker.IsNil)
|
||||
@@ -115,8 +115,8 @@ func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
|
||||
c.Assert(spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
|
||||
c.Assert(spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, string(expected))
|
||||
|
||||
c.Assert(d.Leave(true), checker.IsNil)
|
||||
cli.Docker(cli.Args("swarm", "init"), cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
|
||||
c.Assert(d.SwarmLeave(true), checker.IsNil)
|
||||
cli.Docker(cli.Args("swarm", "init"), cli.Daemon(d)).Assert(c, icmd.Success)
|
||||
|
||||
spec = getSpec()
|
||||
c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 90*24*time.Hour)
|
||||
@@ -126,12 +126,12 @@ func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
|
||||
func (s *DockerSwarmSuite) TestSwarmInitIPv6(c *check.C) {
|
||||
testRequires(c, IPv6)
|
||||
d1 := s.AddDaemon(c, false, false)
|
||||
cli.Docker(cli.Args("swarm", "init", "--listen-add", "::1"), cli.Daemon(d1.Daemon)).Assert(c, icmd.Success)
|
||||
cli.Docker(cli.Args("swarm", "init", "--listen-add", "::1"), cli.Daemon(d1)).Assert(c, icmd.Success)
|
||||
|
||||
d2 := s.AddDaemon(c, false, false)
|
||||
cli.Docker(cli.Args("swarm", "join", "::1"), cli.Daemon(d2.Daemon)).Assert(c, icmd.Success)
|
||||
cli.Docker(cli.Args("swarm", "join", "::1"), cli.Daemon(d2)).Assert(c, icmd.Success)
|
||||
|
||||
out := cli.Docker(cli.Args("info"), cli.Daemon(d2.Daemon)).Assert(c, icmd.Success).Combined()
|
||||
out := cli.Docker(cli.Args("info"), cli.Daemon(d2)).Assert(c, icmd.Success).Combined()
|
||||
c.Assert(out, checker.Contains, "Swarm: active")
|
||||
}
|
||||
|
||||
@@ -145,13 +145,12 @@ func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedAdvertiseAddr(c *check.C) {
|
||||
func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *check.C) {
|
||||
// init swarm mode and stop a daemon
|
||||
d := s.AddDaemon(c, true, true)
|
||||
info, err := d.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := d.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
d.Stop(c)
|
||||
|
||||
// start a daemon with --cluster-store and --cluster-advertise
|
||||
err = d.StartWithError("--cluster-store=consul://consuladdr:consulport/some/path", "--cluster-advertise=1.1.1.1:2375")
|
||||
err := d.StartWithError("--cluster-store=consul://consuladdr:consulport/some/path", "--cluster-advertise=1.1.1.1:2375")
|
||||
c.Assert(err, checker.NotNil)
|
||||
content, err := d.ReadLogFile()
|
||||
c.Assert(err, checker.IsNil)
|
||||
@@ -283,10 +282,10 @@ func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *check.C) {
|
||||
out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", name)
|
||||
out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", "--publish-add", "80:20", name)
|
||||
out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", "--publish-add", "80:20", name)
|
||||
c.Assert(err, checker.NotNil)
|
||||
|
||||
out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.EndpointSpec.Ports }}", name)
|
||||
@@ -426,7 +425,7 @@ func (s *DockerSwarmSuite) TestOverlayAttachableOnSwarmLeave(c *check.C) {
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
|
||||
// Leave the swarm
|
||||
err = d.Leave(true)
|
||||
err = d.SwarmLeave(true)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Check the container is disconnected
|
||||
@@ -989,13 +988,12 @@ func (s *DockerSwarmSuite) TestDNSConfigUpdate(c *check.C) {
|
||||
c.Assert(strings.TrimSpace(out), checker.Equals, "{[1.2.3.4] [example.com] [timeout:3]}")
|
||||
}
|
||||
|
||||
func getNodeStatus(c *check.C, d *daemon.Swarm) swarm.LocalNodeState {
|
||||
info, err := d.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
func getNodeStatus(c *check.C, d *daemon.Daemon) swarm.LocalNodeState {
|
||||
info := d.SwarmInfo(c)
|
||||
return info.LocalNodeState
|
||||
}
|
||||
|
||||
func checkKeyIsEncrypted(d *daemon.Swarm) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
func checkKeyIsEncrypted(d *daemon.Daemon) func(*check.C) (interface{}, check.CommentInterface) {
|
||||
return func(c *check.C) (interface{}, check.CommentInterface) {
|
||||
keyBytes, err := ioutil.ReadFile(filepath.Join(d.Folder, "root", "swarm", "certificates", "swarm-node.key"))
|
||||
if err != nil {
|
||||
@@ -1011,7 +1009,7 @@ func checkKeyIsEncrypted(d *daemon.Swarm) func(*check.C) (interface{}, check.Com
|
||||
}
|
||||
}
|
||||
|
||||
func checkSwarmLockedToUnlocked(c *check.C, d *daemon.Swarm, unlockKey string) {
|
||||
func checkSwarmLockedToUnlocked(c *check.C, d *daemon.Daemon, unlockKey string) {
|
||||
// Wait for the PEM file to become unencrypted
|
||||
waitAndAssert(c, defaultReconciliationTimeout, checkKeyIsEncrypted(d), checker.Equals, false)
|
||||
|
||||
@@ -1019,7 +1017,7 @@ func checkSwarmLockedToUnlocked(c *check.C, d *daemon.Swarm, unlockKey string) {
|
||||
c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
|
||||
}
|
||||
|
||||
func checkSwarmUnlockedToLocked(c *check.C, d *daemon.Swarm) {
|
||||
func checkSwarmUnlockedToLocked(c *check.C, d *daemon.Daemon) {
|
||||
// Wait for the PEM file to become encrypted
|
||||
waitAndAssert(c, defaultReconciliationTimeout, checkKeyIsEncrypted(d), checker.Equals, true)
|
||||
|
||||
@@ -1117,8 +1115,7 @@ func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *check.C) {
|
||||
// It starts off locked
|
||||
d.Restart(c, "--swarm-default-advertise-addr=lo")
|
||||
|
||||
info, err := d.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info := d.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked)
|
||||
|
||||
outs, _ = d.Cmd("node", "ls")
|
||||
@@ -1132,15 +1129,13 @@ func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *check.C) {
|
||||
outs, err = d.Cmd("swarm", "leave", "--force")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
|
||||
|
||||
info, err = d.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
|
||||
|
||||
outs, err = d.Cmd("swarm", "init")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
|
||||
|
||||
info, err = d.SwarmInfo()
|
||||
c.Assert(err, checker.IsNil)
|
||||
info = d.SwarmInfo(c)
|
||||
c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
|
||||
}
|
||||
|
||||
@@ -1176,7 +1171,7 @@ func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *check.C) {
|
||||
c.Assert(outs, checker.Equals, unlockKey+"\n")
|
||||
|
||||
// The ones that got the cluster update should be set to locked
|
||||
for _, d := range []*daemon.Swarm{d1, d3} {
|
||||
for _, d := range []*daemon.Daemon{d1, d3} {
|
||||
checkSwarmUnlockedToLocked(c, d)
|
||||
|
||||
cmd := d.Command("swarm", "unlock")
|
||||
@@ -1197,7 +1192,7 @@ func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *check.C) {
|
||||
c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
|
||||
|
||||
// the ones that got the update are now set to unlocked
|
||||
for _, d := range []*daemon.Swarm{d1, d3} {
|
||||
for _, d := range []*daemon.Daemon{d1, d3} {
|
||||
checkSwarmLockedToUnlocked(c, d, unlockKey)
|
||||
}
|
||||
|
||||
@@ -1247,7 +1242,7 @@ func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *check.C) {
|
||||
c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
|
||||
|
||||
// promote worker
|
||||
outs, err = d1.Cmd("node", "promote", d2.Info.NodeID)
|
||||
outs, err = d1.Cmd("node", "promote", d2.NodeID())
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(outs, checker.Contains, "promoted to a manager in the swarm")
|
||||
|
||||
@@ -1255,7 +1250,7 @@ func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *check.C) {
|
||||
d3 := s.AddDaemon(c, true, true)
|
||||
|
||||
// both new nodes are locked
|
||||
for _, d := range []*daemon.Swarm{d2, d3} {
|
||||
for _, d := range []*daemon.Daemon{d2, d3} {
|
||||
checkSwarmUnlockedToLocked(c, d)
|
||||
|
||||
cmd := d.Command("swarm", "unlock")
|
||||
@@ -1265,7 +1260,7 @@ func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *check.C) {
|
||||
}
|
||||
|
||||
// demote manager back to worker - workers are not locked
|
||||
outs, err = d1.Cmd("node", "demote", d3.Info.NodeID)
|
||||
outs, err = d1.Cmd("node", "demote", d3.NodeID())
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(outs, checker.Contains, "demoted in the swarm")
|
||||
|
||||
@@ -1409,7 +1404,7 @@ func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *check.C) {
|
||||
d2.Restart(c)
|
||||
d3.Restart(c)
|
||||
|
||||
for _, d := range []*daemon.Swarm{d2, d3} {
|
||||
for _, d := range []*daemon.Daemon{d2, d3} {
|
||||
c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
|
||||
|
||||
outs, _ := d.Cmd("node", "ls")
|
||||
@@ -1521,7 +1516,7 @@ func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *check.C) {
|
||||
d3 := s.AddDaemon(c, true, false)
|
||||
|
||||
// Manager Addresses will always show Node 1's address
|
||||
expectedOutput := fmt.Sprintf("Manager Addresses:\n 127.0.0.1:%d\n", d1.Port)
|
||||
expectedOutput := fmt.Sprintf("Manager Addresses:\n 127.0.0.1:%d\n", d1.SwarmPort)
|
||||
|
||||
out, err := d1.Cmd("info")
|
||||
c.Assert(err, checker.IsNil)
|
||||
@@ -1641,7 +1636,7 @@ func (s *DockerSwarmSuite) TestSwarmJoinWithDrain(c *check.C) {
|
||||
|
||||
d1 := s.AddDaemon(c, false, false)
|
||||
|
||||
out, err = d1.Cmd("swarm", "join", "--availability=drain", "--token", token, d.ListenAddr)
|
||||
out, err = d1.Cmd("swarm", "join", "--availability=drain", "--token", token, d.SwarmListenAddr())
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
|
||||
|
||||
@@ -1835,7 +1830,7 @@ func (s *DockerSwarmSuite) TestSwarmJoinLeave(c *check.C) {
|
||||
// Verify that back to back join/leave does not cause panics
|
||||
d1 := s.AddDaemon(c, false, false)
|
||||
for i := 0; i < 10; i++ {
|
||||
out, err = d1.Cmd("swarm", "join", "--token", token, d.ListenAddr)
|
||||
out, err = d1.Cmd("swarm", "join", "--token", token, d.SwarmListenAddr())
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
|
||||
|
||||
@@ -1846,7 +1841,7 @@ func (s *DockerSwarmSuite) TestSwarmJoinLeave(c *check.C) {
|
||||
|
||||
const defaultRetryCount = 10
|
||||
|
||||
func waitForEvent(c *check.C, d *daemon.Swarm, since string, filter string, event string, retry int) string {
|
||||
func waitForEvent(c *check.C, d *daemon.Daemon, since string, filter string, event string, retry int) string {
|
||||
if retry < 1 {
|
||||
c.Fatalf("retry count %d is invalid. It should be no less than 1", retry)
|
||||
return ""
|
||||
@@ -1982,7 +1977,7 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsNode(c *check.C) {
|
||||
s.AddDaemon(c, true, true)
|
||||
d3 := s.AddDaemon(c, true, true)
|
||||
|
||||
d3ID := d3.NodeID
|
||||
d3ID := d3.NodeID()
|
||||
waitForEvent(c, d1, "0", "-f scope=swarm", "node create "+d3ID, defaultRetryCount)
|
||||
|
||||
t1 := daemonUnixTime(c)
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -30,7 +30,7 @@ func TestContainerStartOnDaemonRestart(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon(), "cannot start daemon on remote test run")
|
||||
t.Parallel()
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(t, "--iptables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
containerTypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/integration/internal/request"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
is "github.com/gotestyourself/gotestyourself/assert/cmp"
|
||||
@@ -62,7 +62,7 @@ func TestExportContainerAfterDaemonRestart(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
client, err := d.NewClient()
|
||||
assert.NilError(t, err)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package container // import "github.com/docker/docker/integration/container"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
@@ -12,9 +11,7 @@ import (
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration/internal/request"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
is "github.com/gotestyourself/gotestyourself/assert/cmp"
|
||||
@@ -22,77 +19,6 @@ import (
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
|
||||
func TestContainerShmNoLeak(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon(), "cannot start daemon on remote test run")
|
||||
t.Parallel()
|
||||
d := daemon.New(t, "docker", "dockerd", daemon.Config{})
|
||||
client, err := d.NewClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d.StartWithBusybox(t, "--iptables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
ctx := context.Background()
|
||||
cfg := container.Config{
|
||||
Image: "busybox",
|
||||
Cmd: []string{"top"},
|
||||
}
|
||||
|
||||
ctr, err := client.ContainerCreate(ctx, &cfg, nil, nil, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.ContainerRemove(ctx, ctr.ID, types.ContainerRemoveOptions{Force: true})
|
||||
|
||||
if err := client.ContainerStart(ctx, ctr.ID, types.ContainerStartOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// this should recursively bind mount everything in the test daemons root
|
||||
// except of course we are hoping that the previous containers /dev/shm mount did not leak into this new container
|
||||
hc := container.HostConfig{
|
||||
Mounts: []mount.Mount{
|
||||
{
|
||||
Type: mount.TypeBind,
|
||||
Source: d.Root,
|
||||
Target: "/testdaemonroot",
|
||||
},
|
||||
},
|
||||
}
|
||||
cfg.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("mount | grep testdaemonroot | grep containers | grep %s", ctr.ID)}
|
||||
cfg.AttachStdout = true
|
||||
cfg.AttachStderr = true
|
||||
ctrLeak, err := client.ContainerCreate(ctx, &cfg, &hc, nil, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
attach, err := client.ContainerAttach(ctx, ctrLeak.ID, types.ContainerAttachOptions{
|
||||
Stream: true,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := client.ContainerStart(ctx, ctrLeak.ID, types.ContainerStartOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
if _, err := stdcopy.StdCopy(buf, buf, attach.Reader); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out := bytes.TrimSpace(buf.Bytes())
|
||||
if !bytes.Equal(out, []byte{}) {
|
||||
t.Fatalf("mount leaked: %s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerNetworkMountsNoChown(t *testing.T) {
|
||||
// chown only applies to Linux bind mounted volumes; must be same host to verify
|
||||
skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon())
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
|
||||
@@ -55,7 +55,7 @@ func TestDaemonRestartKillContainers(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
client, err := d.NewClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -2,7 +2,6 @@ package swarm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -11,18 +10,13 @@ import (
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/poll"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
|
||||
const (
|
||||
dockerdBinary = "dockerd"
|
||||
defaultSwarmPort = 2477
|
||||
)
|
||||
|
||||
// ServicePoll tweaks the pollSettings for `service`
|
||||
func ServicePoll(config *poll.Settings) {
|
||||
// Override the default pollSettings for `service` resource here ...
|
||||
@@ -55,23 +49,17 @@ func ContainerPoll(config *poll.Settings) {
|
||||
}
|
||||
|
||||
// NewSwarm creates a swarm daemon for testing
|
||||
func NewSwarm(t *testing.T, testEnv *environment.Execution) *daemon.Swarm {
|
||||
func NewSwarm(t *testing.T, testEnv *environment.Execution, ops ...func(*daemon.Daemon)) *daemon.Daemon {
|
||||
skip.IfCondition(t, testEnv.IsRemoteDaemon())
|
||||
d := &daemon.Swarm{
|
||||
Daemon: daemon.New(t, "", dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
|
||||
}),
|
||||
// TODO: better method of finding an unused port
|
||||
Port: defaultSwarmPort,
|
||||
if testEnv.DaemonInfo.ExperimentalBuild {
|
||||
ops = append(ops, daemon.WithExperimental)
|
||||
}
|
||||
// TODO: move to a NewSwarm constructor
|
||||
d.ListenAddr = fmt.Sprintf("0.0.0.0:%d", d.Port)
|
||||
|
||||
d := daemon.New(t, ops...)
|
||||
// avoid networking conflicts
|
||||
args := []string{"--iptables=false", "--swarm-default-advertise-addr=lo"}
|
||||
d.StartWithBusybox(t, args...)
|
||||
|
||||
assert.NilError(t, d.Init(swarmtypes.InitRequest{}))
|
||||
d.SwarmInit(t, swarmtypes.InitRequest{})
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -79,7 +67,7 @@ func NewSwarm(t *testing.T, testEnv *environment.Execution) *daemon.Swarm {
|
||||
type ServiceSpecOpt func(*swarmtypes.ServiceSpec)
|
||||
|
||||
// CreateService creates a service on the passed in swarm daemon.
|
||||
func CreateService(t *testing.T, d *daemon.Swarm, opts ...ServiceSpecOpt) string {
|
||||
func CreateService(t *testing.T, d *daemon.Daemon, opts ...ServiceSpecOpt) string {
|
||||
spec := defaultServiceSpec()
|
||||
for _, o := range opts {
|
||||
o(&spec)
|
||||
@@ -151,7 +139,7 @@ func ServiceWithName(name string) ServiceSpecOpt {
|
||||
}
|
||||
|
||||
// GetRunningTasks gets the list of running tasks for a service
|
||||
func GetRunningTasks(t *testing.T, d *daemon.Swarm, serviceID string) []swarmtypes.Task {
|
||||
func GetRunningTasks(t *testing.T, d *daemon.Daemon, serviceID string) []swarmtypes.Task {
|
||||
client := GetClient(t, d)
|
||||
|
||||
filterArgs := filters.NewArgs()
|
||||
@@ -167,7 +155,7 @@ func GetRunningTasks(t *testing.T, d *daemon.Swarm, serviceID string) []swarmtyp
|
||||
}
|
||||
|
||||
// ExecTask runs the passed in exec config on the given task
|
||||
func ExecTask(t *testing.T, d *daemon.Swarm, task swarmtypes.Task, config types.ExecConfig) types.HijackedResponse {
|
||||
func ExecTask(t *testing.T, d *daemon.Daemon, task swarmtypes.Task, config types.ExecConfig) types.HijackedResponse {
|
||||
client := GetClient(t, d)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -187,7 +175,7 @@ func ensureContainerSpec(spec *swarmtypes.ServiceSpec) {
|
||||
}
|
||||
|
||||
// GetClient creates a new client for the passed in swarm daemon.
|
||||
func GetClient(t *testing.T, d *daemon.Swarm) client.APIClient {
|
||||
func GetClient(t *testing.T, d *daemon.Daemon) client.APIClient {
|
||||
client, err := client.NewClientWithOpts(client.WithHost((d.Sock())))
|
||||
assert.NilError(t, err)
|
||||
return client
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
)
|
||||
|
||||
const defaultSwarmPort = 2477
|
||||
const dockerdBinary = "dockerd"
|
||||
|
||||
func TestInspectNetwork(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/assert/cmp"
|
||||
@@ -24,7 +24,7 @@ func TestDockerNetworkMacvlanPersistance(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(t)
|
||||
defer d.Stop(t)
|
||||
|
||||
@@ -53,7 +53,7 @@ func TestDockerNetworkMacvlanOverlapParent(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(t)
|
||||
defer d.Stop(t)
|
||||
|
||||
@@ -95,7 +95,7 @@ func TestDockerNetworkMacvlanSubinterface(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(t)
|
||||
defer d.Stop(t)
|
||||
|
||||
@@ -131,7 +131,7 @@ func TestDockerNetworkMacvlanBridgeNilParent(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(t)
|
||||
defer d.Stop(t)
|
||||
client, err := d.NewClient()
|
||||
@@ -157,7 +157,7 @@ func TestDockerNetworkMacvlanBridgeInternal(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(t)
|
||||
defer d.Stop(t)
|
||||
client, err := d.NewClient()
|
||||
@@ -191,7 +191,7 @@ func TestDockerNetworkMacvlanMultiSubnet(t *testing.T) {
|
||||
skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
|
||||
t.Skip("Temporarily skipping while investigating sporadic v6 CI issues")
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(t)
|
||||
defer d.Stop(t)
|
||||
client, err := d.NewClient()
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
"github.com/docker/docker/pkg/authorization"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
@@ -25,8 +25,6 @@ var (
|
||||
server *httptest.Server
|
||||
)
|
||||
|
||||
const dockerdBinary = "dockerd"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testEnv, err = environment.New()
|
||||
@@ -52,9 +50,7 @@ func setupTest(t *testing.T) func() {
|
||||
skip.IfCondition(t, testEnv.IsRemoteDaemon(), "cannot run daemon when remote daemon")
|
||||
environment.ProtectAll(t, testEnv)
|
||||
|
||||
d = daemon.New(t, "", dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
|
||||
})
|
||||
d = daemon.New(t, daemon.WithExperimental)
|
||||
|
||||
return func() {
|
||||
if d != nil {
|
||||
|
||||
@@ -12,8 +12,6 @@ var (
|
||||
testEnv *environment.Execution
|
||||
)
|
||||
|
||||
const dockerdBinary = "dockerd"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testEnv, err = environment.New()
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
@@ -17,7 +17,7 @@ func TestDaemonStartWithLogOpt(t *testing.T) {
|
||||
skip.IfCondition(t, testEnv.IsRemoteDaemon(), "cannot run daemon when remote daemon")
|
||||
t.Parallel()
|
||||
|
||||
d := daemon.New(t, "", dockerdBinary, daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
d.Start(t, "--iptables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ var (
|
||||
testEnv *environment.Execution
|
||||
)
|
||||
|
||||
const dockerdBinary = "dockerd"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testEnv, err = environment.New()
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration-cli/fixtures/plugin"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
func TestPluginWithDevMounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
d := daemon.New(t, "", dockerdBinary, daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
d.Start(t, "--iptables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
|
||||
var testEnv *environment.Execution
|
||||
|
||||
const dockerdBinary = "dockerd"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testEnv, err = environment.New()
|
||||
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
|
||||
var testEnv *environment.Execution
|
||||
|
||||
const dockerdBinary = "dockerd"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testEnv, err = environment.New()
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/internal/test/daemon"
|
||||
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
)
|
||||
@@ -33,7 +33,7 @@ func TestCgroupDriverSystemdMemoryLimit(t *testing.T) {
|
||||
t.Skip("systemd not available")
|
||||
}
|
||||
|
||||
d := daemon.New(t, "docker", "dockerd", daemon.Config{})
|
||||
d := daemon.New(t)
|
||||
client, err := d.NewClient()
|
||||
assert.NilError(t, err)
|
||||
d.StartWithBusybox(t, "--exec-opt", "native.cgroupdriver=systemd", "--iptables=false")
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
)
|
||||
|
||||
// ConfigConstructor defines a swarm config constructor
|
||||
type ConfigConstructor func(*swarm.Config)
|
||||
|
||||
// CreateConfig creates a config given the specified spec
|
||||
func (d *Daemon) CreateConfig(t assert.TestingT, configSpec swarm.ConfigSpec) string {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
scr, err := cli.ConfigCreate(context.Background(), configSpec)
|
||||
assert.NilError(t, err)
|
||||
return scr.ID
|
||||
}
|
||||
|
||||
// ListConfigs returns the list of the current swarm configs
|
||||
func (d *Daemon) ListConfigs(t assert.TestingT) []swarm.Config {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
configs, err := cli.ConfigList(context.Background(), types.ConfigListOptions{})
|
||||
assert.NilError(t, err)
|
||||
return configs
|
||||
}
|
||||
|
||||
// GetConfig returns a swarm config identified by the specified id
|
||||
func (d *Daemon) GetConfig(t assert.TestingT, id string) *swarm.Config {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
config, _, err := cli.ConfigInspectWithRaw(context.Background(), id)
|
||||
assert.NilError(t, err)
|
||||
return &config
|
||||
}
|
||||
|
||||
// DeleteConfig removes the swarm config identified by the specified id
|
||||
func (d *Daemon) DeleteConfig(t assert.TestingT, id string) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
err := cli.ConfigRemove(context.Background(), id)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
// UpdateConfig updates the swarm config identified by the specified id
|
||||
// Currently, only label update is supported.
|
||||
func (d *Daemon) UpdateConfig(t assert.TestingT, id string, f ...ConfigConstructor) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
config := d.GetConfig(t, id)
|
||||
for _, fn := range f {
|
||||
fn(config)
|
||||
}
|
||||
|
||||
err := cli.ConfigUpdate(context.Background(), config.ID, config.Version, config.Spec)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
@@ -0,0 +1,638 @@
|
||||
package daemon // import "github.com/docker/docker/internal/test/daemon"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration-cli/request"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type testingT interface {
|
||||
assert.TestingT
|
||||
logT
|
||||
Fatalf(string, ...interface{})
|
||||
}
|
||||
|
||||
type logT interface {
|
||||
Logf(string, ...interface{})
|
||||
}
|
||||
|
||||
const defaultDockerdBinary = "dockerd"
|
||||
|
||||
var errDaemonNotStarted = errors.New("daemon not started")
|
||||
|
||||
// SockRoot holds the path of the default docker integration daemon socket
|
||||
var SockRoot = filepath.Join(os.TempDir(), "docker-integration")
|
||||
|
||||
type clientConfig struct {
|
||||
transport *http.Transport
|
||||
scheme string
|
||||
addr string
|
||||
}
|
||||
|
||||
// Daemon represents a Docker daemon for the testing framework
|
||||
type Daemon struct {
|
||||
GlobalFlags []string
|
||||
Root string
|
||||
Folder string
|
||||
Wait chan error
|
||||
UseDefaultHost bool
|
||||
UseDefaultTLSHost bool
|
||||
|
||||
id string
|
||||
logFile *os.File
|
||||
cmd *exec.Cmd
|
||||
storageDriver string
|
||||
userlandProxy bool
|
||||
execRoot string
|
||||
experimental bool
|
||||
dockerdBinary string
|
||||
log logT
|
||||
|
||||
// swarm related field
|
||||
swarmListenAddr string
|
||||
SwarmPort int // FIXME(vdemeester) should probably not be exported
|
||||
|
||||
// cached information
|
||||
CachedInfo types.Info
|
||||
}
|
||||
|
||||
// New returns a Daemon instance to be used for testing.
|
||||
// This will create a directory such as d123456789 in the folder specified by $DOCKER_INTEGRATION_DAEMON_DEST or $DEST.
|
||||
// The daemon will not automatically start.
|
||||
func New(t testingT, ops ...func(*Daemon)) *Daemon {
|
||||
dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST")
|
||||
if dest == "" {
|
||||
dest = os.Getenv("DEST")
|
||||
}
|
||||
assert.Check(t, dest != "", "Please set the DOCKER_INTEGRATION_DAEMON_DEST or the DEST environment variable")
|
||||
|
||||
storageDriver := os.Getenv("DOCKER_GRAPHDRIVER")
|
||||
|
||||
assert.NilError(t, os.MkdirAll(SockRoot, 0700), "could not create daemon socket root")
|
||||
|
||||
id := fmt.Sprintf("d%s", stringid.TruncateID(stringid.GenerateRandomID()))
|
||||
dir := filepath.Join(dest, id)
|
||||
daemonFolder, err := filepath.Abs(dir)
|
||||
assert.NilError(t, err, "Could not make %q an absolute path", dir)
|
||||
daemonRoot := filepath.Join(daemonFolder, "root")
|
||||
|
||||
assert.NilError(t, os.MkdirAll(daemonRoot, 0755), "Could not create daemon root %q", dir)
|
||||
|
||||
userlandProxy := true
|
||||
if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" {
|
||||
if val, err := strconv.ParseBool(env); err != nil {
|
||||
userlandProxy = val
|
||||
}
|
||||
}
|
||||
d := &Daemon{
|
||||
id: id,
|
||||
Folder: daemonFolder,
|
||||
Root: daemonRoot,
|
||||
storageDriver: storageDriver,
|
||||
userlandProxy: userlandProxy,
|
||||
execRoot: filepath.Join(os.TempDir(), "docker-execroot", id),
|
||||
dockerdBinary: defaultDockerdBinary,
|
||||
swarmListenAddr: defaultSwarmListenAddr,
|
||||
SwarmPort: defaultSwarmPort,
|
||||
log: t,
|
||||
}
|
||||
|
||||
for _, op := range ops {
|
||||
op(d)
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// RootDir returns the root directory of the daemon.
|
||||
func (d *Daemon) RootDir() string {
|
||||
return d.Root
|
||||
}
|
||||
|
||||
// ID returns the generated id of the daemon
|
||||
func (d *Daemon) ID() string {
|
||||
return d.id
|
||||
}
|
||||
|
||||
// StorageDriver returns the configured storage driver of the daemon
|
||||
func (d *Daemon) StorageDriver() string {
|
||||
return d.storageDriver
|
||||
}
|
||||
|
||||
// Sock returns the socket path of the daemon
|
||||
func (d *Daemon) Sock() string {
|
||||
return fmt.Sprintf("unix://" + d.sockPath())
|
||||
}
|
||||
|
||||
func (d *Daemon) sockPath() string {
|
||||
return filepath.Join(SockRoot, d.id+".sock")
|
||||
}
|
||||
|
||||
// LogFileName returns the path the daemon's log file
|
||||
func (d *Daemon) LogFileName() string {
|
||||
return d.logFile.Name()
|
||||
}
|
||||
|
||||
// ReadLogFile returns the content of the daemon log file
|
||||
func (d *Daemon) ReadLogFile() ([]byte, error) {
|
||||
return ioutil.ReadFile(d.logFile.Name())
|
||||
}
|
||||
|
||||
// NewClient creates new client based on daemon's socket path
|
||||
// FIXME(vdemeester): replace NewClient with NewClientT
|
||||
func (d *Daemon) NewClient() (*client.Client, error) {
|
||||
return client.NewClientWithOpts(
|
||||
client.FromEnv,
|
||||
client.WithHost(d.Sock()))
|
||||
}
|
||||
|
||||
// NewClientT creates new client based on daemon's socket path
|
||||
// FIXME(vdemeester): replace NewClient with NewClientT
|
||||
func (d *Daemon) NewClientT(t assert.TestingT) *client.Client {
|
||||
c, err := client.NewClientWithOpts(
|
||||
client.FromEnv,
|
||||
client.WithHost(d.Sock()))
|
||||
assert.NilError(t, err, "cannot create daemon client")
|
||||
return c
|
||||
}
|
||||
|
||||
// CleanupExecRoot cleans the daemon exec root (network namespaces, ...)
|
||||
func (d *Daemon) CleanupExecRoot(t testingT) {
|
||||
cleanupExecRoot(t, d.execRoot)
|
||||
}
|
||||
|
||||
// Start starts the daemon and return once it is ready to receive requests.
|
||||
func (d *Daemon) Start(t testingT, args ...string) {
|
||||
if err := d.StartWithError(args...); err != nil {
|
||||
t.Fatalf("Error starting daemon with arguments: %v", args)
|
||||
}
|
||||
}
|
||||
|
||||
// StartWithError starts the daemon and return once it is ready to receive requests.
|
||||
// It returns an error in case it couldn't start.
|
||||
func (d *Daemon) StartWithError(args ...string) error {
|
||||
logFile, err := os.OpenFile(filepath.Join(d.Folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[%s] Could not create %s/docker.log", d.id, d.Folder)
|
||||
}
|
||||
|
||||
return d.StartWithLogFile(logFile, args...)
|
||||
}
|
||||
|
||||
// StartWithLogFile will start the daemon and attach its streams to a given file.
|
||||
func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
|
||||
dockerdBinary, err := exec.LookPath(d.dockerdBinary)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[%s] could not find docker binary in $PATH", d.id)
|
||||
}
|
||||
args := append(d.GlobalFlags,
|
||||
"--containerd", "/var/run/docker/containerd/docker-containerd.sock",
|
||||
"--data-root", d.Root,
|
||||
"--exec-root", d.execRoot,
|
||||
"--pidfile", fmt.Sprintf("%s/docker.pid", d.Folder),
|
||||
fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
|
||||
)
|
||||
if d.experimental {
|
||||
args = append(args, "--experimental", "--init")
|
||||
}
|
||||
if !(d.UseDefaultHost || d.UseDefaultTLSHost) {
|
||||
args = append(args, []string{"--host", d.Sock()}...)
|
||||
}
|
||||
if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
|
||||
args = append(args, []string{"--userns-remap", root}...)
|
||||
}
|
||||
|
||||
// If we don't explicitly set the log-level or debug flag(-D) then
|
||||
// turn on debug mode
|
||||
foundLog := false
|
||||
foundSd := false
|
||||
for _, a := range providedArgs {
|
||||
if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") {
|
||||
foundLog = true
|
||||
}
|
||||
if strings.Contains(a, "--storage-driver") {
|
||||
foundSd = true
|
||||
}
|
||||
}
|
||||
if !foundLog {
|
||||
args = append(args, "--debug")
|
||||
}
|
||||
if d.storageDriver != "" && !foundSd {
|
||||
args = append(args, "--storage-driver", d.storageDriver)
|
||||
}
|
||||
|
||||
args = append(args, providedArgs...)
|
||||
d.cmd = exec.Command(dockerdBinary, args...)
|
||||
d.cmd.Env = append(os.Environ(), "DOCKER_SERVICE_PREFER_OFFLINE_IMAGE=1")
|
||||
d.cmd.Stdout = out
|
||||
d.cmd.Stderr = out
|
||||
d.logFile = out
|
||||
|
||||
if err := d.cmd.Start(); err != nil {
|
||||
return errors.Errorf("[%s] could not start daemon container: %v", d.id, err)
|
||||
}
|
||||
|
||||
wait := make(chan error)
|
||||
|
||||
go func() {
|
||||
wait <- d.cmd.Wait()
|
||||
d.log.Logf("[%s] exiting daemon", d.id)
|
||||
close(wait)
|
||||
}()
|
||||
|
||||
d.Wait = wait
|
||||
|
||||
tick := time.Tick(500 * time.Millisecond)
|
||||
// make sure daemon is ready to receive requests
|
||||
startTime := time.Now().Unix()
|
||||
for {
|
||||
d.log.Logf("[%s] waiting for daemon to start", d.id)
|
||||
if time.Now().Unix()-startTime > 5 {
|
||||
// After 5 seconds, give up
|
||||
return errors.Errorf("[%s] Daemon exited and never started", d.id)
|
||||
}
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
return errors.Errorf("[%s] timeout: daemon does not respond", d.id)
|
||||
case <-tick:
|
||||
clientConfig, err := d.getClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: clientConfig.transport,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "/_ping", nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[%s] could not create new request", d.id)
|
||||
}
|
||||
req.URL.Host = clientConfig.addr
|
||||
req.URL.Scheme = clientConfig.scheme
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
d.log.Logf("[%s] received status != 200 OK: %s\n", d.id, resp.Status)
|
||||
}
|
||||
d.log.Logf("[%s] daemon started\n", d.id)
|
||||
d.Root, err = d.queryRootDir()
|
||||
if err != nil {
|
||||
return errors.Errorf("[%s] error querying daemon for root directory: %v", d.id, err)
|
||||
}
|
||||
return nil
|
||||
case <-d.Wait:
|
||||
return errors.Errorf("[%s] Daemon exited during startup", d.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StartWithBusybox will first start the daemon with Daemon.Start()
|
||||
// then save the busybox image from the main daemon and load it into this Daemon instance.
|
||||
func (d *Daemon) StartWithBusybox(t testingT, arg ...string) {
|
||||
d.Start(t, arg...)
|
||||
d.LoadBusybox(t)
|
||||
}
|
||||
|
||||
// Kill will send a SIGKILL to the daemon
|
||||
func (d *Daemon) Kill() error {
|
||||
if d.cmd == nil || d.Wait == nil {
|
||||
return errDaemonNotStarted
|
||||
}
|
||||
|
||||
defer func() {
|
||||
d.logFile.Close()
|
||||
d.cmd = nil
|
||||
}()
|
||||
|
||||
if err := d.cmd.Process.Kill(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder))
|
||||
}
|
||||
|
||||
// Pid returns the pid of the daemon
|
||||
func (d *Daemon) Pid() int {
|
||||
return d.cmd.Process.Pid
|
||||
}
|
||||
|
||||
// Interrupt stops the daemon by sending it an Interrupt signal
|
||||
func (d *Daemon) Interrupt() error {
|
||||
return d.Signal(os.Interrupt)
|
||||
}
|
||||
|
||||
// Signal sends the specified signal to the daemon if running
|
||||
func (d *Daemon) Signal(signal os.Signal) error {
|
||||
if d.cmd == nil || d.Wait == nil {
|
||||
return errDaemonNotStarted
|
||||
}
|
||||
return d.cmd.Process.Signal(signal)
|
||||
}
|
||||
|
||||
// DumpStackAndQuit sends SIGQUIT to the daemon, which triggers it to dump its
|
||||
// stack to its log file and exit
|
||||
// This is used primarily for gathering debug information on test timeout
|
||||
func (d *Daemon) DumpStackAndQuit() {
|
||||
if d.cmd == nil || d.cmd.Process == nil {
|
||||
return
|
||||
}
|
||||
SignalDaemonDump(d.cmd.Process.Pid)
|
||||
}
|
||||
|
||||
// Stop will send a SIGINT every second and wait for the daemon to stop.
|
||||
// If it times out, a SIGKILL is sent.
|
||||
// Stop will not delete the daemon directory. If a purged daemon is needed,
|
||||
// instantiate a new one with NewDaemon.
|
||||
// If an error occurs while starting the daemon, the test will fail.
|
||||
func (d *Daemon) Stop(t testingT) {
|
||||
err := d.StopWithError()
|
||||
if err != nil {
|
||||
if err != errDaemonNotStarted {
|
||||
t.Fatalf("Error while stopping the daemon %s : %v", d.id, err)
|
||||
} else {
|
||||
t.Logf("Daemon %s is not started", d.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StopWithError will send a SIGINT every second and wait for the daemon to stop.
|
||||
// If it timeouts, a SIGKILL is sent.
|
||||
// Stop will not delete the daemon directory. If a purged daemon is needed,
|
||||
// instantiate a new one with NewDaemon.
|
||||
func (d *Daemon) StopWithError() error {
|
||||
if d.cmd == nil || d.Wait == nil {
|
||||
return errDaemonNotStarted
|
||||
}
|
||||
|
||||
defer func() {
|
||||
d.logFile.Close()
|
||||
d.cmd = nil
|
||||
}()
|
||||
|
||||
i := 1
|
||||
tick := time.Tick(time.Second)
|
||||
|
||||
if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
|
||||
if strings.Contains(err.Error(), "os: process already finished") {
|
||||
return errDaemonNotStarted
|
||||
}
|
||||
return errors.Errorf("could not send signal: %v", err)
|
||||
}
|
||||
out1:
|
||||
for {
|
||||
select {
|
||||
case err := <-d.Wait:
|
||||
return err
|
||||
case <-time.After(20 * time.Second):
|
||||
// time for stopping jobs and run onShutdown hooks
|
||||
d.log.Logf("[%s] daemon started", d.id)
|
||||
break out1
|
||||
}
|
||||
}
|
||||
|
||||
out2:
|
||||
for {
|
||||
select {
|
||||
case err := <-d.Wait:
|
||||
return err
|
||||
case <-tick:
|
||||
i++
|
||||
if i > 5 {
|
||||
d.log.Logf("tried to interrupt daemon for %d times, now try to kill it", i)
|
||||
break out2
|
||||
}
|
||||
d.log.Logf("Attempt #%d: daemon is still running with pid %d", i, d.cmd.Process.Pid)
|
||||
if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
|
||||
return errors.Errorf("could not send signal: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := d.cmd.Process.Kill(); err != nil {
|
||||
d.log.Logf("Could not kill daemon: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
d.cmd.Wait()
|
||||
|
||||
return os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder))
|
||||
}
|
||||
|
||||
// Restart will restart the daemon by first stopping it and the starting it.
|
||||
// If an error occurs while starting the daemon, the test will fail.
|
||||
func (d *Daemon) Restart(t testingT, args ...string) {
|
||||
d.Stop(t)
|
||||
d.handleUserns()
|
||||
d.Start(t, args...)
|
||||
}
|
||||
|
||||
// RestartWithError will restart the daemon by first stopping it and then starting it.
|
||||
func (d *Daemon) RestartWithError(arg ...string) error {
|
||||
if err := d.StopWithError(); err != nil {
|
||||
return err
|
||||
}
|
||||
d.handleUserns()
|
||||
return d.StartWithError(arg...)
|
||||
}
|
||||
|
||||
func (d *Daemon) handleUserns() {
|
||||
// in the case of tests running a user namespace-enabled daemon, we have resolved
|
||||
// d.Root to be the actual final path of the graph dir after the "uid.gid" of
|
||||
// remapped root is added--we need to subtract it from the path before calling
|
||||
// start or else we will continue making subdirectories rather than truly restarting
|
||||
// with the same location/root:
|
||||
if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
|
||||
d.Root = filepath.Dir(d.Root)
|
||||
}
|
||||
}
|
||||
|
||||
// ReloadConfig asks the daemon to reload its configuration
|
||||
func (d *Daemon) ReloadConfig() error {
|
||||
if d.cmd == nil || d.cmd.Process == nil {
|
||||
return errors.New("daemon is not running")
|
||||
}
|
||||
|
||||
errCh := make(chan error)
|
||||
started := make(chan struct{})
|
||||
go func() {
|
||||
_, body, err := request.DoOnHost(d.Sock(), "/events", request.Method(http.MethodGet))
|
||||
close(started)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
defer body.Close()
|
||||
dec := json.NewDecoder(body)
|
||||
for {
|
||||
var e events.Message
|
||||
if err := dec.Decode(&e); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if e.Type != events.DaemonEventType {
|
||||
continue
|
||||
}
|
||||
if e.Action != "reload" {
|
||||
continue
|
||||
}
|
||||
close(errCh) // notify that we are done
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
<-started
|
||||
if err := signalDaemonReload(d.cmd.Process.Pid); err != nil {
|
||||
return errors.Errorf("error signaling daemon reload: %v", err)
|
||||
}
|
||||
select {
|
||||
case err := <-errCh:
|
||||
if err != nil {
|
||||
return errors.Errorf("error waiting for daemon reload event: %v", err)
|
||||
}
|
||||
case <-time.After(30 * time.Second):
|
||||
return errors.New("timeout waiting for daemon reload event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadBusybox image into the daemon
|
||||
func (d *Daemon) LoadBusybox(t assert.TestingT) {
|
||||
clientHost, err := client.NewEnvClient()
|
||||
assert.NilError(t, err, "failed to create client")
|
||||
defer clientHost.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
reader, err := clientHost.ImageSave(ctx, []string{"busybox:latest"})
|
||||
assert.NilError(t, err, "failed to download busybox")
|
||||
defer reader.Close()
|
||||
|
||||
client, err := d.NewClient()
|
||||
assert.NilError(t, err, "failed to create client")
|
||||
defer client.Close()
|
||||
|
||||
resp, err := client.ImageLoad(ctx, reader, true)
|
||||
assert.NilError(t, err, "failed to load busybox")
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
func (d *Daemon) getClientConfig() (*clientConfig, error) {
|
||||
var (
|
||||
transport *http.Transport
|
||||
scheme string
|
||||
addr string
|
||||
proto string
|
||||
)
|
||||
if d.UseDefaultTLSHost {
|
||||
option := &tlsconfig.Options{
|
||||
CAFile: "fixtures/https/ca.pem",
|
||||
CertFile: "fixtures/https/client-cert.pem",
|
||||
KeyFile: "fixtures/https/client-key.pem",
|
||||
}
|
||||
tlsConfig, err := tlsconfig.Client(*option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transport = &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
addr = fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort)
|
||||
scheme = "https"
|
||||
proto = "tcp"
|
||||
} else if d.UseDefaultHost {
|
||||
addr = opts.DefaultUnixSocket
|
||||
proto = "unix"
|
||||
scheme = "http"
|
||||
transport = &http.Transport{}
|
||||
} else {
|
||||
addr = d.sockPath()
|
||||
proto = "unix"
|
||||
scheme = "http"
|
||||
transport = &http.Transport{}
|
||||
}
|
||||
|
||||
if err := sockets.ConfigureTransport(transport, proto, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transport.DisableKeepAlives = true
|
||||
|
||||
return &clientConfig{
|
||||
transport: transport,
|
||||
scheme: scheme,
|
||||
addr: addr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Daemon) queryRootDir() (string, error) {
|
||||
// update daemon root by asking /info endpoint (to support user
|
||||
// namespaced daemon with root remapped uid.gid directory)
|
||||
clientConfig, err := d.getClientConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: clientConfig.transport,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "/info", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.URL.Host = clientConfig.addr
|
||||
req.URL.Scheme = clientConfig.scheme
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
|
||||
return resp.Body.Close()
|
||||
})
|
||||
|
||||
type Info struct {
|
||||
DockerRootDir string
|
||||
}
|
||||
var b []byte
|
||||
var i Info
|
||||
b, err = request.ReadBody(body)
|
||||
if err == nil && resp.StatusCode == http.StatusOK {
|
||||
// read the docker root dir
|
||||
if err = json.Unmarshal(b, &i); err == nil {
|
||||
return i.DockerRootDir, nil
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Info returns the info struct for this daemon
|
||||
func (d *Daemon) Info(t assert.TestingT) types.Info {
|
||||
apiclient, err := d.NewClient()
|
||||
assert.NilError(t, err)
|
||||
info, err := apiclient.Info(context.Background())
|
||||
assert.NilError(t, err)
|
||||
return info
|
||||
}
|
||||
+3
-4
@@ -1,16 +1,15 @@
|
||||
// +build !windows
|
||||
|
||||
package daemon // import "github.com/docker/docker/integration-cli/daemon"
|
||||
package daemon // import "github.com/docker/docker/internal/test/daemon"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-check/check"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func cleanupExecRoot(c *check.C, execRoot string) {
|
||||
func cleanupExecRoot(t testingT, execRoot string) {
|
||||
// Cleanup network namespaces in the exec root of this
|
||||
// daemon because this exec root is specific to this
|
||||
// daemon instance and has no chance of getting
|
||||
@@ -19,7 +18,7 @@ func cleanupExecRoot(c *check.C, execRoot string) {
|
||||
netnsPath := filepath.Join(execRoot, "netns")
|
||||
filepath.Walk(netnsPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err := unix.Unmount(path, unix.MNT_FORCE); err != nil {
|
||||
c.Logf("unmount of %s failed: %v", path, err)
|
||||
t.Logf("unmount of %s failed: %v", path, err)
|
||||
}
|
||||
os.Remove(path)
|
||||
return nil
|
||||
+2
-3
@@ -1,10 +1,9 @@
|
||||
package daemon // import "github.com/docker/docker/integration-cli/daemon"
|
||||
package daemon // import "github.com/docker/docker/internal/test/daemon"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-check/check"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -22,5 +21,5 @@ func signalDaemonReload(pid int) error {
|
||||
return fmt.Errorf("daemon reload not supported")
|
||||
}
|
||||
|
||||
func cleanupExecRoot(c *check.C, execRoot string) {
|
||||
func cleanupExecRoot(t testingT, execRoot string) {
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
)
|
||||
|
||||
// NodeConstructor defines a swarm node constructor
|
||||
type NodeConstructor func(*swarm.Node)
|
||||
|
||||
// GetNode returns a swarm node identified by the specified id
|
||||
func (d *Daemon) GetNode(t assert.TestingT, id string) *swarm.Node {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
node, _, err := cli.NodeInspectWithRaw(context.Background(), id)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, node.ID == id)
|
||||
return &node
|
||||
}
|
||||
|
||||
// RemoveNode removes the specified node
|
||||
func (d *Daemon) RemoveNode(t assert.TestingT, id string, force bool) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
options := types.NodeRemoveOptions{
|
||||
Force: force,
|
||||
}
|
||||
err := cli.NodeRemove(context.Background(), id, options)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
// UpdateNode updates a swarm node with the specified node constructor
|
||||
func (d *Daemon) UpdateNode(t assert.TestingT, id string, f ...NodeConstructor) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
for i := 0; ; i++ {
|
||||
node := d.GetNode(t, id)
|
||||
for _, fn := range f {
|
||||
fn(node)
|
||||
}
|
||||
|
||||
err := cli.NodeUpdate(context.Background(), node.ID, node.Version, node.Spec)
|
||||
if i < 10 && err != nil && strings.Contains(err.Error(), "update out of sequence") {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ListNodes returns the list of the current swarm nodes
|
||||
func (d *Daemon) ListNodes(t assert.TestingT) []swarm.Node {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
nodes, err := cli.NodeList(context.Background(), types.NodeListOptions{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
return nodes
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package daemon
|
||||
|
||||
// WithExperimental sets the daemon in experimental mode
|
||||
func WithExperimental(d *Daemon) {
|
||||
d.experimental = true
|
||||
}
|
||||
|
||||
// WithDockerdBinary sets the dockerd binary to the specified one
|
||||
func WithDockerdBinary(dockerdBinary string) func(*Daemon) {
|
||||
return func(d *Daemon) {
|
||||
d.dockerdBinary = dockerdBinary
|
||||
}
|
||||
}
|
||||
|
||||
// WithSwarmPort sets the swarm port to use for swarm mode
|
||||
func WithSwarmPort(port int) func(*Daemon) {
|
||||
return func(d *Daemon) {
|
||||
d.SwarmPort = port
|
||||
}
|
||||
}
|
||||
|
||||
// WithSwarmListenAddr sets the swarm listen addr to use for swarm mode
|
||||
func WithSwarmListenAddr(listenAddr string) func(*Daemon) {
|
||||
return func(d *Daemon) {
|
||||
d.swarmListenAddr = listenAddr
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
)
|
||||
|
||||
// SecretConstructor defines a swarm secret constructor
|
||||
type SecretConstructor func(*swarm.Secret)
|
||||
|
||||
// CreateSecret creates a secret given the specified spec
|
||||
func (d *Daemon) CreateSecret(t assert.TestingT, secretSpec swarm.SecretSpec) string {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
scr, err := cli.SecretCreate(context.Background(), secretSpec)
|
||||
assert.NilError(t, err)
|
||||
|
||||
return scr.ID
|
||||
}
|
||||
|
||||
// ListSecrets returns the list of the current swarm secrets
|
||||
func (d *Daemon) ListSecrets(t assert.TestingT) []swarm.Secret {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
secrets, err := cli.SecretList(context.Background(), types.SecretListOptions{})
|
||||
assert.NilError(t, err)
|
||||
return secrets
|
||||
}
|
||||
|
||||
// GetSecret returns a swarm secret identified by the specified id
|
||||
func (d *Daemon) GetSecret(t assert.TestingT, id string) *swarm.Secret {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
secret, _, err := cli.SecretInspectWithRaw(context.Background(), id)
|
||||
assert.NilError(t, err)
|
||||
return &secret
|
||||
}
|
||||
|
||||
// DeleteSecret removes the swarm secret identified by the specified id
|
||||
func (d *Daemon) DeleteSecret(t assert.TestingT, id string) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
err := cli.SecretRemove(context.Background(), id)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
// UpdateSecret updates the swarm secret identified by the specified id
|
||||
// Currently, only label update is supported.
|
||||
func (d *Daemon) UpdateSecret(t assert.TestingT, id string, f ...SecretConstructor) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
secret := d.GetSecret(t, id)
|
||||
for _, fn := range f {
|
||||
fn(secret)
|
||||
}
|
||||
|
||||
err := cli.SecretUpdate(context.Background(), secret.ID, secret.Version, secret.Spec)
|
||||
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
)
|
||||
|
||||
// ServiceConstructor defines a swarm service constructor function
|
||||
type ServiceConstructor func(*swarm.Service)
|
||||
|
||||
// CreateServiceWithOptions creates a swarm service given the specified service constructors
|
||||
// and auth config
|
||||
func (d *Daemon) CreateServiceWithOptions(t assert.TestingT, opts types.ServiceCreateOptions, f ...ServiceConstructor) string {
|
||||
var service swarm.Service
|
||||
for _, fn := range f {
|
||||
fn(&service)
|
||||
}
|
||||
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := cli.ServiceCreate(ctx, service.Spec, opts)
|
||||
assert.NilError(t, err)
|
||||
return res.ID
|
||||
}
|
||||
|
||||
// CreateService creates a swarm service given the specified service constructor
|
||||
func (d *Daemon) CreateService(t assert.TestingT, f ...ServiceConstructor) string {
|
||||
return d.CreateServiceWithOptions(t, types.ServiceCreateOptions{}, f...)
|
||||
}
|
||||
|
||||
// GetService returns the swarm service corresponding to the specified id
|
||||
func (d *Daemon) GetService(t assert.TestingT, id string) *swarm.Service {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
service, _, err := cli.ServiceInspectWithRaw(context.Background(), id, types.ServiceInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
return &service
|
||||
}
|
||||
|
||||
// GetServiceTasks returns the swarm tasks for the specified service
|
||||
func (d *Daemon) GetServiceTasks(t assert.TestingT, service string) []swarm.Task {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
filterArgs := filters.NewArgs()
|
||||
filterArgs.Add("desired-state", "running")
|
||||
filterArgs.Add("service", service)
|
||||
|
||||
options := types.TaskListOptions{
|
||||
Filters: filterArgs,
|
||||
}
|
||||
|
||||
tasks, err := cli.TaskList(context.Background(), options)
|
||||
assert.NilError(t, err)
|
||||
return tasks
|
||||
}
|
||||
|
||||
// UpdateService updates a swarm service with the specified service constructor
|
||||
func (d *Daemon) UpdateService(t assert.TestingT, service *swarm.Service, f ...ServiceConstructor) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
for _, fn := range f {
|
||||
fn(service)
|
||||
}
|
||||
|
||||
_, err := cli.ServiceUpdate(context.Background(), service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
// RemoveService removes the specified service
|
||||
func (d *Daemon) RemoveService(t assert.TestingT, id string) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
err := cli.ServiceRemove(context.Background(), id)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
// ListServices returns the list of the current swarm services
|
||||
func (d *Daemon) ListServices(t assert.TestingT) []swarm.Service {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
services, err := cli.ServiceList(context.Background(), types.ServiceListOptions{})
|
||||
assert.NilError(t, err)
|
||||
return services
|
||||
}
|
||||
|
||||
// GetTask returns the swarm task identified by the specified id
|
||||
func (d *Daemon) GetTask(t assert.TestingT, id string) swarm.Task {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
task, _, err := cli.TaskInspectWithRaw(context.Background(), id)
|
||||
assert.NilError(t, err)
|
||||
return task
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSwarmPort = 2477
|
||||
defaultSwarmListenAddr = "0.0.0.0"
|
||||
)
|
||||
|
||||
// SpecConstructor defines a swarm spec constructor
|
||||
type SpecConstructor func(*swarm.Spec)
|
||||
|
||||
// SwarmListenAddr returns the listen-addr used for the daemon
|
||||
func (d *Daemon) SwarmListenAddr() string {
|
||||
return fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort)
|
||||
}
|
||||
|
||||
// NodeID returns the swarm mode node ID
|
||||
func (d *Daemon) NodeID() string {
|
||||
return d.CachedInfo.Swarm.NodeID
|
||||
}
|
||||
|
||||
// SwarmInit initializes a new swarm cluster.
|
||||
func (d *Daemon) SwarmInit(t assert.TestingT, req swarm.InitRequest) {
|
||||
if req.ListenAddr == "" {
|
||||
req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort)
|
||||
}
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
_, err := cli.SwarmInit(context.Background(), req)
|
||||
assert.NilError(t, err, "initializing swarm")
|
||||
d.CachedInfo = d.Info(t)
|
||||
}
|
||||
|
||||
// SwarmJoin joins a daemon to an existing cluster.
|
||||
func (d *Daemon) SwarmJoin(t assert.TestingT, req swarm.JoinRequest) {
|
||||
if req.ListenAddr == "" {
|
||||
req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort)
|
||||
}
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
err := cli.SwarmJoin(context.Background(), req)
|
||||
assert.NilError(t, err, "initializing swarm")
|
||||
d.CachedInfo = d.Info(t)
|
||||
}
|
||||
|
||||
// SwarmLeave forces daemon to leave current cluster.
|
||||
func (d *Daemon) SwarmLeave(force bool) error {
|
||||
cli, err := d.NewClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("leaving swarm: failed to create client %v", err)
|
||||
}
|
||||
defer cli.Close()
|
||||
err = cli.SwarmLeave(context.Background(), force)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("leaving swarm: %v", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SwarmInfo returns the swarm information of the daemon
|
||||
func (d *Daemon) SwarmInfo(t assert.TestingT) swarm.Info {
|
||||
cli := d.NewClientT(t)
|
||||
info, err := cli.Info(context.Background())
|
||||
assert.NilError(t, err, "get swarm info")
|
||||
return info.Swarm
|
||||
}
|
||||
|
||||
// SwarmUnlock tries to unlock a locked swarm
|
||||
func (d *Daemon) SwarmUnlock(req swarm.UnlockRequest) error {
|
||||
cli, err := d.NewClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unlocking swarm: failed to create client %v", err)
|
||||
}
|
||||
defer cli.Close()
|
||||
err = cli.SwarmUnlock(context.Background(), req)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unlocking swarm")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetSwarm returns the current swarm object
|
||||
func (d *Daemon) GetSwarm(t assert.TestingT) swarm.Swarm {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
sw, err := cli.SwarmInspect(context.Background())
|
||||
assert.NilError(t, err)
|
||||
return sw
|
||||
}
|
||||
|
||||
// UpdateSwarm updates the current swarm object with the specified spec constructors
|
||||
func (d *Daemon) UpdateSwarm(t assert.TestingT, f ...SpecConstructor) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
sw := d.GetSwarm(t)
|
||||
for _, fn := range f {
|
||||
fn(&sw.Spec)
|
||||
}
|
||||
|
||||
err := cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, swarm.UpdateFlags{})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
// RotateTokens update the swarm to rotate tokens
|
||||
func (d *Daemon) RotateTokens(t assert.TestingT) {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
sw, err := cli.SwarmInspect(context.Background())
|
||||
assert.NilError(t, err)
|
||||
|
||||
flags := swarm.UpdateFlags{
|
||||
RotateManagerToken: true,
|
||||
RotateWorkerToken: true,
|
||||
}
|
||||
|
||||
err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, flags)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
// JoinTokens returns the current swarm join tokens
|
||||
func (d *Daemon) JoinTokens(t assert.TestingT) swarm.JoinTokens {
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
sw, err := cli.SwarmInspect(context.Background())
|
||||
assert.NilError(t, err)
|
||||
return sw.JoinTokens
|
||||
}
|
||||
+7
-3
@@ -1,11 +1,15 @@
|
||||
// +build linux,cgo,!libdm_no_deferred_remove
|
||||
// +build linux,cgo,!static_build
|
||||
// +build !libdm_dlsym_deferred_remove,!libdm_no_deferred_remove
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
// #include <libdevmapper.h>
|
||||
/*
|
||||
#include <libdevmapper.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// LibraryDeferredRemovalSupport tells if the feature is enabled in the build
|
||||
// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
||||
// current Docker invocation.
|
||||
const LibraryDeferredRemovalSupport = true
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
// +build linux,cgo,!static_build
|
||||
// +build libdm_dlsym_deferred_remove,!libdm_no_deferred_remove
|
||||
|
||||
package devicemapper
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -ldl
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <libdevmapper.h>
|
||||
|
||||
// Yes, I know this looks scary. In order to be able to fill our own internal
|
||||
// dm_info with deferred_remove we need to have a struct definition that is
|
||||
// correct (regardless of the version of libdm that was used to compile it). To
|
||||
// this end, we define struct_backport_dm_info. This code comes from lvm2, and
|
||||
// I have verified that the structure has only ever had elements *appended* to
|
||||
// it (since 2001).
|
||||
//
|
||||
// It is also important that this structure be _larger_ than the dm_info that
|
||||
// libdevmapper expected. Otherwise libdm might try to write to memory it
|
||||
// shouldn't (they don't have a "known size" API).
|
||||
struct backport_dm_info {
|
||||
int exists;
|
||||
int suspended;
|
||||
int live_table;
|
||||
int inactive_table;
|
||||
int32_t open_count;
|
||||
uint32_t event_nr;
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
int read_only;
|
||||
|
||||
int32_t target_count;
|
||||
|
||||
int deferred_remove;
|
||||
int internal_suspend;
|
||||
|
||||
// Padding, purely for our own safety. This is to avoid cases where libdm
|
||||
// was updated underneath us and we call into dm_task_get_info() with too
|
||||
// small of a buffer.
|
||||
char _[512];
|
||||
};
|
||||
|
||||
// We have to wrap this in CGo, because Go really doesn't like function pointers.
|
||||
int call_dm_task_deferred_remove(void *fn, struct dm_task *task)
|
||||
{
|
||||
int (*_dm_task_deferred_remove)(struct dm_task *task) = fn;
|
||||
return _dm_task_deferred_remove(task);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// dm_task_deferred_remove is not supported by all distributions, due to
|
||||
// out-dated versions of devicemapper. However, in the case where the
|
||||
// devicemapper library was updated without rebuilding Docker (which can happen
|
||||
// in some distributions) then we should attempt to dynamically load the
|
||||
// relevant object rather than try to link to it.
|
||||
|
||||
// dmTaskDeferredRemoveFct is a "bound" version of dm_task_deferred_remove.
|
||||
// It is nil if dm_task_deferred_remove was not found in the libdevmapper that
|
||||
// is currently loaded.
|
||||
var dmTaskDeferredRemovePtr unsafe.Pointer
|
||||
|
||||
// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
||||
// current Docker invocation. This value is fixed during init.
|
||||
var LibraryDeferredRemovalSupport bool
|
||||
|
||||
func init() {
|
||||
// Clear any errors.
|
||||
var err *C.char
|
||||
C.dlerror()
|
||||
|
||||
// The symbol we want to fetch.
|
||||
symName := C.CString("dm_task_deferred_remove")
|
||||
defer C.free(unsafe.Pointer(symName))
|
||||
|
||||
// See if we can find dm_task_deferred_remove. Since we already are linked
|
||||
// to libdevmapper, we can search our own address space (rather than trying
|
||||
// to guess what libdevmapper is called). We use NULL here, as RTLD_DEFAULT
|
||||
// is not available in CGO (even if you set _GNU_SOURCE for some reason).
|
||||
// The semantics are identical on glibc.
|
||||
sym := C.dlsym(nil, symName)
|
||||
err = C.dlerror()
|
||||
if err != nil {
|
||||
logrus.Debugf("devmapper: could not load dm_task_deferred_remove: %s", C.GoString(err))
|
||||
return
|
||||
}
|
||||
|
||||
logrus.Debugf("devmapper: found dm_task_deferred_remove at %x", uintptr(sym))
|
||||
dmTaskDeferredRemovePtr = sym
|
||||
LibraryDeferredRemovalSupport = true
|
||||
}
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||
sym := dmTaskDeferredRemovePtr
|
||||
if sym == nil || !LibraryDeferredRemovalSupport {
|
||||
return -1
|
||||
}
|
||||
return int(C.call_dm_task_deferred_remove(sym, (*C.struct_dm_task)(task)))
|
||||
}
|
||||
|
||||
func dmTaskGetInfoWithDeferredFct(task *cdmTask, info *Info) int {
|
||||
if !LibraryDeferredRemovalSupport {
|
||||
return -1
|
||||
}
|
||||
|
||||
Cinfo := C.struct_backport_dm_info{}
|
||||
defer func() {
|
||||
info.Exists = int(Cinfo.exists)
|
||||
info.Suspended = int(Cinfo.suspended)
|
||||
info.LiveTable = int(Cinfo.live_table)
|
||||
info.InactiveTable = int(Cinfo.inactive_table)
|
||||
info.OpenCount = int32(Cinfo.open_count)
|
||||
info.EventNr = uint32(Cinfo.event_nr)
|
||||
info.Major = uint32(Cinfo.major)
|
||||
info.Minor = uint32(Cinfo.minor)
|
||||
info.ReadOnly = int(Cinfo.read_only)
|
||||
info.TargetCount = int32(Cinfo.target_count)
|
||||
info.DeferredRemove = int(Cinfo.deferred_remove)
|
||||
}()
|
||||
return int(C.dm_task_get_info((*C.struct_dm_task)(task), (*C.struct_dm_info)(unsafe.Pointer(&Cinfo))))
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
// +build linux,cgo,libdm_no_deferred_remove
|
||||
// +build linux,cgo
|
||||
// +build !libdm_dlsym_deferred_remove,libdm_no_deferred_remove
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
// LibraryDeferredRemovalSupport tells if the feature is enabled in the build
|
||||
// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
||||
// current Docker invocation.
|
||||
const LibraryDeferredRemovalSupport = false
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||
|
||||
Reference in New Issue
Block a user