Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions lib/instances/docker_integration_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//go:build linux

package instances

import (
"context"
"os"
"strings"
"testing"
"time"

"github.com/kernel/hypeman/lib/hypervisor"
"github.com/kernel/hypeman/lib/volumes"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const dockerInVMManualEnv = "HYPEMAN_RUN_DOCKER_IN_VM_TESTS"

func requireDockerInVMManualRun(t *testing.T) {
t.Helper()
if os.Getenv(dockerInVMManualEnv) != "1" {
t.Skipf("set %s=1 to run docker-in-vm integration tests", dockerInVMManualEnv)
}
}

func TestDockerInVMCloudHypervisorWithAttachedVolume(t *testing.T) {
requireDockerInVMManualRun(t)
requireKVMAccess(t)

ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute)
defer cancel()

manager, _ := setupCompressionTestManagerForHypervisor(t, hypervisor.TypeCloudHypervisor)
imageName := integrationTestImageRef(t, "docker.io/library/debian:12-slim")

createImageAndWait(t, ctx, manager.imageManager, imageName)
require.NoError(t, manager.systemManager.EnsureSystemFiles(ctx))

volumeManager := volumes.NewManager(manager.paths, 0, nil)
vol, err := volumeManager.CreateVolume(ctx, volumes.CreateVolumeRequest{
Name: "docker-data",
SizeGb: 8,
})
require.NoError(t, err)

var inst *Instance
t.Cleanup(func() {
if inst != nil {
logInstanceArtifactsOnFailure(t, manager, inst.Id)
if t.Failed() {
if output, code, err := execCommand(context.Background(), inst, "sh", "-lc", "cat /tmp/dockerd.log || true"); err == nil {
t.Logf("dockerd log (exit=%d):\n%s", code, output)
}
}
_ = manager.DeleteInstance(context.Background(), inst.Id)
}
_ = volumeManager.DeleteVolume(context.Background(), vol.Id)
})

inst, err = manager.CreateInstance(ctx, CreateInstanceRequest{
Name: "docker-in-vm",
Image: imageName,
Size: 4 * 1024 * 1024 * 1024,
HotplugSize: 512 * 1024 * 1024,
OverlaySize: 5 * 1024 * 1024 * 1024,
Vcpus: 2,
NetworkEnabled: true,
Hypervisor: hypervisor.TypeCloudHypervisor,
Entrypoint: []string{"/bin/sh", "-lc"},
Cmd: []string{"sleep infinity"},
Volumes: []VolumeAttachment{
{
VolumeID: vol.Id,
MountPath: "/mnt/docker-data",
Readonly: false,
},
},
})
require.NoError(t, err)

_, err = waitForInstanceState(ctx, manager, inst.Id, StateRunning, 60*time.Second)
require.NoError(t, err)
require.NoError(t, waitForExecAgent(ctx, manager, inst.Id, 60*time.Second))

output, exitCode, err := execCommand(ctx, inst, "sh", "-lc", "findmnt -n -o FSTYPE,SOURCE /mnt/docker-data")
require.NoError(t, err)
require.Equal(t, 0, exitCode, "findmnt should succeed: %s", output)
assert.Contains(t, output, "ext4", "docker data volume should be ext4-backed")
assert.Contains(t, output, "/dev/vd", "docker data volume should come from an attached block device")

output, exitCode, err = execCommand(ctx, inst, "sh", "-lc", `
set -eux
mkdir -p /var/lib/docker
mount --bind /mnt/docker-data /var/lib/docker
findmnt -n -o FSTYPE,SOURCE /var/lib/docker >/tmp/docker-bind-mount.txt
grep -q ext4 /tmp/docker-bind-mount.txt
`)
require.NoError(t, err)
require.Equal(t, 0, exitCode, "docker bind mount should work before docker install: %s", output)

output, exitCode, err = execCommand(ctx, inst, "sh", "-lc", `
set -eux
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y docker.io curl
`)
require.NoError(t, err)
require.Equal(t, 0, exitCode, "docker install should succeed: %s", output)

output, exitCode, err = execCommand(ctx, inst, "sh", "-lc", `
set -eux
nohup dockerd >/tmp/dockerd.log 2>&1 &
for i in $(seq 1 90); do
if docker info >/tmp/docker-info.txt 2>/tmp/docker-info.err; then
exit 0
fi
sleep 1
done
cat /tmp/docker-info.err || true
cat /tmp/dockerd.log || true
exit 1
`)
require.NoError(t, err)
require.Equal(t, 0, exitCode, "dockerd should become ready: %s", output)

output, exitCode, err = execCommand(ctx, inst, "sh", "-lc", "docker info --format '{{.Driver}}'")
require.NoError(t, err)
require.Equal(t, 0, exitCode, "docker info should succeed: %s", output)
assert.Equal(t, "overlay2", strings.TrimSpace(output), "docker should use overlay2 on the attached volume")

output, exitCode, err = execCommand(ctx, inst, "sh", "-lc", "docker run --rm hello-world")
require.NoError(t, err)
require.Equal(t, 0, exitCode, "hello-world should run successfully: %s", output)
assert.Contains(t, output, "Hello from Docker!", "hello-world output should confirm container execution")

output, exitCode, err = execCommand(ctx, inst, "sh", "-lc", `
set -eux
docker rm -f docker-nginx >/dev/null 2>&1 || true
docker run -d --rm --name docker-nginx -p 8080:80 nginx:alpine
for i in $(seq 1 60); do
if curl -fsS http://127.0.0.1:8080 >/tmp/docker-nginx.html; then
grep -q 'Welcome to nginx!' /tmp/docker-nginx.html
exit 0
fi
sleep 1
done
docker logs docker-nginx || true
exit 1
`)
require.NoError(t, err)
require.Equal(t, 0, exitCode, "docker port publishing should work: %s", output)
}
14 changes: 13 additions & 1 deletion lib/system/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ const (

// Kernel_202603091 is the current kernel version with iptables filter/xt match support for nested Hypeman networking
Kernel_202603091 KernelVersion = "ch-6.12.8-kernel-1.5-202603091"

// Kernel_202603301 is the current kernel version with expanded nftables/raw support for Docker bridge networking
Kernel_202603301 KernelVersion = "ch-6.12.8-kernel-1.6-202603301"
)

