Files
docker-cli/components/engine/integration-cli/docker_cli_daemon_test.go
Jana Radhakrishnan 2ee4e129d1 libnetwork: Add garbage collection trigger
When the daemon is going down trigger immediate
garbage collection of libnetwork resources deleted
like namespace path since there will be no way to
remove them when the daemon restarts.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
Upstream-commit: c68e7f96f9636a9b2ab0c2c0dbf753161fa73fc2
Component: engine
2015-06-07 21:33:51 -07:00

1224 lines
39 KiB
Go

// +build daemon
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"github.com/docker/libnetwork/iptables"
"github.com/docker/libtrust"
"github.com/go-check/check"
)
func (s *DockerDaemonSuite) TestDaemonRestartWithRunningContainersPorts(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatalf("Could not start daemon with busybox: %v", err)
}
if out, err := s.d.Cmd("run", "-d", "--name", "top1", "-p", "1234:80", "--restart", "always", "busybox:latest", "top"); err != nil {
c.Fatalf("Could not run top1: err=%v\n%s", err, out)
}
// --restart=no by default
if out, err := s.d.Cmd("run", "-d", "--name", "top2", "-p", "80", "busybox:latest", "top"); err != nil {
c.Fatalf("Could not run top2: err=%v\n%s", err, out)
}
testRun := func(m map[string]bool, prefix string) {
var format string
for cont, shouldRun := range m {
out, err := s.d.Cmd("ps")
if err != nil {
c.Fatalf("Could not run ps: err=%v\n%q", err, out)
}
if shouldRun {
format = "%scontainer %q is not running"
} else {
format = "%scontainer %q is running"
}
if shouldRun != strings.Contains(out, cont) {
c.Fatalf(format, prefix, cont)
}
}
}
testRun(map[string]bool{"top1": true, "top2": true}, "")
if err := s.d.Restart(); err != nil {
c.Fatalf("Could not restart daemon: %v", err)
}
testRun(map[string]bool{"top1": true, "top2": false}, "After daemon restart: ")
}
func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatal(err)
}
if out, err := s.d.Cmd("run", "-d", "--name", "volrestarttest1", "-v", "/foo", "busybox"); err != nil {
c.Fatal(err, out)
}
if err := s.d.Restart(); err != nil {
c.Fatal(err)
}
if _, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil {
c.Fatal(err)
}
if out, err := s.d.Cmd("rm", "-fv", "volrestarttest2"); err != nil {
c.Fatal(err, out)
}
v, err := s.d.Cmd("inspect", "--format", "{{ json .Volumes }}", "volrestarttest1")
if err != nil {
c.Fatal(err)
}
volumes := make(map[string]string)
json.Unmarshal([]byte(v), &volumes)
if _, err := os.Stat(volumes["/foo"]); err != nil {
c.Fatalf("Expected volume to exist: %s - %s", volumes["/foo"], err)
}
}
func (s *DockerDaemonSuite) TestDaemonStartIptablesFalse(c *check.C) {
if err := s.d.Start("--iptables=false"); err != nil {
c.Fatalf("we should have been able to start the daemon with passing iptables=false: %v", err)
}
}
// Issue #8444: If docker0 bridge is modified (intentionally or unintentionally) and
// no longer has an IP associated, we should gracefully handle that case and associate
// an IP with it rather than fail daemon start
func (s *DockerDaemonSuite) TestDaemonStartBridgeWithoutIPAssociation(c *check.C) {
// rather than depending on brctl commands to verify docker0 is created and up
// let's start the daemon and stop it, and then make a modification to run the
// actual test
if err := s.d.Start(); err != nil {
c.Fatalf("Could not start daemon: %v", err)
}
if err := s.d.Stop(); err != nil {
c.Fatalf("Could not stop daemon: %v", err)
}
// now we will remove the ip from docker0 and then try starting the daemon
ipCmd := exec.Command("ip", "addr", "flush", "dev", "docker0")
stdout, stderr, _, err := runCommandWithStdoutStderr(ipCmd)
if err != nil {
c.Fatalf("failed to remove docker0 IP association: %v, stdout: %q, stderr: %q", err, stdout, stderr)
}
if err := s.d.Start(); err != nil {
warning := "**WARNING: Docker bridge network in bad state--delete docker0 bridge interface to fix"
c.Fatalf("Could not start daemon when docker0 has no IP address: %v\n%s", err, warning)
}
}
func (s *DockerDaemonSuite) TestDaemonIptablesClean(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatalf("Could not start daemon with busybox: %v", err)
}
if out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil {
c.Fatalf("Could not run top: %s, %v", out, err)
}
// get output from iptables with container running
ipTablesSearchString := "tcp dpt:80"
ipTablesCmd := exec.Command("iptables", "-nvL")
out, _, err := runCommandWithOutput(ipTablesCmd)
if err != nil {
c.Fatalf("Could not run iptables -nvL: %s, %v", out, err)
}
if !strings.Contains(out, ipTablesSearchString) {
c.Fatalf("iptables output should have contained %q, but was %q", ipTablesSearchString, out)
}
if err := s.d.Stop(); err != nil {
c.Fatalf("Could not stop daemon: %v", err)
}
// get output from iptables after restart
ipTablesCmd = exec.Command("iptables", "-nvL")
out, _, err = runCommandWithOutput(ipTablesCmd)
if err != nil {
c.Fatalf("Could not run iptables -nvL: %s, %v", out, err)
}
if strings.Contains(out, ipTablesSearchString) {
c.Fatalf("iptables output should not have contained %q, but was %q", ipTablesSearchString, out)
}
}
func (s *DockerDaemonSuite) TestDaemonIptablesCreate(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatalf("Could not start daemon with busybox: %v", err)
}
if out, err := s.d.Cmd("run", "-d", "--name", "top", "--restart=always", "-p", "80", "busybox:latest", "top"); err != nil {
c.Fatalf("Could not run top: %s, %v", out, err)
}
// get output from iptables with container running
ipTablesSearchString := "tcp dpt:80"
ipTablesCmd := exec.Command("iptables", "-nvL")
out, _, err := runCommandWithOutput(ipTablesCmd)
if err != nil {
c.Fatalf("Could not run iptables -nvL: %s, %v", out, err)
}
if !strings.Contains(out, ipTablesSearchString) {
c.Fatalf("iptables output should have contained %q, but was %q", ipTablesSearchString, out)
}
if err := s.d.Restart(); err != nil {
c.Fatalf("Could not restart daemon: %v", err)
}
// make sure the container is not running
runningOut, err := s.d.Cmd("inspect", "--format='{{.State.Running}}'", "top")
if err != nil {
c.Fatalf("Could not inspect on container: %s, %v", out, err)
}
if strings.TrimSpace(runningOut) != "true" {
c.Fatalf("Container should have been restarted after daemon restart. Status running should have been true but was: %q", strings.TrimSpace(runningOut))
}
// get output from iptables after restart
ipTablesCmd = exec.Command("iptables", "-nvL")
out, _, err = runCommandWithOutput(ipTablesCmd)
if err != nil {
c.Fatalf("Could not run iptables -nvL: %s, %v", out, err)
}
if !strings.Contains(out, ipTablesSearchString) {
c.Fatalf("iptables output after restart should have contained %q, but was %q", ipTablesSearchString, out)
}
}
func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) {
c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level"))
}
func (s *DockerDaemonSuite) TestDaemonLogLevelDebug(c *check.C) {
if err := s.d.Start("--log-level=debug"); err != nil {
c.Fatal(err)
}
content, _ := ioutil.ReadFile(s.d.logFile.Name())
if !strings.Contains(string(content), `level=debug`) {
c.Fatalf(`Missing level="debug" in log file:\n%s`, string(content))
}
}
func (s *DockerDaemonSuite) TestDaemonLogLevelFatal(c *check.C) {
// we creating new daemons to create new logFile
if err := s.d.Start("--log-level=fatal"); err != nil {
c.Fatal(err)
}
content, _ := ioutil.ReadFile(s.d.logFile.Name())
if strings.Contains(string(content), `level=debug`) {
c.Fatalf(`Should not have level="debug" in log file:\n%s`, string(content))
}
}
func (s *DockerDaemonSuite) TestDaemonFlagD(c *check.C) {
if err := s.d.Start("-D"); err != nil {
c.Fatal(err)
}
content, _ := ioutil.ReadFile(s.d.logFile.Name())
if !strings.Contains(string(content), `level=debug`) {
c.Fatalf(`Should have level="debug" in log file using -D:\n%s`, string(content))
}
}
func (s *DockerDaemonSuite) TestDaemonFlagDebug(c *check.C) {
if err := s.d.Start("--debug"); err != nil {
c.Fatal(err)
}
content, _ := ioutil.ReadFile(s.d.logFile.Name())
if !strings.Contains(string(content), `level=debug`) {
c.Fatalf(`Should have level="debug" in log file using --debug:\n%s`, string(content))
}
}
func (s *DockerDaemonSuite) TestDaemonFlagDebugLogLevelFatal(c *check.C) {
if err := s.d.Start("--debug", "--log-level=fatal"); err != nil {
c.Fatal(err)
}
content, _ := ioutil.ReadFile(s.d.logFile.Name())
if !strings.Contains(string(content), `level=debug`) {
c.Fatalf(`Should have level="debug" in log file when using both --debug and --log-level=fatal:\n%s`, string(content))
}
}
func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *check.C) {
listeningPorts := [][]string{
{"0.0.0.0", "0.0.0.0", "5678"},
{"127.0.0.1", "127.0.0.1", "1234"},
{"localhost", "127.0.0.1", "1235"},
}
cmdArgs := []string{}
for _, hostDirective := range listeningPorts {
cmdArgs = append(cmdArgs, "--host", fmt.Sprintf("tcp://%s:%s", hostDirective[0], hostDirective[2]))
}
if err := s.d.StartWithBusybox(cmdArgs...); err != nil {
c.Fatalf("Could not start daemon with busybox: %v", err)
}
for _, hostDirective := range listeningPorts {
output, err := s.d.Cmd("run", "-p", fmt.Sprintf("%s:%s:80", hostDirective[1], hostDirective[2]), "busybox", "true")
if err == nil {
c.Fatalf("Container should not start, expected port already allocated error: %q", output)
} else if !strings.Contains(output, "port is already allocated") {
c.Fatalf("Expected port is already allocated error: %q", output)
}
}
}
func (s *DockerDaemonSuite) TestDaemonKeyGeneration(c *check.C) {
// TODO: skip or update for Windows daemon
os.Remove("/etc/docker/key.json")
if err := s.d.Start(); err != nil {
c.Fatalf("Could not start daemon: %v", err)
}
s.d.Stop()
k, err := libtrust.LoadKeyFile("/etc/docker/key.json")
if err != nil {
c.Fatalf("Error opening key file")
}
kid := k.KeyID()
// Test Key ID is a valid fingerprint (e.g. QQXN:JY5W:TBXI:MK3X:GX6P:PD5D:F56N:NHCS:LVRZ:JA46:R24J:XEFF)
if len(kid) != 59 {
c.Fatalf("Bad key ID: %s", kid)
}
}
func (s *DockerDaemonSuite) TestDaemonKeyMigration(c *check.C) {
// TODO: skip or update for Windows daemon
os.Remove("/etc/docker/key.json")
k1, err := libtrust.GenerateECP256PrivateKey()
if err != nil {
c.Fatalf("Error generating private key: %s", err)
}
if err := os.MkdirAll(filepath.Join(os.Getenv("HOME"), ".docker"), 0755); err != nil {
c.Fatalf("Error creating .docker directory: %s", err)
}
if err := libtrust.SaveKey(filepath.Join(os.Getenv("HOME"), ".docker", "key.json"), k1); err != nil {
c.Fatalf("Error saving private key: %s", err)
}
if err := s.d.Start(); err != nil {
c.Fatalf("Could not start daemon: %v", err)
}
s.d.Stop()
k2, err := libtrust.LoadKeyFile("/etc/docker/key.json")
if err != nil {
c.Fatalf("Error opening key file")
}
if k1.KeyID() != k2.KeyID() {
c.Fatalf("Key not migrated")
}
}
// GH#11320 - verify that the daemon exits on failure properly
// Note that this explicitly tests the conflict of {-b,--bridge} and {--bip} options as the means
// to get a daemon init failure; no other tests for -b/--bip conflict are therefore required
func (s *DockerDaemonSuite) TestDaemonExitOnFailure(c *check.C) {
//attempt to start daemon with incorrect flags (we know -b and --bip conflict)
if err := s.d.Start("--bridge", "nosuchbridge", "--bip", "1.1.1.1"); err != nil {
//verify we got the right error
if !strings.Contains(err.Error(), "Daemon exited and never started") {
c.Fatalf("Expected daemon not to start, got %v", err)
}
// look in the log and make sure we got the message that daemon is shutting down
runCmd := exec.Command("grep", "Error starting daemon", s.d.LogfileName())
if out, _, err := runCommandWithOutput(runCmd); err != nil {
c.Fatalf("Expected 'Error starting daemon' message; but doesn't exist in log: %q, err: %v", out, err)
}
} else {
//if we didn't get an error and the daemon is running, this is a failure
c.Fatal("Conflicting options should cause the daemon to error out with a failure")
}
}
func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) {
d := s.d
err := d.Start("--bridge", "nosuchbridge")
c.Assert(err, check.NotNil, check.Commentf("--bridge option with an invalid bridge should cause the daemon to fail"))
defer d.Restart()
bridgeName := "external-bridge"
bridgeIp := "192.169.1.1/24"
_, bridgeIPNet, _ := net.ParseCIDR(bridgeIp)
out, err := createInterface(c, "bridge", bridgeName, bridgeIp)
c.Assert(err, check.IsNil, check.Commentf(out))
defer deleteInterface(c, bridgeName)
err = d.StartWithBusybox("--bridge", bridgeName)
c.Assert(err, check.IsNil)
ipTablesSearchString := bridgeIPNet.String()
ipTablesCmd := exec.Command("iptables", "-t", "nat", "-nvL")
out, _, err = runCommandWithOutput(ipTablesCmd)
c.Assert(err, check.IsNil)
c.Assert(strings.Contains(out, ipTablesSearchString), check.Equals, true,
check.Commentf("iptables output should have contained %q, but was %q",
ipTablesSearchString, out))
_, err = d.Cmd("run", "-d", "--name", "ExtContainer", "busybox", "top")
c.Assert(err, check.IsNil)
containerIp := d.findContainerIP("ExtContainer")
ip := net.ParseIP(containerIp)
c.Assert(bridgeIPNet.Contains(ip), check.Equals, true,
check.Commentf("Container IP-Address must be in the same subnet range : %s",
containerIp))
}
func createInterface(c *check.C, ifType string, ifName string, ipNet string) (string, error) {
args := []string{"link", "add", "name", ifName, "type", ifType}
ipLinkCmd := exec.Command("ip", args...)
out, _, err := runCommandWithOutput(ipLinkCmd)
if err != nil {
return out, err
}
ifCfgCmd := exec.Command("ifconfig", ifName, ipNet, "up")
out, _, err = runCommandWithOutput(ifCfgCmd)
return out, err
}
func deleteInterface(c *check.C, ifName string) {
ifCmd := exec.Command("ip", "link", "delete", ifName)
out, _, err := runCommandWithOutput(ifCmd)
c.Assert(err, check.IsNil, check.Commentf(out))
flushCmd := exec.Command("iptables", "-t", "nat", "--flush")
out, _, err = runCommandWithOutput(flushCmd)
c.Assert(err, check.IsNil, check.Commentf(out))
flushCmd = exec.Command("iptables", "--flush")
out, _, err = runCommandWithOutput(flushCmd)
c.Assert(err, check.IsNil, check.Commentf(out))
}
func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) {
// TestDaemonBridgeIP Steps
// 1. Delete the existing docker0 Bridge
// 2. Set --bip daemon configuration and start the new Docker Daemon
// 3. Check if the bip config has taken effect using ifconfig and iptables commands
// 4. Launch a Container and make sure the IP-Address is in the expected subnet
// 5. Delete the docker0 Bridge
// 6. Restart the Docker Daemon (via defered action)
// This Restart takes care of bringing docker0 interface back to auto-assigned IP
defaultNetworkBridge := "docker0"
deleteInterface(c, defaultNetworkBridge)
d := s.d
bridgeIp := "192.169.1.1/24"
ip, bridgeIPNet, _ := net.ParseCIDR(bridgeIp)
err := d.StartWithBusybox("--bip", bridgeIp)
c.Assert(err, check.IsNil)
defer d.Restart()
ifconfigSearchString := ip.String()
ifconfigCmd := exec.Command("ifconfig", defaultNetworkBridge)
out, _, _, err := runCommandWithStdoutStderr(ifconfigCmd)
c.Assert(err, check.IsNil)
c.Assert(strings.Contains(out, ifconfigSearchString), check.Equals, true,
check.Commentf("ifconfig output should have contained %q, but was %q",
ifconfigSearchString, out))
ipTablesSearchString := bridgeIPNet.String()
ipTablesCmd := exec.Command("iptables", "-t", "nat", "-nvL")
out, _, err = runCommandWithOutput(ipTablesCmd)
c.Assert(err, check.IsNil)
c.Assert(strings.Contains(out, ipTablesSearchString), check.Equals, true,
check.Commentf("iptables output should have contained %q, but was %q",
ipTablesSearchString, out))
out, err = d.Cmd("run", "-d", "--name", "test", "busybox", "top")
c.Assert(err, check.IsNil)
containerIp := d.findContainerIP("test")
ip = net.ParseIP(containerIp)
c.Assert(bridgeIPNet.Contains(ip), check.Equals, true,
check.Commentf("Container IP-Address must be in the same subnet range : %s",
containerIp))
deleteInterface(c, defaultNetworkBridge)
}
func (s *DockerDaemonSuite) TestDaemonRestartWithBridgeIPChange(c *check.C) {
if err := s.d.Start(); err != nil {
c.Fatalf("Could not start daemon: %v", err)
}
defer s.d.Restart()
if err := s.d.Stop(); err != nil {
c.Fatalf("Could not stop daemon: %v", err)
}
// now we will change the docker0's IP and then try starting the daemon
bridgeIP := "192.169.100.1/24"
_, bridgeIPNet, _ := net.ParseCIDR(bridgeIP)
ipCmd := exec.Command("ifconfig", "docker0", bridgeIP)
stdout, stderr, _, err := runCommandWithStdoutStderr(ipCmd)
if err != nil {
c.Fatalf("failed to change docker0's IP association: %v, stdout: %q, stderr: %q", err, stdout, stderr)
}
if err := s.d.Start("--bip", bridgeIP); err != nil {
c.Fatalf("Could not start daemon: %v", err)
}
//check if the iptables contains new bridgeIP MASQUERADE rule
ipTablesSearchString := bridgeIPNet.String()
ipTablesCmd := exec.Command("iptables", "-t", "nat", "-nvL")
out, _, err := runCommandWithOutput(ipTablesCmd)
if err != nil {
c.Fatalf("Could not run iptables -nvL: %s, %v", out, err)
}
if !strings.Contains(out, ipTablesSearchString) {
c.Fatalf("iptables output should have contained new MASQUERADE rule with IP %q, but was %q", ipTablesSearchString, out)
}
}
func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) {
d := s.d
bridgeName := "external-bridge"
bridgeIp := "192.169.1.1/24"
out, err := createInterface(c, "bridge", bridgeName, bridgeIp)
c.Assert(err, check.IsNil, check.Commentf(out))
defer deleteInterface(c, bridgeName)
args := []string{"--bridge", bridgeName, "--fixed-cidr", "192.169.1.0/30"}
err = d.StartWithBusybox(args...)
c.Assert(err, check.IsNil)
defer d.Restart()
for i := 0; i < 4; i++ {
cName := "Container" + strconv.Itoa(i)
out, err := d.Cmd("run", "-d", "--name", cName, "busybox", "top")
if err != nil {
c.Assert(strings.Contains(out, "no available ip addresses"), check.Equals, true,
check.Commentf("Could not run a Container : %s %s", err.Error(), out))
}
}
}
func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) {
d := s.d
ipStr := "192.170.1.1/24"
ip, _, _ := net.ParseCIDR(ipStr)
args := []string{"--ip", ip.String()}
err := d.StartWithBusybox(args...)
c.Assert(err, check.IsNil)
defer d.Restart()
out, err := d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top")
c.Assert(err, check.NotNil,
check.Commentf("Running a container must fail with an invalid --ip option"))
c.Assert(strings.Contains(out, "Error starting userland proxy"), check.Equals, true)
ifName := "dummy"
out, err = createInterface(c, "dummy", ifName, ipStr)
c.Assert(err, check.IsNil, check.Commentf(out))
defer deleteInterface(c, ifName)
_, err = d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top")
c.Assert(err, check.IsNil)
ipTablesCmd := exec.Command("iptables", "-t", "nat", "-nvL")
out, _, err = runCommandWithOutput(ipTablesCmd)
c.Assert(err, check.IsNil)
regex := fmt.Sprintf("DNAT.*%s.*dpt:8000", ip.String())
matched, _ := regexp.MatchString(regex, out)
c.Assert(matched, check.Equals, true,
check.Commentf("iptables output should have contained %q, but was %q", regex, out))
}
func (s *DockerDaemonSuite) TestDaemonICCPing(c *check.C) {
d := s.d
bridgeName := "external-bridge"
bridgeIp := "192.169.1.1/24"
out, err := createInterface(c, "bridge", bridgeName, bridgeIp)
c.Assert(err, check.IsNil, check.Commentf(out))
defer deleteInterface(c, bridgeName)
args := []string{"--bridge", bridgeName, "--icc=false"}
err = d.StartWithBusybox(args...)
c.Assert(err, check.IsNil)
defer d.Restart()
ipTablesCmd := exec.Command("iptables", "-nvL", "FORWARD")
out, _, err = runCommandWithOutput(ipTablesCmd)
c.Assert(err, check.IsNil)
regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName)
matched, _ := regexp.MatchString(regex, out)
c.Assert(matched, check.Equals, true,
check.Commentf("iptables output should have contained %q, but was %q", regex, out))
// Pinging another container must fail with --icc=false
pingContainers(c, d, true)
ipStr := "192.171.1.1/24"
ip, _, _ := net.ParseCIDR(ipStr)
ifName := "icc-dummy"
createInterface(c, "dummy", ifName, ipStr)
// But, Pinging external or a Host interface must succeed
pingCmd := fmt.Sprintf("ping -c 1 %s -W 1", ip.String())
runArgs := []string{"--rm", "busybox", "sh", "-c", pingCmd}
_, err = d.Cmd("run", runArgs...)
c.Assert(err, check.IsNil)
}
func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) {
d := s.d
bridgeName := "external-bridge"
bridgeIp := "192.169.1.1/24"
out, err := createInterface(c, "bridge", bridgeName, bridgeIp)
c.Assert(err, check.IsNil, check.Commentf(out))
defer deleteInterface(c, bridgeName)
args := []string{"--bridge", bridgeName, "--icc=false"}
err = d.StartWithBusybox(args...)
c.Assert(err, check.IsNil)
defer d.Restart()
ipTablesCmd := exec.Command("iptables", "-nvL", "FORWARD")
out, _, err = runCommandWithOutput(ipTablesCmd)
c.Assert(err, check.IsNil)
regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName)
matched, _ := regexp.MatchString(regex, out)
c.Assert(matched, check.Equals, true,
check.Commentf("iptables output should have contained %q, but was %q", regex, out))
out, err = d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567")
c.Assert(err, check.IsNil, check.Commentf(out))
out, err = d.Cmd("run", "--link", "icc1:icc1", "busybox", "nc", "icc1", "4567")
c.Assert(err, check.IsNil, check.Commentf(out))
}
func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *check.C) {
bridgeName := "external-bridge"
bridgeIp := "192.169.1.1/24"
out, err := createInterface(c, "bridge", bridgeName, bridgeIp)
c.Assert(err, check.IsNil, check.Commentf(out))
defer deleteInterface(c, bridgeName)
args := []string{"--bridge", bridgeName, "--icc=false"}
err = s.d.StartWithBusybox(args...)
c.Assert(err, check.IsNil)
defer s.d.Restart()
_, err = s.d.Cmd("run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top")
c.Assert(err, check.IsNil)
_, err = s.d.Cmd("run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top")
c.Assert(err, check.IsNil)
childIP := s.d.findContainerIP("child")
parentIP := s.d.findContainerIP("parent")
sourceRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
destinationRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
if !iptables.Exists("filter", "DOCKER", sourceRule...) || !iptables.Exists("filter", "DOCKER", destinationRule...) {
c.Fatal("Iptables rules not found")
}
s.d.Cmd("rm", "--link", "parent/http")
if iptables.Exists("filter", "DOCKER", sourceRule...) || iptables.Exists("filter", "DOCKER", destinationRule...) {
c.Fatal("Iptables rules should be removed when unlink")
}
s.d.Cmd("kill", "child")
s.d.Cmd("kill", "parent")
}
func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) {
testRequires(c, NativeExecDriver)
if err := s.d.StartWithBusybox("--default-ulimit", "nofile=42:42", "--default-ulimit", "nproc=1024:1024"); err != nil {
c.Fatal(err)
}
out, err := s.d.Cmd("run", "--ulimit", "nproc=2048", "--name=test", "busybox", "/bin/sh", "-c", "echo $(ulimit -n); echo $(ulimit -p)")
if err != nil {
c.Fatal(out, err)
}
outArr := strings.Split(out, "\n")
if len(outArr) < 2 {
c.Fatalf("got unexpected output: %s", out)
}
nofile := strings.TrimSpace(outArr[0])
nproc := strings.TrimSpace(outArr[1])
if nofile != "42" {
c.Fatalf("expected `ulimit -n` to be `42`, got: %s", nofile)
}
if nproc != "2048" {
c.Fatalf("exepcted `ulimit -p` to be 2048, got: %s", nproc)
}
// Now restart daemon with a new default
if err := s.d.Restart("--default-ulimit", "nofile=43"); err != nil {
c.Fatal(err)
}
out, err = s.d.Cmd("start", "-a", "test")
if err != nil {
c.Fatal(err)
}
outArr = strings.Split(out, "\n")
if len(outArr) < 2 {
c.Fatalf("got unexpected output: %s", out)
}
nofile = strings.TrimSpace(outArr[0])
nproc = strings.TrimSpace(outArr[1])
if nofile != "43" {
c.Fatalf("expected `ulimit -n` to be `43`, got: %s", nofile)
}
if nproc != "2048" {
c.Fatalf("exepcted `ulimit -p` to be 2048, got: %s", nproc)
}
}
// #11315
func (s *DockerDaemonSuite) TestDaemonRestartRenameContainer(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatal(err)
}
if out, err := s.d.Cmd("run", "--name=test", "busybox"); err != nil {
c.Fatal(err, out)
}
if out, err := s.d.Cmd("rename", "test", "test2"); err != nil {
c.Fatal(err, out)
}
if err := s.d.Restart(); err != nil {
c.Fatal(err)
}
if out, err := s.d.Cmd("start", "test2"); err != nil {
c.Fatal(err, out)
}
}
func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefault(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatal(err)
}
out, err := s.d.Cmd("run", "-d", "busybox", "echo", "testline")
if err != nil {
c.Fatal(out, err)
}
id := strings.TrimSpace(out)
if out, err := s.d.Cmd("wait", id); err != nil {
c.Fatal(out, err)
}
logPath := filepath.Join(s.d.folder, "graph", "containers", id, id+"-json.log")
if _, err := os.Stat(logPath); err != nil {
c.Fatal(err)
}
f, err := os.Open(logPath)
if err != nil {
c.Fatal(err)
}
var res struct {
Log string `json:"log"`
Stream string `json:"stream"`
Time time.Time `json:"time"`
}
if err := json.NewDecoder(f).Decode(&res); err != nil {
c.Fatal(err)
}
if res.Log != "testline\n" {
c.Fatalf("Unexpected log line: %q, expected: %q", res.Log, "testline\n")
}
if res.Stream != "stdout" {
c.Fatalf("Unexpected stream: %q, expected: %q", res.Stream, "stdout")
}
if !time.Now().After(res.Time) {
c.Fatalf("Log time %v in future", res.Time)
}
}
func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefaultOverride(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatal(err)
}
out, err := s.d.Cmd("run", "-d", "--log-driver=none", "busybox", "echo", "testline")
if err != nil {
c.Fatal(out, err)
}
id := strings.TrimSpace(out)
if out, err := s.d.Cmd("wait", id); err != nil {
c.Fatal(out, err)
}
logPath := filepath.Join(s.d.folder, "graph", "containers", id, id+"-json.log")
if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) {
c.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err)
}
}
func (s *DockerDaemonSuite) TestDaemonLoggingDriverNone(c *check.C) {
if err := s.d.StartWithBusybox("--log-driver=none"); err != nil {
c.Fatal(err)
}
out, err := s.d.Cmd("run", "-d", "busybox", "echo", "testline")
if err != nil {
c.Fatal(out, err)
}
id := strings.TrimSpace(out)
if out, err := s.d.Cmd("wait", id); err != nil {
c.Fatal(out, err)
}
logPath := filepath.Join(s.d.folder, "graph", "containers", id, id+"-json.log")
if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) {
c.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err)
}
}
func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneOverride(c *check.C) {
if err := s.d.StartWithBusybox("--log-driver=none"); err != nil {
c.Fatal(err)
}
out, err := s.d.Cmd("run", "-d", "--log-driver=json-file", "busybox", "echo", "testline")
if err != nil {
c.Fatal(out, err)
}
id := strings.TrimSpace(out)
if out, err := s.d.Cmd("wait", id); err != nil {
c.Fatal(out, err)
}
logPath := filepath.Join(s.d.folder, "graph", "containers", id, id+"-json.log")
if _, err := os.Stat(logPath); err != nil {
c.Fatal(err)
}
f, err := os.Open(logPath)
if err != nil {
c.Fatal(err)
}
var res struct {
Log string `json:"log"`
Stream string `json:"stream"`
Time time.Time `json:"time"`
}
if err := json.NewDecoder(f).Decode(&res); err != nil {
c.Fatal(err)
}
if res.Log != "testline\n" {
c.Fatalf("Unexpected log line: %q, expected: %q", res.Log, "testline\n")
}
if res.Stream != "stdout" {
c.Fatalf("Unexpected stream: %q, expected: %q", res.Stream, "stdout")
}
if !time.Now().After(res.Time) {
c.Fatalf("Log time %v in future", res.Time)
}
}
func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneLogsError(c *check.C) {
if err := s.d.StartWithBusybox("--log-driver=none"); err != nil {
c.Fatal(err)
}
out, err := s.d.Cmd("run", "-d", "busybox", "echo", "testline")
if err != nil {
c.Fatal(out, err)
}
id := strings.TrimSpace(out)
out, err = s.d.Cmd("logs", id)
if err == nil {
c.Fatalf("Logs should fail with \"none\" driver")
}
if !strings.Contains(out, `"logs" command is supported only for "json-file" logging driver`) {
c.Fatalf("There should be error about non-json-file driver, got: %s", out)
}
}
func (s *DockerDaemonSuite) TestDaemonDots(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatal(err)
}
// Now create 4 containers
if _, err := s.d.Cmd("create", "busybox"); err != nil {
c.Fatalf("Error creating container: %q", err)
}
if _, err := s.d.Cmd("create", "busybox"); err != nil {
c.Fatalf("Error creating container: %q", err)
}
if _, err := s.d.Cmd("create", "busybox"); err != nil {
c.Fatalf("Error creating container: %q", err)
}
if _, err := s.d.Cmd("create", "busybox"); err != nil {
c.Fatalf("Error creating container: %q", err)
}
s.d.Stop()
s.d.Start("--log-level=debug")
s.d.Stop()
content, _ := ioutil.ReadFile(s.d.logFile.Name())
if strings.Contains(string(content), "....") {
c.Fatalf("Debug level should not have ....\n%s", string(content))
}
s.d.Start("--log-level=error")
s.d.Stop()
content, _ = ioutil.ReadFile(s.d.logFile.Name())
if strings.Contains(string(content), "....") {
c.Fatalf("Error level should not have ....\n%s", string(content))
}
s.d.Start("--log-level=info")
s.d.Stop()
content, _ = ioutil.ReadFile(s.d.logFile.Name())
if !strings.Contains(string(content), "....") {
c.Fatalf("Info level should have ....\n%s", string(content))
}
}
func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *check.C) {
dir, err := ioutil.TempDir("", "socket-cleanup-test")
if err != nil {
c.Fatal(err)
}
defer os.RemoveAll(dir)
sockPath := filepath.Join(dir, "docker.sock")
if err := s.d.Start("--host", "unix://"+sockPath); err != nil {
c.Fatal(err)
}
if _, err := os.Stat(sockPath); err != nil {
c.Fatal("socket does not exist")
}
if err := s.d.Stop(); err != nil {
c.Fatal(err)
}
if _, err := os.Stat(sockPath); err == nil || !os.IsNotExist(err) {
c.Fatal("unix socket is not cleaned up")
}
}
func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *check.C) {
type Config struct {
Crv string `json:"crv"`
D string `json:"d"`
Kid string `json:"kid"`
Kty string `json:"kty"`
X string `json:"x"`
Y string `json:"y"`
}
os.Remove("/etc/docker/key.json")
if err := s.d.Start(); err != nil {
c.Fatalf("Failed to start daemon: %v", err)
}
if err := s.d.Stop(); err != nil {
c.Fatalf("Could not stop daemon: %v", err)
}
config := &Config{}
bytes, err := ioutil.ReadFile("/etc/docker/key.json")
if err != nil {
c.Fatalf("Error reading key.json file: %s", err)
}
// byte[] to Data-Struct
if err := json.Unmarshal(bytes, &config); err != nil {
c.Fatalf("Error Unmarshal: %s", err)
}
//replace config.Kid with the fake value
config.Kid = "VSAJ:FUYR:X3H2:B2VZ:KZ6U:CJD5:K7BX:ZXHY:UZXT:P4FT:MJWG:HRJ4"
// NEW Data-Struct to byte[]
newBytes, err := json.Marshal(&config)
if err != nil {
c.Fatalf("Error Marshal: %s", err)
}
// write back
if err := ioutil.WriteFile("/etc/docker/key.json", newBytes, 0400); err != nil {
c.Fatalf("Error ioutil.WriteFile: %s", err)
}
defer os.Remove("/etc/docker/key.json")
if err := s.d.Start(); err == nil {
c.Fatalf("It should not be successful to start daemon with wrong key: %v", err)
}
content, _ := ioutil.ReadFile(s.d.logFile.Name())
if !strings.Contains(string(content), "Public Key ID does not match") {
c.Fatal("Missing KeyID message from daemon logs")
}
}
func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatalf("Could not start daemon with busybox: %v", err)
}
out, err := s.d.Cmd("run", "-id", "busybox", "/bin/cat")
if err != nil {
c.Fatalf("Could not run /bin/cat: err=%v\n%s", err, out)
}
containerID := strings.TrimSpace(out)
if out, err := s.d.Cmd("kill", containerID); err != nil {
c.Fatalf("Could not kill %s: err=%v\n%s", containerID, err, out)
}
if err := s.d.Restart(); err != nil {
c.Fatalf("Could not restart daemon: %v", err)
}
errchan := make(chan error)
go func() {
if out, err := s.d.Cmd("wait", containerID); err != nil {
errchan <- fmt.Errorf("%v:\n%s", err, out)
}
close(errchan)
}()
select {
case <-time.After(5 * time.Second):
c.Fatal("Waiting on a stopped (killed) container timed out")
case err := <-errchan:
if err != nil {
c.Fatal(err)
}
}
}
// TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint
func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) {
const (
testDaemonHttpsAddr = "localhost:4271"
)
if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem",
"--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHttpsAddr); err != nil {
c.Fatalf("Could not start daemon with busybox: %v", err)
}
//force tcp protocol
host := fmt.Sprintf("tcp://%s", testDaemonHttpsAddr)
daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-cert.pem", "--tlskey", "fixtures/https/client-key.pem"}
out, err := s.d.CmdWithArgs(daemonArgs, "info")
if err != nil {
c.Fatalf("Error Occurred: %s and output: %s", err, out)
}
}
// TestHttpsInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint
// by using a rogue client certificate and checks that it fails with the expected error.
func (s *DockerDaemonSuite) TestHttpsInfoRogueCert(c *check.C) {
const (
errBadCertificate = "remote error: bad certificate"
testDaemonHttpsAddr = "localhost:4271"
)
if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem",
"--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHttpsAddr); err != nil {
c.Fatalf("Could not start daemon with busybox: %v", err)
}
//force tcp protocol
host := fmt.Sprintf("tcp://%s", testDaemonHttpsAddr)
daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
out, err := s.d.CmdWithArgs(daemonArgs, "info")
if err == nil || !strings.Contains(out, errBadCertificate) {
c.Fatalf("Expected err: %s, got instead: %s and output: %s", errBadCertificate, err, out)
}
}
// TestHttpsInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint
// which provides a rogue server certificate and checks that it fails with the expected error
func (s *DockerDaemonSuite) TestHttpsInfoRogueServerCert(c *check.C) {
const (
errCaUnknown = "x509: certificate signed by unknown authority"
testDaemonRogueHttpsAddr = "localhost:4272"
)
if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-rogue-cert.pem",
"--tlskey", "fixtures/https/server-rogue-key.pem", "-H", testDaemonRogueHttpsAddr); err != nil {
c.Fatalf("Could not start daemon with busybox: %v", err)
}
//force tcp protocol
host := fmt.Sprintf("tcp://%s", testDaemonRogueHttpsAddr)
daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
out, err := s.d.CmdWithArgs(daemonArgs, "info")
if err == nil || !strings.Contains(out, errCaUnknown) {
c.Fatalf("Expected err: %s, got instead: %s and output: %s", errCaUnknown, err, out)
}
}
func pingContainers(c *check.C, d *Daemon, expectFailure bool) {
var dargs []string
if d != nil {
dargs = []string{"--host", d.sock()}
}
args := append(dargs, "run", "-d", "--name", "container1", "busybox", "top")
_, err := runCommand(exec.Command(dockerBinary, args...))
c.Assert(err, check.IsNil)
args = append(dargs, "run", "--rm", "--link", "container1:alias1", "busybox", "sh", "-c")
pingCmd := "ping -c 1 %s -W 1"
args = append(args, fmt.Sprintf(pingCmd, "alias1"))
_, err = runCommand(exec.Command(dockerBinary, args...))
if expectFailure {
c.Assert(err, check.NotNil)
} else {
c.Assert(err, check.IsNil)
}
args = append(dargs, "rm", "-f", "container1")
runCommand(exec.Command(dockerBinary, args...))
}
func (s *DockerDaemonSuite) TestDaemonRestartWithSocketAsVolume(c *check.C) {
c.Assert(s.d.StartWithBusybox(), check.IsNil)
socket := filepath.Join(s.d.folder, "docker.sock")
out, err := s.d.Cmd("run", "-d", "-v", socket+":/sock", "busybox")
c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
c.Assert(s.d.Restart(), check.IsNil)
}
func (s *DockerDaemonSuite) TestCleanupMountsAfterCrash(c *check.C) {
c.Assert(s.d.StartWithBusybox(), check.IsNil)
out, err := s.d.Cmd("run", "-d", "busybox", "top")
c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
id := strings.TrimSpace(out)
c.Assert(s.d.cmd.Process.Signal(os.Kill), check.IsNil)
c.Assert(s.d.Start(), check.IsNil)
mountOut, err := exec.Command("mount").CombinedOutput()
c.Assert(err, check.IsNil, check.Commentf("Output: %s", mountOut))
c.Assert(strings.Contains(string(mountOut), id), check.Equals, false, check.Commentf("Something mounted from older daemon start: %s", mountOut))
}
func (s *DockerDaemonSuite) TestRunContainerWithBridgeNone(c *check.C) {
testRequires(c, NativeExecDriver)
c.Assert(s.d.StartWithBusybox("-b", "none"), check.IsNil)
out, err := s.d.Cmd("run", "--rm", "busybox", "ip", "l")
c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
c.Assert(strings.Contains(out, "eth0"), check.Equals, false,
check.Commentf("There shouldn't be eth0 in container when network is disabled: %s", out))
}
func (s *DockerDaemonSuite) TestDaemonRestartWithContainerRunning(t *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
t.Fatal(err)
}
if out, err := s.d.Cmd("run", "-ti", "-d", "--name", "test", "busybox"); err != nil {
t.Fatal(out, err)
}
if err := s.d.Restart(); err != nil {
t.Fatal(err)
}
// Container 'test' should be removed without error
if out, err := s.d.Cmd("rm", "test"); err != nil {
t.Fatal(out, err)
}
}
func (s *DockerDaemonSuite) TestDaemonRestartCleanupNetns(c *check.C) {
if err := s.d.StartWithBusybox(); err != nil {
c.Fatal(err)
}
out, err := s.d.Cmd("run", "--name", "netns", "-d", "busybox", "top")
if err != nil {
c.Fatal(out, err)
}
if out, err := s.d.Cmd("stop", "netns"); err != nil {
c.Fatal(out, err)
}
// Construct netns file name from container id
out = strings.TrimSpace(out)
nsFile := out[:12]
// Test if the file still exists
out, _, err = runCommandWithOutput(exec.Command("stat", "-c", "%n", "/var/run/docker/netns/"+nsFile))
out = strings.TrimSpace(out)
c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
c.Assert(out, check.Equals, "/var/run/docker/netns/"+nsFile, check.Commentf("Output: %s", out))
// Remove the container and restart the daemon
if out, err := s.d.Cmd("rm", "netns"); err != nil {
c.Fatal(out, err)
}
if err := s.d.Restart(); err != nil {
c.Fatal(err)
}
// Test again and see now the netns file does not exist
out, _, err = runCommandWithOutput(exec.Command("stat", "-c", "%n", "/var/run/docker/netns/"+nsFile))
out = strings.TrimSpace(out)
c.Assert(err, check.Not(check.IsNil), check.Commentf("Output: %s", out))
// c.Assert(out, check.Equals, "", check.Commentf("Output: %s", out))
}