Browse Source

Merge pull request #382 from thaJeztah/19.03_backport_test_fixes

[19.03 backport] Testing and Jenkinsfile changes [step 1]
Kirill Kolyshkin 5 years ago
parent
commit
9eec36e483
65 changed files with 533 additions and 419 deletions
  1. 1 3
      Dockerfile
  2. 2 1
      Dockerfile.windows
  3. 91 13
      Jenkinsfile
  4. 1 1
      MAINTAINERS
  5. 0 1
      Makefile
  6. 1 1
      api/swagger.yaml
  7. 37 44
      builder/dockerfile/evaluator_test.go
  8. 9 1
      cmd/dockerd/config.go
  9. 3 0
      daemon/config/config.go
  10. 4 4
      daemon/daemon.go
  11. 3 0
      daemon/daemon_unix.go
  12. 9 4
      daemon/logger/awslogs/cloudwatchlogs_test.go
  13. 6 1
      daemon/logger/splunk/splunk_test.go
  14. 53 4
      hack/ci/windows.ps1
  15. 1 1
      hack/make.ps1
  16. 0 24
      hack/make.sh
  17. 20 2
      hack/make/.integration-test-helpers
  18. 5 5
      hack/make/binary-daemon
  19. 1 0
      hack/make/test-docker-py
  20. 0 6
      hack/make/test-integration
  21. 4 3
      hack/validate/gometalinter
  22. 1 1
      integration-cli/benchmark_test.go
  23. 4 5
      integration-cli/check_test.go
  24. 22 0
      integration-cli/daemon/daemon_swarm.go
  25. 0 2
      integration-cli/docker_api_exec_test.go
  26. 2 2
      integration-cli/docker_api_images_test.go
  27. 14 14
      integration-cli/docker_cli_build_test.go
  28. 8 0
      integration-cli/docker_cli_create_test.go
  29. 3 3
      integration-cli/docker_cli_daemon_plugins_test.go
  30. 3 6
      integration-cli/docker_cli_daemon_test.go
  31. 0 2
      integration-cli/docker_cli_events_unix_test.go
  32. 1 4
      integration-cli/docker_cli_exec_test.go
  33. 1 1
      integration-cli/docker_cli_exec_unix_test.go
  34. 0 1
      integration-cli/docker_cli_info_test.go
  35. 2 2
      integration-cli/docker_cli_links_test.go
  36. 1 3
      integration-cli/docker_cli_network_unix_test.go
  37. 0 1
      integration-cli/docker_cli_proxy_test.go
  38. 1 1
      integration-cli/docker_cli_restart_test.go
  39. 3 5
      integration-cli/docker_cli_run_test.go
  40. 4 4
      integration-cli/docker_cli_run_unix_test.go
  41. 1 1
      integration-cli/docker_cli_service_health_test.go
  42. 2 2
      integration-cli/docker_cli_service_scale_test.go
  43. 8 4
      integration-cli/docker_cli_swarm_test.go
  44. 1 1
      integration-cli/docker_cli_userns_test.go
  45. 2 2
      integration-cli/docker_utils_test.go
  46. 0 4
      integration-cli/requirements_test.go
  47. 0 23
      integration-cli/requirements_unix_test.go
  48. 0 8
      integration-cli/test_vars_exec_test.go
  49. 0 8
      integration-cli/test_vars_noexec_test.go
  50. 0 4
      integration-cli/test_vars_unix_test.go
  51. 0 4
      integration-cli/test_vars_windows_test.go
  52. 3 12
      integration/build/build_session_test.go
  53. 1 1
      integration/build/build_squash_test.go
  54. 3 5
      integration/network/ipvlan/ipvlan_test.go
  55. 2 0
      integration/network/ipvlan/main_test.go
  56. 1 0
      integration/network/ipvlan/main_windows_test.go
  57. 1 8
      integration/network/macvlan/macvlan_test.go
  58. 2 0
      integration/network/macvlan/main_test.go
  59. 1 0
      integration/network/macvlan/main_windows_test.go
  60. 1 21
      integration/service/inspect_test.go
  61. 3 17
      integration/session/session_test.go
  62. 22 7
      internal/test/daemon/daemon.go
  63. 17 6
      internal/test/daemon/swarm.go
  64. 139 103
      pkg/term/proxy_test.go
  65. 2 2
      plugin/executor/containerd/containerd.go

+ 1 - 3
Dockerfile

@@ -36,7 +36,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/${APT_MIRROR:-deb.debian.org}/g" /etc/
 FROM base AS criu
 FROM base AS criu
 ARG DEBIAN_FRONTEND
 ARG DEBIAN_FRONTEND
 # Install CRIU for checkpoint/restore support
 # Install CRIU for checkpoint/restore support
-ENV CRIU_VERSION 3.11
+ENV CRIU_VERSION 3.12
 # Install dependency packages specific to criu
 # Install dependency packages specific to criu
 RUN apt-get update && apt-get install -y --no-install-recommends \
 RUN apt-get update && apt-get install -y --no-install-recommends \
 	libnet-dev \
 	libnet-dev \
@@ -281,8 +281,6 @@ COPY --from=djs55/vpnkit@sha256:e508a17cfacc8fd39261d5b4e397df2b953690da577e2c98
 
 
 ENV PATH=/usr/local/cli:$PATH
 ENV PATH=/usr/local/cli:$PATH
 ENV DOCKER_BUILDTAGS apparmor seccomp selinux
 ENV DOCKER_BUILDTAGS apparmor seccomp selinux
-# Options for hack/validate/gometalinter
-ENV GOMETALINTER_OPTS="--deadline=2m"
 WORKDIR /go/src/github.com/docker/docker
 WORKDIR /go/src/github.com/docker/docker
 VOLUME /var/lib/docker
 VOLUME /var/lib/docker
 # Wrap all commands in the "docker-in-docker" script to allow nested containers
 # Wrap all commands in the "docker-in-docker" script to allow nested containers

+ 2 - 1
Dockerfile.windows

@@ -214,7 +214,8 @@ RUN `
   Download-File $location C:\gitsetup.zip; `
   Download-File $location C:\gitsetup.zip; `
   `
   `
   Write-Host INFO: Downloading go...; `
   Write-Host INFO: Downloading go...; `
-  Download-File $('https://golang.org/dl/go'+$Env:GO_VERSION+'.windows-amd64.zip') C:\go.zip; `
+  $dlGoVersion=$Env:GO_VERSION -replace '\.0$',''; `
+  Download-File "https://golang.org/dl/go${dlGoVersion}.windows-amd64.zip" C:\go.zip; `
   `
   `
   Write-Host INFO: Downloading compiler 1 of 3...; `
   Write-Host INFO: Downloading compiler 1 of 3...; `
   Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip C:\gcc.zip; `
   Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip C:\gcc.zip; `

+ 91 - 13
Jenkinsfile

@@ -22,15 +22,27 @@ pipeline {
         DOCKER_GRAPHDRIVER  = 'overlay2'
         DOCKER_GRAPHDRIVER  = 'overlay2'
         APT_MIRROR          = 'cdn-fastly.deb.debian.org'
         APT_MIRROR          = 'cdn-fastly.deb.debian.org'
         CHECK_CONFIG_COMMIT = '78405559cfe5987174aa2cb6463b9b2c1b917255'
         CHECK_CONFIG_COMMIT = '78405559cfe5987174aa2cb6463b9b2c1b917255'
+        TESTDEBUG           = '0'
         TIMEOUT             = '120m'
         TIMEOUT             = '120m'
     }
     }
     stages {
     stages {
+        stage('pr-hack') {
+            when { changeRequest() }
+            steps {
+                script {
+                    echo "Workaround for PR auto-cancel feature. Borrowed from https://issues.jenkins-ci.org/browse/JENKINS-43353"
+                    def buildNumber = env.BUILD_NUMBER as int
+                    if (buildNumber > 1) milestone(buildNumber - 1)
+                    milestone(buildNumber)
+                }
+            }
+        }
         stage('DCO-check') {
         stage('DCO-check') {
             when {
             when {
                 beforeAgent true
                 beforeAgent true
                 expression { !params.skip_dco }
                 expression { !params.skip_dco }
             }
             }
-            agent { label 'linux' }
+            agent { label 'amd64 && ubuntu-1804 && overlay2' }
             steps {
             steps {
                 sh '''
                 sh '''
                 docker run --rm \
                 docker run --rm \
@@ -257,13 +269,13 @@ pipeline {
                                 run_tests() {
                                 run_tests() {
                                         [ -n "$TESTDEBUG" ] && rm= || rm=--rm;
                                         [ -n "$TESTDEBUG" ] && rm= || rm=--rm;
                                         docker run $rm -t --privileged \
                                         docker run $rm -t --privileged \
-                                          -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
+                                          -v "$WORKSPACE/bundles/${TEST_INTEGRATION_DEST}:/go/src/github.com/docker/docker/bundles" \
+                                          -v "$WORKSPACE/bundles/dynbinary-daemon:/go/src/github.com/docker/docker/bundles/dynbinary-daemon" \
                                           -v "$WORKSPACE/.git:/go/src/github.com/docker/docker/.git" \
                                           -v "$WORKSPACE/.git:/go/src/github.com/docker/docker/.git" \
                                           --name "$CONTAINER_NAME" \
                                           --name "$CONTAINER_NAME" \
                                           -e KEEPBUNDLE=1 \
                                           -e KEEPBUNDLE=1 \
                                           -e TESTDEBUG \
                                           -e TESTDEBUG \
                                           -e TESTFLAGS \
                                           -e TESTFLAGS \
-                                          -e TEST_INTEGRATION_DEST \
                                           -e TEST_SKIP_INTEGRATION \
                                           -e TEST_SKIP_INTEGRATION \
                                           -e TEST_SKIP_INTEGRATION_CLI \
                                           -e TEST_SKIP_INTEGRATION_CLI \
                                           -e DOCKER_GITCOMMIT=${GIT_COMMIT} \
                                           -e DOCKER_GITCOMMIT=${GIT_COMMIT} \
@@ -308,6 +320,11 @@ pipeline {
                                 exit $c
                                 exit $c
                                 '''
                                 '''
                             }
                             }
+                            post {
+                                always {
+                                    junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true
+                                }
+                            }
                         }
                         }
                     }
                     }
 
 
@@ -315,7 +332,8 @@ pipeline {
                         always {
                         always {
                             sh '''
                             sh '''
                             echo "Ensuring container killed."
                             echo "Ensuring container killed."
-                            docker rm -vf docker-pr$BUILD_NUMBER || true
+                            cids=$(docker ps -aq -f name=docker-pr${BUILD_NUMBER}-*)
+                            [ -n "$cids" ] && docker rm -vf $cids || true
                             '''
                             '''
 
 
                             sh '''
                             sh '''
@@ -328,7 +346,7 @@ pipeline {
                                 bundleName=amd64
                                 bundleName=amd64
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 # exclude overlay2 directories
                                 # exclude overlay2 directories
-                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*.log' -o -name '*.prof' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
+                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
                                 '''
                                 '''
 
 
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
@@ -397,6 +415,7 @@ pipeline {
                                   -e DOCKER_EXPERIMENTAL \
                                   -e DOCKER_EXPERIMENTAL \
                                   -e DOCKER_GITCOMMIT=${GIT_COMMIT} \
                                   -e DOCKER_GITCOMMIT=${GIT_COMMIT} \
                                   -e DOCKER_GRAPHDRIVER \
                                   -e DOCKER_GRAPHDRIVER \
+                                  -e TESTDEBUG \
                                   -e TEST_SKIP_INTEGRATION_CLI \
                                   -e TEST_SKIP_INTEGRATION_CLI \
                                   -e TIMEOUT \
                                   -e TIMEOUT \
                                   docker:${GIT_COMMIT} \
                                   docker:${GIT_COMMIT} \
@@ -405,6 +424,11 @@ pipeline {
                                     test-integration
                                     test-integration
                                 '''
                                 '''
                             }
                             }
+                            post {
+                                always {
+                                    junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true
+                                }
+                            }
                         }
                         }
                     }
                     }
 
 
@@ -425,7 +449,7 @@ pipeline {
                                 bundleName=s390x-integration
                                 bundleName=s390x-integration
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 # exclude overlay2 directories
                                 # exclude overlay2 directories
-                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*.log' -o -name '*.prof' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
+                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
                                 '''
                                 '''
 
 
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
@@ -483,6 +507,11 @@ pipeline {
                                     test-integration
                                     test-integration
                                 '''
                                 '''
                             }
                             }
+                            post {
+                                always {
+                                    junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true
+                                }
+                            }
                         }
                         }
                     }
                     }
 
 
@@ -503,7 +532,7 @@ pipeline {
                                 bundleName=s390x-integration-cli
                                 bundleName=s390x-integration-cli
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 # exclude overlay2 directories
                                 # exclude overlay2 directories
-                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*.log' -o -name '*.prof' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
+                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
                                 '''
                                 '''
 
 
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
@@ -570,6 +599,7 @@ pipeline {
                                   -e DOCKER_EXPERIMENTAL \
                                   -e DOCKER_EXPERIMENTAL \
                                   -e DOCKER_GITCOMMIT=${GIT_COMMIT} \
                                   -e DOCKER_GITCOMMIT=${GIT_COMMIT} \
                                   -e DOCKER_GRAPHDRIVER \
                                   -e DOCKER_GRAPHDRIVER \
+                                  -e TESTDEBUG \
                                   -e TEST_SKIP_INTEGRATION_CLI \
                                   -e TEST_SKIP_INTEGRATION_CLI \
                                   -e TIMEOUT \
                                   -e TIMEOUT \
                                   docker:${GIT_COMMIT} \
                                   docker:${GIT_COMMIT} \
@@ -578,6 +608,11 @@ pipeline {
                                     test-integration
                                     test-integration
                                 '''
                                 '''
                             }
                             }
+                            post {
+                                always {
+                                    junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true
+                                }
+                            }
                         }
                         }
                     }
                     }
 
 
@@ -598,7 +633,7 @@ pipeline {
                                 bundleName=ppc64le-integration
                                 bundleName=ppc64le-integration
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 # exclude overlay2 directories
                                 # exclude overlay2 directories
-                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*.log' -o -name '*.prof' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
+                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
                                 '''
                                 '''
 
 
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
@@ -654,6 +689,11 @@ pipeline {
                                     test-integration
                                     test-integration
                                 '''
                                 '''
                             }
                             }
