mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #29483 from vdemeester/extract-and-refactor-requirement
[test-integration] Refactor and extract TestRequire functionality
This commit is contained in:
commit
f847a3b7ce
10 changed files with 402 additions and 453 deletions
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -13,9 +13,7 @@ import (
|
|||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
var (
|
||||
MacvlanKernelSupport = testRequirement{
|
||||
func() bool {
|
||||
func macvlanKernelSupport() bool {
|
||||
const macvlanKernelVer = 3 // minimum macvlan kernel support
|
||||
const macvlanMajorVer = 9 // minimum macvlan major kernel support
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
|
@ -27,11 +25,9 @@ var (
|
|||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
"kernel version failed to meet the minimum macvlan kernel requirement of 3.9",
|
||||
}
|
||||
IpvlanKernelSupport = testRequirement{
|
||||
func() bool {
|
||||
|
||||
func ipvlanKernelSupport() bool {
|
||||
const ipvlanKernelVer = 4 // minimum ipvlan kernel support
|
||||
const ipvlanMajorVer = 2 // minimum ipvlan major kernel support
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
|
@ -43,14 +39,11 @@ var (
|
|||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
"kernel version failed to meet the minimum ipvlan kernel requirement of 4.0.0",
|
||||
}
|
||||
)
|
||||
|
||||
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))
|
||||
|
|
26
integration-cli/requirement/requirement.go
Normal file
26
integration-cli/requirement/requirement.go
Normal file
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
211
integration-cli/requirements_test.go
Normal file
211
integration-cli/requirements_test.go
Normal file
|
@ -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...)
|
||||
}
|
|
@ -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)
|
||||
}
|
115
integration-cli/requirements_unix_test.go
Normal file
115
integration-cli/requirements_unix_test.go
Normal file
|
@ -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)
|
||||
}
|
Loading…
Add table
Reference in a new issue