diff --git a/integration/container_update_resources_test.go b/integration/container_update_resources_test.go new file mode 100644 index 000000000..23be1d034 --- /dev/null +++ b/integration/container_update_resources_test.go @@ -0,0 +1,109 @@ +/* +Copyright 2017 The Kubernetes 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 ( + "testing" + + "github.com/containerd/cgroups" + runtimespec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" +) + +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 TestUpdateContainerResources(t *testing.T) { + t.Logf("Create a sandbox") + sbConfig := PodSandboxConfig("sandbox", "update-container-resources") + sb, err := runtimeService.RunPodSandbox(sbConfig) + require.NoError(t, err) + defer func() { + assert.NoError(t, runtimeService.StopPodSandbox(sb)) + assert.NoError(t, runtimeService.RemovePodSandbox(sb)) + }() + + t.Logf("Create a container with memory limit") + cnConfig := ContainerConfig( + "container", + pauseImage, + WithResources(&runtime.LinuxContainerResources{ + MemoryLimitInBytes: 2 * 1024 * 1024, + }), + ) + cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig) + require.NoError(t, err) + + t.Logf("Check memory limit in container OCI spec") + container, err := containerdClient.LoadContainer(context.Background(), cn) + require.NoError(t, err) + spec, err := container.Spec() + require.NoError(t, err) + checkMemoryLimit(t, spec, 2*1024*1024) + + t.Logf("Update container memory limit after created") + err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{ + MemoryLimitInBytes: 4 * 1024 * 1024, + }) + require.NoError(t, err) + + t.Logf("Check memory limit in container OCI spec") + container, err = containerdClient.LoadContainer(context.Background(), cn) + require.NoError(t, err) + spec, err = container.Spec() + require.NoError(t, err) + checkMemoryLimit(t, spec, 4*1024*1024) + + t.Logf("Start the container") + require.NoError(t, runtimeService.StartContainer(cn)) + task, err := container.Task(context.Background(), nil) + require.NoError(t, err) + + t.Logf("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(4*1024*1024), stat.Memory.Usage.Limit) + + t.Logf("Update container memory limit after started") + err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{ + MemoryLimitInBytes: 8 * 1024 * 1024, + }) + require.NoError(t, err) + + t.Logf("Check memory limit in container OCI spec") + container, err = containerdClient.LoadContainer(context.Background(), cn) + require.NoError(t, err) + spec, err = container.Spec() + require.NoError(t, err) + checkMemoryLimit(t, spec, 8*1024*1024) + + t.Logf("Check memory limit in cgroup") + stat, err = cgroup.Stat(cgroups.IgnoreNotExist) + require.NoError(t, err) + assert.Equal(t, uint64(8*1024*1024), stat.Memory.Usage.Limit) +} diff --git a/integration/test_utils.go b/integration/test_utils.go index 12783cb73..498c62c59 100644 --- a/integration/test_utils.go +++ b/integration/test_utils.go @@ -20,6 +20,7 @@ import ( "errors" "time" + "github.com/containerd/containerd" "github.com/golang/glog" "k8s.io/kubernetes/pkg/kubelet/apis/cri" "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" @@ -29,14 +30,17 @@ import ( ) const ( - sock = "/var/run/cri-containerd.sock" - timeout = 1 * time.Minute - pauseImage = "gcr.io/google_containers/pause:3.0" + sock = "/var/run/cri-containerd.sock" + timeout = 1 * time.Minute + pauseImage = "gcr.io/google_containers/pause:3.0" // This is the same with default sandbox image. + k8sNamespace = "k8s.io" // This is the same with server.k8sContainerdNamespace. + containerdEndpoint = "/run/containerd/containerd.sock" ) var ( - runtimeService cri.RuntimeService - imageService cri.ImageManagerService + runtimeService cri.RuntimeService + imageService cri.ImageManagerService + containerdClient *containerd.Client ) func init() { @@ -49,6 +53,10 @@ func init() { if err != nil { glog.Exitf("Failed to create image service: %v", err) } + containerdClient, err = containerd.New(containerdEndpoint, containerd.WithDefaultNamespace(k8sNamespace)) + if err != nil { + glog.Exitf("Failed to connect containerd: %v", err) + } } // Opts sets specific information in pod sandbox config. @@ -88,6 +96,16 @@ func WithTestAnnotations() ContainerOpts { } } +// Add container resource limits. +func WithResources(r *runtime.LinuxContainerResources) ContainerOpts { + return func(cf *runtime.ContainerConfig) { + if cf.Linux == nil { + cf.Linux = &runtime.LinuxContainerConfig{} + } + cf.Linux.Resources = r + } +} + // ContainerConfig creates a container config given a name and image name // and additional container config options func ContainerConfig(name, image string, opts ...ContainerOpts) *runtime.ContainerConfig {