gopkg.in/docker/docker.v20@v20.10.27/integration-cli/docker_cli_update_unix_test.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"os/exec"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/creack/pty"
    16  	"github.com/docker/docker/api/types"
    17  	"github.com/docker/docker/client"
    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) TestUpdateSwapMemoryOnly(c *testing.T) {
   126  	testRequires(c, DaemonIsLinux)
   127  	testRequires(c, memoryLimitSupport)
   128  	testRequires(c, swapMemorySupport)
   129  
   130  	name := "test-update-container"
   131  	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
   132  	dockerCmd(c, "update", "--memory-swap", "600M", name)
   133  
   134  	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
   135  
   136  	file := "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
   137  	out, _ := dockerCmd(c, "exec", name, "cat", file)
   138  	assert.Equal(c, strings.TrimSpace(out), "629145600")
   139  }
   140  
   141  func (s *DockerSuite) TestUpdateInvalidSwapMemory(c *testing.T) {
   142  	testRequires(c, DaemonIsLinux)
   143  	testRequires(c, memoryLimitSupport)
   144  	testRequires(c, swapMemorySupport)
   145  
   146  	name := "test-update-container"
   147  	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
   148  	_, _, err := dockerCmdWithError("update", "--memory-swap", "200M", name)
   149  	// Update invalid swap memory should fail.
   150  	// This will pass docker config validation, but failed at kernel validation
   151  	assert.ErrorContains(c, err, "")
   152  
   153  	// Update invalid swap memory with failure should not change HostConfig
   154  	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "314572800")
   155  	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "524288000")
   156  
   157  	dockerCmd(c, "update", "--memory-swap", "600M", name)
   158  
   159  	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
   160  
   161  	file := "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
   162  	out, _ := dockerCmd(c, "exec", name, "cat", file)
   163  	assert.Equal(c, strings.TrimSpace(out), "629145600")
   164  }
   165  
   166  func (s *DockerSuite) TestUpdateStats(c *testing.T) {
   167  	testRequires(c, DaemonIsLinux)
   168  	testRequires(c, memoryLimitSupport)
   169  	testRequires(c, cpuCfsQuota)
   170  	name := "foo"
   171  	dockerCmd(c, "run", "-d", "-ti", "--name", name, "-m", "500m", "busybox")
   172  
   173  	assert.NilError(c, waitRun(name))
   174  
   175  	getMemLimit := func(id string) uint64 {
   176  		resp, body, err := request.Get(fmt.Sprintf("/containers/%s/stats?stream=false", id))
   177  		assert.NilError(c, err)
   178  		assert.Equal(c, resp.Header.Get("Content-Type"), "application/json")
   179  
   180  		var v *types.Stats
   181  		err = json.NewDecoder(body).Decode(&v)
   182  		assert.NilError(c, err)
   183  		body.Close()
   184  
   185  		return v.MemoryStats.Limit
   186  	}
   187  	preMemLimit := getMemLimit(name)
   188  
   189  	dockerCmd(c, "update", "--cpu-quota", "2000", name)
   190  
   191  	curMemLimit := getMemLimit(name)
   192  	assert.Equal(c, preMemLimit, curMemLimit)
   193  }
   194  
   195  func (s *DockerSuite) TestUpdateMemoryWithSwapMemory(c *testing.T) {
   196  	testRequires(c, DaemonIsLinux)
   197  	testRequires(c, memoryLimitSupport)
   198  	testRequires(c, swapMemorySupport)
   199  
   200  	name := "test-update-container"
   201  	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "busybox", "top")
   202  	out, _, err := dockerCmdWithError("update", "--memory", "800M", name)
   203  	assert.ErrorContains(c, err, "")
   204  	assert.Assert(c, strings.Contains(out, "Memory limit should be smaller than already set memoryswap limit"))
   205  
   206  	dockerCmd(c, "update", "--memory", "800M", "--memory-swap", "1000M", name)
   207  }
   208  
   209  func (s *DockerSuite) TestUpdateNotAffectMonitorRestartPolicy(c *testing.T) {
   210  	testRequires(c, DaemonIsLinux, cpuShare)
   211  
   212  	out, _ := dockerCmd(c, "run", "-tid", "--restart=always", "busybox", "sh")
   213  	id := strings.TrimSpace(out)
   214  	dockerCmd(c, "update", "--cpu-shares", "512", id)
   215  
   216  	cpty, tty, err := pty.Open()
   217  	assert.NilError(c, err)
   218  	defer cpty.Close()
   219  
   220  	cmd := exec.Command(dockerBinary, "attach", id)
   221  	cmd.Stdin = tty
   222  
   223  	assert.NilError(c, cmd.Start())
   224  	defer cmd.Process.Kill()
   225  
   226  	_, err = cpty.Write([]byte("exit\n"))
   227  	assert.NilError(c, err)
   228  
   229  	assert.NilError(c, cmd.Wait())
   230  
   231  	// container should restart again and keep running
   232  	err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
   233  	assert.NilError(c, err)
   234  	assert.NilError(c, waitRun(id))
   235  }
   236  
   237  func (s *DockerSuite) TestUpdateWithNanoCPUs(c *testing.T) {
   238  	testRequires(c, cpuCfsQuota, cpuCfsPeriod)
   239  
   240  	file1 := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
   241  	file2 := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
   242  
   243  	out, _ := dockerCmd(c, "run", "-d", "--cpus", "0.5", "--name", "top", "busybox", "top")
   244  	assert.Assert(c, strings.TrimSpace(out) != "")
   245  
   246  	out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
   247  	assert.Equal(c, strings.TrimSpace(out), "50000\n100000")
   248  
   249  	clt, err := client.NewClientWithOpts(client.FromEnv)
   250  	assert.NilError(c, err)
   251  	inspect, err := clt.ContainerInspect(context.Background(), "top")
   252  	assert.NilError(c, err)
   253  	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(500000000))
   254  
   255  	out = inspectField(c, "top", "HostConfig.CpuQuota")
   256  	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
   257  	out = inspectField(c, "top", "HostConfig.CpuPeriod")
   258  	assert.Equal(c, out, "0", "CPU CFS period should be 0")
   259  
   260  	out, _, err = dockerCmdWithError("update", "--cpu-quota", "80000", "top")
   261  	assert.ErrorContains(c, err, "")
   262  	assert.Assert(c, strings.Contains(out, "Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set"))
   263  
   264  	dockerCmd(c, "update", "--cpus", "0.8", "top")
   265  	inspect, err = clt.ContainerInspect(context.Background(), "top")
   266  	assert.NilError(c, err)
   267  	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(800000000))
   268  
   269  	out = inspectField(c, "top", "HostConfig.CpuQuota")
   270  	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
   271  	out = inspectField(c, "top", "HostConfig.CpuPeriod")
   272  	assert.Equal(c, out, "0", "CPU CFS period should be 0")
   273  
   274  	out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
   275  	assert.Equal(c, strings.TrimSpace(out), "80000\n100000")
   276  }