k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/windows/hyperv.go (about)

     1  /*
     2  Copyright 2023 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package windows
    18  
    19  import (
    20  	"context"
    21  	"time"
    22  
    23  	"github.com/onsi/ginkgo/v2"
    24  	"github.com/onsi/gomega"
    25  	v1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/kubernetes/test/e2e/feature"
    28  	"k8s.io/kubernetes/test/e2e/framework"
    29  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    30  	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
    31  	imageutils "k8s.io/kubernetes/test/utils/image"
    32  	admissionapi "k8s.io/pod-security-admission/api"
    33  )
    34  
    35  var (
    36  	WindowsHyperVContainerRuntimeClass = "runhcs-wcow-hypervisor"
    37  )
    38  
    39  var _ = sigDescribe(feature.WindowsHyperVContainers, "HyperV containers", skipUnlessWindows(func() {
    40  	ginkgo.BeforeEach(func() {
    41  		e2eskipper.SkipUnlessNodeOSDistroIs("windows")
    42  	})
    43  
    44  	f := framework.NewDefaultFramework("windows-hyperv-test")
    45  	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
    46  
    47  	ginkgo.It("should start a hyperv isolated container", func(ctx context.Context) {
    48  
    49  		// HyperV isolated containers are only supported on containerd 1.7+
    50  		skipUnlessContainerdOneSevenOrGreater(ctx, f)
    51  
    52  		// check if hyperv runtime class is on node and skip otherwise
    53  		// Note: the runtime class is expected to be added to a cluster before running this test.
    54  		// see https://github.com/kubernetes-sigs/windows-testing/tree/master/helpers/hyper-v-mutating-webhook/hyperv-runtimeclass.yaml
    55  		// for an example.
    56  		_, err := f.ClientSet.NodeV1().RuntimeClasses().Get(ctx, WindowsHyperVContainerRuntimeClass, metav1.GetOptions{})
    57  		if err != nil {
    58  			framework.Logf("error getting runtime class: %v", err)
    59  			e2eskipper.Skipf("skipping test because runhcs-wcow-hypervisor runtime class is not present")
    60  		}
    61  
    62  		ginkgo.By("selecting a Windows node")
    63  		targetNode, err := findWindowsNode(ctx, f)
    64  		framework.ExpectNoError(err, "error finding Windows node")
    65  		framework.Logf("Using node: %v", targetNode.Name)
    66  
    67  		ginkgo.By("schedule a pod to that node")
    68  		image := imageutils.GetE2EImage(imageutils.BusyBox)
    69  		hypervPodName := "hyperv-test-pod"
    70  		hypervPod := &v1.Pod{
    71  			ObjectMeta: metav1.ObjectMeta{
    72  				Name: hypervPodName,
    73  			},
    74  			Spec: v1.PodSpec{
    75  				Containers: []v1.Container{
    76  					{
    77  						Image:   image,
    78  						Name:    "busybox-1",
    79  						Command: []string{"powershell.exe", "-Command", "Write-Host 'Hello'; sleep -Seconds 600"},
    80  					},
    81  					{
    82  						Image:   image,
    83  						Name:    "busybox-2",
    84  						Command: []string{"powershell.exe", "-Command", "Write-Host 'Hello'; sleep -Seconds 600"},
    85  					},
    86  				},
    87  				RestartPolicy:    v1.RestartPolicyNever,
    88  				RuntimeClassName: &WindowsHyperVContainerRuntimeClass,
    89  				NodeName:         targetNode.Name,
    90  			},
    91  		}
    92  
    93  		pc := e2epod.NewPodClient(f)
    94  
    95  		pc.Create(ctx, hypervPod)
    96  		ginkgo.By("waiting for the pod to be running")
    97  		timeout := 3 * time.Minute
    98  		err = e2epod.WaitForPodsRunningReady(ctx, f.ClientSet, f.Namespace.Name, 1, timeout)
    99  		framework.ExpectNoError(err)
   100  
   101  		ginkgo.By("creating a host process container in another pod to verify the pod is running hyperv isolated containers")
   102  
   103  		// Note: each pod runs in a separate UVM so even though we are scheduling 2 containers in the test pod
   104  		// we should only expect a single UVM to be running on the host.
   105  		podName := "validation-pod"
   106  		pod := &v1.Pod{
   107  			ObjectMeta: metav1.ObjectMeta{
   108  				Name: podName,
   109  			},
   110  			Spec: v1.PodSpec{
   111  				SecurityContext: &v1.PodSecurityContext{
   112  					WindowsOptions: &v1.WindowsSecurityContextOptions{
   113  						HostProcess:   &trueVar,
   114  						RunAsUserName: &User_NTAuthoritySystem,
   115  					},
   116  				},
   117  				HostNetwork: true,
   118  				Containers: []v1.Container{
   119  					{
   120  						Image:   image,
   121  						Name:    "container",
   122  						Command: []string{"powershell.exe", "-Command", "$vms = Get-ComputeProcess | Where-Object { ($_.Type -EQ 'VirtualMachine') -and ($_.Owner -EQ 'containerd-shim-runhcs-v1.exe') } ; if ($vms.Length -le 0) { throw 'error' }"},
   123  					},
   124  				},
   125  				RestartPolicy: v1.RestartPolicyNever,
   126  				NodeName:      targetNode.Name,
   127  			},
   128  		}
   129  
   130  		pc.Create(ctx, pod)
   131  		ginkgo.By("waiting for the pod to be run")
   132  		pc.WaitForFinish(ctx, podName, timeout)
   133  
   134  		ginkgo.By("then ensuring pod finished running successfully")
   135  		p, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Get(ctx, podName, metav1.GetOptions{})
   136  		framework.ExpectNoError(err, "error getting pod")
   137  
   138  		if p.Status.Phase != v1.PodSucceeded {
   139  			logs, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, podName, "container")
   140  			if err != nil {
   141  				framework.Logf("Error pulling logs: %v", err)
   142  			}
   143  			framework.Logf("Pod phase: %v\nlogs:\n%s", p.Status.Phase, logs)
   144  		}
   145  
   146  		gomega.Expect(p.Status.Phase).To(gomega.Equal(v1.PodSucceeded), "pod should have succeeded")
   147  	})
   148  }))