var (
// DefaultKernelVersion is the kernel version used for new instances
DefaultKernelVersion = Kernel_202603091
DefaultKernelVersion = Kernel_202603301

// SupportedKernelVersions lists all supported kernel versions
SupportedKernelVersions = []KernelVersion{
Kernel_202603301,
Kernel_202603091,
Kernel_202602101,
Kernel_202601152,
Expand All @@ -30,6 +34,10 @@ var (

// KernelDownloadURLs maps kernel versions and architectures to download URLs
var KernelDownloadURLs = map[KernelVersion]map[string]string{
Kernel_202603301: {
"x86_64": "https://github.com/kernel/linux/releases/download/ch-6.12.8-kernel-1.6-202603301/vmlinux-x86_64",
"aarch64": "https://github.com/kernel/linux/releases/download/ch-6.12.8-kernel-1.6-202603301/Image-arm64",
},
Kernel_202603091: {
"x86_64": "https://github.com/kernel/linux/releases/download/ch-6.12.8-kernel-1.5-202603091/vmlinux-x86_64",
"aarch64": "https://github.com/kernel/linux/releases/download/ch-6.12.8-kernel-1.5-202603091/Image-arm64",
Expand All @@ -47,6 +55,10 @@ var KernelDownloadURLs = map[KernelVersion]map[string]string{
// KernelHeaderURLs maps kernel versions and architectures to kernel header tarball URLs
// These tarballs contain kernel headers needed for DKMS to build out-of-tree modules (e.g., NVIDIA vGPU drivers)
var KernelHeaderURLs = map[KernelVersion]map[string]string{
Kernel_202603301: {
"x86_64": "https://github.com/kernel/linux/releases/download/ch-6.12.8-kernel-1.6-202603301/kernel-headers-x86_64.tar.gz",
"aarch64": "https://github.com/kernel/linux/releases/download/ch-6.12.8-kernel-1.6-202603301/kernel-headers-aarch64.tar.gz",
},
Kernel_202603091: {
"x86_64": "https://github.com/kernel/linux/releases/download/ch-6.12.8-kernel-1.5-202603091/kernel-headers-x86_64.tar.gz",
"aarch64": "https://github.com/kernel/linux/releases/download/ch-6.12.8-kernel-1.5-202603091/kernel-headers-aarch64.tar.gz",
Expand Down
Loading