+                            post {
+                                always {
+                                    junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true
+                                }
+                            }
                         }
                         }
                     }
                     }
 
 
@@ -662,8 +702,6 @@ pipeline {
                             sh '''
                             sh '''
                             echo "Ensuring container killed."
                             echo "Ensuring container killed."
                             docker rm -vf docker-pr$BUILD_NUMBER || true
                             docker rm -vf docker-pr$BUILD_NUMBER || true
-                            cids=$(docker ps -aq -f name=docker-pr${BUILD_NUMBER}-*)
-                            [ -n "$cids" ] && docker rm -vf $cids || true
                             '''
                             '''
 
 
                             sh '''
                             sh '''
@@ -676,7 +714,7 @@ pipeline {
                                 bundleName=ppc64le-integration-cli
                                 bundleName=ppc64le-integration-cli
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 echo "Creating ${bundleName}-bundles.tar.gz"
                                 # exclude overlay2 directories
                                 # exclude overlay2 directories
-                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*.log' -o -name '*.prof' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
+                                find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
                                 '''
                                 '''
 
 
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
                                 archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
@@ -699,11 +737,12 @@ pipeline {
                     }
                     }
                     environment {
                     environment {
                         DOCKER_BUILDKIT        = '0'
                         DOCKER_BUILDKIT        = '0'
+                        DOCKER_DUT_DEBUG       = '1'
                         SKIP_VALIDATION_TESTS  = '1'
                         SKIP_VALIDATION_TESTS  = '1'
                         SOURCES_DRIVE          = 'd'
                         SOURCES_DRIVE          = 'd'
                         SOURCES_SUBDIR         = 'gopath'
                         SOURCES_SUBDIR         = 'gopath'
                         TESTRUN_DRIVE          = 'd'
                         TESTRUN_DRIVE          = 'd'
-                        TESTRUN_SUBDIR         = "CI-$BUILD_NUMBER"
+                        TESTRUN_SUBDIR         = "CI"
                         WINDOWS_BASE_IMAGE     = 'mcr.microsoft.com/windows/servercore'
                         WINDOWS_BASE_IMAGE     = 'mcr.microsoft.com/windows/servercore'
                         WINDOWS_BASE_IMAGE_TAG = 'ltsc2016'
                         WINDOWS_BASE_IMAGE_TAG = 'ltsc2016'
                     }
                     }
@@ -732,6 +771,25 @@ pipeline {
                             }
                             }
                         }
                         }
                     }
                     }
+                    post {
+                        always {
+                            catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') {
+                                powershell '''
+                                $bundleName="windowsRS1-integration"
+                                Write-Host -ForegroundColor Green "Creating ${bundleName}-bundles.zip"
+
+                                # archiveArtifacts does not support env-vars to , so save the artifacts in a fixed location
+                                Compress-Archive -Path "${env:TEMP}/CIDUT.out", "${env:TEMP}/CIDUT.err" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip"
+                                '''
+
+                                archiveArtifacts artifacts: '*-bundles.zip', allowEmptyArchive: true
+                            }
+                        }
+                        cleanup {
+                            sh 'make clean'
+                            deleteDir()
+                        }
+                    }
                 }
                 }
                 stage('win-RS5') {
                 stage('win-RS5') {
                     when {
                     when {
@@ -740,11 +798,12 @@ pipeline {
                     }
                     }
                     environment {
                     environment {
                         DOCKER_BUILDKIT        = '0'
                         DOCKER_BUILDKIT        = '0'
+                        DOCKER_DUT_DEBUG       = '1'
                         SKIP_VALIDATION_TESTS  = '1'
                         SKIP_VALIDATION_TESTS  = '1'
                         SOURCES_DRIVE          = 'd'
                         SOURCES_DRIVE          = 'd'
                         SOURCES_SUBDIR         = 'gopath'
                         SOURCES_SUBDIR         = 'gopath'
                         TESTRUN_DRIVE          = 'd'
                         TESTRUN_DRIVE          = 'd'
-                        TESTRUN_SUBDIR         = "CI-$BUILD_NUMBER"
+                        TESTRUN_SUBDIR         = "CI"
                         WINDOWS_BASE_IMAGE     = 'mcr.microsoft.com/windows/servercore'
                         WINDOWS_BASE_IMAGE     = 'mcr.microsoft.com/windows/servercore'
                         WINDOWS_BASE_IMAGE_TAG = 'ltsc2019'
                         WINDOWS_BASE_IMAGE_TAG = 'ltsc2019'
                     }
                     }
@@ -772,6 +831,25 @@ pipeline {
                             }
                             }
                         }
                         }
                     }
                     }
+                    post {
+                        always {
+                            catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') {
+                                powershell '''
+                                $bundleName="windowsRS5-integration"
+                                Write-Host -ForegroundColor Green "Creating ${bundleName}-bundles.zip"
+
+                                # archiveArtifacts does not support env-vars to , so save the artifacts in a fixed location
+                                Compress-Archive -Path "${env:TEMP}/CIDUT.out", "${env:TEMP}/CIDUT.err" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip"
+                                '''
+
+                                archiveArtifacts artifacts: '*-bundles.zip', allowEmptyArchive: true
+                            }
+                        }
+                        cleanup {
+                            sh 'make clean'
+                            deleteDir()
+                        }
+                    }
                 }
                 }
             }
             }
         }
         }

+ 1 - 1
MAINTAINERS

@@ -111,7 +111,7 @@
 			# still stumble into him in our issue tracker, or on IRC.
 			# still stumble into him in our issue tracker, or on IRC.
 			"erikh",
 			"erikh",
 
 
-			# Evan Hazlett is the creator of of the Shipyard and Interlock open source projects,
+			# Evan Hazlett is the creator of the Shipyard and Interlock open source projects,
 			# and the author of "Orca", which became the foundation of Docker Universal Control
 			# and the author of "Orca", which became the foundation of Docker Universal Control
 			# Plane (UCP). As a maintainer, Evan helped integrating SwarmKit (secrets, tasks)
 			# Plane (UCP). As a maintainer, Evan helped integrating SwarmKit (secrets, tasks)
 			# into the Docker engine.
 			# into the Docker engine.

+ 0 - 1
Makefile

@@ -53,7 +53,6 @@ DOCKER_ENVS := \
 	-e DOCKER_TEST_HOST \
 	-e DOCKER_TEST_HOST \
 	-e DOCKER_USERLANDPROXY \
 	-e DOCKER_USERLANDPROXY \
 	-e DOCKERD_ARGS \
 	-e DOCKERD_ARGS \
-	-e TEST_INTEGRATION_DEST \
 	-e TEST_INTEGRATION_DIR \
 	-e TEST_INTEGRATION_DIR \
 	-e TEST_SKIP_INTEGRATION \
 	-e TEST_SKIP_INTEGRATION \
 	-e TEST_SKIP_INTEGRATION_CLI \
 	-e TEST_SKIP_INTEGRATION_CLI \

+ 1 - 1
api/swagger.yaml

@@ -3262,7 +3262,7 @@ definitions:
 
 
           <p><br /></p>
           <p><br /></p>
 
 
-          - "ingress" makes the target port accessible on on every node,
+          - "ingress" makes the target port accessible on every node,
             regardless of whether there is a task for the service running on
             regardless of whether there is a task for the service running on
             that node or not.
             that node or not.
           - "host" bypasses the routing mesh and publish the port directly on
           - "host" bypasses the routing mesh and publish the port directly on

+ 37 - 44
builder/dockerfile/evaluator_test.go

@@ -24,8 +24,11 @@ func init() {
 	reexec.Init()
 	reexec.Init()
 }
 }
 
 
-func initDispatchTestCases() []dispatchTestCase {
-	dispatchTestCases := []dispatchTestCase{
+func TestDispatch(t *testing.T) {
+	if runtime.GOOS != "windows" {
+		skip.If(t, os.Getuid() != 0, "skipping test that requires root")
+	}
+	testCases := []dispatchTestCase{
 		{
 		{
 			name: "ADD multiple files to file",
 			name: "ADD multiple files to file",
 			cmd: &instructions.AddCommand{SourcesAndDest: instructions.SourcesAndDest{
 			cmd: &instructions.AddCommand{SourcesAndDest: instructions.SourcesAndDest{
@@ -92,56 +95,46 @@ func initDispatchTestCases() []dispatchTestCase {
 			}},
 			}},
 			expectedError: "source can't be a URL for COPY",
 			expectedError: "source can't be a URL for COPY",
 			files:         nil,
 			files:         nil,
-		}}
-
-	return dispatchTestCases
-}
-
-func TestDispatch(t *testing.T) {
-	if runtime.GOOS != "windows" {
-		skip.If(t, os.Getuid() != 0, "skipping test that requires root")
+		},
 	}
 	}
-	testCases := initDispatchTestCases()
 
 
-	for _, testCase := range testCases {
-		executeTestCase(t, testCase)
-	}
-}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
+			defer cleanup()
 
 
-func executeTestCase(t *testing.T, testCase dispatchTestCase) {
-	contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
-	defer cleanup()
+			for filename, content := range tc.files {
+				createTestTempFile(t, contextDir, filename, content, 0777)
+			}
 
 
-	for filename, content := range testCase.files {
-		createTestTempFile(t, contextDir, filename, content, 0777)
-	}
+			tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
 
 
-	tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
+			if err != nil {
+				t.Fatalf("Error when creating tar stream: %s", err)
+			}
 
 
-	if err != nil {
-		t.Fatalf("Error when creating tar stream: %s", err)
-	}
-
-	defer func() {
-		if err = tarStream.Close(); err != nil {
-			t.Fatalf("Error when closing tar stream: %s", err)
-		}
-	}()
+			defer func() {
+				if err = tarStream.Close(); err != nil {
+					t.Fatalf("Error when closing tar stream: %s", err)
+				}
+			}()
 
 
-	context, err := remotecontext.FromArchive(tarStream)
+			context, err := remotecontext.FromArchive(tarStream)
 
 
-	if err != nil {
-		t.Fatalf("Error when creating tar context: %s", err)
-	}
+			if err != nil {
+				t.Fatalf("Error when creating tar context: %s", err)
+			}
 
 
-	defer func() {
-		if err = context.Close(); err != nil {
-			t.Fatalf("Error when closing tar context: %s", err)
-		}
-	}()
+			defer func() {
+				if err = context.Close(); err != nil {
+					t.Fatalf("Error when closing tar context: %s", err)
+				}
+			}()
 
 
-	b := newBuilderWithMockBackend()
-	sb := newDispatchRequest(b, '`', context, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
-	err = dispatch(sb, testCase.cmd)
-	assert.Check(t, is.ErrorContains(err, testCase.expectedError))
+			b := newBuilderWithMockBackend()
+			sb := newDispatchRequest(b, '`', context, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
+			err = dispatch(sb, tc.cmd)
+			assert.Check(t, is.ErrorContains(err, tc.expectedError))
+		})
+	}
 }
 }

+ 9 - 1
cmd/dockerd/config.go

@@ -3,8 +3,10 @@ package main
 import (
 import (
 	"runtime"
 	"runtime"
 
 
+	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
+	"github.com/docker/docker/plugin/executor/containerd"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/spf13/pflag"
 	"github.com/spf13/pflag"
 )
 )
@@ -85,7 +87,13 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {
 
 
 	conf.MaxConcurrentDownloads = &maxConcurrentDownloads
 	conf.MaxConcurrentDownloads = &maxConcurrentDownloads
 	conf.MaxConcurrentUploads = &maxConcurrentUploads
 	conf.MaxConcurrentUploads = &maxConcurrentUploads
-	return nil
+
+	flags.StringVar(&conf.ContainerdNamespace, "containerd-namespace", daemon.ContainersNamespace, "Containerd namespace to use")
+	if err := flags.MarkHidden("containerd-namespace"); err != nil {
+		return err
+	}
+	flags.StringVar(&conf.ContainerdPluginNamespace, "containerd-plugins-namespace", containerd.PluginNamespace, "Containerd namespace to use for plugins")
+	return flags.MarkHidden("containerd-plugins-namespace")
 }
 }
 
 
 func installRegistryServiceFlags(options *registry.ServiceOptions, flags *pflag.FlagSet) {
 func installRegistryServiceFlags(options *registry.ServiceOptions, flags *pflag.FlagSet) {

+ 3 - 0
daemon/config/config.go

@@ -235,6 +235,9 @@ type CommonConfig struct {
 	Features map[string]bool `json:"features,omitempty"`
 	Features map[string]bool `json:"features,omitempty"`
 
 
 	Builder BuilderConfig `json:"builder,omitempty"`
 	Builder BuilderConfig `json:"builder,omitempty"`
+
+	ContainerdNamespace       string `json:"containerd-namespace,omitempty"`
+	ContainerdPluginNamespace string `json:"containerd-plugin-namespace,omitempty"`
 }
 }
 
 
 // IsValueSet returns true if a configuration value
 // IsValueSet returns true if a configuration value

+ 4 - 4
daemon/daemon.go

@@ -875,7 +875,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 		grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
 		grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
 	}
 	}
 	if config.ContainerdAddr != "" {
 	if config.ContainerdAddr != "" {
-		d.containerdCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(ContainersNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
+		d.containerdCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(config.ContainerdNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
 		if err != nil {
 		if err != nil {
 			return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr)
 			return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr)
 		}
 		}
@@ -887,13 +887,13 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 		// Windows is not currently using containerd, keep the
 		// Windows is not currently using containerd, keep the
 		// client as nil
 		// client as nil
 		if config.ContainerdAddr != "" {
 		if config.ContainerdAddr != "" {
-			pluginCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(pluginexec.PluginNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
+			pluginCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(config.ContainerdPluginNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second))
 			if err != nil {
 			if err != nil {
 				return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr)
 				return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr)
 			}
 			}
 		}
 		}
 
 
-		return pluginexec.New(ctx, getPluginExecRoot(config.Root), pluginCli, m)
+		return pluginexec.New(ctx, getPluginExecRoot(config.Root), pluginCli, config.ContainerdPluginNamespace, m)
 	}
 	}
 
 
 	// Plugin system initialization should happen before restore. Do not change order.
 	// Plugin system initialization should happen before restore. Do not change order.
