requirements.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. "os"
  7. "os/exec"
  8. "strings"
  9. "time"
  10. "github.com/go-check/check"
  11. )
  12. type testCondition func() bool
  13. type testRequirement struct {
  14. Condition testCondition
  15. SkipMessage string
  16. }
  17. // List test requirements
  18. var (
  19. DaemonIsWindows = testRequirement{
  20. func() bool { return daemonPlatform == "windows" },
  21. "Test requires a Windows daemon",
  22. }
  23. DaemonIsLinux = testRequirement{
  24. func() bool { return daemonPlatform == "linux" },
  25. "Test requires a Linux daemon",
  26. }
  27. ExperimentalDaemon = testRequirement{
  28. func() bool { return experimentalDaemon },
  29. "Test requires an experimental daemon",
  30. }
  31. NotExperimentalDaemon = testRequirement{
  32. func() bool { return !experimentalDaemon },
  33. "Test requires a non experimental daemon",
  34. }
  35. IsAmd64 = testRequirement{
  36. func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") == "amd64" },
  37. "Test requires a daemon running on amd64",
  38. }
  39. NotArm = testRequirement{
  40. func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") != "arm" },
  41. "Test requires a daemon not running on ARM",
  42. }
  43. NotArm64 = testRequirement{
  44. func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") != "arm64" },
  45. "Test requires a daemon not running on arm64",
  46. }
  47. NotPpc64le = testRequirement{
  48. func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") != "ppc64le" },
  49. "Test requires a daemon not running on ppc64le",
  50. }
  51. NotS390X = testRequirement{
  52. func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") != "s390x" },
  53. "Test requires a daemon not running on s390x",
  54. }
  55. SameHostDaemon = testRequirement{
  56. func() bool { return isLocalDaemon },
  57. "Test requires docker daemon to run on the same machine as CLI",
  58. }
  59. UnixCli = testRequirement{
  60. func() bool { return isUnixCli },
  61. "Test requires posix utilities or functionality to run.",
  62. }
  63. ExecSupport = testRequirement{
  64. func() bool { return supportsExec },
  65. "Test requires 'docker exec' capabilities on the tested daemon.",
  66. }
  67. Network = testRequirement{
  68. func() bool {
  69. // Set a timeout on the GET at 15s
  70. var timeout = time.Duration(15 * time.Second)
  71. var url = "https://hub.docker.com"
  72. client := http.Client{
  73. Timeout: timeout,
  74. }
  75. resp, err := client.Get(url)
  76. if err != nil && strings.Contains(err.Error(), "use of closed network connection") {
  77. panic(fmt.Sprintf("Timeout for GET request on %s", url))
  78. }
  79. if resp != nil {
  80. resp.Body.Close()
  81. }
  82. return err == nil
  83. },
  84. "Test requires network availability, environment variable set to none to run in a non-network enabled mode.",
  85. }
  86. Apparmor = testRequirement{
  87. func() bool {
  88. buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
  89. return err == nil && len(buf) > 1 && buf[0] == 'Y'
  90. },
  91. "Test requires apparmor is enabled.",
  92. }
  93. RegistryHosting = testRequirement{
  94. func() bool {
  95. // for now registry binary is built only if we're running inside
  96. // container through `make test`. Figure that out by testing if
  97. // registry binary is in PATH.
  98. _, err := exec.LookPath(v2binary)
  99. return err == nil
  100. },
  101. fmt.Sprintf("Test requires an environment that can host %s in the same host", v2binary),
  102. }
  103. NotaryHosting = testRequirement{
  104. func() bool {
  105. // for now notary binary is built only if we're running inside
  106. // container through `make test`. Figure that out by testing if
  107. // notary-server binary is in PATH.
  108. _, err := exec.LookPath(notaryServerBinary)
  109. return err == nil
  110. },
  111. fmt.Sprintf("Test requires an environment that can host %s in the same host", notaryServerBinary),
  112. }
  113. NotaryServerHosting = testRequirement{
  114. func() bool {
  115. // for now notary-server binary is built only if we're running inside
  116. // container through `make test`. Figure that out by testing if
  117. // notary-server binary is in PATH.
  118. _, err := exec.LookPath(notaryServerBinary)
  119. return err == nil
  120. },
  121. fmt.Sprintf("Test requires an environment that can host %s in the same host", notaryServerBinary),
  122. }
  123. NotOverlay = testRequirement{
  124. func() bool {
  125. return !strings.HasPrefix(daemonStorageDriver, "overlay")
  126. },
  127. "Test requires underlying root filesystem not be backed by overlay.",
  128. }
  129. Devicemapper = testRequirement{
  130. func() bool {
  131. return strings.HasPrefix(daemonStorageDriver, "devicemapper")
  132. },
  133. "Test requires underlying root filesystem to be backed by devicemapper.",
  134. }
  135. IPv6 = testRequirement{
  136. func() bool {
  137. cmd := exec.Command("test", "-f", "/proc/net/if_inet6")
  138. if err := cmd.Run(); err != nil {
  139. return true
  140. }
  141. return false
  142. },
  143. "Test requires support for IPv6",
  144. }
  145. UserNamespaceROMount = testRequirement{
  146. func() bool {
  147. // quick case--userns not enabled in this test run
  148. if os.Getenv("DOCKER_REMAP_ROOT") == "" {
  149. return true
  150. }
  151. if _, _, err := dockerCmdWithError("run", "--rm", "--read-only", "busybox", "date"); err != nil {
  152. return false
  153. }
  154. return true
  155. },
  156. "Test cannot be run if user namespaces enabled but readonly mounts fail on this kernel.",
  157. }
  158. UserNamespaceInKernel = testRequirement{
  159. func() bool {
  160. if _, err := os.Stat("/proc/self/uid_map"); os.IsNotExist(err) {
  161. /*
  162. * This kernel-provided file only exists if user namespaces are
  163. * supported
  164. */
  165. return false
  166. }
  167. // We need extra check on redhat based distributions
  168. if f, err := os.Open("/sys/module/user_namespace/parameters/enable"); err == nil {
  169. defer f.Close()
  170. b := make([]byte, 1)
  171. _, _ = f.Read(b)
  172. if string(b) == "N" {
  173. return false
  174. }
  175. return true
  176. }
  177. return true
  178. },
  179. "Kernel must have user namespaces configured and enabled.",
  180. }
  181. NotUserNamespace = testRequirement{
  182. func() bool {
  183. root := os.Getenv("DOCKER_REMAP_ROOT")
  184. if root != "" {
  185. return false
  186. }
  187. return true
  188. },
  189. "Test cannot be run when remapping root",
  190. }
  191. IsPausable = testRequirement{
  192. func() bool {
  193. if daemonPlatform == "windows" {
  194. return isolation == "hyperv"
  195. }
  196. return true
  197. },
  198. "Test requires containers are pausable.",
  199. }
  200. NotPausable = testRequirement{
  201. func() bool {
  202. if daemonPlatform == "windows" {
  203. return isolation == "process"
  204. }
  205. return false
  206. },
  207. "Test requires containers are not pausable.",
  208. }
  209. IsolationIsHyperv = testRequirement{
  210. func() bool {
  211. return daemonPlatform == "windows" && isolation == "hyperv"
  212. },
  213. "Test requires a Windows daemon running default isolation mode of hyperv.",
  214. }
  215. IsolationIsProcess = testRequirement{
  216. func() bool {
  217. return daemonPlatform == "windows" && isolation == "process"
  218. },
  219. "Test requires a Windows daemon running default isolation mode of process.",
  220. }
  221. )
  222. // testRequires checks if the environment satisfies the requirements
  223. // for the test to run or skips the tests.
  224. func testRequires(c *check.C, requirements ...testRequirement) {
  225. for _, r := range requirements {
  226. if !r.Condition() {
  227. c.Skip(r.SkipMessage)
  228. }
  229. }
  230. }