diff --git a/test/cri-containerd/container_layers_packing_test.go b/test/cri-containerd/container_layers_packing_test.go index 63f8a1bcf8..a0407f8999 100644 --- a/test/cri-containerd/container_layers_packing_test.go +++ b/test/cri-containerd/container_layers_packing_test.go @@ -3,14 +3,11 @@ package cri_containerd import ( - "bufio" - "bytes" "context" "fmt" "strings" "testing" - "github.com/Microsoft/hcsshim/internal/shimdiag" "github.com/Microsoft/hcsshim/osversion" "github.com/Microsoft/hcsshim/pkg/annotations" testutilities "github.com/Microsoft/hcsshim/test/functional/utilities" @@ -22,46 +19,12 @@ const ( alpine70ExtraLayers = "cplatpublic.azurecr.io/alpine70extra:latest" ) -func filterStrings(input []string, include string) []string { - var result []string - for _, str := range input { - if strings.Contains(str, include) { - result = append(result, str) - } - } - return result -} - -func shimDiagExec(ctx context.Context, t *testing.T, podID string, cmd []string) string { - shimName := fmt.Sprintf("k8s.io-%s", podID) - shim, err := shimdiag.GetShim(shimName) - if err != nil { - t.Fatalf("failed to find shim %v: %v", shimName, err) - } - shimClient := shimdiag.NewShimDiagClient(shim) - - bufOut := &bytes.Buffer{} - bw := bufio.NewWriter(bufOut) - bufErr := &bytes.Buffer{} - bwErr := bufio.NewWriter(bufErr) - - exitCode, err := execInHost(ctx, shimClient, cmd, nil, bw, bwErr) - if err != nil { - t.Fatalf("failed to exec request in the host with: %v and %v", err, bufErr.String()) - } - if exitCode != 0 { - t.Fatalf("exec request in host failed with exit code %v: %v", exitCode, bufErr.String()) - } - - return strings.TrimSpace(bufOut.String()) -} - func validateTargets(ctx context.Context, t *testing.T, deviceNumber int, podID string, expected int) { - dmDiag := shimDiagExec(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"}) + dmDiag := shimDiagExecOutput(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"}) dmPattern := fmt.Sprintf("dm-linear-pmem%d", deviceNumber) dmLines := filterStrings(strings.Split(dmDiag, "\n"), dmPattern) - lrDiag := shimDiagExec(ctx, t, podID, []string{"ls", "-l", "/run/layers"}) + lrDiag := shimDiagExecOutput(ctx, t, podID, []string{"ls", "-l", "/run/layers"}) lrPattern := fmt.Sprintf("p%d", deviceNumber) lrLines := filterStrings(strings.Split(lrDiag, "\n"), lrPattern) if len(lrLines) != len(dmLines) { @@ -184,7 +147,7 @@ func Test_Annotation_Disable_Multi_Mapping(t *testing.T) { startContainer(t, client, ctx, containerID) defer stopContainer(t, client, ctx, containerID) - dmDiag := shimDiagExec(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"}) + dmDiag := shimDiagExecOutput(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"}) filtered := filterStrings(strings.Split(dmDiag, "\n"), "dm-linear") if len(filtered) > 0 { t.Fatalf("no linear devices should've been created.\n%s", dmDiag) diff --git a/test/cri-containerd/exec.go b/test/cri-containerd/exec.go index 16346aa38a..7f787482e3 100644 --- a/test/cri-containerd/exec.go +++ b/test/cri-containerd/exec.go @@ -3,8 +3,12 @@ package cri_containerd import ( + "bufio" + "bytes" "context" + "fmt" "io" + "strings" "testing" "github.com/Microsoft/hcsshim/internal/cmd" @@ -69,3 +73,38 @@ func execInHost(ctx context.Context, client shimdiag.ShimDiagService, args []str } return resp.ExitCode, nil } + +// shimDiagExecOutput is a small wrapper on top of execInHost, that returns the exec output +func shimDiagExecOutput(ctx context.Context, t *testing.T, podID string, cmd []string) string { + shimName := fmt.Sprintf("k8s.io-%s", podID) + shim, err := shimdiag.GetShim(shimName) + if err != nil { + t.Fatalf("failed to find shim %v: %v", shimName, err) + } + shimClient := shimdiag.NewShimDiagClient(shim) + + bufOut := &bytes.Buffer{} + bw := bufio.NewWriter(bufOut) + bufErr := &bytes.Buffer{} + bwErr := bufio.NewWriter(bufErr) + + exitCode, err := execInHost(ctx, shimClient, cmd, nil, bw, bwErr) + if err != nil { + t.Fatalf("failed to exec request in the host with: %v and %v", err, bufErr.String()) + } + if exitCode != 0 { + t.Fatalf("exec request in host failed with exit code %v: %v", exitCode, bufErr.String()) + } + + return strings.TrimSpace(bufOut.String()) +} + +func filterStrings(input []string, include string) []string { + var result []string + for _, str := range input { + if strings.Contains(str, include) { + result = append(result, str) + } + } + return result +} diff --git a/test/cri-containerd/layer_integrity_test.go b/test/cri-containerd/layer_integrity_test.go new file mode 100644 index 0000000000..101d624447 --- /dev/null +++ b/test/cri-containerd/layer_integrity_test.go @@ -0,0 +1,93 @@ +// +build functional + +package cri_containerd + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + + "github.com/Microsoft/hcsshim/pkg/annotations" +) + +func Test_LCOW_Layer_Integrity(t *testing.T) { + requireFeatures(t, featureLCOWIntegrity, featureLCOW) + + client := newTestRuntimeClient(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + pullRequiredLCOWImages(t, []string{imageLcowK8sPause}) + + // Delete container image in case it already exists. + removeImages(t, []string{imageLcowAlpine}) + + // Pull image with dm-verity enabled. + pullRequiredLCOWImages( + t, + []string{imageLcowAlpine}, + WithSandboxLabels(map[string]string{ + "containerd.io/diff/io.microsoft.lcow.append-dm-verity": "true", + }), + ) + + type config struct { + layerType string + vPMemCount int + rootFSType string + } + + for _, scenario := range []config{ + { + layerType: "scsi", + vPMemCount: 0, + rootFSType: "initrd", + }, + { + layerType: "pmem", + vPMemCount: 16, + rootFSType: "initrd", + }, + { + layerType: "pmem", + vPMemCount: 16, + rootFSType: "vhd", + }, + } { + t.Run(fmt.Sprintf("Integrity-For-%s-%s", scenario.layerType, scenario.rootFSType), func(t *testing.T) { + podReq := getRunPodSandboxRequest( + t, + lcowRuntimeHandler, + WithSandboxAnnotations(map[string]string{ + annotations.VPMemCount: strconv.Itoa(scenario.vPMemCount), + annotations.PreferredRootFSType: scenario.rootFSType, + }), + ) + podID := runPodSandbox(t, client, ctx, podReq) + defer removePodSandbox(t, client, ctx, podID) + + // Launch container + cmd := []string{"ash", "-c", "while true; do sleep 1; done"} + contReq := getCreateContainerRequest( + podID, + fmt.Sprintf("alpine-%s-%s", scenario.layerType, scenario.rootFSType), + imageLcowAlpine, + cmd, + podReq.Config, + ) + contID := createContainer(t, client, ctx, contReq) + defer removeContainer(t, client, ctx, contID) + startContainer(t, client, ctx, contID) + defer stopContainer(t, client, ctx, contID) + + // Validate that verity target(s) present + output := shimDiagExecOutput(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"}) + filtered := filterStrings(strings.Split(output, "\n"), fmt.Sprintf("dm-verity-%s", scenario.layerType)) + if len(filtered) == 0 { + t.Fatalf("expected verity targets for %s devices, none found.\n%s\n", scenario.layerType, output) + } + }) + } +} diff --git a/test/cri-containerd/main_test.go b/test/cri-containerd/main_test.go index 4ab48be1f8..551f77fa96 100644 --- a/test/cri-containerd/main_test.go +++ b/test/cri-containerd/main_test.go @@ -96,6 +96,7 @@ const ( featureGPU = "GPU" featureCRIUpdateContainer = "UpdateContainer" featureTerminateOnRestart = "TerminateOnRestart" + featureLCOWIntegrity = "LCOWIntegrity" ) var allFeatures = []string{