diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 88d85232f1..e33200dca6 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -1665,7 +1665,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) { }, } - if SameHostDaemon.Condition() { + if SameHostDaemon() { tmpDir, err := ioutils.TempDir("", "test-mounts-api") c.Assert(err, checker.IsNil) defer os.RemoveAll(tmpDir) @@ -1696,7 +1696,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) { }...) } - if DaemonIsLinux.Condition() { + if DaemonIsLinux() { cases = append(cases, []testCase{ { config: cfg{ @@ -1823,7 +1823,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) { {mounttypes.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mounttypes.VolumeOptions{DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName}}}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", Name: "test3", RW: true, Destination: destPath}}, } - if SameHostDaemon.Condition() { + if SameHostDaemon() { // setup temp dir for testing binds tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1") c.Assert(err, checker.IsNil) @@ -1834,7 +1834,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) { }...) // for modes only supported on Linux - if DaemonIsLinux.Condition() { + if DaemonIsLinux() { tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3") c.Assert(err, checker.IsNil) defer os.RemoveAll(tmpDir3) diff --git a/integration-cli/docker_cli_cp_utils.go b/integration-cli/docker_cli_cp_utils.go index 0501c5d735..fb4b869e56 100644 --- a/integration-cli/docker_cli_cp_utils.go +++ b/integration-cli/docker_cli_cp_utils.go @@ -289,7 +289,7 @@ func containerStartOutputEquals(c *check.C, containerID, contents string) (err e } func defaultVolumes(tmpDir string) []string { - if SameHostDaemon.Condition() { + if SameHostDaemon() { return []string{ "/vol1", fmt.Sprintf("%s:/vol2", tmpDir), diff --git a/integration-cli/docker_cli_info_test.go b/integration-cli/docker_cli_info_test.go index bb4cc5c17b..063cd4f895 100644 --- a/integration-cli/docker_cli_info_test.go +++ b/integration-cli/docker_cli_info_test.go @@ -40,7 +40,7 @@ func (s *DockerSuite) TestInfoEnsureSucceeds(c *check.C) { stringsToCheck = append(stringsToCheck, "Init Binary:", "Security Options:", "containerd version:", "runc version:", "init version:") } - if DaemonIsLinux.Condition() { + if DaemonIsLinux() { stringsToCheck = append(stringsToCheck, "Runtimes:", "Default Runtime: runc") } diff --git a/integration-cli/docker_cli_ps_test.go b/integration-cli/docker_cli_ps_test.go index f3bdb39d00..e36bd00f6b 100644 --- a/integration-cli/docker_cli_ps_test.go +++ b/integration-cli/docker_cli_ps_test.go @@ -749,7 +749,7 @@ func (s *DockerSuite) TestPsShowMounts(c *check.C) { // bind mount container var bindMountSource string var bindMountDestination string - if DaemonIsWindows.Condition() { + if DaemonIsWindows() { bindMountSource = "c:\\" bindMountDestination = "c:\\t" } else { diff --git a/integration-cli/docker_experimental_network_test.go b/integration-cli/docker_experimental_network_test.go index 4631de31f2..70b3851f52 100644 --- a/integration-cli/docker_experimental_network_test.go +++ b/integration-cli/docker_experimental_network_test.go @@ -13,44 +13,37 @@ import ( "github.com/go-check/check" ) -var ( - MacvlanKernelSupport = testRequirement{ - func() bool { - const macvlanKernelVer = 3 // minimum macvlan kernel support - const macvlanMajorVer = 9 // minimum macvlan major kernel support - kv, err := kernel.GetKernelVersion() - if err != nil { - return false - } - // ensure Kernel version is >= v3.9 for macvlan support - if kv.Kernel < macvlanKernelVer || (kv.Kernel == macvlanKernelVer && kv.Major < macvlanMajorVer) { - return false - } - return true - }, - "kernel version failed to meet the minimum macvlan kernel requirement of 3.9", +func macvlanKernelSupport() bool { + const macvlanKernelVer = 3 // minimum macvlan kernel support + const macvlanMajorVer = 9 // minimum macvlan major kernel support + kv, err := kernel.GetKernelVersion() + if err != nil { + return false } - IpvlanKernelSupport = testRequirement{ - func() bool { - const ipvlanKernelVer = 4 // minimum ipvlan kernel support - const ipvlanMajorVer = 2 // minimum ipvlan major kernel support - kv, err := kernel.GetKernelVersion() - if err != nil { - return false - } - // ensure Kernel version is >= v4.2 for ipvlan support - if kv.Kernel < ipvlanKernelVer || (kv.Kernel == ipvlanKernelVer && kv.Major < ipvlanMajorVer) { - return false - } - return true - }, - "kernel version failed to meet the minimum ipvlan kernel requirement of 4.0.0", + // ensure Kernel version is >= v3.9 for macvlan support + if kv.Kernel < macvlanKernelVer || (kv.Kernel == macvlanKernelVer && kv.Major < macvlanMajorVer) { + return false } -) + return true +} + +func ipvlanKernelSupport() bool { + const ipvlanKernelVer = 4 // minimum ipvlan kernel support + const ipvlanMajorVer = 2 // minimum ipvlan major kernel support + kv, err := kernel.GetKernelVersion() + if err != nil { + return false + } + // ensure Kernel version is >= v4.2 for ipvlan support + if kv.Kernel < ipvlanKernelVer || (kv.Kernel == ipvlanKernelVer && kv.Major < ipvlanMajorVer) { + return false + } + return true +} func (s *DockerNetworkSuite) TestDockerNetworkMacvlanPersistance(c *check.C) { // verify the driver automatically provisions the 802.1q link (dm-dummy0.60) - testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) // master dummy interface 'dm' abbreviation represents 'docker macvlan' master := "dm-dummy0" @@ -70,7 +63,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanPersistance(c *check.C) { func (s *DockerNetworkSuite) TestDockerNetworkIpvlanPersistance(c *check.C) { // verify the driver automatically provisions the 802.1q link (di-dummy0.70) - testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, ipvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) // master dummy interface 'di' notation represent 'docker ipvlan' master := "di-dummy0" // simulate the master link the vlan tagged subinterface parent link will use @@ -89,7 +82,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanPersistance(c *check.C) { func (s *DockerNetworkSuite) TestDockerNetworkMacvlanSubIntCreate(c *check.C) { // verify the driver automatically provisions the 802.1q link (dm-dummy0.50) - testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) // master dummy interface 'dm' abbreviation represents 'docker macvlan' master := "dm-dummy0" // simulate the master link the vlan tagged subinterface parent link will use @@ -104,7 +97,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanSubIntCreate(c *check.C) { func (s *DockerNetworkSuite) TestDockerNetworkIpvlanSubIntCreate(c *check.C) { // verify the driver automatically provisions the 802.1q link (di-dummy0.50) - testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, ipvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) // master dummy interface 'dm' abbreviation represents 'docker ipvlan' master := "di-dummy0" // simulate the master link the vlan tagged subinterface parent link will use @@ -119,7 +112,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanSubIntCreate(c *check.C) { func (s *DockerNetworkSuite) TestDockerNetworkMacvlanOverlapParent(c *check.C) { // verify the same parent interface cannot be used if already in use by an existing network - testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) // master dummy interface 'dm' abbreviation represents 'docker macvlan' master := "dm-dummy0" out, err := createMasterDummy(c, master) @@ -139,7 +132,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanOverlapParent(c *check.C) { func (s *DockerNetworkSuite) TestDockerNetworkIpvlanOverlapParent(c *check.C) { // verify the same parent interface cannot be used if already in use by an existing network - testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, ipvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) // master dummy interface 'dm' abbreviation represents 'docker ipvlan' master := "di-dummy0" out, err := createMasterDummy(c, master) @@ -159,7 +152,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanOverlapParent(c *check.C) { func (s *DockerNetworkSuite) TestDockerNetworkMacvlanMultiSubnet(c *check.C) { // create a dual stack multi-subnet Macvlan bridge mode network and validate connectivity between four containers, two on each subnet - testRequires(c, DaemonIsLinux, IPv6, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, IPv6, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=macvlan", "--ipv6", "--subnet=172.28.100.0/24", "--subnet=172.28.102.0/24", "--gateway=172.28.102.254", "--subnet=2001:db8:abc2::/64", "--subnet=2001:db8:abc4::/64", "--gateway=2001:db8:abc4::254", "dualstackbridge") // Ensure the network was created @@ -214,7 +207,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanMultiSubnet(c *check.C) { func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL2MultiSubnet(c *check.C) { // create a dual stack multi-subnet Ipvlan L2 network and validate connectivity within the subnets, two on each subnet - testRequires(c, DaemonIsLinux, IPv6, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, IPv6, ipvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=ipvlan", "--ipv6", "--subnet=172.28.200.0/24", "--subnet=172.28.202.0/24", "--gateway=172.28.202.254", "--subnet=2001:db8:abc8::/64", "--subnet=2001:db8:abc6::/64", "--gateway=2001:db8:abc6::254", "dualstackl2") // Ensure the network was created @@ -268,7 +261,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL2MultiSubnet(c *check.C) { func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL3MultiSubnet(c *check.C) { // create a dual stack multi-subnet Ipvlan L3 network and validate connectivity between all four containers per L3 mode - testRequires(c, DaemonIsLinux, IPv6, IpvlanKernelSupport, NotUserNamespace, NotArm, IPv6, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, IPv6, ipvlanKernelSupport, NotUserNamespace, NotArm, IPv6, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=ipvlan", "--ipv6", "--subnet=172.28.10.0/24", "--subnet=172.28.12.0/24", "--gateway=172.28.12.254", "--subnet=2001:db8:abc9::/64", "--subnet=2001:db8:abc7::/64", "--gateway=2001:db8:abc7::254", "-o", "ipvlan_mode=l3", "dualstackl3") // Ensure the network was created @@ -327,7 +320,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL3MultiSubnet(c *check.C) { func (s *DockerNetworkSuite) TestDockerNetworkIpvlanAddressing(c *check.C) { // Ensure the default gateways, next-hops and default dev devices are properly set - testRequires(c, DaemonIsLinux, IPv6, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, IPv6, ipvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=macvlan", "--ipv6", "--subnet=172.28.130.0/24", "--subnet=2001:db8:abca::/64", "--gateway=2001:db8:abca::254", "-o", "macvlan_mode=bridge", "dualstackbridge") assertNwIsAvailable(c, "dualstackbridge") @@ -373,7 +366,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanAddressing(c *check.C) { func (s *DockerSuite) TestDockerNetworkMacVlanBridgeNilParent(c *check.C) { // macvlan bridge mode - dummy parent interface is provisioned dynamically - testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=macvlan", "dm-nil-parent") assertNwIsAvailable(c, "dm-nil-parent") @@ -390,7 +383,7 @@ func (s *DockerSuite) TestDockerNetworkMacVlanBridgeNilParent(c *check.C) { func (s *DockerSuite) TestDockerNetworkMacVlanBridgeInternalMode(c *check.C) { // macvlan bridge mode --internal containers can communicate inside the network but not externally - testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=macvlan", "--internal", "dm-internal") assertNwIsAvailable(c, "dm-internal") nr := getNetworkResource(c, "dm-internal") @@ -413,7 +406,7 @@ func (s *DockerSuite) TestDockerNetworkMacVlanBridgeInternalMode(c *check.C) { func (s *DockerSuite) TestDockerNetworkIpvlanL2NilParent(c *check.C) { // ipvlan l2 mode - dummy parent interface is provisioned dynamically - testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, ipvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=ipvlan", "di-nil-parent") assertNwIsAvailable(c, "di-nil-parent") @@ -430,7 +423,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL2NilParent(c *check.C) { func (s *DockerSuite) TestDockerNetworkIpvlanL2InternalMode(c *check.C) { // ipvlan l2 mode --internal containers can communicate inside the network but not externally - testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, ipvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=ipvlan", "--internal", "di-internal") assertNwIsAvailable(c, "di-internal") nr := getNetworkResource(c, "di-internal") @@ -452,7 +445,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL2InternalMode(c *check.C) { func (s *DockerSuite) TestDockerNetworkIpvlanL3NilParent(c *check.C) { // ipvlan l3 mode - dummy parent interface is provisioned dynamically - testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, ipvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.230.0/24", "--subnet=172.28.220.0/24", "-o", "ipvlan_mode=l3", "di-nil-parent-l3") assertNwIsAvailable(c, "di-nil-parent-l3") @@ -470,7 +463,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL3NilParent(c *check.C) { func (s *DockerSuite) TestDockerNetworkIpvlanL3InternalMode(c *check.C) { // ipvlan l3 mode --internal containers can communicate inside the network but not externally - testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, ipvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.230.0/24", "--subnet=172.28.220.0/24", "-o", "ipvlan_mode=l3", "--internal", "di-internal-l3") assertNwIsAvailable(c, "di-internal-l3") @@ -493,7 +486,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL3InternalMode(c *check.C) { func (s *DockerSuite) TestDockerNetworkMacVlanExistingParent(c *check.C) { // macvlan bridge mode - empty parent interface containers can reach each other internally but not externally - testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) netName := "dm-parent-exists" out, err := createMasterDummy(c, "dm-dummy0") //out, err := createVlanInterface(c, "dm-parent", "dm-slave", "macvlan", "bridge") @@ -513,7 +506,7 @@ func (s *DockerSuite) TestDockerNetworkMacVlanExistingParent(c *check.C) { func (s *DockerSuite) TestDockerNetworkMacVlanSubinterface(c *check.C) { // macvlan bridge mode - empty parent interface containers can reach each other internally but not externally - testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) + testRequires(c, DaemonIsLinux, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) netName := "dm-subinterface" out, err := createMasterDummy(c, "dm-dummy0") c.Assert(err, check.IsNil, check.Commentf(out)) diff --git a/integration-cli/requirement/requirement.go b/integration-cli/requirement/requirement.go new file mode 100644 index 0000000000..82a1b9ad68 --- /dev/null +++ b/integration-cli/requirement/requirement.go @@ -0,0 +1,26 @@ +package requirement + +import ( + "fmt" + "reflect" + "runtime" +) + +type skipT interface { + Skip(reason string) +} + +// Test represent a function that can be used as a requirement validation. +type Test func() bool + +// Is checks if the environment satisfies the requirements +// for the test to run or skips the tests. +func Is(s skipT, requirements ...Test) { + for _, r := range requirements { + isValid := r() + if !isValid { + requirementFunc := runtime.FuncForPC(reflect.ValueOf(r).Pointer()).Name() + s.Skip(fmt.Sprintf("unmatched requirement %s", requirementFunc)) + } + } +} diff --git a/integration-cli/requirements.go b/integration-cli/requirements.go deleted file mode 100644 index 6eb56b7e80..0000000000 --- a/integration-cli/requirements.go +++ /dev/null @@ -1,237 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "net/http" - "os" - "os/exec" - "strings" - "time" - - "github.com/go-check/check" -) - -type testCondition func() bool - -type testRequirement struct { - Condition testCondition - SkipMessage string -} - -// List test requirements -var ( - DaemonIsWindows = testRequirement{ - func() bool { return daemonPlatform == "windows" }, - "Test requires a Windows daemon", - } - DaemonIsLinux = testRequirement{ - func() bool { return daemonPlatform == "linux" }, - "Test requires a Linux daemon", - } - ExperimentalDaemon = testRequirement{ - func() bool { return experimentalDaemon }, - "Test requires an experimental daemon", - } - NotExperimentalDaemon = testRequirement{ - func() bool { return !experimentalDaemon }, - "Test requires a non experimental daemon", - } - IsAmd64 = testRequirement{ - func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") == "amd64" }, - "Test requires a daemon running on amd64", - } - NotArm = testRequirement{ - func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") != "arm" }, - "Test requires a daemon not running on ARM", - } - NotArm64 = testRequirement{ - func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") != "arm64" }, - "Test requires a daemon not running on arm64", - } - NotPpc64le = testRequirement{ - func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") != "ppc64le" }, - "Test requires a daemon not running on ppc64le", - } - NotS390X = testRequirement{ - func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") != "s390x" }, - "Test requires a daemon not running on s390x", - } - SameHostDaemon = testRequirement{ - func() bool { return isLocalDaemon }, - "Test requires docker daemon to run on the same machine as CLI", - } - UnixCli = testRequirement{ - func() bool { return isUnixCli }, - "Test requires posix utilities or functionality to run.", - } - ExecSupport = testRequirement{ - func() bool { return supportsExec }, - "Test requires 'docker exec' capabilities on the tested daemon.", - } - Network = testRequirement{ - func() bool { - // Set a timeout on the GET at 15s - var timeout = time.Duration(15 * time.Second) - var url = "https://hub.docker.com" - - client := http.Client{ - Timeout: timeout, - } - - resp, err := client.Get(url) - if err != nil && strings.Contains(err.Error(), "use of closed network connection") { - panic(fmt.Sprintf("Timeout for GET request on %s", url)) - } - if resp != nil { - resp.Body.Close() - } - return err == nil - }, - "Test requires network availability, environment variable set to none to run in a non-network enabled mode.", - } - Apparmor = testRequirement{ - func() bool { - buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled") - return err == nil && len(buf) > 1 && buf[0] == 'Y' - }, - "Test requires apparmor is enabled.", - } - RegistryHosting = testRequirement{ - func() bool { - // for now registry binary is built only if we're running inside - // container through `make test`. Figure that out by testing if - // registry binary is in PATH. - _, err := exec.LookPath(v2binary) - return err == nil - }, - fmt.Sprintf("Test requires an environment that can host %s in the same host", v2binary), - } - NotaryHosting = testRequirement{ - func() bool { - // for now notary binary is built only if we're running inside - // container through `make test`. Figure that out by testing if - // notary-server binary is in PATH. - _, err := exec.LookPath(notaryServerBinary) - return err == nil - }, - fmt.Sprintf("Test requires an environment that can host %s in the same host", notaryServerBinary), - } - NotaryServerHosting = testRequirement{ - func() bool { - // for now notary-server binary is built only if we're running inside - // container through `make test`. Figure that out by testing if - // notary-server binary is in PATH. - _, err := exec.LookPath(notaryServerBinary) - return err == nil - }, - fmt.Sprintf("Test requires an environment that can host %s in the same host", notaryServerBinary), - } - NotOverlay = testRequirement{ - func() bool { - return !strings.HasPrefix(daemonStorageDriver, "overlay") - }, - "Test requires underlying root filesystem not be backed by overlay.", - } - - Devicemapper = testRequirement{ - func() bool { - return strings.HasPrefix(daemonStorageDriver, "devicemapper") - }, - "Test requires underlying root filesystem to be backed by devicemapper.", - } - - IPv6 = testRequirement{ - func() bool { - cmd := exec.Command("test", "-f", "/proc/net/if_inet6") - - if err := cmd.Run(); err != nil { - return true - } - return false - }, - "Test requires support for IPv6", - } - UserNamespaceROMount = testRequirement{ - func() bool { - // quick case--userns not enabled in this test run - if os.Getenv("DOCKER_REMAP_ROOT") == "" { - return true - } - if _, _, err := dockerCmdWithError("run", "--rm", "--read-only", "busybox", "date"); err != nil { - return false - } - return true - }, - "Test cannot be run if user namespaces enabled but readonly mounts fail on this kernel.", - } - UserNamespaceInKernel = testRequirement{ - func() bool { - if _, err := os.Stat("/proc/self/uid_map"); os.IsNotExist(err) { - /* - * This kernel-provided file only exists if user namespaces are - * supported - */ - return false - } - - // We need extra check on redhat based distributions - if f, err := os.Open("/sys/module/user_namespace/parameters/enable"); err == nil { - defer f.Close() - b := make([]byte, 1) - _, _ = f.Read(b) - return string(b) != "N" - } - - return true - }, - "Kernel must have user namespaces configured and enabled.", - } - NotUserNamespace = testRequirement{ - func() bool { - root := os.Getenv("DOCKER_REMAP_ROOT") - return root == "" - }, - "Test cannot be run when remapping root", - } - IsPausable = testRequirement{ - func() bool { - if daemonPlatform == "windows" { - return isolation == "hyperv" - } - return true - }, - "Test requires containers are pausable.", - } - NotPausable = testRequirement{ - func() bool { - if daemonPlatform == "windows" { - return isolation == "process" - } - return false - }, - "Test requires containers are not pausable.", - } - IsolationIsHyperv = testRequirement{ - func() bool { - return daemonPlatform == "windows" && isolation == "hyperv" - }, - "Test requires a Windows daemon running default isolation mode of hyperv.", - } - IsolationIsProcess = testRequirement{ - func() bool { - return daemonPlatform == "windows" && isolation == "process" - }, - "Test requires a Windows daemon running default isolation mode of process.", - } -) - -// testRequires checks if the environment satisfies the requirements -// for the test to run or skips the tests. -func testRequires(c *check.C, requirements ...testRequirement) { - for _, r := range requirements { - if !r.Condition() { - c.Skip(r.SkipMessage) - } - } -} diff --git a/integration-cli/requirements_test.go b/integration-cli/requirements_test.go new file mode 100644 index 0000000000..455be53dab --- /dev/null +++ b/integration-cli/requirements_test.go @@ -0,0 +1,211 @@ +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" + "os/exec" + "strings" + "time" + + "github.com/docker/docker/integration-cli/requirement" + "github.com/go-check/check" +) + +func PlatformIs(platform string) bool { + return daemonPlatform == platform +} + +func ArchitectureIs(arch string) bool { + return os.Getenv("DOCKER_ENGINE_GOARCH") == arch +} + +func ArchitectureIsNot(arch string) bool { + return os.Getenv("DOCKER_ENGINE_GOARCH") != arch +} + +func StorageDriverIs(storageDriver string) bool { + return strings.HasPrefix(daemonStorageDriver, storageDriver) +} + +func StorageDriverIsNot(storageDriver string) bool { + return !strings.HasPrefix(daemonStorageDriver, storageDriver) +} + +func DaemonIsWindows() bool { + return PlatformIs("windows") +} + +func DaemonIsLinux() bool { + return PlatformIs("linux") +} + +func ExperimentalDaemon() bool { + return experimentalDaemon +} + +func NotExperimentalDaemon() bool { + return !experimentalDaemon +} + +func IsAmd64() bool { + return ArchitectureIs("amd64") +} + +func NotArm() bool { + return ArchitectureIsNot("arm") +} + +func NotArm64() bool { + return ArchitectureIsNot("arm64") +} + +func NotPpc64le() bool { + return ArchitectureIsNot("ppc64le") +} + +func NotS390X() bool { + return ArchitectureIsNot("s390x") +} + +func SameHostDaemon() bool { + return isLocalDaemon +} + +func UnixCli() bool { + return isUnixCli +} + +func ExecSupport() bool { + return supportsExec +} + +func Network() bool { + // Set a timeout on the GET at 15s + var timeout = time.Duration(15 * time.Second) + var url = "https://hub.docker.com" + + client := http.Client{ + Timeout: timeout, + } + + resp, err := client.Get(url) + if err != nil && strings.Contains(err.Error(), "use of closed network connection") { + panic(fmt.Sprintf("Timeout for GET request on %s", url)) + } + if resp != nil { + resp.Body.Close() + } + return err == nil +} + +func Apparmor() bool { + buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled") + return err == nil && len(buf) > 1 && buf[0] == 'Y' +} + +func RegistryHosting() bool { + // for now registry binary is built only if we're running inside + // container through `make test`. Figure that out by testing if + // registry binary is in PATH. + _, err := exec.LookPath(v2binary) + return err == nil +} + +func NotaryHosting() bool { + // for now notary binary is built only if we're running inside + // container through `make test`. Figure that out by testing if + // notary-server binary is in PATH. + _, err := exec.LookPath(notaryServerBinary) + return err == nil +} + +func NotaryServerHosting() bool { + // for now notary-server binary is built only if we're running inside + // container through `make test`. Figure that out by testing if + // notary-server binary is in PATH. + _, err := exec.LookPath(notaryServerBinary) + return err == nil +} + +func NotOverlay() bool { + return StorageDriverIsNot("overlay") +} + +func Devicemapper() bool { + return StorageDriverIs("devicemapper") +} + +func IPv6() bool { + cmd := exec.Command("test", "-f", "/proc/net/if_inet6") + return cmd.Run() != nil +} + +func UserNamespaceROMount() bool { + // quick case--userns not enabled in this test run + if os.Getenv("DOCKER_REMAP_ROOT") == "" { + return true + } + if _, _, err := dockerCmdWithError("run", "--rm", "--read-only", "busybox", "date"); err != nil { + return false + } + return true +} + +func NotUserNamespace() bool { + root := os.Getenv("DOCKER_REMAP_ROOT") + return root == "" +} + +func UserNamespaceInKernel() bool { + if _, err := os.Stat("/proc/self/uid_map"); os.IsNotExist(err) { + /* + * This kernel-provided file only exists if user namespaces are + * supported + */ + return false + } + + // We need extra check on redhat based distributions + if f, err := os.Open("/sys/module/user_namespace/parameters/enable"); err == nil { + defer f.Close() + b := make([]byte, 1) + _, _ = f.Read(b) + return string(b) != "N" + } + + return true +} + +func IsPausable() bool { + if daemonPlatform == "windows" { + return isolation == "hyperv" + } + return true +} + +func NotPausable() bool { + if daemonPlatform == "windows" { + return isolation == "process" + } + return false +} + +func IsolationIs(expectedIsolation string) bool { + return daemonPlatform == "windows" && string(isolation) == expectedIsolation +} + +func IsolationIsHyperv() bool { + return IsolationIs("hyperv") +} + +func IsolationIsProcess() bool { + return IsolationIs("process") +} + +// testRequires checks if the environment satisfies the requirements +// for the test to run or skips the tests. +func testRequires(c *check.C, requirements ...requirement.Test) { + requirement.Is(c, requirements...) +} diff --git a/integration-cli/requirements_unix.go b/integration-cli/requirements_unix.go deleted file mode 100644 index ef017d8a76..0000000000 --- a/integration-cli/requirements_unix.go +++ /dev/null @@ -1,159 +0,0 @@ -// +build !windows - -package main - -import ( - "bytes" - "io/ioutil" - "os/exec" - "strings" - - "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/docker/pkg/sysinfo" -) - -var ( - // SysInfo stores information about which features a kernel supports. - SysInfo *sysinfo.SysInfo - cpuCfsPeriod = testRequirement{ - func() bool { - return SysInfo.CPUCfsPeriod - }, - "Test requires an environment that supports cgroup cfs period.", - } - cpuCfsQuota = testRequirement{ - func() bool { - return SysInfo.CPUCfsQuota - }, - "Test requires an environment that supports cgroup cfs quota.", - } - cpuShare = testRequirement{ - func() bool { - return SysInfo.CPUShares - }, - "Test requires an environment that supports cgroup cpu shares.", - } - oomControl = testRequirement{ - func() bool { - return SysInfo.OomKillDisable - }, - "Test requires Oom control enabled.", - } - pidsLimit = testRequirement{ - func() bool { - return SysInfo.PidsLimit - }, - "Test requires pids limit enabled.", - } - kernelMemorySupport = testRequirement{ - func() bool { - return SysInfo.KernelMemory - }, - "Test requires an environment that supports cgroup kernel memory.", - } - memoryLimitSupport = testRequirement{ - func() bool { - return SysInfo.MemoryLimit - }, - "Test requires an environment that supports cgroup memory limit.", - } - memoryReservationSupport = testRequirement{ - func() bool { - return SysInfo.MemoryReservation - }, - "Test requires an environment that supports cgroup memory reservation.", - } - swapMemorySupport = testRequirement{ - func() bool { - return SysInfo.SwapLimit - }, - "Test requires an environment that supports cgroup swap memory limit.", - } - memorySwappinessSupport = testRequirement{ - func() bool { - return SysInfo.MemorySwappiness - }, - "Test requires an environment that supports cgroup memory swappiness.", - } - blkioWeight = testRequirement{ - func() bool { - return SysInfo.BlkioWeight - }, - "Test requires an environment that supports blkio weight.", - } - cgroupCpuset = testRequirement{ - func() bool { - return SysInfo.Cpuset - }, - "Test requires an environment that supports cgroup cpuset.", - } - seccompEnabled = testRequirement{ - func() bool { - return supportsSeccomp && SysInfo.Seccomp - }, - "Test requires that seccomp support be enabled in the daemon.", - } - bridgeNfIptables = testRequirement{ - func() bool { - return !SysInfo.BridgeNFCallIPTablesDisabled - }, - "Test requires that bridge-nf-call-iptables support be enabled in the daemon.", - } - bridgeNfIP6tables = testRequirement{ - func() bool { - return !SysInfo.BridgeNFCallIP6TablesDisabled - }, - "Test requires that bridge-nf-call-ip6tables support be enabled in the daemon.", - } - unprivilegedUsernsClone = testRequirement{ - func() bool { - content, err := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone") - if err == nil && strings.Contains(string(content), "0") { - return false - } - return true - }, - "Test cannot be run with 'sysctl kernel.unprivileged_userns_clone' = 0", - } - ambientCapabilities = testRequirement{ - func() bool { - content, err := ioutil.ReadFile("/proc/self/status") - if err == nil && strings.Contains(string(content), "CapAmb:") { - return true - } - return false - }, - "Test cannot be run without a kernel (4.3+) supporting ambient capabilities", - } - overlayFSSupported = testRequirement{ - func() bool { - cmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "/bin/sh", "-c", "cat /proc/filesystems") - out, err := cmd.CombinedOutput() - if err != nil { - return false - } - return bytes.Contains(out, []byte("overlay\n")) - }, - "Test cannot be run without suppport for overlayfs", - } - overlay2Supported = testRequirement{ - func() bool { - if !overlayFSSupported.Condition() { - return false - } - - daemonV, err := kernel.ParseRelease(daemonKernelVersion) - if err != nil { - return false - } - requiredV := kernel.VersionInfo{Kernel: 4} - return kernel.CompareKernelVersion(*daemonV, requiredV) > -1 - - }, - "Test cannot be run without overlay2 support (kernel 4.0+)", - } -) - -func init() { - SysInfo = sysinfo.New(true) -} diff --git a/integration-cli/requirements_unix_test.go b/integration-cli/requirements_unix_test.go new file mode 100644 index 0000000000..a6d3c3c9a1 --- /dev/null +++ b/integration-cli/requirements_unix_test.go @@ -0,0 +1,115 @@ +// +build !windows + +package main + +import ( + "bytes" + "io/ioutil" + "os/exec" + "strings" + + "github.com/docker/docker/pkg/parsers/kernel" + "github.com/docker/docker/pkg/sysinfo" +) + +var ( + // SysInfo stores information about which features a kernel supports. + SysInfo *sysinfo.SysInfo +) + +func cpuCfsPeriod() bool { + return SysInfo.CPUCfsPeriod +} + +func cpuCfsQuota() bool { + return SysInfo.CPUCfsQuota +} + +func cpuShare() bool { + return SysInfo.CPUShares +} + +func oomControl() bool { + return SysInfo.OomKillDisable +} + +func pidsLimit() bool { + return SysInfo.PidsLimit +} + +func kernelMemorySupport() bool { + return SysInfo.KernelMemory +} + +func memoryLimitSupport() bool { + return SysInfo.MemoryLimit +} + +func memoryReservationSupport() bool { + return SysInfo.MemoryReservation +} + +func swapMemorySupport() bool { + return SysInfo.SwapLimit +} + +func memorySwappinessSupport() bool { + return SysInfo.MemorySwappiness +} + +func blkioWeight() bool { + return SysInfo.BlkioWeight +} + +func cgroupCpuset() bool { + return SysInfo.Cpuset +} + +func seccompEnabled() bool { + return supportsSeccomp && SysInfo.Seccomp +} + +func bridgeNfIptables() bool { + return !SysInfo.BridgeNFCallIPTablesDisabled +} + +func bridgeNfIP6tables() bool { + return !SysInfo.BridgeNFCallIP6TablesDisabled +} + +func unprivilegedUsernsClone() bool { + content, err := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone") + return err != nil || !strings.Contains(string(content), "0") +} + +func ambientCapabilities() bool { + content, err := ioutil.ReadFile("/proc/self/status") + return err != nil || strings.Contains(string(content), "CapAmb:") +} + +func overlayFSSupported() bool { + cmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "/bin/sh", "-c", "cat /proc/filesystems") + out, err := cmd.CombinedOutput() + if err != nil { + return false + } + return bytes.Contains(out, []byte("overlay\n")) +} + +func overlay2Supported() bool { + if !overlayFSSupported() { + return false + } + + daemonV, err := kernel.ParseRelease(daemonKernelVersion) + if err != nil { + return false + } + requiredV := kernel.VersionInfo{Kernel: 4} + return kernel.CompareKernelVersion(*daemonV, requiredV) > -1 + +} + +func init() { + SysInfo = sysinfo.New(true) +}