diff --git a/integration/main_test.go b/integration/main_test.go index 94740112e..c74ae1f13 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -192,6 +192,17 @@ func PodSandboxConfigWithCleanup(t *testing.T, name, ns string, opts ...PodSandb return sb, sbConfig } +// Set Windows HostProcess. +func WithWindowsHostProcess(p *runtime.PodSandboxConfig) { //nolint:unused + if p.Windows == nil { + p.Windows = &runtime.WindowsPodSandboxConfig{} + } + if p.Windows.SecurityContext == nil { + p.Windows.SecurityContext = &runtime.WindowsSandboxSecurityContext{} + } + p.Windows.SecurityContext.HostProcess = true +} + // ContainerOpts to set any specific attribute like labels, // annotations, metadata etc type ContainerOpts func(*runtime.ContainerConfig) @@ -227,6 +238,18 @@ func WithVolumeMount(hostPath, containerPath string) ContainerOpts { } } +func WithWindowsUsername(username string) ContainerOpts { //nolint:unused + return func(c *runtime.ContainerConfig) { + if c.Windows == nil { + c.Windows = &runtime.WindowsContainerConfig{} + } + if c.Windows.SecurityContext == nil { + c.Windows.SecurityContext = &runtime.WindowsContainerSecurityContext{} + } + c.Windows.SecurityContext.RunAsUsername = username + } +} + // Add container command. func WithCommand(cmd string, args ...string) ContainerOpts { return func(c *runtime.ContainerConfig) { diff --git a/integration/windows_hostprocess_test.go b/integration/windows_hostprocess_test.go new file mode 100644 index 000000000..9e8442160 --- /dev/null +++ b/integration/windows_hostprocess_test.go @@ -0,0 +1,92 @@ +//go:build windows +// +build windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package integration + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + defaultCommand = WithCommand("Powershell", "/c", "$env:CONTAINER_SANDBOX_MOUNT_POINT/pause.exe") + localServiceUsername = WithWindowsUsername("NT AUTHORITY\\Local service") + localSystemUsername = WithWindowsUsername("NT AUTHORITY\\System") +) + +// Tests to verify the Windows HostProcess +func TestWindowsHostProcess(t *testing.T) { + EnsureImageExists(t, pauseImage) + + t.Run("run as Local Service", func(t *testing.T) { + runHostProcess(t, false, pauseImage, localServiceUsername, defaultCommand) + }) + t.Run("run as Local System", func(t *testing.T) { + runHostProcess(t, false, pauseImage, localSystemUsername, defaultCommand) + }) + t.Run("run as unacceptable user", func(t *testing.T) { + runHostProcess(t, true, pauseImage, WithWindowsUsername("Guest"), defaultCommand) + }) + t.Run("run command on host", func(t *testing.T) { + cmd := WithCommand("Powershell", "/c", "Get-Command containerd.exe") + runHostProcess(t, false, pauseImage, localServiceUsername, cmd) + }) + t.Run("run withHostNetwork", func(t *testing.T) { + hostname, err := os.Hostname() + require.NoError(t, err) + cmd := WithCommand("Powershell", "/c", fmt.Sprintf("if ($env:COMPUTERNAME -ne %s) { exit -1 }", hostname)) + runHostProcess(t, false, pauseImage, localServiceUsername, cmd) + }) + t.Run("run with a different os.version image", func(t *testing.T) { + image := "docker.io/e2eteam/busybox:1.29-windows-amd64-1909" + EnsureImageExists(t, image) + runHostProcess(t, false, image, localServiceUsername, defaultCommand) + }) +} + +func runHostProcess(t *testing.T, expectErr bool, image string, opts ...ContainerOpts) { + t.Logf("Create a pod config and run sandbox container") + sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox1", "hostprocess", WithWindowsHostProcess) + + t.Logf("Create a container config and run container in a pod") + containerConfig := ContainerConfig( + "container1", + image, + opts..., + ) + cn, err := runtimeService.CreateContainer(sb, containerConfig, sbConfig) + require.NoError(t, err) + defer func() { + assert.NoError(t, runtimeService.RemoveContainer(cn)) + }() + _, err = t, runtimeService.StartContainer(cn) + if err != nil { + if !expectErr { + t.Fatalf("Unexpected error while starting Container: %v", err) + } + return + } + defer func() { + assert.NoError(t, runtimeService.StopContainer(cn, 10)) + }() +} diff --git a/script/test/cri-integration.sh b/script/test/cri-integration.sh index 133e0d0be..07a71f245 100755 --- a/script/test/cri-integration.sh +++ b/script/test/cri-integration.sh @@ -44,8 +44,6 @@ ${sudo} bin/cri-integration.test --test.run="${FOCUS}" --test.v \ --cri-root="${CRI_ROOT}" \ --runtime-handler="${RUNTIME}" \ --containerd-bin="${CONTAINERD_BIN}" \ - --image-list="${TEST_IMAGE_LIST:-}" - -test_exit_code=$? + --image-list="${TEST_IMAGE_LIST:-}" && test_exit_code=$? || test_exit_code=$? exit ${test_exit_code} diff --git a/script/test/utils.sh b/script/test/utils.sh index f08cdfef0..677d8d635 100755 --- a/script/test/utils.sh +++ b/script/test/utils.sh @@ -96,9 +96,21 @@ test_setup() { set -m # Create containerd in a different process group # so that we can easily clean them up. - keepalive "${sudo} bin/containerd ${CONTAINERD_FLAGS}" \ - "${RESTART_WAIT_PERIOD}" &> "${report_dir}/containerd.log" & - pid=$! + if [ $IS_WINDOWS -eq 0 ]; then + keepalive "${sudo} bin/containerd ${CONTAINERD_FLAGS}" \ + "${RESTART_WAIT_PERIOD}" &> "${report_dir}/containerd.log" & + pid=$! + else + # NOTE(claudiub): For Windows HostProcess containers, containerd needs to be privileged enough to + # start them. For this, we can register containerd as a service, so the LocalSystem will run it + # for us. Additionally, we don't need to worry about keeping it alive, Windows will do it for us. + nssm install containerd-test "$(pwd)/bin/containerd.exe" ${CONTAINERD_FLAGS} \ + --log-file "${report_dir}/containerd.log" + + # it might still result in SERVICE_START_PENDING, but we can ignore it. + nssm start containerd-test || true + pid="1" # for teardown + fi set +m # Wait for containerd to be running by using the containerd client ctr to check the version @@ -116,9 +128,8 @@ test_setup() { test_teardown() { if [ -n "${pid}" ]; then if [ $IS_WINDOWS -eq 1 ]; then - # NOTE(claudiub): The containerd process will have the same PGID as the keepalive process, - # so we can kill both of them by matching the PGID. - ${sudo} ps | awk "{if (\$3 == ${pid}) print \$1}" | xargs kill + nssm stop containerd-test + nssm remove containerd-test confirm else ${sudo} pkill -g $(ps -o pgid= -p "${pid}") fi