github.com/afbjorklund/moby@v20.10.5+incompatible/integration-cli/docker_cli_update_unix_test.go (about)

     1  // +build !windows
     2  
     3  package main
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"os/exec"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/creack/pty"
    15  	"github.com/docker/docker/api/types"
    16  	"github.com/docker/docker/client"
    17  	"github.com/docker/docker/pkg/parsers/kernel"
    18  	"github.com/docker/docker/testutil/request"
    19  	"gotest.tools/v3/assert"
    20  )
    21  
    22  func (s *DockerSuite) TestUpdateRunningContainer(c *testing.T) {
    23  	testRequires(c, DaemonIsLinux)
    24  	testRequires(c, memoryLimitSupport)
    25  
    26  	name := "test-update-container"
    27  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
    28  	dockerCmd(c, "update", "-m", "500M", name)
    29  
    30  	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
    31  
    32  	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
    33  	out, _ := dockerCmd(c, "exec", name, "cat", file)
    34  	assert.Equal(c, strings.TrimSpace(out), "524288000")
    35  }
    36  
    37  func (s *DockerSuite) TestUpdateRunningContainerWithRestart(c *testing.T) {
    38  	testRequires(c, DaemonIsLinux)
    39  	testRequires(c, memoryLimitSupport)
    40  
    41  	name := "test-update-container"
    42  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
    43  	dockerCmd(c, "update", "-m", "500M", name)
    44  	dockerCmd(c, "restart", name)
    45  
    46  	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
    47  
    48  	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
    49  	out, _ := dockerCmd(c, "exec", name, "cat", file)
    50  	assert.Equal(c, strings.TrimSpace(out), "524288000")
    51  }
    52  
    53  func (s *DockerSuite) TestUpdateStoppedContainer(c *testing.T) {
    54  	testRequires(c, DaemonIsLinux)
    55  	testRequires(c, memoryLimitSupport)
    56  
    57  	name := "test-update-container"
    58  	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
    59  	dockerCmd(c, "run", "--name", name, "-m", "300M", "busybox", "cat", file)
    60  	dockerCmd(c, "update", "-m", "500M", name)
    61  
    62  	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
    63  
    64  	out, _ := dockerCmd(c, "start", "-a", name)
    65  	assert.Equal(c, strings.TrimSpace(out), "524288000")
    66  }
    67  
    68  func (s *DockerSuite) TestUpdatePausedContainer(c *testing.T) {
    69  	testRequires(c, DaemonIsLinux)
    70  	testRequires(c, cpuShare)
    71  
    72  	name := "test-update-container"
    73  	dockerCmd(c, "run", "-d", "--name", name, "--cpu-shares", "1000", "busybox", "top")
    74  	dockerCmd(c, "pause", name)
    75  	dockerCmd(c, "update", "--cpu-shares", "500", name)
    76  
    77  	assert.Equal(c, inspectField(c, name, "HostConfig.CPUShares"), "500")
    78  
    79  	dockerCmd(c, "unpause", name)
    80  	file := "/sys/fs/cgroup/cpu/cpu.shares"
    81  	out, _ := dockerCmd(c, "exec", name, "cat", file)
    82  	assert.Equal(c, strings.TrimSpace(out), "500")
    83  }
    84  
    85  func (s *DockerSuite) TestUpdateWithUntouchedFields(c *testing.T) {
    86  	testRequires(c, DaemonIsLinux)
    87  	testRequires(c, memoryLimitSupport)
    88  	testRequires(c, cpuShare)
    89  
    90  	name := "test-update-container"
    91  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "--cpu-shares", "800", "busybox", "top")
    92  	dockerCmd(c, "update", "-m", "500M", name)
    93  
    94  	// Update memory and not touch cpus, `cpuset.cpus` should still have the old value
    95  	out := inspectField(c, name, "HostConfig.CPUShares")
    96  	assert.Equal(c, out, "800")
    97  
    98  	file := "/sys/fs/cgroup/cpu/cpu.shares"
    99  	out, _ = dockerCmd(c, "exec", name, "cat", file)
   100  	assert.Equal(c, strings.TrimSpace(out), "800")
   101  }
   102  
   103  func (s *DockerSuite) TestUpdateContainerInvalidValue(c *testing.T) {
   104  	testRequires(c, DaemonIsLinux)
   105  	testRequires(c, memoryLimitSupport)
   106  
   107  	name := "test-update-container"
   108  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
   109  	out, _, err := dockerCmdWithError("update", "-m", "2M", name)
   110  	assert.ErrorContains(c, err, "")
   111  	expected := "Minimum memory limit allowed is 6MB"
   112  	assert.Assert(c, strings.Contains(out, expected))
   113  }
   114  
   115  func (s *DockerSuite) TestUpdateContainerWithoutFlags(c *testing.T) {
   116  	testRequires(c, DaemonIsLinux)
   117  	testRequires(c, memoryLimitSupport)
   118  
   119  	name := "test-update-container"
   120  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
   121  	_, _, err := dockerCmdWithError("update", name)
   122  	assert.ErrorContains(c, err, "")
   123  }
   124  
   125  func (s *DockerSuite) TestUpdateKernelMemory(c *testing.T) {
   126  	testRequires(c, DaemonIsLinux, kernelMemorySupport)
   127  
   128  	name := "test-update-container"
   129  	dockerCmd(c, "run", "-d", "--name", name, "--kernel-memory", "50M", "busybox", "top")
   130  	dockerCmd(c, "update", "--kernel-memory", "100M", name)
   131  
   132  	assert.Equal(c, inspectField(c, name, "HostConfig.KernelMemory"), "104857600")
   133  
   134  	file := "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"
   135  	out, _ := dockerCmd(c, "exec", name, "cat", file)
   136  	assert.Equal(c, strings.TrimSpace(out), "104857600")
   137  }
   138  
   139  func (s *DockerSuite) TestUpdateKernelMemoryUninitialized(c *testing.T) {
   140  	testRequires(c, DaemonIsLinux, kernelMemorySupport)
   141  
   142  	isNewKernel := CheckKernelVersion(4, 6, 0)
   143  	name := "test-update-container"
   144  	dockerCmd(c, "run", "-d", "--name", name, "busybox", "top")
   145  	_, _, err := dockerCmdWithError("update", "--kernel-memory", "100M", name)
   146  	// Update kernel memory to a running container without kernel memory initialized
   147  	// is not allowed before kernel version 4.6.
   148  	if !isNewKernel {
   149  		assert.ErrorContains(c, err, "")
   150  	} else {
   151  		assert.NilError(c, err)
   152  	}
   153  
   154  	dockerCmd(c, "pause", name)
   155  	_, _, err = dockerCmdWithError("update", "--kernel-memory", "200M", name)
   156  	if !isNewKernel {
   157  		assert.ErrorContains(c, err, "")
   158  	} else {
   159  		assert.NilError(c, err)
   160  	}
   161  	dockerCmd(c, "unpause", name)
   162  
   163  	dockerCmd(c, "stop", name)
   164  	dockerCmd(c, "update", "--kernel-memory", "300M", name)
   165  	dockerCmd(c, "start", name)
   166  
   167  	assert.Equal(c, inspectField(c, name, "HostConfig.KernelMemory"), "314572800")
   168  
   169  	file := "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"
   170  	out, _ := dockerCmd(c, "exec", name, "cat", file)
   171  	assert.Equal(c, strings.TrimSpace(out), "314572800")
   172  }
   173  
   174  // GetKernelVersion gets the current kernel version.
   175  func GetKernelVersion() *kernel.VersionInfo {
   176  	v, _ := kernel.ParseRelease(testEnv.DaemonInfo.KernelVersion)
   177  	return v
   178  }
   179  
   180  // CheckKernelVersion checks if current kernel is newer than (or equal to)
   181  // the given version.
   182  func CheckKernelVersion(k, major, minor int) bool {
   183  	return kernel.CompareKernelVersion(*GetKernelVersion(), kernel.VersionInfo{Kernel: k, Major: major, Minor: minor}) >= 0
   184  }
   185  
   186  func (s *DockerSuite) TestUpdateSwapMemoryOnly(c *testing.T) {
   187  	testRequires(c, DaemonIsLinux)
   188  	testRequires(c, memoryLimitSupport)
   189  	testRequires(c, swapMemorySupport)
   190  
   191  	name := "test-update-container"
   192  	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
   193  	dockerCmd(c, "update", "--memory-swap", "600M", name)
   194  
   195  	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
   196  
   197  	file := "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
   198  	out, _ := dockerCmd(c, "exec", name, "cat", file)
   199  	assert.Equal(c, strings.TrimSpace(out), "629145600")
   200  }
   201  
   202  func (s *DockerSuite) TestUpdateInvalidSwapMemory(c *testing.T) {
   203  	testRequires(c, DaemonIsLinux)
   204  	testRequires(c, memoryLimitSupport)
   205  	testRequires(c, swapMemorySupport)
   206  
   207  	name := "test-update-container"
   208  	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
   209  	_, _, err := dockerCmdWithError("update", "--memory-swap", "200M", name)
   210  	// Update invalid swap memory should fail.
   211  	// This will pass docker config validation, but failed at kernel validation
   212  	assert.ErrorContains(c, err, "")
   213  
   214  	// Update invalid swap memory with failure should not change HostConfig
   215  	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "314572800")
   216  	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "524288000")
   217  
   218  	dockerCmd(c, "update", "--memory-swap", "600M", name)
   219  
   220  	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
   221  
   222  	file := "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
   223  	out, _ := dockerCmd(c, "exec", name, "cat", file)
   224  	assert.Equal(c, strings.TrimSpace(out), "629145600")
   225  }
   226  
   227  func (s *DockerSuite) TestUpdateStats(c *testing.T) {
   228  	testRequires(c, DaemonIsLinux)
   229  	testRequires(c, memoryLimitSupport)
   230  	testRequires(c, cpuCfsQuota)
   231  	name := "foo"
   232  	dockerCmd(c, "run", "-d", "-ti", "--name", name, "-m", "500m", "busybox")
   233  
   234  	assert.NilError(c, waitRun(name))
   235  
   236  	getMemLimit := func(id string) uint64 {
   237  		resp, body, err := request.Get(fmt.Sprintf("/containers/%s/stats?stream=false", id))
   238  		assert.NilError(c, err)
   239  		assert.Equal(c, resp.Header.Get("Content-Type"), "application/json")
   240  
   241  		var v *types.Stats
   242  		err = json.NewDecoder(body).Decode(&v)
   243  		assert.NilError(c, err)
   244  		body.Close()
   245  
   246  		return v.MemoryStats.Limit
   247  	}
   248  	preMemLimit := getMemLimit(name)
   249  
   250  	dockerCmd(c, "update", "--cpu-quota", "2000", name)
   251  
   252  	curMemLimit := getMemLimit(name)
   253  	assert.Equal(c, preMemLimit, curMemLimit)
   254  }
   255  
   256  func (s *DockerSuite) TestUpdateMemoryWithSwapMemory(c *testing.T) {
   257  	testRequires(c, DaemonIsLinux)
   258  	testRequires(c, memoryLimitSupport)
   259  	testRequires(c, swapMemorySupport)
   260  
   261  	name := "test-update-container"
   262  	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "busybox", "top")
   263  	out, _, err := dockerCmdWithError("update", "--memory", "800M", name)
   264  	assert.ErrorContains(c, err, "")
   265  	assert.Assert(c, strings.Contains(out, "Memory limit should be smaller than already set memoryswap limit"))
   266  
   267  	dockerCmd(c, "update", "--memory", "800M", "--memory-swap", "1000M", name)
   268  }
   269  
   270  func (s *DockerSuite) TestUpdateNotAffectMonitorRestartPolicy(c *testing.T) {
   271  	testRequires(c, DaemonIsLinux, cpuShare)
   272  
   273  	out, _ := dockerCmd(c, "run", "-tid", "--restart=always", "busybox", "sh")
   274  	id := strings.TrimSpace(out)
   275  	dockerCmd(c, "update", "--cpu-shares", "512", id)
   276  
   277  	cpty, tty, err := pty.Open()
   278  	assert.NilError(c, err)
   279  	defer cpty.Close()
   280  
   281  	cmd := exec.Command(dockerBinary, "attach", id)
   282  	cmd.Stdin = tty
   283  
   284  	assert.NilError(c, cmd.Start())
   285  	defer cmd.Process.Kill()
   286  
   287  	_, err = cpty.Write([]byte("exit\n"))
   288  	assert.NilError(c, err)
   289  
   290  	assert.NilError(c, cmd.Wait())
   291  
   292  	// container should restart again and keep running
   293  	err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
   294  	assert.NilError(c, err)
   295  	assert.NilError(c, waitRun(id))
   296  }
   297  
   298  func (s *DockerSuite) TestUpdateWithNanoCPUs(c *testing.T) {
   299  	testRequires(c, cpuCfsQuota, cpuCfsPeriod)
   300  
   301  	file1 := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
   302  	file2 := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
   303  
   304  	out, _ := dockerCmd(c, "run", "-d", "--cpus", "0.5", "--name", "top", "busybox", "top")
   305  	assert.Assert(c, strings.TrimSpace(out) != "")
   306  
   307  	out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
   308  	assert.Equal(c, strings.TrimSpace(out), "50000\n100000")
   309  
   310  	clt, err := client.NewClientWithOpts(client.FromEnv)
   311  	assert.NilError(c, err)
   312  	inspect, err := clt.ContainerInspect(context.Background(), "top")
   313  	assert.NilError(c, err)
   314  	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(500000000))
   315  
   316  	out = inspectField(c, "top", "HostConfig.CpuQuota")
   317  	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
   318  	out = inspectField(c, "top", "HostConfig.CpuPeriod")
   319  	assert.Equal(c, out, "0", "CPU CFS period should be 0")
   320  
   321  	out, _, err = dockerCmdWithError("update", "--cpu-quota", "80000", "top")
   322  	assert.ErrorContains(c, err, "")
   323  	assert.Assert(c, strings.Contains(out, "Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set"))
   324  
   325  	dockerCmd(c, "update", "--cpus", "0.8", "top")
   326  	inspect, err = clt.ContainerInspect(context.Background(), "top")
   327  	assert.NilError(c, err)
   328  	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(800000000))
   329  
   330  	out = inspectField(c, "top", "HostConfig.CpuQuota")
   331  	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
   332  	out = inspectField(c, "top", "HostConfig.CpuPeriod")
   333  	assert.Equal(c, out, "0", "CPU CFS period should be 0")
   334  
   335  	out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
   336  	assert.Equal(c, strings.TrimSpace(out), "80000\n100000")
   337  }