1// +build functional 2 3package cri_containerd 4 5import ( 6 "context" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "strings" 11 "testing" 12 "time" 13 14 runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" 15) 16 17// This test requires compiling a helper logging binary which can be found 18// at test/cri-containerd/helpers/log.go. Copy log.exe as "sample-logging-driver.exe" 19// to ContainerPlat install directory or set "TEST_BINARY_ROOT" environment variable, 20// which this test will use to construct logPath for CreateContainerRequest and as 21// the location of stdout artifacts created by the binary 22func Test_Run_Container_With_Binary_Logger(t *testing.T) { 23 client := newTestRuntimeClient(t) 24 ctx, cancel := context.WithCancel(context.Background()) 25 defer cancel() 26 27 logBinaryRoot := os.Getenv("TEST_BINARY_ROOT") 28 if logBinaryRoot == "" { 29 logBinaryRoot = "/ContainerPlat" 30 } 31 32 binaryPath := logBinaryRoot + "/sample-logging-driver.exe" 33 34 if _, err := os.Stat(binaryPath); os.IsNotExist(err) { 35 t.Skip("skipping: sample logging driver missing") 36 } 37 38 logPath := "binary://" + binaryPath 39 40 type config struct { 41 name string 42 containerName string 43 requiredFeatures []string 44 runtimeHandler string 45 sandboxImage string 46 containerImage string 47 cmd []string 48 expectedContent string 49 } 50 51 tests := []config{ 52 { 53 name: "WCOW_Process", 54 containerName: t.Name() + "-Container-WCOW_Process", 55 requiredFeatures: []string{featureWCOWProcess}, 56 runtimeHandler: wcowProcessRuntimeHandler, 57 sandboxImage: imageWindowsNanoserver, 58 containerImage: imageWindowsNanoserver, 59 cmd: []string{"ping", "-t", "127.0.0.1"}, 60 expectedContent: "Pinging 127.0.0.1 with 32 bytes of data", 61 }, 62 { 63 name: "WCOW_Hypervisor", 64 containerName: t.Name() + "-Container-WCOW_Hypervisor", 65 requiredFeatures: []string{featureWCOWHypervisor}, 66 runtimeHandler: wcowHypervisorRuntimeHandler, 67 sandboxImage: imageWindowsNanoserver, 68 containerImage: imageWindowsNanoserver, 69 cmd: []string{"ping", "-t", "127.0.0.1"}, 70 expectedContent: "Pinging 127.0.0.1 with 32 bytes of data", 71 }, 72 { 73 name: "LCOW", 74 containerName: t.Name() + "-Container-LCOW", 75 requiredFeatures: []string{featureLCOW}, 76 runtimeHandler: lcowRuntimeHandler, 77 sandboxImage: imageLcowK8sPause, 78 containerImage: imageLcowAlpine, 79 cmd: []string{"ash", "-c", "while true; do echo 'Hello, World!'; sleep 1; done"}, 80 expectedContent: "Hello, World!", 81 }, 82 } 83 84 // Positive tests 85 for _, test := range tests { 86 t.Run(test.name+"_Positive", func(t *testing.T) { 87 requireFeatures(t, test.requiredFeatures...) 88 89 requiredImages := []string{test.sandboxImage, test.containerImage} 90 if test.runtimeHandler == lcowRuntimeHandler { 91 pullRequiredLcowImages(t, requiredImages) 92 } else { 93 pullRequiredImages(t, requiredImages) 94 } 95 96 podReq := getRunPodSandboxRequest(t, test.runtimeHandler) 97 podID := runPodSandbox(t, client, ctx, podReq) 98 defer removePodSandbox(t, client, ctx, podID) 99 100 logFileName := fmt.Sprintf("%s/stdout-%s.txt", logBinaryRoot, test.name) 101 conReq := getCreateContainerRequest(podID, test.containerName, test.containerImage, test.cmd, podReq.Config) 102 conReq.Config.LogPath = logPath + fmt.Sprintf("?%s", logFileName) 103 104 createAndRunContainer(t, client, ctx, conReq) 105 106 if _, err := os.Stat(logFileName); os.IsNotExist(err) { 107 t.Fatalf("log file was not created: %s", logFileName) 108 } 109 defer os.Remove(logFileName) 110 111 ok, err := assertFileContent(logFileName, test.expectedContent) 112 if err != nil { 113 t.Fatalf("failed to read log file: %s", err) 114 } 115 116 if !ok { 117 t.Fatalf("file content validation failed: %s", test.expectedContent) 118 } 119 }) 120 } 121 122 // Negative tests 123 for _, test := range tests { 124 t.Run(test.name+"_Negative", func(t *testing.T) { 125 requireFeatures(t, test.requiredFeatures...) 126 127 requiredImages := []string{test.sandboxImage, test.containerImage} 128 if test.runtimeHandler == lcowRuntimeHandler { 129 pullRequiredLcowImages(t, requiredImages) 130 } else { 131 pullRequiredImages(t, requiredImages) 132 } 133 134 podReq := getRunPodSandboxRequest(t, test.runtimeHandler) 135 podID := runPodSandbox(t, client, ctx, podReq) 136 defer removePodSandbox(t, client, ctx, podID) 137 138 nonExistentPath := "/does/not/exist/log.txt" 139 conReq := getCreateContainerRequest(podID, test.containerName, test.containerImage, test.cmd, podReq.Config) 140 conReq.Config.LogPath = logPath + fmt.Sprintf("?%s", nonExistentPath) 141 142 containerID := createContainer(t, client, ctx, conReq) 143 defer removeContainer(t, client, ctx, containerID) 144 145 // This should fail, since the filepath doesn't exist 146 _, err := client.StartContainer(ctx, &runtime.StartContainerRequest{ 147 ContainerId: containerID, 148 }) 149 if err == nil { 150 t.Fatal("container start should fail") 151 } 152 153 if !strings.Contains(err.Error(), "failed to start binary logger") { 154 t.Fatalf("expected 'failed to start binary logger' error, got: %s", err) 155 } 156 }) 157 } 158} 159 160func createAndRunContainer(t *testing.T, client runtime.RuntimeServiceClient, ctx context.Context, conReq *runtime.CreateContainerRequest) { 161 containerID := createContainer(t, client, ctx, conReq) 162 defer removeContainer(t, client, ctx, containerID) 163 164 startContainer(t, client, ctx, containerID) 165 defer stopContainer(t, client, ctx, containerID) 166 167 // Let stdio kick in 168 time.Sleep(time.Second * 1) 169} 170 171func assertFileContent(path string, content string) (bool, error) { 172 fileContent, err := ioutil.ReadFile(path) 173 if err != nil { 174 return false, err 175 } 176 177 return strings.Contains(string(fileContent), content), nil 178} 179