Skip to content

Commit e322ac5

Browse files
authored
Add test for support of NFS mount (microsoft#1726)
LCOW kernel needs to be built with certain config options(`CONFIG_NFS_FS=y`, `CONFIG_NFS_V4=y` & `CONFIG_NFS_V4_1=y`)_in order to be able to successfully run a NFS client and mount a NFS inside a container. This test attempts to mount a (fake) NFS server to ensure that the kernel has the capabilities of running a NFS client. We don't mount a real NFS server because creating a real NFS server that will work in all kinds of test environments is not simple. Instead, we look at the error returned by the NFS mount operation and decide if the failure is because the server wasn't available (i.e a `Connection refused` error) or because the kernel doesn't support NFS clients (`No Device` error). Limitations on different approaches of starting a real NFS server: 1. Starting another LCOW container that runs a NFS server: By default on Linux the NFS server runs in the kernel and to enable that the kernel must be built with `NFSD_*` config options (note that the config options for running NFS server are different than the config options required for NFS client), which we don't currently do and it doesn't make sense to just enable these options for a test. 2. Running a userspace NFS server: There are a few userspace NFS server projects but getting them to run inside the UtilityVM wasn't very easy. We didn't want to spend a lot of time on this test. 3. Running NFS server on the windows host: Not all builds of windows support this so the test won't run in all environments. Signed-off-by: Amit Barve <[email protected]>
1 parent f1a2711 commit e322ac5

2 files changed

Lines changed: 72 additions & 0 deletions

File tree

test/cri-containerd/container_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,3 +874,74 @@ func Test_RunContainer_ExecUser_Root_LCOW(t *testing.T) {
874874
t.Fatalf("expected user for exec to be 'root', got %q", string(r.Stdout))
875875
}
876876
}
877+
878+
// creates a linux container and attempts to mount a (non-existent) nfs share. Tests if the kernel has the
879+
// required modules for supporting NFS mount.
880+
func Test_Container_NFSMount_LCOW(t *testing.T) {
881+
requireFeatures(t, featureLCOW)
882+
883+
pullRequiredLCOWImages(t, []string{imageLcowK8sPause, imageLcowUbuntu})
884+
885+
client := newTestRuntimeClient(t)
886+
ctx, cancel := context.WithCancel(context.Background())
887+
defer cancel()
888+
889+
// start a privileged pod & container. container must be privileged in order to be able to mount a NFS
890+
// share.
891+
sandboxRequest := getRunPodSandboxRequest(t, lcowRuntimeHandler)
892+
sandboxRequest.Config.Linux = &runtime.LinuxPodSandboxConfig{
893+
SecurityContext: &runtime.LinuxSandboxSecurityContext{
894+
Privileged: true,
895+
},
896+
}
897+
podID := runPodSandbox(t, client, ctx, sandboxRequest)
898+
defer removePodSandbox(t, client, ctx, podID)
899+
defer stopPodSandbox(t, client, ctx, podID)
900+
901+
requestTemplate := getCreateContainerRequest(
902+
podID,
903+
t.Name()+"-container",
904+
imageLcowUbuntu,
905+
[]string{"bash", "-c", "while true; do echo 'hello'; sleep 1; done"},
906+
sandboxRequest.Config,
907+
)
908+
requestTemplate.Config.Linux = &runtime.LinuxContainerConfig{
909+
SecurityContext: &runtime.LinuxContainerSecurityContext{
910+
Privileged: true,
911+
},
912+
}
913+
containerID := createContainer(t, client, ctx, requestTemplate)
914+
defer removeContainer(t, client, ctx, containerID)
915+
startContainer(t, client, ctx, containerID)
916+
defer stopContainer(t, client, ctx, containerID)
917+
918+
execHelper := func(ctrID string, cmd []string) {
919+
stdout, stderr, errcode := execContainer(t, client, ctx, ctrID, cmd)
920+
if errcode != 0 {
921+
t.Helper()
922+
t.Logf("stdout: %s \n\n stderr: %s\n\n", stdout, stderr)
923+
t.Fatalf("failed to run '%v'\n: errcode: %d", cmd, errcode)
924+
}
925+
}
926+
927+
// setup nfs client
928+
nfsdir := "/mnt/nfstest"
929+
execHelper(containerID, []string{"apt", "update"})
930+
execHelper(containerID, []string{"apt", "install", "-y", "nfs-common"})
931+
execHelper(containerID, []string{"mkdir", "-p", nfsdir})
932+
933+
// There is no NFS daemon running in the container, so it is expected that the mount call fails with
934+
// the connection refused error. However getting upto the connection refused error verifies that the
935+
// container has all the required NFS client modules to successfully mount a NFS. (This also means
936+
// that the kernel was correctly built with the NFS client options). `retry=0` ensures it fails
937+
// immediately instead of retrying. If the kernel isn't correctly configured the call would fail with
938+
// "No Device" error.
939+
stdout, stderr, errcode := execContainer(t, client, ctx, containerID, []string{"mount", "-v", "-t", "nfs", "localhost:/fake/nfs/mount", nfsdir, "-o", "vers=4,minorversion=1,sec=sys,retry=0"})
940+
if errcode != 32 { // 32 is mount failure
941+
t.Logf("stdout: %s \n\n stderr: %s\n", stdout, stderr)
942+
t.Fatalf("mount call is expected to fail with mount failure error code: 32, errcode was %d instead", errcode)
943+
} else if !strings.Contains(stderr, "Connection refused") {
944+
t.Logf("stdout: %s \n\n stderr: %s\n", stdout, stderr)
945+
t.Fatalf("mount call is expected to fail with Connection refused error")
946+
}
947+
}

test/cri-containerd/main_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const (
5757
imageLcowAlpineCoreDump = "cplatpublic.azurecr.io/stackoverflow-alpine:latest"
5858
imageLcowCosmos = "cosmosarno/spark-master:2.4.1_2019-04-18_8e864ce"
5959
imageLcowCustomUser = "cplatpublic.azurecr.io/linux_custom_user:latest"
60+
imageLcowUbuntu = "ubuntu:latest"
6061
alpineAspNet = "mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine3.11"
6162
alpineAspnetUpgrade = "mcr.microsoft.com/dotnet/core/aspnet:3.1.2-alpine3.11"
6263

0 commit comments

Comments
 (0)