@@ -1041,7 +1041,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 
 
 	go d.execCommandGC()
 	go d.execCommandGC()
 
 
-	d.containerd, err = libcontainerd.NewClient(ctx, d.containerdCli, filepath.Join(config.ExecRoot, "containerd"), ContainersNamespace, d)
+	d.containerd, err = libcontainerd.NewClient(ctx, d.containerdCli, filepath.Join(config.ExecRoot, "containerd"), config.ContainerdNamespace, d)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 3 - 0
daemon/daemon_unix.go

@@ -736,6 +736,9 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error
 
 
 // verifyDaemonSettings performs validation of daemon config struct
 // verifyDaemonSettings performs validation of daemon config struct
 func verifyDaemonSettings(conf *config.Config) error {
 func verifyDaemonSettings(conf *config.Config) error {
+	if conf.ContainerdNamespace == conf.ContainerdPluginNamespace {
+		return errors.New("containers namespace and plugins namespace cannot be the same")
+	}
 	// Check for mutually incompatible config options
 	// Check for mutually incompatible config options
 	if conf.BridgeConfig.Iface != "" && conf.BridgeConfig.IP != "" {
 	if conf.BridgeConfig.Iface != "" && conf.BridgeConfig.IP != "" {
 		return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one")
 		return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one")

+ 9 - 4
daemon/logger/awslogs/cloudwatchlogs_test.go

@@ -285,6 +285,9 @@ func TestLogClosed(t *testing.T) {
 	}
 	}
 }
 }
 
 
+// TestLogBlocking tests that the Log method blocks appropriately when
+// non-blocking behavior is not enabled.  Blocking is achieved through an
+// internal channel that must be drained for Log to return.
 func TestLogBlocking(t *testing.T) {
 func TestLogBlocking(t *testing.T) {
 	mockClient := newMockClient()
 	mockClient := newMockClient()
 	stream := &logStream{
 	stream := &logStream{
@@ -299,18 +302,20 @@ func TestLogBlocking(t *testing.T) {
 		err := stream.Log(&logger.Message{})
 		err := stream.Log(&logger.Message{})
 		errorCh <- err
 		errorCh <- err
 	}()
 	}()
+	// block until the goroutine above has started
 	<-started
 	<-started
 	select {
 	select {
 	case err := <-errorCh:
 	case err := <-errorCh:
 		t.Fatal("Expected stream.Log to block: ", err)
 		t.Fatal("Expected stream.Log to block: ", err)
 	default:
 	default:
-		break
 	}
 	}
+	// assuming it is blocked, we can now try to drain the internal channel and
+	// unblock it
 	select {
 	select {
-	case <-stream.messages:
-		break
-	default:
+	case <-time.After(10 * time.Millisecond):
+		// if we're unable to drain the channel within 10ms, something seems broken
 		t.Fatal("Expected to be able to read from stream.messages but was unable to")
 		t.Fatal("Expected to be able to read from stream.messages but was unable to")
+	case <-stream.messages:
 	}
 	}
 	select {
 	select {
 	case err := <-errorCh:
 	case err := <-errorCh:

+ 6 - 1
daemon/logger/splunk/splunk_test.go

@@ -925,7 +925,12 @@ func TestFrequency(t *testing.T) {
 
 
 	// 1 to verify connection and 10 to verify that we have sent messages with required frequency,
 	// 1 to verify connection and 10 to verify that we have sent messages with required frequency,
 	// but because frequency is too small (to keep test quick), instead of 11, use 9 if context switches will be slow
 	// but because frequency is too small (to keep test quick), instead of 11, use 9 if context switches will be slow
-	if hec.numOfRequests < 9 {
+	expectedRequests := 9
+	if runtime.GOOS == "windows" {
+		// sometimes in Windows, this test fails with number of requests showing 8. So be more conservative.
+		expectedRequests = 7
+	}
+	if hec.numOfRequests < expectedRequests {
 		t.Fatalf("Unexpected number of requests %d", hec.numOfRequests)
 		t.Fatalf("Unexpected number of requests %d", hec.numOfRequests)
 	}
 	}
 
 

+ 53 - 4
hack/ci/windows.ps1

@@ -15,6 +15,11 @@ if ($env:BUILD_TAG -match "-LoW") { $env:LCOW_MODE=1 }
 if ($env:BUILD_TAG -match "-WoW") { $env:LCOW_MODE="" }
 if ($env:BUILD_TAG -match "-WoW") { $env:LCOW_MODE="" }
 
 
 
 
+Write-Host -ForegroundColor Red "DEBUG: print all environment variables to check how Jenkins runs this script"
+$allArgs = [Environment]::GetCommandLineArgs()
+Write-Host -ForegroundColor Red $allArgs
+Write-Host -ForegroundColor Red "----------------------------------------------------------------------------"
+
 # -------------------------------------------------------------------------------------------
 # -------------------------------------------------------------------------------------------
 # When executed, we rely on four variables being set in the environment:
 # When executed, we rely on four variables being set in the environment:
 #
 #
@@ -46,10 +51,24 @@ if ($env:BUILD_TAG -match "-WoW") { $env:LCOW_MODE="" }
 #                        TESTRUN_DRIVE\TESTRUN_SUBDIR\CI-<CommitID> or
 #                        TESTRUN_DRIVE\TESTRUN_SUBDIR\CI-<CommitID> or
 #                        d:\CI\CI-<CommitID>
 #                        d:\CI\CI-<CommitID>
 #
 #
+# Optional environment variables help in CI:
+#
+#    BUILD_NUMBER + BRANCH_NAME   are optional variables to be added to the directory below TESTRUN_SUBDIR
+#                        to have individual folder per CI build. If some files couldn't be
+#                        cleaned up and we want to re-run the build in CI.
+#                        Hence, the daemon under test is run under
+#                        TESTRUN_DRIVE\TESTRUN_SUBDIR\PR-<PR-Number>\<BuildNumber> or
+#                        d:\CI\PR-<PR-Number>\<BuildNumber>
+#
 # In addition, the following variables can control the run configuration:
 # In addition, the following variables can control the run configuration:
 #
 #
 #    DOCKER_DUT_DEBUG         if defined starts the daemon under test in debug mode.
 #    DOCKER_DUT_DEBUG         if defined starts the daemon under test in debug mode.
 #
 #
+#   DOCKER_STORAGE_OPTS       comma-separated list of optional storage driver options for the daemon under test
+#                             examples:
+#                             DOCKER_STORAGE_OPTS="size=40G"
+#                             DOCKER_STORAGE_OPTS="lcow.globalmode=false,lcow.kernel=kernel.efi"
+#
 #    SKIP_VALIDATION_TESTS    if defined skips the validation tests
 #    SKIP_VALIDATION_TESTS    if defined skips the validation tests
 #
 #
 #    SKIP_UNIT_TESTS          if defined skips the unit tests
 #    SKIP_UNIT_TESTS          if defined skips the unit tests
@@ -266,7 +285,7 @@ Try {
         }
         }
       }
       }
     } Catch {}
     } Catch {}
-    if ($defender) { Throw "ERROR: Windows Defender real time protection must be disabled for integration tests" }
+    if ($defender) { Write-Host -ForegroundColor Magenta "WARN: Windows Defender real time protection is enabled, which may cause some integration tests to fail" }
 
 
     # Make sure SOURCES_DRIVE is set
     # Make sure SOURCES_DRIVE is set
     if ($null -eq $env:SOURCES_DRIVE) { Throw "ERROR: Environment variable SOURCES_DRIVE is not set" }
     if ($null -eq $env:SOURCES_DRIVE) { Throw "ERROR: Environment variable SOURCES_DRIVE is not set" }
@@ -418,7 +437,12 @@ Try {
 
 
     # Redirect to a temporary location. 
     # Redirect to a temporary location. 
     $TEMPORIG=$env:TEMP
     $TEMPORIG=$env:TEMP
-    $env:TEMP="$env:TESTRUN_DRIVE`:\$env:TESTRUN_SUBDIR\CI-$COMMITHASH"
+    if ($null -eq $env:BUILD_NUMBER) {
+      $env:TEMP="$env:TESTRUN_DRIVE`:\$env:TESTRUN_SUBDIR\CI-$COMMITHASH"
+    } else {
+      # individual temporary location per CI build that better matches the BUILD_URL
+      $env:TEMP="$env:TESTRUN_DRIVE`:\$env:TESTRUN_SUBDIR\$env:BRANCH_NAME\$env:BUILD_NUMBER"
+    }
     $env:LOCALAPPDATA="$env:TEMP\localappdata"
     $env:LOCALAPPDATA="$env:TEMP\localappdata"
     $errorActionPreference='Stop'
     $errorActionPreference='Stop'
     New-Item -ItemType Directory "$env:TEMP" -ErrorAction SilentlyContinue | Out-Null
     New-Item -ItemType Directory "$env:TEMP" -ErrorAction SilentlyContinue | Out-Null
@@ -580,6 +604,15 @@ Try {
         $dutArgs += "--exec-opt isolation=hyperv"
         $dutArgs += "--exec-opt isolation=hyperv"
     }
     }
 
 
+    # Arguments: Allow setting optional storage-driver options
+    # example usage: DOCKER_STORAGE_OPTS="lcow.globalmode=false,lcow.kernel=kernel.efi"
+    if (-not ("$env:DOCKER_STORAGE_OPTS" -eq "")) {
+        Write-Host -ForegroundColor Green "INFO: Running the daemon under test with storage-driver options ${env:DOCKER_STORAGE_OPTS}"
+        $env:DOCKER_STORAGE_OPTS.Split(",") | ForEach {
+            $dutArgs += "--storage-opt $_"
+        }
+    }
+
     # Start the daemon under test, ensuring everything is redirected to folders under $TEMP.
     # Start the daemon under test, ensuring everything is redirected to folders under $TEMP.
     # Important - we launch the -$COMMITHASH version so that we can kill it without
     # Important - we launch the -$COMMITHASH version so that we can kill it without
     # killing the control daemon. 
     # killing the control daemon. 
@@ -608,7 +641,8 @@ Try {
 
 
     # Start tailing the daemon under test if the command is installed
     # Start tailing the daemon under test if the command is installed
     if ($null -ne (Get-Command "tail" -ErrorAction SilentlyContinue)) {
     if ($null -ne (Get-Command "tail" -ErrorAction SilentlyContinue)) {
-        $tail = start-process "tail" -ArgumentList "-f $env:TEMP\dut.out" -ErrorAction SilentlyContinue
+        Write-Host -ForegroundColor green "INFO: Start tailing logs of the daemon under tests"
+        $tail = Start-Process "tail" -ArgumentList "-f $env:TEMP\dut.out" -PassThru -ErrorAction SilentlyContinue
     }
     }
 
 
     # Verify we can get the daemon under test to respond 
     # Verify we can get the daemon under test to respond 
@@ -717,7 +751,7 @@ Try {
     
     
         # Inspect the pulled or loaded image to get the version directly
         # Inspect the pulled or loaded image to get the version directly
         $ErrorActionPreference = "SilentlyContinue"
         $ErrorActionPreference = "SilentlyContinue"
-        $dutimgVersion = $(&"$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" inspect  $($env:WINDOWS_BASE_IMAGE) --format "{{.OsVersion}}")
+        $dutimgVersion = $(&"$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" inspect "$($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG" --format "{{.OsVersion}}")
         $ErrorActionPreference = "Stop"
         $ErrorActionPreference = "Stop"
         Write-Host -ForegroundColor Green $("INFO: Version of $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG is '"+$dutimgVersion+"'")
         Write-Host -ForegroundColor Green $("INFO: Version of $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG is '"+$dutimgVersion+"'")
     }
     }
@@ -944,6 +978,12 @@ Try {
         Remove-Item "$env:TEMP\docker.pid" -force -ErrorAction SilentlyContinue
         Remove-Item "$env:TEMP\docker.pid" -force -ErrorAction SilentlyContinue
     }
     }
 
 
+    # Stop the tail process (if started)
+    if ($null -ne $tail) {
+        Write-Host -ForegroundColor green "INFO: Stop tailing logs of the daemon under tests"
+        Stop-Process -InputObject $tail -Force
+    }
+
     Write-Host -ForegroundColor Green "INFO: executeCI.ps1 Completed successfully at $(Get-Date)."
     Write-Host -ForegroundColor Green "INFO: executeCI.ps1 Completed successfully at $(Get-Date)."
 }
 }
 Catch [Exception] {
 Catch [Exception] {
@@ -960,6 +1000,9 @@ Catch [Exception] {
     Throw $_
     Throw $_
 }
 }
 Finally {
 Finally {
+    # Preserve the LastExitCode of the tests
+    $tmpLastExitCode = $LastExitCode
+
     $ErrorActionPreference="SilentlyContinue"
     $ErrorActionPreference="SilentlyContinue"
     $global:ProgressPreference=$origProgressPreference
     $global:ProgressPreference=$origProgressPreference
     Write-Host  -ForegroundColor Green "INFO: Tidying up at end of run"
     Write-Host  -ForegroundColor Green "INFO: Tidying up at end of run"
@@ -991,6 +1034,12 @@ Finally {
 
 
     Set-Location "$env:SOURCES_DRIVE\$env:SOURCES_SUBDIR" -ErrorAction SilentlyContinue
     Set-Location "$env:SOURCES_DRIVE\$env:SOURCES_SUBDIR" -ErrorAction SilentlyContinue
     Nuke-Everything
     Nuke-Everything
+
+    # Restore the TEMP path
+    if ($null -ne $TEMPORIG) { $env:TEMP="$TEMPORIG" }
+
     $Dur=New-TimeSpan -Start $StartTime -End $(Get-Date)
     $Dur=New-TimeSpan -Start $StartTime -End $(Get-Date)
     Write-Host -ForegroundColor $FinallyColour "`nINFO: executeCI.ps1 exiting at $(date). Duration $dur`n"
     Write-Host -ForegroundColor $FinallyColour "`nINFO: executeCI.ps1 exiting at $(date). Duration $dur`n"
+
+    exit $tmpLastExitCode
 }
 }

+ 1 - 1
hack/make.ps1

@@ -134,7 +134,7 @@ Function Check-InContainer() {
 # outside of a container where it may be out of date with master.
 # outside of a container where it may be out of date with master.
 Function Verify-GoVersion() {
 Function Verify-GoVersion() {
     Try {
     Try {
-        $goVersionDockerfile=(Select-String -Path ".\Dockerfile" -Pattern "^ARG[\s]+GO_VERSION=(.*)$").Matches.groups[1].Value.TrimEnd(".0")
+        $goVersionDockerfile=(Select-String -Path ".\Dockerfile" -Pattern "^ARG[\s]+GO_VERSION=(.*)$").Matches.groups[1].Value -replace '\.0$',''
         $goVersionInstalled=(go version).ToString().Split(" ")[2].SubString(2)
         $goVersionInstalled=(go version).ToString().Split(" ")[2].SubString(2)
     }
     }
     Catch [Exception] {
     Catch [Exception] {

+ 0 - 24
hack/make.sh

@@ -28,30 +28,6 @@ export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 export MAKEDIR="$SCRIPTDIR/make"
 export MAKEDIR="$SCRIPTDIR/make"
 export PKG_CONFIG=${PKG_CONFIG:-pkg-config}
 export PKG_CONFIG=${PKG_CONFIG:-pkg-config}
 
 
-# We're a nice, sexy, little shell script, and people might try to run us;
-# but really, they shouldn't. We want to be in a container!
-inContainer="AssumeSoInitially"
-if [ "$(go env GOHOSTOS)" = 'windows' ]; then
-	if [ -z "$FROM_DOCKERFILE" ]; then
-		unset inContainer
-	fi
-else
-	if [ "$PWD" != "/go/src/$DOCKER_PKG" ]; then
-		unset inContainer
-	fi
-fi
-
-if [ -z "$inContainer" ]; then
-	{
-		echo "# WARNING! I don't seem to be running in a Docker container."
-		echo "# The result of this command might be an incorrect build, and will not be"
-		echo "# officially supported."
-		echo "#"
-		echo "# Try this instead: make all"
-		echo "#"
-	} >&2
-fi
-
 echo
 echo
 
 
 # List of bundles to create when no argument is passed
 # List of bundles to create when no argument is passed

+ 20 - 2
hack/make/.integration-test-helpers

@@ -72,9 +72,27 @@ run_test_integration_suites() {
 	for dir in ${integration_api_dirs}; do
 	for dir in ${integration_api_dirs}; do
 		if ! (
 		if ! (
 			cd "$dir"
 			cd "$dir"
-			echo "Running $PWD flags=${flags}"
+			# Create a useful package name based on the tests's $dir. We need to take
+			# into account that  "$dir" can be either an absolute (/go/src/github.com/docker/docker/integration/foo)
+			# or relative (./integration/foo) path. To account for both, first we strip
+			# the absolute path, then remove any leading periods and slashes.
+			pkgname="${dir}"
+			pkgname="${pkgname#*${GOPATH}/src/${DOCKER_PKG}}"
+			pkgname="${pkgname#*.}"
+			pkgname="${pkgname#*\/}"
+
+			# Finally, we use periods as separator (instead of slashes) to be more
+			# in line with Java package names (which is what junit.xml was designed for)
+			pkgname="$(go env GOARCH).${pkgname//\//.}"
+			echo "Running $PWD (${pkgname}) flags=${flags}"
+			[ -n "$TESTDEBUG" ] && set -x
 			# shellcheck disable=SC2086
 			# shellcheck disable=SC2086
-			test_env ./test.main ${flags}
+			test_env gotestsum \
+				--format=standard-verbose \
+				--jsonfile="${ABS_DEST}/${pkgname//./-}-go-test-report.json" \
+				--junitfile="${ABS_DEST}/${pkgname//./-}-junit-report.xml" \
+				--raw-command \
+				-- go tool test2json -p "${pkgname}" -t ./test.main ${flags}
 		); then exit 1; fi
 		); then exit 1; fi
 	done
 	done
 }
 }

+ 5 - 5
hack/make/binary-daemon

@@ -15,16 +15,16 @@ copy_binaries() {
 	fi
 	fi
 	echo "Copying nested executables into $dir"
 	echo "Copying nested executables into $dir"
 	for file in containerd containerd-shim ctr runc docker-init docker-proxy rootlesskit rootlesskit-docker-proxy dockerd-rootless.sh; do
 	for file in containerd containerd-shim ctr runc docker-init docker-proxy rootlesskit rootlesskit-docker-proxy dockerd-rootless.sh; do
-		cp -f `which "$file"` "$dir/"
-		if [ "$hash" == "hash" ]; then
+		cp -f "$(command -v "$file")" "$dir/"
+		if [ "$hash" = "hash" ]; then
 			hash_files "$dir/$file"
 			hash_files "$dir/$file"
 		fi
 		fi
 	done
 	done
 
 
 	# vpnkit is amd64 only
 	# vpnkit is amd64 only
-	if which "vpnkit.$(uname -m)" 2>&1 >/dev/null; then
-		cp -f `which "vpnkit.$(uname -m)"` "$dir/vpnkit"
-		if [ "$hash" == "hash" ]; then
+	if command -v "vpnkit.$(uname -m)" 2>&1 >/dev/null; then
+		cp -f "$(command -v "vpnkit.$(uname -m)")" "$dir/vpnkit"
+		if [ "$hash" = "hash" ]; then
 			hash_files "$dir/vpnkit"
 			hash_files "$dir/vpnkit"
 		fi
 		fi
 	fi
 	fi

+ 1 - 0
hack/make/test-docker-py

@@ -13,6 +13,7 @@ source hack/make/.integration-test-helpers
 # TODO remove these skip once we update to a docker-py version that has https://github.com/docker/docker-py/pull/2369, https://github.com/docker/docker-py/pull/2380, https://github.com/docker/docker-py/pull/2382
 # TODO remove these skip once we update to a docker-py version that has https://github.com/docker/docker-py/pull/2369, https://github.com/docker/docker-py/pull/2380, https://github.com/docker/docker-py/pull/2382
 : "${PY_TEST_OPTIONS:=\
 : "${PY_TEST_OPTIONS:=\
 --deselect=tests/integration/api_swarm_test.py::SwarmTest::test_init_swarm_data_path_addr \
 --deselect=tests/integration/api_swarm_test.py::SwarmTest::test_init_swarm_data_path_addr \
+--deselect=tests/integration/api_container_test.py::AttachContainerTest::test_attach_no_stream \
 --deselect=tests/integration/api_exec_test.py::ExecTest::test_detach_with_arg \
 --deselect=tests/integration/api_exec_test.py::ExecTest::test_detach_with_arg \
 --deselect=tests/integration/api_exec_test.py::ExecDemuxTest::test_exec_command_tty_stream_no_demux \
 --deselect=tests/integration/api_exec_test.py::ExecDemuxTest::test_exec_command_tty_stream_no_demux \
 --deselect=tests/integration/api_build_test.py::BuildTest::test_build_invalid_platform \
 --deselect=tests/integration/api_build_test.py::BuildTest::test_build_invalid_platform \

+ 0 - 6
hack/make/test-integration

@@ -1,12 +1,6 @@
 #!/usr/bin/env bash
 #!/usr/bin/env bash
 set -e -o pipefail
 set -e -o pipefail
 
 
-if [ -n "$TEST_INTEGRATION_DEST" ]; then
-	export DEST="$ABS_DEST/$TEST_INTEGRATION_DEST"
-	export DOCKER_INTEGRATION_DAEMON_DEST="$DEST"
-	mkdir -p "$DEST"
-fi
-
 source hack/make/.integration-test-helpers
 source hack/make/.integration-test-helpers
 
 
 if [ ! -z "${TEST_SKIP_INTEGRATION}" ] && [ ! -z "${TEST_SKIP_INTEGRATION_CLI}" ]; then
 if [ ! -z "${TEST_SKIP_INTEGRATION}" ] && [ ! -z "${TEST_SKIP_INTEGRATION_CLI}" ]; then

+ 4 - 3
hack/validate/gometalinter

@@ -4,10 +4,11 @@ set -e -o pipefail
 SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 
 
 # CI platforms differ, so per-platform GOMETALINTER_OPTS can be set
 # CI platforms differ, so per-platform GOMETALINTER_OPTS can be set
-# from a platform-specific Dockerfile, otherwise let's just set
+# in the Jenkinsfile, otherwise let's just set a
 # (somewhat pessimistic) default of 10 minutes.
 # (somewhat pessimistic) default of 10 minutes.
-: ${GOMETALINTER_OPTS=--deadline=10m}
+: "${GOMETALINTER_OPTS=--deadline=10m}"
 
 
+# shellcheck disable=SC2086
 gometalinter \
 gometalinter \
 	${GOMETALINTER_OPTS} \
 	${GOMETALINTER_OPTS} \
-	--config ${SCRIPTDIR}/gometalinter.json ./...
+	--config "${SCRIPTDIR}/gometalinter.json" ./...

+ 1 - 1
integration-cli/benchmark_test.go

@@ -28,7 +28,7 @@ func (s *DockerSuite) BenchmarkConcurrentContainerActions(c *check.C) {
 			go func() {
 			go func() {
 				defer innerGroup.Done()
 				defer innerGroup.Done()
 				for i := 0; i < numIterations; i++ {
 				for i := 0; i < numIterations; i++ {
-					args := []string{"run", "-d", defaultSleepImage}
+					args := []string{"run", "-d", "busybox"}
 					args = append(args, sleepCommandForDaemonPlatform()...)
 					args = append(args, sleepCommandForDaemonPlatform()...)
 					out, _, err := dockerCmdWithError(args...)
 					out, _, err := dockerCmdWithError(args...)
 					if err != nil {
 					if err != nil {

+ 4 - 5
integration-cli/check_test.go

@@ -304,8 +304,8 @@ func init() {
 type DockerSwarmSuite struct {
 type DockerSwarmSuite struct {
 	server      *httptest.Server
 	server      *httptest.Server
 	ds          *DockerSuite
 	ds          *DockerSuite
+	daemonsLock sync.Mutex // protect access to daemons and portIndex
 	daemons     []*daemon.Daemon
 	daemons     []*daemon.Daemon
-	daemonsLock sync.Mutex // protect access to daemons
 	portIndex   int
 	portIndex   int
 }
 }
 
 
@@ -333,11 +333,11 @@ func (s *DockerSwarmSuite) AddDaemon(c *check.C, joinSwarm, manager bool) *daemo
 			d.StartAndSwarmInit(c)
 			d.StartAndSwarmInit(c)
 		}
 		}
 	} else {
 	} else {
-		d.StartNode(c)
+		d.StartNodeWithBusybox(c)
 	}
 	}
 
 
-	s.portIndex++
 	s.daemonsLock.Lock()
 	s.daemonsLock.Lock()
+	s.portIndex++
 	s.daemons = append(s.daemons, d)
 	s.daemons = append(s.daemons, d)
 	s.daemonsLock.Unlock()
 	s.daemonsLock.Unlock()
 
 
@@ -354,9 +354,8 @@ func (s *DockerSwarmSuite) TearDownTest(c *check.C) {
 		}
 		}
 	}
 	}
 	s.daemons = nil
 	s.daemons = nil
-	s.daemonsLock.Unlock()
-
 	s.portIndex = 0
 	s.portIndex = 0
+	s.daemonsLock.Unlock()
 	s.ds.TearDownTest(c)
 	s.ds.TearDownTest(c)
 }
 }
 
 

+ 22 - 0
integration-cli/daemon/daemon_swarm.go

@@ -189,3 +189,25 @@ func (d *Daemon) CheckLeader(c *check.C) (interface{}, check.CommentInterface) {
 	}
 	}
 	return fmt.Errorf("no leader"), check.Commentf("could not find leader")
 	return fmt.Errorf("no leader"), check.Commentf("could not find leader")
 }
 }
+
+// CmdRetryOutOfSequence tries the specified command against the current daemon
+// up to 10 times, retrying if it encounters an "update out of sequence" error.
+func (d *Daemon) CmdRetryOutOfSequence(args ...string) (string, error) {
+	var (
+		output string
+		err    error
+	)
+
+	for i := 0; i < 10; i++ {
+		output, err = d.Cmd(args...)
+		// error, no error, whatever. if we don't have "update out of
+		// sequence", we don't retry, we just return.
+		if !strings.Contains(output, "update out of sequence") {
+			return output, err
+		}
+	}
+
+	// otherwise, once all of our attempts have been exhausted, just return
+	// whatever the last values were.
+	return output, err
+}

+ 0 - 2
integration-cli/docker_api_exec_test.go

@@ -1,5 +1,3 @@
-// +build !test_no_exec
-
 package main
 package main
 
 
 import (
 import (

+ 2 - 2
integration-cli/docker_api_images_test.go

@@ -62,8 +62,8 @@ func (s *DockerSuite) TestAPIImagesSaveAndLoad(c *check.C) {
 		v, err := kernel.GetKernelVersion()
 		v, err := kernel.GetKernelVersion()
 		assert.NilError(c, err)
 		assert.NilError(c, err)
 		build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
 		build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
-		if build == 16299 {
-			c.Skip("Temporarily disabled on RS3 builds")
+		if build <= 16299 {
+			c.Skip("Temporarily disabled on RS3 and older because they are too slow. See #39909")
 		}
 		}
 	}
 	}
 
 

+ 14 - 14
integration-cli/docker_cli_build_test.go

@@ -4535,17 +4535,17 @@ func (s *DockerSuite) TestBuildBuildTimeArgEnv(c *check.C) {
 		ARG FOO6
 		ARG FOO6
 		ARG FO10
 		ARG FO10
 		RUN env
 		RUN env
-		RUN [ "$FOO1" == "fromcmd" ]
-		RUN [ "$FOO2" == "" ]
-		RUN [ "$FOO3" == "fromenv" ]
-		RUN [ "$FOO4" == "fromfile" ]
-		RUN [ "$FOO5" == "fromcmd" ]
+		RUN [ "$FOO1" = "fromcmd" ]
+		RUN [ "$FOO2" = "" ]
+		RUN [ "$FOO3" = "fromenv" ]
+		RUN [ "$FOO4" = "fromfile" ]
+		RUN [ "$FOO5" = "fromcmd" ]
 		# The following should not exist at all in the env
 		# The following should not exist at all in the env
-		RUN [ "$(env | grep FOO6)" == "" ]
-		RUN [ "$(env | grep FOO7)" == "" ]
-		RUN [ "$(env | grep FOO8)" == "" ]
-		RUN [ "$(env | grep FOO9)" == "" ]
-		RUN [ "$FO10" == "" ]
+		RUN [ "$(env | grep FOO6)" = "" ]
+		RUN [ "$(env | grep FOO7)" = "" ]
+		RUN [ "$(env | grep FOO8)" = "" ]
+		RUN [ "$(env | grep FOO9)" = "" ]
+		RUN [ "$FO10" = "" ]
 	    `
 	    `
 	result := buildImage("testbuildtimeargenv",
 	result := buildImage("testbuildtimeargenv",
 		cli.WithFlags(
 		cli.WithFlags(
@@ -4615,9 +4615,9 @@ func (s *DockerSuite) TestBuildBuildTimeArgEmptyValVariants(c *check.C) {
 		ARG %s=
 		ARG %s=
 		ARG %s=""
 		ARG %s=""
 		ARG %s=''
 		ARG %s=''
-		RUN [ "$%s" == "$%s" ]
-		RUN [ "$%s" == "$%s" ]
-		RUN [ "$%s" == "$%s" ]`, envKey, envKey1, envKey2, envKey, envKey1, envKey1, envKey2, envKey, envKey2)
+		RUN [ "$%s" = "$%s" ]
+		RUN [ "$%s" = "$%s" ]
+		RUN [ "$%s" = "$%s" ]`, envKey, envKey1, envKey2, envKey, envKey1, envKey1, envKey2, envKey, envKey2)
 	buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile))
 	buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile))
 }
 }
 
 
@@ -5952,7 +5952,7 @@ func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *check.C) {
 }
 }
 
 
 // #33176
 // #33176
-func (s *DockerSuite) TestBuildMulitStageResetScratch(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageResetScratch(c *check.C) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, DaemonIsLinux)
 
 
 	dockerfile := `
 	dockerfile := `

+ 8 - 0
integration-cli/docker_cli_create_test.go

@@ -309,7 +309,15 @@ func (s *DockerSuite) TestCreateWithWorkdir(c *check.C) {
 	// Windows does not create the workdir until the container is started
 	// Windows does not create the workdir until the container is started
 	if testEnv.OSType == "windows" {
 	if testEnv.OSType == "windows" {
 		dockerCmd(c, "start", name)
 		dockerCmd(c, "start", name)
+		if IsolationIsHyperv() {
+			// Hyper-V isolated containers do not allow file-operations on a
+			// running container. This test currently uses `docker cp` to verify
+			// that the WORKDIR was automatically created, which cannot be done
+			// while the container is running.
+			dockerCmd(c, "stop", name)
+		}
 	}
 	}
+	// TODO: rewrite this test to not use `docker cp` for verifying that the WORKDIR was created
 	dockerCmd(c, "cp", fmt.Sprintf("%s:%s", name, dir), prefix+slash+"tmp")
 	dockerCmd(c, "cp", fmt.Sprintf("%s:%s", name, dir), prefix+slash+"tmp")
 }
 }
 
 

+ 3 - 3
integration-cli/docker_cli_daemon_plugins_test.go

@@ -121,7 +121,7 @@ func (s *DockerDaemonSuite) TestDaemonShutdownLiveRestoreWithPlugins(c *check.C)
 
 
 // TestDaemonShutdownWithPlugins shuts down running plugins.
 // TestDaemonShutdownWithPlugins shuts down running plugins.
 func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
-	testRequires(c, IsAmd64, Network, testEnv.IsLocalDaemon)
+	testRequires(c, IsAmd64, Network)
 
 
 	s.d.Start(c)
 	s.d.Start(c)
 	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
 	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
@@ -159,7 +159,7 @@ func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
 
 
 // TestDaemonKillWithPlugins leaves plugins running.
 // TestDaemonKillWithPlugins leaves plugins running.
 func (s *DockerDaemonSuite) TestDaemonKillWithPlugins(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonKillWithPlugins(c *check.C) {
-	testRequires(c, IsAmd64, Network, testEnv.IsLocalDaemon)
+	testRequires(c, IsAmd64, Network)
 
 
 	s.d.Start(c)
 	s.d.Start(c)
 	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
 	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
@@ -232,7 +232,7 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestPluginVolumeRemoveOnRestart(c *check.C) {
 func (s *DockerDaemonSuite) TestPluginVolumeRemoveOnRestart(c *check.C) {
-	testRequires(c, DaemonIsLinux, Network, IsAmd64)
+	testRequires(c, IsAmd64, Network)
 
 
 	s.d.Start(c, "--live-restore=true")
 	s.d.Start(c, "--live-restore=true")
 
 

+ 3 - 6
integration-cli/docker_cli_daemon_test.go

@@ -26,7 +26,6 @@ import (
 	"github.com/cloudflare/cfssl/helpers"
 	"github.com/cloudflare/cfssl/helpers"
 	"github.com/creack/pty"
 	"github.com/creack/pty"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
-	moby_daemon "github.com/docker/docker/daemon"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
 	"github.com/docker/docker/integration-cli/cli/build"
@@ -826,7 +825,6 @@ func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4ExplicitOutsideContainer
 }
 }
 
 
 func (s *DockerDaemonSuite) TestDaemonDefaultNetworkInvalidClusterConfig(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonDefaultNetworkInvalidClusterConfig(c *check.C) {
-	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 
 
 	// Start daemon without docker0 bridge
 	// Start daemon without docker0 bridge
 	defaultNetworkBridge := "docker0"
 	defaultNetworkBridge := "docker0"
@@ -967,7 +965,6 @@ func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *che
 }
 }
 
 
 func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) {
-	testRequires(c, DaemonIsLinux)
 
 
 	s.d.StartWithBusybox(c, "--default-ulimit", "nofile=42:42", "--default-ulimit", "nproc=1024:1024")
 	s.d.StartWithBusybox(c, "--default-ulimit", "nofile=42:42", "--default-ulimit", "nproc=1024:1024")
 
 
@@ -1457,7 +1454,7 @@ func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonAndContainerKill(c *chec
 
 
 	// kill the container
 	// kill the container
 	icmd.RunCommand(ctrBinary, "--address", containerdSocket,
 	icmd.RunCommand(ctrBinary, "--address", containerdSocket,
-		"--namespace", moby_daemon.ContainersNamespace, "tasks", "kill", id).Assert(c, icmd.Success)
+		"--namespace", d.ContainersNamespace(), "tasks", "kill", id).Assert(c, icmd.Success)
 
 
 	// restart daemon.
 	// restart daemon.
 	d.Restart(c)
 	d.Restart(c)
@@ -1977,7 +1974,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithKilledRunningContainer(t *check
 
 
 	// kill the container
 	// kill the container
 	icmd.RunCommand(ctrBinary, "--address", containerdSocket,
 	icmd.RunCommand(ctrBinary, "--address", containerdSocket,
-		"--namespace", moby_daemon.ContainersNamespace, "tasks", "kill", cid).Assert(t, icmd.Success)
+		"--namespace", s.d.ContainersNamespace(), "tasks", "kill", cid).Assert(t, icmd.Success)
 
 
 	// Give time to containerd to process the command if we don't
 	// Give time to containerd to process the command if we don't
 	// the exit event might be received after we do the inspect
 	// the exit event might be received after we do the inspect
@@ -2080,7 +2077,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *che
 	result := icmd.RunCommand(
 	result := icmd.RunCommand(
 		ctrBinary,
 		ctrBinary,
 		"--address", containerdSocket,
 		"--address", containerdSocket,
-		"--namespace", moby_daemon.ContainersNamespace,
+		"--namespace", s.d.ContainersNamespace(),
 		"tasks", "resume", cid)
 		"tasks", "resume", cid)
 	result.Assert(t, icmd.Success)
 	result.Assert(t, icmd.Success)
 
 

+ 0 - 2
integration-cli/docker_cli_events_unix_test.go

@@ -388,7 +388,6 @@ func (s *DockerSuite) TestEventsFilterNetworkID(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
-	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 
 	// daemon config file
 	// daemon config file
 	configFilePath := "test.json"
 	configFilePath := "test.json"
@@ -457,7 +456,6 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
-	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 
 	// daemon config file
 	// daemon config file
 	configFilePath := "test.json"
 	configFilePath := "test.json"

+ 1 - 4
integration-cli/docker_cli_exec_test.go

@@ -1,5 +1,3 @@
-// +build !test_no_exec
-
 package main
 package main
 
 
 import (
 import (
@@ -81,8 +79,7 @@ func (s *DockerSuite) TestExecAfterContainerRestart(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestExecAfterDaemonRestart(c *check.C) {
 func (s *DockerDaemonSuite) TestExecAfterDaemonRestart(c *check.C) {
-	// TODO Windows CI: Requires a little work to get this ported.
-	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
+	// TODO Windows CI: DockerDaemonSuite doesn't run on Windows, and requires a little work to get this ported.
 	s.d.StartWithBusybox(c)
 	s.d.StartWithBusybox(c)
 
 
 	out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top")
 	out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top")

+ 1 - 1
integration-cli/docker_cli_exec_unix_test.go

@@ -1,4 +1,4 @@
-// +build !windows,!test_no_exec
+// +build !windows
 
 
 package main
 package main
 
 

+ 0 - 1
integration-cli/docker_cli_info_test.go

@@ -211,7 +211,6 @@ func (s *DockerSuite) TestInsecureRegistries(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestRegistryMirrors(c *check.C) {
 func (s *DockerDaemonSuite) TestRegistryMirrors(c *check.C) {
-	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 
 	registryMirror1 := "https://192.168.1.2"
 	registryMirror1 := "https://192.168.1.2"
 	registryMirror2 := "http://registry.mirror.com:5000"
 	registryMirror2 := "http://registry.mirror.com:5000"

+ 2 - 2
integration-cli/docker_cli_links_test.go

@@ -140,7 +140,7 @@ func (s *DockerSuite) TestLinksNotStartedParentNotFail(c *check.C) {
 
 
 func (s *DockerSuite) TestLinksHostsFilesInject(c *check.C) {
 func (s *DockerSuite) TestLinksHostsFilesInject(c *check.C) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, DaemonIsLinux)
-	testRequires(c, testEnv.IsLocalDaemon, ExecSupport)
+	testRequires(c, testEnv.IsLocalDaemon)
 
 
 	out, _ := dockerCmd(c, "run", "-itd", "--name", "one", "busybox", "top")
 	out, _ := dockerCmd(c, "run", "-itd", "--name", "one", "busybox", "top")
 	idOne := strings.TrimSpace(out)
 	idOne := strings.TrimSpace(out)
@@ -158,7 +158,7 @@ func (s *DockerSuite) TestLinksHostsFilesInject(c *check.C) {
 
 
 func (s *DockerSuite) TestLinksUpdateOnRestart(c *check.C) {
 func (s *DockerSuite) TestLinksUpdateOnRestart(c *check.C) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, DaemonIsLinux)
-	testRequires(c, testEnv.IsLocalDaemon, ExecSupport)
+	testRequires(c, testEnv.IsLocalDaemon)
 	dockerCmd(c, "run", "-d", "--name", "one", "busybox", "top")
 	dockerCmd(c, "run", "-d", "--name", "one", "busybox", "top")
 	out, _ := dockerCmd(c, "run", "-d", "--name", "two", "--link", "one:onetwo", "--link", "one:one", "busybox", "top")
 	out, _ := dockerCmd(c, "run", "-d", "--name", "two", "--link", "one:onetwo", "--link", "one:one", "busybox", "top")
 	id := strings.TrimSpace(string(out))
 	id := strings.TrimSpace(string(out))

+ 1 - 3
integration-cli/docker_cli_network_unix_test.go

@@ -805,7 +805,6 @@ func (s *DockerNetworkSuite) TestDockerPluginV2NetworkDriver(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *check.C) {
 func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *check.C) {
-	testRequires(c, ExecSupport)
 	// On default bridge network built-in service discovery should not happen
 	// On default bridge network built-in service discovery should not happen
 	hostsFile := "/etc/hosts"
 	hostsFile := "/etc/hosts"
 	bridgeName := "external-bridge"
 	bridgeName := "external-bridge"
@@ -863,7 +862,7 @@ func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *
 }
 }
 
 
 func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
 func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
-	testRequires(c, ExecSupport, NotArm)
+	testRequires(c, NotArm)
 	hostsFile := "/etc/hosts"
 	hostsFile := "/etc/hosts"
 	cstmBridgeNw := "custom-bridge-nw"
 	cstmBridgeNw := "custom-bridge-nw"
 	cstmBridgeNw1 := "custom-bridge-nw1"
 	cstmBridgeNw1 := "custom-bridge-nw1"
@@ -1665,7 +1664,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkCreateDeleteSpecialCharacters(c *c
 }
 }
 
 
 func (s *DockerDaemonSuite) TestDaemonRestartRestoreBridgeNetwork(t *check.C) {
 func (s *DockerDaemonSuite) TestDaemonRestartRestoreBridgeNetwork(t *check.C) {
-	testRequires(t, DaemonIsLinux)
 	s.d.StartWithBusybox(t, "--live-restore")
 	s.d.StartWithBusybox(t, "--live-restore")
 	defer s.d.Stop(t)
 	defer s.d.Stop(t)
 	oldCon := "old"
 	oldCon := "old"

+ 0 - 1
integration-cli/docker_cli_proxy_test.go

@@ -21,7 +21,6 @@ func (s *DockerSuite) TestCLIProxyDisableProxyUnixSock(c *check.C) {
 // Can't use localhost here since go has a special case to not use proxy if connecting to localhost
 // Can't use localhost here since go has a special case to not use proxy if connecting to localhost
 // See https://golang.org/pkg/net/http/#ProxyFromEnvironment
 // See https://golang.org/pkg/net/http/#ProxyFromEnvironment
 func (s *DockerDaemonSuite) TestCLIProxyProxyTCPSock(c *check.C) {
 func (s *DockerDaemonSuite) TestCLIProxyProxyTCPSock(c *check.C) {
-	testRequires(c, testEnv.IsLocalDaemon)
 	// get the IP to use to connect since we can't use localhost
 	// get the IP to use to connect since we can't use localhost
 	addrs, err := net.InterfaceAddrs()
 	addrs, err := net.InterfaceAddrs()
 	assert.NilError(c, err)
 	assert.NilError(c, err)

+ 1 - 1
integration-cli/docker_cli_restart_test.go

@@ -292,7 +292,7 @@ func (s *DockerSuite) TestRestartContainerwithRestartPolicy(c *check.C) {
 	dockerCmd(c, "start", id1)
 	dockerCmd(c, "start", id1)
 	dockerCmd(c, "start", id2)
 	dockerCmd(c, "start", id2)
 
 
-	// Kill the containers, making sure the are stopped at the end of the test
+	// Kill the containers, making sure they are stopped at the end of the test
 	dockerCmd(c, "kill", id1)
 	dockerCmd(c, "kill", id1)
 	dockerCmd(c, "kill", id2)
 	dockerCmd(c, "kill", id2)
 	err = waitInspect(id1, "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTimeout)
 	err = waitInspect(id1, "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTimeout)

+ 3 - 5
integration-cli/docker_cli_run_test.go

@@ -1792,16 +1792,14 @@ func (s *DockerSuite) TestRunExitOnStdinClose(c *check.C) {
 func (s *DockerSuite) TestRunInteractiveWithRestartPolicy(c *check.C) {
 func (s *DockerSuite) TestRunInteractiveWithRestartPolicy(c *check.C) {
 	name := "test-inter-restart"
 	name := "test-inter-restart"
 
 
-	result := icmd.StartCmd(icmd.Cmd{
+	result := icmd.RunCmd(icmd.Cmd{
 		Command: []string{dockerBinary, "run", "-i", "--name", name, "--restart=always", "busybox", "sh"},
 		Command: []string{dockerBinary, "run", "-i", "--name", name, "--restart=always", "busybox", "sh"},
 		Stdin:   bytes.NewBufferString("exit 11"),
 		Stdin:   bytes.NewBufferString("exit 11"),
 	})
 	})
-	assert.NilError(c, result.Error)
 	defer func() {
 	defer func() {
-		dockerCmdWithResult("stop", name).Assert(c, icmd.Success)
+		cli.Docker(cli.Args("stop", name)).Assert(c, icmd.Success)
 	}()
 	}()
 
 
-	result = icmd.WaitOnCmd(60*time.Second, result)
 	result.Assert(c, icmd.Expected{ExitCode: 11})
 	result.Assert(c, icmd.Expected{ExitCode: 11})
 }
 }
 
 
@@ -3430,7 +3428,7 @@ func (s *DockerSuite) TestRunLoopbackWhenNetworkDisabled(c *check.C) {
 
 
 func (s *DockerSuite) TestRunModeNetContainerHostname(c *check.C) {
 func (s *DockerSuite) TestRunModeNetContainerHostname(c *check.C) {
 	// Windows does not support --net=container
 	// Windows does not support --net=container
-	testRequires(c, DaemonIsLinux, ExecSupport)
+	testRequires(c, DaemonIsLinux)
 
 
 	dockerCmd(c, "run", "-i", "-d", "--name", "parent", "busybox", "top")
 	dockerCmd(c, "run", "-i", "-d", "--name", "parent", "busybox", "top")
 	out, _ := dockerCmd(c, "exec", "parent", "cat", "/etc/hostname")
 	out, _ := dockerCmd(c, "exec", "parent", "cat", "/etc/hostname")

+ 4 - 4
integration-cli/docker_cli_run_unix_test.go

@@ -1442,7 +1442,7 @@ func (s *DockerSuite) TestRunUserDeviceAllowed(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestRunSeccompJSONNewFormat(c *check.C) {
 func (s *DockerDaemonSuite) TestRunSeccompJSONNewFormat(c *check.C) {
-	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
+	testRequires(c, seccompEnabled)
 
 
 	s.d.StartWithBusybox(c)
 	s.d.StartWithBusybox(c)
 
 
@@ -1467,7 +1467,7 @@ func (s *DockerDaemonSuite) TestRunSeccompJSONNewFormat(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestRunSeccompJSONNoNameAndNames(c *check.C) {
 func (s *DockerDaemonSuite) TestRunSeccompJSONNoNameAndNames(c *check.C) {
-	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
+	testRequires(c, seccompEnabled)
 
 
 	s.d.StartWithBusybox(c)
 	s.d.StartWithBusybox(c)
 
 
@@ -1493,7 +1493,7 @@ func (s *DockerDaemonSuite) TestRunSeccompJSONNoNameAndNames(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *check.C) {
 func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *check.C) {
-	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
+	testRequires(c, seccompEnabled)
 
 
 	s.d.StartWithBusybox(c)
 	s.d.StartWithBusybox(c)
 
 
@@ -1530,7 +1530,7 @@ func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *check.C) {
 }
 }
 
 
 func (s *DockerDaemonSuite) TestRunWithDaemonDefaultSeccompProfile(c *check.C) {
 func (s *DockerDaemonSuite) TestRunWithDaemonDefaultSeccompProfile(c *check.C) {
-	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
+	testRequires(c, seccompEnabled)
 
 
 	s.d.StartWithBusybox(c)
 	s.d.StartWithBusybox(c)
 
 

+ 1 - 1
integration-cli/docker_cli_service_health_test.go

@@ -28,7 +28,7 @@ func (s *DockerSwarmSuite) TestServiceHealthRun(c *check.C) {
 	result := cli.BuildCmd(c, imageName, cli.Daemon(d),
 	result := cli.BuildCmd(c, imageName, cli.Daemon(d),
 		build.WithDockerfile(`FROM busybox
 		build.WithDockerfile(`FROM busybox
 		RUN touch /status
 		RUN touch /status
-		HEALTHCHECK --interval=1s --timeout=1s --retries=1\
+		HEALTHCHECK --interval=1s --timeout=5s --retries=1\
 		  CMD cat /status`),
 		  CMD cat /status`),
 	)
 	)
 	result.Assert(c, icmd.Success)
 	result.Assert(c, icmd.Success)

+ 2 - 2
integration-cli/docker_cli_service_scale_test.go

@@ -14,11 +14,11 @@ func (s *DockerSwarmSuite) TestServiceScale(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 	d := s.AddDaemon(c, true, true)
 
 
 	service1Name := "TestService1"
 	service1Name := "TestService1"
-	service1Args := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", service1Name, defaultSleepImage}, sleepCommandForDaemonPlatform()...)
+	service1Args := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", service1Name, "busybox"}, sleepCommandForDaemonPlatform()...)
 
 
 	// global mode
 	// global mode
 	service2Name := "TestService2"
 	service2Name := "TestService2"
-	service2Args := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", service2Name, "--mode=global", defaultSleepImage}, sleepCommandForDaemonPlatform()...)
+	service2Args := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", service2Name, "--mode=global", "busybox"}, sleepCommandForDaemonPlatform()...)
 
 
 	// Create services
 	// Create services
 	_, err := d.Cmd(service1Args...)
 	_, err := d.Cmd(service1Args...)

+ 8 - 4
integration-cli/docker_cli_swarm_test.go

@@ -277,19 +277,23 @@ func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 	d := s.AddDaemon(c, true, true)
 
 
 	name := "top"
 	name := "top"
+	// this first command does not have to be retried because service creates
+	// don't return out of sequence errors.
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--label", "x=y", "busybox", "top")
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--label", "x=y", "busybox", "top")
 	assert.NilError(c, err, out)
 	assert.NilError(c, err, out)
 	assert.Assert(c, strings.TrimSpace(out) != "")
 	assert.Assert(c, strings.TrimSpace(out) != "")
 
 
-	out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name)
+	out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", name)
 	assert.NilError(c, err, out)
 	assert.NilError(c, err, out)
 
 
-	out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name)
+	out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", name)
 	assert.NilError(c, err, out)
 	assert.NilError(c, err, out)
 
 
-	_, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", "--publish-add", "80:20", name)
+	_, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", "--publish-add", "80:20", name)
 	assert.ErrorContains(c, err, "")
 	assert.ErrorContains(c, err, "")
 
 
+	// this last command does not have to be retried because service inspect
+	// does not return out of sequence errors.
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.EndpointSpec.Ports }}", name)
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.EndpointSpec.Ports }}", name)
 	assert.NilError(c, err, out)
 	assert.NilError(c, err, out)
 	assert.Equal(c, strings.TrimSpace(out), "[{ tcp 80 80 ingress}]")
 	assert.Equal(c, strings.TrimSpace(out), "[{ tcp 80 80 ingress}]")
@@ -838,7 +842,7 @@ func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *check.C) {
 
 
 	name := "top"
 	name := "top"
 
 
-	ttyCheck := "if [ -t 0 ]; then echo TTY > /status && top; else echo none > /status && top; fi"
+	ttyCheck := "if [ -t 0 ]; then echo TTY > /status; else echo none > /status; fi; exec top"
 
 
 	// Without --tty
 	// Without --tty
 	expectedOutput := "none"
 	expectedOutput := "none"

+ 1 - 1
integration-cli/docker_cli_userns_test.go

@@ -23,7 +23,7 @@ import (
 // 1. validate uid/gid maps are set properly
 // 1. validate uid/gid maps are set properly
 // 2. verify that files created are owned by remapped root
 // 2. verify that files created are owned by remapped root
 func (s *DockerDaemonSuite) TestDaemonUserNamespaceRootSetting(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonUserNamespaceRootSetting(c *check.C) {
-	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, UserNamespaceInKernel)
+	testRequires(c, UserNamespaceInKernel)
 
 
 	s.d.StartWithBusybox(c, "--userns-remap", "default")
 	s.d.StartWithBusybox(c, "--userns-remap", "default")
 
 

+ 2 - 2
integration-cli/docker_utils_test.go

@@ -63,7 +63,7 @@ func getContainerCount(c *check.C) int {
 	for _, line := range lines {
 	for _, line := range lines {
 		if strings.Contains(line, containers) {
 		if strings.Contains(line, containers) {
 			output := strings.TrimSpace(line)
 			output := strings.TrimSpace(line)
-			output = strings.TrimLeft(output, containers)
+			output = strings.TrimPrefix(output, containers)
 			output = strings.Trim(output, " ")
 			output = strings.Trim(output, " ")
 			containerCount, err := strconv.Atoi(output)
 			containerCount, err := strconv.Atoi(output)
 			assert.NilError(c, err)
 			assert.NilError(c, err)
@@ -347,7 +347,7 @@ func getInspectBody(c *check.C, version, id string) []byte {
 // Run a long running idle task in a background container using the
 // Run a long running idle task in a background container using the
 // system-specific default image and command.
 // system-specific default image and command.
 func runSleepingContainer(c *check.C, extraArgs ...string) string {
 func runSleepingContainer(c *check.C, extraArgs ...string) string {
-	return runSleepingContainerInImage(c, defaultSleepImage, extraArgs...)
+	return runSleepingContainerInImage(c, "busybox", extraArgs...)
 }
 }
 
 
 // Run a long running idle task in a background container using the specified
 // Run a long running idle task in a background container using the specified

+ 0 - 4
integration-cli/requirements_test.go

@@ -80,10 +80,6 @@ func UnixCli() bool {
 	return isUnixCli
 	return isUnixCli
 }
 }
 
 
-func ExecSupport() bool {
-	return supportsExec
-}
-
 func Network() bool {
 func Network() bool {
 	// Set a timeout on the GET at 15s
 	// Set a timeout on the GET at 15s
 	var timeout = time.Duration(15 * time.Second)
 	var timeout = time.Duration(15 * time.Second)

+ 0 - 23
integration-cli/requirements_unix_test.go

@@ -84,20 +84,11 @@ func bridgeNfIptables() bool {
 	return !SysInfo.BridgeNFCallIPTablesDisabled
 	return !SysInfo.BridgeNFCallIPTablesDisabled
 }
 }
 
 
-func bridgeNfIP6tables() bool {
-	return !SysInfo.BridgeNFCallIP6TablesDisabled
-}
-
 func unprivilegedUsernsClone() bool {
 func unprivilegedUsernsClone() bool {
 	content, err := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
 	content, err := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
 	return err != nil || !strings.Contains(string(content), "0")
 	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 {
 func overlayFSSupported() bool {
 	cmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "/bin/sh", "-c", "cat /proc/filesystems")
 	cmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "/bin/sh", "-c", "cat /proc/filesystems")
 	out, err := cmd.CombinedOutput()
 	out, err := cmd.CombinedOutput()
@@ -107,20 +98,6 @@ func overlayFSSupported() bool {
 	return bytes.Contains(out, []byte("overlay\n"))
 	return bytes.Contains(out, []byte("overlay\n"))
 }
 }
 
 
-func overlay2Supported() bool {
-	if !overlayFSSupported() {
-		return false
-	}
-
-	daemonV, err := kernel.ParseRelease(testEnv.DaemonInfo.KernelVersion)
-	if err != nil {
-		return false
-	}
-	requiredV := kernel.VersionInfo{Kernel: 4}
-	return kernel.CompareKernelVersion(*daemonV, requiredV) > -1
-
-}
-
 func init() {
 func init() {
 	if testEnv.IsLocalDaemon() {
 	if testEnv.IsLocalDaemon() {
 		SysInfo = sysinfo.New(true)
 		SysInfo = sysinfo.New(true)

+ 0 - 8
integration-cli/test_vars_exec_test.go

@@ -1,8 +0,0 @@
-// +build !test_no_exec
-
-package main
-
-const (
-	// indicates docker daemon tested supports 'docker exec'
-	supportsExec = true
-)

+ 0 - 8
integration-cli/test_vars_noexec_test.go

@@ -1,8 +0,0 @@
-// +build test_no_exec
-
-package main
-
-const (
-	// indicates docker daemon tested supports 'docker exec'
-	supportsExec = false
-)

+ 0 - 4
integration-cli/test_vars_unix_test.go

@@ -7,8 +7,4 @@ const (
 	isUnixCli = true
 	isUnixCli = true
 
 
 	expectedFileChmod = "-rw-r--r--"
 	expectedFileChmod = "-rw-r--r--"
-
-	// On Unix variants, the busybox image comes with the `top` command which
-	// runs indefinitely while still being interruptible by a signal.
-	defaultSleepImage = "busybox"
 )
 )

+ 0 - 4
integration-cli/test_vars_windows_test.go

@@ -8,8 +8,4 @@ const (
 
 
 	// this is the expected file permission set on windows: gh#11395
 	// this is the expected file permission set on windows: gh#11395
 	expectedFileChmod = "-rwxr-xr-x"
 	expectedFileChmod = "-rwxr-xr-x"
-
-	// On Windows, the busybox image doesn't have the `top` command, so we rely
-	// on `sleep` with a high duration.
-	defaultSleepImage = "busybox"
 )
 )

+ 3 - 12
integration/build/build_session_test.go

@@ -9,8 +9,8 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/versions"
 	dclient "github.com/docker/docker/client"
 	dclient "github.com/docker/docker/client"
-	"github.com/docker/docker/internal/test/daemon"
 	"github.com/docker/docker/internal/test/fakecontext"
 	"github.com/docker/docker/internal/test/fakecontext"
 	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/internal/test/request"
 	"github.com/moby/buildkit/session"
 	"github.com/moby/buildkit/session"
@@ -23,18 +23,9 @@ import (
 
 
 func TestBuildWithSession(t *testing.T) {
 func TestBuildWithSession(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "experimental in older versions")
 
 
-	var client dclient.APIClient
-	if !testEnv.DaemonInfo.ExperimentalBuild {
-		skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
-
-		d := daemon.New(t, daemon.WithExperimental)
-		d.StartWithBusybox(t)
-		defer d.Stop(t)
-		client = d.NewClientT(t)
-	} else {
-		client = testEnv.APIClient()
-	}
+	client := testEnv.APIClient()
 
 
 	dockerfile := `
 	dockerfile := `
 		FROM busybox
 		FROM busybox

+ 1 - 1
integration/build/build_squash_test.go

@@ -100,7 +100,7 @@ func TestBuildSquashParent(t *testing.T) {
 	)
 	)
 	container.Run(ctx, t, client,
 	container.Run(ctx, t, client,
 		container.WithImage(name),
 		container.WithImage(name),
-		container.WithCmd("/bin/sh", "-c", `[ "$(echo $HELLO)" == "world" ]`),
+		container.WithCmd("/bin/sh", "-c", `[ "$(echo $HELLO)" = "world" ]`),
 	)
 	)
 
 
 	origHistory, err := client.ImageHistory(ctx, origID)
 	origHistory, err := client.ImageHistory(ctx, origID)

+ 3 - 5
integration/network/ipvlan/ipvlan_test.go

@@ -1,6 +1,6 @@
 // +build !windows
 // +build !windows
 
 
-package ipvlan
+package ipvlan // import "github.com/docker/docker/integration/network/ipvlan"
 
 
 import (
 import (
 	"context"
 	"context"
@@ -22,11 +22,10 @@ import (
 
 
 func TestDockerNetworkIpvlanPersistance(t *testing.T) {
 func TestDockerNetworkIpvlanPersistance(t *testing.T) {
 	// verify the driver automatically provisions the 802.1q link (di-dummy0.70)
 	// verify the driver automatically provisions the 802.1q link (di-dummy0.70)
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, !ipvlanKernelSupport(t), "Kernel doesn't support ipvlan")
 	skip.If(t, !ipvlanKernelSupport(t), "Kernel doesn't support ipvlan")
 
 
-	d := daemon.New(t, daemon.WithExperimental)
+	d := daemon.New(t)
 	d.StartWithBusybox(t)
 	d.StartWithBusybox(t)
 	defer d.Stop(t)
 	defer d.Stop(t)
 
 
@@ -50,7 +49,6 @@ func TestDockerNetworkIpvlanPersistance(t *testing.T) {
 }
 }
 
 
 func TestDockerNetworkIpvlan(t *testing.T) {
 func TestDockerNetworkIpvlan(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, !ipvlanKernelSupport(t), "Kernel doesn't support ipvlan")
 	skip.If(t, !ipvlanKernelSupport(t), "Kernel doesn't support ipvlan")
 
 
@@ -87,7 +85,7 @@ func TestDockerNetworkIpvlan(t *testing.T) {
 			test: testIpvlanAddressing,
 			test: testIpvlanAddressing,
 		},
 		},
 	} {
 	} {
-		d := daemon.New(t, daemon.WithExperimental)
+		d := daemon.New(t)
 		d.StartWithBusybox(t)
 		d.StartWithBusybox(t)
 		c := d.NewClientT(t)
 		c := d.NewClientT(t)
 
 

+ 2 - 0
integration/network/ipvlan/main_test.go

@@ -1,3 +1,5 @@
+// +build !windows
+
 package ipvlan // import "github.com/docker/docker/integration/network/ipvlan"
 package ipvlan // import "github.com/docker/docker/integration/network/ipvlan"
 
 
 import (
 import (

+ 1 - 0
integration/network/ipvlan/main_windows_test.go

@@ -0,0 +1 @@
+package ipvlan // import "github.com/docker/docker/integration/network/ipvlan"

+ 1 - 8
integration/network/macvlan/macvlan_test.go

@@ -1,6 +1,6 @@
 // +build !windows
 // +build !windows
 
 
-package macvlan
+package macvlan // import "github.com/docker/docker/integration/network/macvlan"
 
 
 import (
 import (
 	"context"
 	"context"
@@ -20,7 +20,6 @@ import (
 func TestDockerNetworkMacvlanPersistance(t *testing.T) {
 func TestDockerNetworkMacvlanPersistance(t *testing.T) {
 	// verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
 	// verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
 	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, testEnv.IsRemoteDaemon)
-	skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
 
 
 	d := daemon.New(t)
 	d := daemon.New(t)
 	d.StartWithBusybox(t)
 	d.StartWithBusybox(t)
@@ -43,7 +42,6 @@ func TestDockerNetworkMacvlanPersistance(t *testing.T) {
 
 
 func TestDockerNetworkMacvlan(t *testing.T) {
 func TestDockerNetworkMacvlan(t *testing.T) {
 	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, testEnv.IsRemoteDaemon)
-	skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
 
 
 	for _, tc := range []struct {
 	for _, tc := range []struct {
 		name string
 		name string
@@ -271,8 +269,3 @@ func testMacvlanAddressing(client client.APIClient) func(*testing.T) {
 		assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abca::254 dev eth0"))
 		assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abca::254 dev eth0"))
 	}
 	}
 }
 }
-
-// ensure Kernel version is >= v3.9 for macvlan support
-func macvlanKernelSupport() bool {
-	return n.CheckKernelMajorVersionGreaterOrEqualThen(3, 9)
-}

+ 2 - 0
integration/network/macvlan/main_test.go

@@ -1,3 +1,5 @@
+// +build !windows
+
 package macvlan // import "github.com/docker/docker/integration/network/macvlan"
 package macvlan // import "github.com/docker/docker/integration/network/macvlan"
 
 
 import (
 import (

+ 1 - 0
integration/network/macvlan/main_windows_test.go

@@ -0,0 +1 @@
+package macvlan // import "github.com/docker/docker/integration/network/macvlan"

+ 1 - 21
integration/service/inspect_test.go

@@ -7,9 +7,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
-	"github.com/docker/docker/api/types/filters"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/swarm"
 	"github.com/docker/docker/integration/internal/swarm"
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
@@ -38,7 +36,7 @@ func TestInspect(t *testing.T) {
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	id := resp.ID
 	id := resp.ID
-	poll.WaitOn(t, serviceContainerCount(client, id, instances))
+	poll.WaitOn(t, swarm.RunningTasksCount(client, id, instances))
 
 
 	service, _, err := client.ServiceInspectWithRaw(ctx, id, types.ServiceInspectOptions{})
 	service, _, err := client.ServiceInspectWithRaw(ctx, id, types.ServiceInspectOptions{})
 	assert.NilError(t, err)
 	assert.NilError(t, err)
@@ -134,21 +132,3 @@ func fullSwarmServiceSpec(name string, replicas uint64) swarmtypes.ServiceSpec {
 		},
 		},
 	}
 	}
 }
 }
-
-func serviceContainerCount(client client.ServiceAPIClient, id string, count uint64) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		filter := filters.NewArgs()
-		filter.Add("service", id)
-		tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
-			Filters: filter,
-		})
-		switch {
-		case err != nil:
-			return poll.Error(err)
-		case len(tasks) == int(count):
-			return poll.Success()
-		default:
-			return poll.Continue("task count at %d waiting for %d", len(tasks), count)
-		}
-	}
-}

+ 3 - 17
integration/session/session_test.go

@@ -4,7 +4,7 @@ import (
 	"net/http"
 	"net/http"
 	"testing"
 	"testing"
 
 
-	"github.com/docker/docker/internal/test/daemon"
+	"github.com/docker/docker/api/types/versions"
 	req "github.com/docker/docker/internal/test/request"
 	req "github.com/docker/docker/internal/test/request"
 	"gotest.tools/assert"
 	"gotest.tools/assert"
 	is "gotest.tools/assert/cmp"
 	is "gotest.tools/assert/cmp"
@@ -13,17 +13,10 @@ import (
 
 
 func TestSessionCreate(t *testing.T) {
 func TestSessionCreate(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "experimental in older versions")
 
 
 	defer setupTest(t)()
 	defer setupTest(t)()
 	daemonHost := req.DaemonHost()
 	daemonHost := req.DaemonHost()
-	if !testEnv.DaemonInfo.ExperimentalBuild {
-		skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
-
-		d := daemon.New(t, daemon.WithExperimental)
-		d.StartWithBusybox(t)
-		defer d.Stop(t)
-		daemonHost = d.Sock()
-	}
 
 
 	res, body, err := req.Post("/session",
 	res, body, err := req.Post("/session",
 		req.Host(daemonHost),
 		req.Host(daemonHost),
@@ -41,17 +34,10 @@ func TestSessionCreate(t *testing.T) {
 
 
 func TestSessionCreateWithBadUpgrade(t *testing.T) {
 func TestSessionCreateWithBadUpgrade(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "experimental in older versions")
 
 
 	defer setupTest(t)()
 	defer setupTest(t)()
 	daemonHost := req.DaemonHost()
 	daemonHost := req.DaemonHost()
-	if !testEnv.DaemonInfo.ExperimentalBuild {
-		skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
-
-		d := daemon.New(t, daemon.WithExperimental)
-		d.StartWithBusybox(t)
-		defer d.Stop(t)
-		daemonHost = d.Sock()
-	}
 
 
 	res, body, err := req.Post("/session", req.Host(daemonHost))
 	res, body, err := req.Post("/session", req.Host(daemonHost))
 	assert.NilError(t, err)
 	assert.NilError(t, err)

+ 22 - 7
internal/test/daemon/daemon.go

@@ -147,6 +147,11 @@ func New(t testingT, ops ...func(*Daemon)) *Daemon {
 	return d
 	return d
 }
 }
 
 
+// ContainersNamespace returns the containerd namespace used for containers.
+func (d *Daemon) ContainersNamespace() string {
+	return d.id
+}
+
 // RootDir returns the root directory of the daemon.
 // RootDir returns the root directory of the daemon.
 func (d *Daemon) RootDir() string {
 func (d *Daemon) RootDir() string {
 	return d.Root
 	return d.Root
@@ -231,12 +236,15 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
 	if err != nil {
 	if err != nil {
 		return errors.Wrapf(err, "[%s] could not find docker binary in $PATH", d.id)
 		return errors.Wrapf(err, "[%s] could not find docker binary in $PATH", d.id)
 	}
 	}
+
 	args := append(d.GlobalFlags,
 	args := append(d.GlobalFlags,
 		"--containerd", containerdSocket,
 		"--containerd", containerdSocket,
 		"--data-root", d.Root,
 		"--data-root", d.Root,
 		"--exec-root", d.execRoot,
 		"--exec-root", d.execRoot,
 		"--pidfile", fmt.Sprintf("%s/docker.pid", d.Folder),
 		"--pidfile", fmt.Sprintf("%s/docker.pid", d.Folder),
 		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
 		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
+		"--containerd-namespace", d.id,
+		"--containerd-plugins-namespace", d.id+"p",
 	)
 	)
 	if d.experimental {
 	if d.experimental {
 		args = append(args, "--experimental")
 		args = append(args, "--experimental")
@@ -313,7 +321,7 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
 	defer cancel()
 	defer cancel()
 
 
 	// make sure daemon is ready to receive requests
 	// make sure daemon is ready to receive requests
-	for {
+	for i := 0; ; i++ {
 		d.log.Logf("[%s] waiting for daemon to start", d.id)
 		d.log.Logf("[%s] waiting for daemon to start", d.id)
 
 
 		select {
 		select {
@@ -327,9 +335,14 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
 
 
 			resp, err := client.Do(req.WithContext(rctx))
 			resp, err := client.Do(req.WithContext(rctx))
 			if err != nil {
 			if err != nil {
-				d.log.Logf("[%s] error pinging daemon on start: %v", d.id, err)
-
-				time.Sleep(500 * time.Millisecond)
+				if i > 2 { // don't log the first couple, this ends up just being noise
+					d.log.Logf("[%s] error pinging daemon on start: %v", d.id, err)
+				}
+
+				select {
+				case <-ctx.Done():
+				case <-time.After(500 * time.Millisecond):
+				}
 				continue
 				continue
 			}
 			}
 
 
@@ -700,8 +713,10 @@ func cleanupRaftDir(t testingT, rootPath string) {
 	if ht, ok := t.(test.HelperT); ok {
 	if ht, ok := t.(test.HelperT); ok {
 		ht.Helper()
 		ht.Helper()
 	}
 	}
-	walDir := filepath.Join(rootPath, "swarm/raft/wal")
-	if err := os.RemoveAll(walDir); err != nil {
-		t.Logf("error removing %v: %v", walDir, err)
+	for _, p := range []string{"wal", "wal-v3-encrypted", "snap-v3-encrypted"} {
+		dir := filepath.Join(rootPath, "swarm/raft", p)
+		if err := os.RemoveAll(dir); err != nil {
+			t.Logf("error removing %v: %v", dir, err)
+		}
 	}
 	}
 }
 }

+ 17 - 6
internal/test/daemon/swarm.go

@@ -20,12 +20,19 @@ var (
 	startArgs = []string{"--iptables=false", "--swarm-default-advertise-addr=lo"}
 	startArgs = []string{"--iptables=false", "--swarm-default-advertise-addr=lo"}
 )
 )
 
 
-// StartNode starts daemon to be used as a swarm node
+// StartNode (re)starts the daemon
 func (d *Daemon) StartNode(t testingT) {
 func (d *Daemon) StartNode(t testingT) {
 	if ht, ok := t.(test.HelperT); ok {
 	if ht, ok := t.(test.HelperT); ok {
 		ht.Helper()
 		ht.Helper()
 	}
 	}
-	// avoid networking conflicts
+	d.Start(t, startArgs...)
+}
+
+// StartNodeWithBusybox starts daemon to be used as a swarm node, and loads the busybox image
+func (d *Daemon) StartNodeWithBusybox(t testingT) {
+	if ht, ok := t.(test.HelperT); ok {
+		ht.Helper()
+	}
 	d.StartWithBusybox(t, startArgs...)
 	d.StartWithBusybox(t, startArgs...)
 }
 }
 
 
@@ -36,24 +43,28 @@ func (d *Daemon) RestartNode(t testingT) {
 	}
 	}
 	// avoid networking conflicts
 	// avoid networking conflicts
 	d.Stop(t)
 	d.Stop(t)
-	d.StartWithBusybox(t, startArgs...)
+	d.Start(t, startArgs...)
 }
 }
 
 
 // StartAndSwarmInit starts the daemon (with busybox) and init the swarm
 // StartAndSwarmInit starts the daemon (with busybox) and init the swarm
 func (d *Daemon) StartAndSwarmInit(t testingT) {
 func (d *Daemon) StartAndSwarmInit(t testingT) {
-	d.StartNode(t)
+	d.StartNodeWithBusybox(t)
 	d.SwarmInit(t, swarm.InitRequest{})
 	d.SwarmInit(t, swarm.InitRequest{})
 }
 }
 
 
 // StartAndSwarmJoin starts the daemon (with busybox) and join the specified swarm as worker or manager
 // StartAndSwarmJoin starts the daemon (with busybox) and join the specified swarm as worker or manager
 func (d *Daemon) StartAndSwarmJoin(t testingT, leader *Daemon, manager bool) {
 func (d *Daemon) StartAndSwarmJoin(t testingT, leader *Daemon, manager bool) {
-	d.StartNode(t)
+	if th, ok := t.(test.HelperT); ok {
+		th.Helper()
+	}
+	d.StartNodeWithBusybox(t)
 
 
 	tokens := leader.JoinTokens(t)
 	tokens := leader.JoinTokens(t)
 	token := tokens.Worker
 	token := tokens.Worker
 	if manager {
 	if manager {
 		token = tokens.Manager
 		token = tokens.Manager
 	}
 	}
+	t.Logf("[%s] joining swarm manager [%s]@%s, swarm listen addr %s", d.id, leader.id, leader.SwarmListenAddr(), d.SwarmListenAddr())
 	d.SwarmJoin(t, swarm.JoinRequest{
 	d.SwarmJoin(t, swarm.JoinRequest{
 		RemoteAddrs: []string{leader.SwarmListenAddr()},
 		RemoteAddrs: []string{leader.SwarmListenAddr()},
 		JoinToken:   token,
 		JoinToken:   token,
@@ -106,7 +117,7 @@ func (d *Daemon) SwarmJoin(t assert.TestingT, req swarm.JoinRequest) {
 	cli := d.NewClientT(t)
 	cli := d.NewClientT(t)
 	defer cli.Close()
 	defer cli.Close()
 	err := cli.SwarmJoin(context.Background(), req)
 	err := cli.SwarmJoin(context.Background(), req)
-	assert.NilError(t, err, "initializing swarm")
+	assert.NilError(t, err, "[%s] joining swarm", d.id)
 	d.CachedInfo = d.Info(t)
 	d.CachedInfo = d.Info(t)
 }
 }
 
 

+ 139 - 103
pkg/term/proxy_test.go

@@ -2,7 +2,6 @@ package term // import "github.com/docker/docker/pkg/term"
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	"fmt"
 	"testing"
 	"testing"
 
 
 	"gotest.tools/assert"
 	"gotest.tools/assert"
@@ -10,106 +9,143 @@ import (
 )
 )
 
 
 func TestEscapeProxyRead(t *testing.T) {
 func TestEscapeProxyRead(t *testing.T) {
-	escapeKeys, _ := ToBytes("")
-	keys, _ := ToBytes("a")
-	reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf := make([]byte, len(keys))
-	nr, err := reader.Read(buf)
-	assert.NilError(t, err)
-	assert.Equal(t, nr, len(keys), fmt.Sprintf("nr %d should be equal to the number of %d", nr, len(keys)))
-	assert.DeepEqual(t, keys, buf)
-
-	keys, _ = ToBytes("a,b,c")
-	reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf = make([]byte, len(keys))
-	nr, err = reader.Read(buf)
-	assert.NilError(t, err)
-	assert.Equal(t, nr, len(keys), fmt.Sprintf("nr %d should be equal to the number of %d", nr, len(keys)))
-	assert.DeepEqual(t, keys, buf)
-
-	keys, _ = ToBytes("")
-	reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf = make([]byte, len(keys))
-	nr, err = reader.Read(buf)
-	assert.Assert(t, is.ErrorContains(err, ""), "Should throw error when no keys are to read")
-	assert.Equal(t, nr, 0, "nr should be zero")
-	assert.Check(t, is.Len(keys, 0))
-	assert.Check(t, is.Len(buf, 0))
-
-	escapeKeys, _ = ToBytes("DEL")
-	keys, _ = ToBytes("a,b,c,+")
-	reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf = make([]byte, len(keys))
-	nr, err = reader.Read(buf)
-	assert.NilError(t, err)
-	assert.Equal(t, nr, len(keys), fmt.Sprintf("nr %d should be equal to the number of %d", nr, len(keys)))
-	assert.DeepEqual(t, keys, buf)
-
-	keys, _ = ToBytes("")
-	reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf = make([]byte, len(keys))
-	nr, err = reader.Read(buf)
-	assert.Assert(t, is.ErrorContains(err, ""), "Should throw error when no keys are to read")
-	assert.Equal(t, nr, 0, "nr should be zero")
-	assert.Check(t, is.Len(keys, 0))
-	assert.Check(t, is.Len(buf, 0))
-
-	escapeKeys, _ = ToBytes("ctrl-x,ctrl-@")
-	keys, _ = ToBytes("DEL")
-	reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf = make([]byte, len(keys))
-	nr, err = reader.Read(buf)
-	assert.NilError(t, err)
-	assert.Equal(t, nr, 1, fmt.Sprintf("nr %d should be equal to the number of 1", nr))
-	assert.DeepEqual(t, keys, buf)
-
-	escapeKeys, _ = ToBytes("ctrl-c")
-	keys, _ = ToBytes("ctrl-c")
-	reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf = make([]byte, len(keys))
-	nr, err = reader.Read(buf)
-	assert.Error(t, err, "read escape sequence")
-	assert.Equal(t, nr, 0, "nr should be equal to 0")
-	assert.DeepEqual(t, keys, buf)
-
-	escapeKeys, _ = ToBytes("ctrl-c,ctrl-z")
-	keys, _ = ToBytes("ctrl-c,ctrl-z")
-	reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf = make([]byte, 1)
-	nr, err = reader.Read(buf)
-	assert.NilError(t, err)
-	assert.Equal(t, nr, 0, "nr should be equal to 0")
-	assert.DeepEqual(t, keys[0:1], buf)
-	nr, err = reader.Read(buf)
-	assert.Error(t, err, "read escape sequence")
-	assert.Equal(t, nr, 0, "nr should be equal to 0")
-	assert.DeepEqual(t, keys[1:], buf)
-
-	escapeKeys, _ = ToBytes("ctrl-c,ctrl-z")
-	keys, _ = ToBytes("ctrl-c,DEL,+")
-	reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf = make([]byte, 1)
-	nr, err = reader.Read(buf)
-	assert.NilError(t, err)
-	assert.Equal(t, nr, 0, "nr should be equal to 0")
-	assert.DeepEqual(t, keys[0:1], buf)
-	buf = make([]byte, len(keys))
-	nr, err = reader.Read(buf)
-	assert.NilError(t, err)
-	assert.Equal(t, nr, len(keys), fmt.Sprintf("nr should be equal to %d", len(keys)))
-	assert.DeepEqual(t, keys, buf)
-
-	escapeKeys, _ = ToBytes("ctrl-c,ctrl-z")
-	keys, _ = ToBytes("ctrl-c,DEL")
-	reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
-	buf = make([]byte, 1)
-	nr, err = reader.Read(buf)
-	assert.NilError(t, err)
-	assert.Equal(t, nr, 0, "nr should be equal to 0")
-	assert.DeepEqual(t, keys[0:1], buf)
-	buf = make([]byte, len(keys))
-	nr, err = reader.Read(buf)
-	assert.NilError(t, err)
-	assert.Equal(t, nr, len(keys), fmt.Sprintf("nr should be equal to %d", len(keys)))
-	assert.DeepEqual(t, keys, buf)
+	t.Run("no escape keys, keys a", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("")
+		keys, _ := ToBytes("a")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, len(keys))
+		nr, err := reader.Read(buf)
+		assert.NilError(t, err)
+		assert.Equal(t, nr, len(keys))
+		assert.DeepEqual(t, keys, buf)
+	})
+
+	t.Run("no escape keys, keys a,b,c", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("")
+		keys, _ := ToBytes("a,b,c")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, len(keys))
+		nr, err := reader.Read(buf)
+		assert.NilError(t, err)
+		assert.Equal(t, nr, len(keys))
+		assert.DeepEqual(t, keys, buf)
+	})
+
+	t.Run("no escape keys, no keys", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("")
+		keys, _ := ToBytes("")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, len(keys))
+		nr, err := reader.Read(buf)
+		assert.Assert(t, is.ErrorContains(err, ""), "Should throw error when no keys are to read")
+		assert.Equal(t, nr, 0)
+		assert.Check(t, is.Len(keys, 0))
+		assert.Check(t, is.Len(buf, 0))
+	})
+
+	t.Run("DEL escape key, keys a,b,c,+", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("DEL")
+		keys, _ := ToBytes("a,b,c,+")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, len(keys))
+		nr, err := reader.Read(buf)
+		assert.NilError(t, err)
+		assert.Equal(t, nr, len(keys))
+		assert.DeepEqual(t, keys, buf)
+	})
+
+	t.Run("DEL escape key, no keys", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("DEL")
+		keys, _ := ToBytes("")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, len(keys))
+		nr, err := reader.Read(buf)
+		assert.Assert(t, is.ErrorContains(err, ""), "Should throw error when no keys are to read")
+		assert.Equal(t, nr, 0)
+		assert.Check(t, is.Len(keys, 0))
+		assert.Check(t, is.Len(buf, 0))
+	})
+
+	t.Run("ctrl-x,ctrl-@ escape key, keys DEL", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("ctrl-x,ctrl-@")
+		keys, _ := ToBytes("DEL")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, len(keys))
+		nr, err := reader.Read(buf)
+		assert.NilError(t, err)
+		assert.Equal(t, nr, 1)
+		assert.DeepEqual(t, keys, buf)
+	})
+
+	t.Run("ctrl-c escape key, keys ctrl-c", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("ctrl-c")
+		keys, _ := ToBytes("ctrl-c")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, len(keys))
+		nr, err := reader.Read(buf)
+		assert.Error(t, err, "read escape sequence")
+		assert.Equal(t, nr, 0)
+		assert.DeepEqual(t, keys, buf)
+	})
+
+	t.Run("ctrl-c,ctrl-z escape key, keys ctrl-c,ctrl-z", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("ctrl-c,ctrl-z")
+		keys, _ := ToBytes("ctrl-c,ctrl-z")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, 1)
+		nr, err := reader.Read(buf)
+		assert.NilError(t, err)
+		assert.Equal(t, nr, 0)
+		assert.DeepEqual(t, keys[0:1], buf)
+
+		nr, err = reader.Read(buf)
+		assert.Error(t, err, "read escape sequence")
+		assert.Equal(t, nr, 0)
+		assert.DeepEqual(t, keys[1:], buf)
+	})
+
+	t.Run("ctrl-c,ctrl-z escape key, keys ctrl-c,DEL,+", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("ctrl-c,ctrl-z")
+		keys, _ := ToBytes("ctrl-c,DEL,+")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, 1)
+		nr, err := reader.Read(buf)
+		assert.NilError(t, err)
+		assert.Equal(t, nr, 0)
+		assert.DeepEqual(t, keys[0:1], buf)
+
+		buf = make([]byte, len(keys))
+		nr, err = reader.Read(buf)
+		assert.NilError(t, err)
+		assert.Equal(t, nr, len(keys))
+		assert.DeepEqual(t, keys, buf)
+	})
+
+	t.Run("ctrl-c,ctrl-z escape key, keys ctrl-c,DEL", func(t *testing.T) {
+		escapeKeys, _ := ToBytes("ctrl-c,ctrl-z")
+		keys, _ := ToBytes("ctrl-c,DEL")
+		reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys)
+
+		buf := make([]byte, 1)
+		nr, err := reader.Read(buf)
+		assert.NilError(t, err)
+		assert.Equal(t, nr, 0)
+		assert.DeepEqual(t, keys[0:1], buf)
+
+		buf = make([]byte, len(keys))
+		nr, err = reader.Read(buf)
+		assert.NilError(t, err)
+		assert.Equal(t, nr, len(keys))
+		assert.DeepEqual(t, keys, buf)
+	})
+
 }
 }

+ 2 - 2
plugin/executor/containerd/containerd.go

@@ -26,13 +26,13 @@ type ExitHandler interface {
 }
 }
 
 
 // New creates a new containerd plugin executor
 // New creates a new containerd plugin executor
-func New(ctx context.Context, rootDir string, cli *containerd.Client, exitHandler ExitHandler) (*Executor, error) {
+func New(ctx context.Context, rootDir string, cli *containerd.Client, ns string, exitHandler ExitHandler) (*Executor, error) {
 	e := &Executor{
 	e := &Executor{
 		rootDir:     rootDir,
 		rootDir:     rootDir,
 		exitHandler: exitHandler,
 		exitHandler: exitHandler,
 	}
 	}
 
 
-	client, err := libcontainerd.NewClient(ctx, cli, rootDir, PluginNamespace, e)
+	client, err := libcontainerd.NewClient(ctx, cli, rootDir, ns, e)
 	if err != nil {
 	if err != nil {
 		return nil, errors.Wrap(err, "error creating containerd exec client")
 		return nil, errors.Wrap(err, "error creating containerd exec client")
 	}
 	}