containerd/integration/container_update_resources_test.go
Danielle Lancashire 2fa4e9c0e2 cri: add support for configuring swap
Signed-off-by: Danielle Lancashire <dani@builds.terrible.systems>
2021-12-02 21:25:33 +01:00

268 lines
8.2 KiB
Go

//go:build linux
// +build linux
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package integration
import (
"bytes"
"io/ioutil"
"strings"
"testing"
"github.com/containerd/cgroups"
cgroupsv2 "github.com/containerd/cgroups/v2"
"github.com/containerd/containerd"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)
func checkMemoryLimit(t *testing.T, spec *runtimespec.Spec, memLimit int64) {
require.NotNil(t, spec)
require.NotNil(t, spec.Linux)
require.NotNil(t, spec.Linux.Resources)
require.NotNil(t, spec.Linux.Resources.Memory)
require.NotNil(t, spec.Linux.Resources.Memory.Limit)
assert.Equal(t, memLimit, *spec.Linux.Resources.Memory.Limit)
}
func checkMemorySwapLimit(t *testing.T, spec *runtimespec.Spec, memLimit *int64) {
require.NotNil(t, spec)
require.NotNil(t, spec.Linux)
require.NotNil(t, spec.Linux.Resources)
require.NotNil(t, spec.Linux.Resources.Memory)
if memLimit == nil {
require.Nil(t, spec.Linux.Resources.Memory.Swap)
} else {
require.NotNil(t, spec.Linux.Resources.Memory.Swap)
assert.Equal(t, *memLimit, *spec.Linux.Resources.Memory.Swap)
}
}
func getCgroupSwapLimitForTask(t *testing.T, task containerd.Task) uint64 {
if cgroups.Mode() == cgroups.Unified {
groupPath, err := cgroupsv2.PidGroupPath(int(task.Pid()))
if err != nil {
t.Fatal(err)
}
cgroup2, err := cgroupsv2.LoadManager("/sys/fs/cgroup", groupPath)
if err != nil {
t.Fatal(err)
}
stat, err := cgroup2.Stat()
if err != nil {
t.Fatal(err)
}
return stat.Memory.SwapLimit
}
cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
if err != nil {
t.Fatal(err)
}
stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
if err != nil {
t.Fatal(err)
}
return stat.Memory.HierarchicalSwapLimit
}
func getCgroupMemoryLimitForTask(t *testing.T, task containerd.Task) uint64 {
if cgroups.Mode() == cgroups.Unified {
groupPath, err := cgroupsv2.PidGroupPath(int(task.Pid()))
if err != nil {
t.Fatal(err)
}
cgroup2, err := cgroupsv2.LoadManager("/sys/fs/cgroup", groupPath)
if err != nil {
t.Fatal(err)
}
stat, err := cgroup2.Stat()
if err != nil {
t.Fatal(err)
}
return stat.Memory.UsageLimit
}
cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
if err != nil {
t.Fatal(err)
}
stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
if err != nil {
t.Fatal(err)
}
return stat.Memory.Usage.Limit
}
func isSwapLikelyEnabled() bool {
// Check whether swap is enabled.
swapFile := "/proc/swaps"
swapData, err := ioutil.ReadFile(swapFile)
if err != nil {
// We can't read the file or it doesn't exist, assume we don't have swap.
return false
}
swapData = bytes.TrimSpace(swapData) // extra trailing \n
swapLines := strings.Split(string(swapData), "\n")
// If there is more than one line (table headers) in /proc/swaps, swap is enabled
return len(swapLines) > 1
}
func TestUpdateContainerResources_MemorySwap(t *testing.T) {
if !isSwapLikelyEnabled() {
t.Skipf("Swap is not enabled, or /proc/swaps is not readable. Swap is required for this test")
return
}
t.Log("Create a sandbox")
sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-swap-resources")
EnsureImageExists(t, pauseImage)
memoryLimit := int64(128 * 1024 * 1024)
baseSwapLimit := int64(200 * 1024 * 1024)
increasedSwapLimit := int64(256 * 1024 * 1024)
t.Log("Create a container with memory limit but no swap")
cnConfig := ContainerConfig(
"container",
pauseImage,
WithResources(&runtime.LinuxContainerResources{
MemoryLimitInBytes: memoryLimit,
MemorySwapLimitInBytes: baseSwapLimit,
}),
)
cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
require.NoError(t, err)
t.Log("Check memory limit in container OCI spec")
container, err := containerdClient.LoadContainer(context.Background(), cn)
require.NoError(t, err)
spec, err := container.Spec(context.Background())
require.NoError(t, err)
checkMemoryLimit(t, spec, memoryLimit)
checkMemorySwapLimit(t, spec, &baseSwapLimit)
t.Log("Update container swap limit after created")
err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
MemorySwapLimitInBytes: baseSwapLimit,
}, nil)
require.NoError(t, err)
t.Log("Check memory limit in container OCI spec")
spec, err = container.Spec(context.Background())
require.NoError(t, err)
sw1 := baseSwapLimit
checkMemorySwapLimit(t, spec, &sw1)
t.Log("Start the container")
require.NoError(t, runtimeService.StartContainer(cn))
task, err := container.Task(context.Background(), nil)
require.NoError(t, err)
t.Log("Check memory limit in cgroup")
memLimit := getCgroupMemoryLimitForTask(t, task)
swapLimit := getCgroupSwapLimitForTask(t, task)
assert.Equal(t, uint64(memoryLimit), memLimit)
assert.Equal(t, uint64(baseSwapLimit-memoryLimit), swapLimit)
t.Log("Update container memory limit after started")
err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
MemorySwapLimitInBytes: increasedSwapLimit,
}, nil)
require.NoError(t, err)
t.Log("Check memory limit in container OCI spec")
spec, err = container.Spec(context.Background())
require.NoError(t, err)
checkMemorySwapLimit(t, spec, &increasedSwapLimit)
t.Log("Check memory limit in cgroup")
swapLimit = getCgroupSwapLimitForTask(t, task)
assert.Equal(t, uint64(increasedSwapLimit-memoryLimit), swapLimit)
}
func TestUpdateContainerResources_MemoryLimit(t *testing.T) {
// TODO(claudiub): Make this test work once https://github.com/microsoft/hcsshim/pull/931 merges.
t.Log("Create a sandbox")
sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-resources")
EnsureImageExists(t, pauseImage)
t.Log("Create a container with memory limit")
cnConfig := ContainerConfig(
"container",
pauseImage,
WithResources(&runtime.LinuxContainerResources{
MemoryLimitInBytes: 200 * 1024 * 1024,
}),
)
cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
require.NoError(t, err)
t.Log("Check memory limit in container OCI spec")
container, err := containerdClient.LoadContainer(context.Background(), cn)
require.NoError(t, err)
spec, err := container.Spec(context.Background())
require.NoError(t, err)
checkMemoryLimit(t, spec, 200*1024*1024)
t.Log("Update container memory limit after created")
err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
MemoryLimitInBytes: 400 * 1024 * 1024,
}, nil)
require.NoError(t, err)
t.Log("Check memory limit in container OCI spec")
spec, err = container.Spec(context.Background())
require.NoError(t, err)
checkMemoryLimit(t, spec, 400*1024*1024)
t.Log("Start the container")
require.NoError(t, runtimeService.StartContainer(cn))
task, err := container.Task(context.Background(), nil)
require.NoError(t, err)
t.Log("Check memory limit in cgroup")
cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
require.NoError(t, err)
stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
require.NoError(t, err)
assert.Equal(t, uint64(400*1024*1024), stat.Memory.Usage.Limit)
t.Log("Update container memory limit after started")
err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
MemoryLimitInBytes: 800 * 1024 * 1024,
}, nil)
require.NoError(t, err)
t.Log("Check memory limit in container OCI spec")
spec, err = container.Spec(context.Background())
require.NoError(t, err)
checkMemoryLimit(t, spec, 800*1024*1024)
t.Log("Check memory limit in cgroup")
stat, err = cgroup.Stat(cgroups.IgnoreNotExist)
require.NoError(t, err)
assert.Equal(t, uint64(800*1024*1024), stat.Memory.Usage.Limit)
}