github.com/wozhu6104/docker@v20.10.10+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/testutil/request"
    18  	"gotest.tools/v3/assert"
    19  )
    20  
    21  func (s *DockerSuite) TestUpdateRunningContainer(c *testing.T) {
    22  	testRequires(c, DaemonIsLinux)
    23  	testRequires(c, memoryLimitSupport)
    24  
    25  	name := "test-update-container"
    26  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
    27  	dockerCmd(c, "update", "-m", "500M", name)
    28  
    29  	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
    30  
    31  	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
    32  	out, _ := dockerCmd(c, "exec", name, "cat", file)
    33  	assert.Equal(c, strings.TrimSpace(out), "524288000")
    34  }
    35  
    36  func (s *DockerSuite) TestUpdateRunningContainerWithRestart(c *testing.T) {
    37  	testRequires(c, DaemonIsLinux)
    38  	testRequires(c, memoryLimitSupport)
    39  
    40  	name := "test-update-container"
    41  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
    42  	dockerCmd(c, "update", "-m", "500M", name)
    43  	dockerCmd(c, "restart", name)
    44  
    45  	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
    46  
    47  	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
    48  	out, _ := dockerCmd(c, "exec", name, "cat", file)
    49  	assert.Equal(c, strings.TrimSpace(out), "524288000")
    50  }
    51  
    52  func (s *DockerSuite) TestUpdateStoppedContainer(c *testing.T) {
    53  	testRequires(c, DaemonIsLinux)
    54  	testRequires(c, memoryLimitSupport)
    55  
    56  	name := "test-update-container"
    57  	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
    58  	dockerCmd(c, "run", "--name", name, "-m", "300M", "busybox", "cat", file)
    59  	dockerCmd(c, "update", "-m", "500M", name)
    60  
    61  	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
    62  
    63  	out, _ := dockerCmd(c, "start", "-a", name)
    64  	assert.Equal(c, strings.TrimSpace(out), "524288000")
    65  }
    66  
    67  func (s *DockerSuite) TestUpdatePausedContainer(c *testing.T) {
    68  	testRequires(c, DaemonIsLinux)
    69  	testRequires(c, cpuShare)
    70  
    71  	name := "test-update-container"
    72  	dockerCmd(c, "run", "-d", "--name", name, "--cpu-shares", "1000", "busybox", "top")
    73  	dockerCmd(c, "pause", name)
    74  	dockerCmd(c, "update", "--cpu-shares", "500", name)
    75  
    76  	assert.Equal(c, inspectField(c, name, "HostConfig.CPUShares"), "500")
    77  
    78  	dockerCmd(c, "unpause", name)
    79  	file := "/sys/fs/cgroup/cpu/cpu.shares"
    80  	out, _ := dockerCmd(c, "exec", name, "cat", file)
    81  	assert.Equal(c, strings.TrimSpace(out), "500")
    82  }
    83  
    84  func (s *DockerSuite) TestUpdateWithUntouchedFields(c *testing.T) {
    85  	testRequires(c, DaemonIsLinux)
    86  	testRequires(c, memoryLimitSupport)
    87  	testRequires(c, cpuShare)
    88  
    89  	name := "test-update-container"
    90  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "--cpu-shares", "800", "busybox", "top")
    91  	dockerCmd(c, "update", "-m", "500M", name)
    92  
    93  	// Update memory and not touch cpus, `cpuset.cpus` should still have the old value
    94  	out := inspectField(c, name, "HostConfig.CPUShares")
    95  	assert.Equal(c, out, "800")
    96  
    97  	file := "/sys/fs/cgroup/cpu/cpu.shares"
    98  	out, _ = dockerCmd(c, "exec", name, "cat", file)
    99  	assert.Equal(c, strings.TrimSpace(out), "800")
   100  }
   101  
   102  func (s *DockerSuite) TestUpdateContainerInvalidValue(c *testing.T) {
   103  	testRequires(c, DaemonIsLinux)
   104  	testRequires(c, memoryLimitSupport)
   105  
   106  	name := "test-update-container"
   107  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
   108  	out, _, err := dockerCmdWithError("update", "-m", "2M", name)
   109  	assert.ErrorContains(c, err, "")
   110  	expected := "Minimum memory limit allowed is 6MB"
   111  	assert.Assert(c, strings.Contains(out, expected))
   112  }
   113  
   114  func (s *DockerSuite) TestUpdateContainerWithoutFlags(c *testing.T) {
   115  	testRequires(c, DaemonIsLinux)
   116  	testRequires(c, memoryLimitSupport)
   117  
   118  	name := "test-update-container"
   119  	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
   120  	_, _, err := dockerCmdWithError("update", name)
   121  	assert.ErrorContains(c, err, "")
   122  }
   123  
   124  func (s *DockerSuite) TestUpdateSwapMemoryOnly(c *testing.T) {
   125  	testRequires(c, DaemonIsLinux)
   126  	testRequires(c, memoryLimitSupport)
   127  	testRequires(c, swapMemorySupport)
   128  
   129  	name := "test-update-container"
   130  	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
   131  	dockerCmd(c, "update", "--memory-swap", "600M", name)
   132  
   133  	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
   134  
   135  	file := "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
   136  	out, _ := dockerCmd(c, "exec", name, "cat", file)
   137  	assert.Equal(c, strings.TrimSpace(out), "629145600")
   138  }
   139  
   140  func (s *DockerSuite) TestUpdateInvalidSwapMemory(c *testing.T) {
   141  	testRequires(c, DaemonIsLinux)
   142  	testRequires(c, memoryLimitSupport)
   143  	testRequires(c, swapMemorySupport)
   144  
   145  	name := "test-update-container"
   146  	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
   147  	_, _, err := dockerCmdWithError("update", "--memory-swap", "200M", name)
   148  	// Update invalid swap memory should fail.
   149  	// This will pass docker config validation, but failed at kernel validation
   150  	assert.ErrorContains(c, err, "")
   151  
   152  	// Update invalid swap memory with failure should not change HostConfig
   153  	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "314572800")
   154  	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "524288000")
   155  
   156  	dockerCmd(c, "update", "--memory-swap", "600M", name)
   157  
   158  	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
   159  
   160  	file := "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
   161  	out, _ := dockerCmd(c, "exec", name, "cat", file)
   162  	assert.Equal(c, strings.TrimSpace(out), "629145600")
   163  }
   164  
   165  func (s *DockerSuite) TestUpdateStats(c *testing.T) {
   166  	testRequires(c, DaemonIsLinux)
   167  	testRequires(c, memoryLimitSupport)
   168  	testRequires(c, cpuCfsQuota)
   169  	name := "foo"
   170  	dockerCmd(c, "run", "-d", "-ti", "--name", name, "-m", "500m", "busybox")
   171  
   172  	assert.NilError(c, waitRun(name))
   173  
   174  	getMemLimit := func(id string) uint64 {
   175  		resp, body, err := request.Get(fmt.Sprintf("/containers/%s/stats?stream=false", id))
   176  		assert.NilError(c, err)
   177  		assert.Equal(c, resp.Header.Get("Content-Type"), "application/json")
   178  
   179  		var v *types.Stats
   180  		err = json.NewDecoder(body).Decode(&v)
   181  		assert.NilError(c, err)
   182  		body.Close()
   183  
   184  		return v.MemoryStats.Limit
   185  	}
   186  	preMemLimit := getMemLimit(name)
   187  
   188  	dockerCmd(c, "update", "--cpu-quota", "2000", name)
   189  
   190  	curMemLimit := getMemLimit(name)
   191  	assert.Equal(c, preMemLimit, curMemLimit)
   192  }
   193  
   194  func (s *DockerSuite) TestUpdateMemoryWithSwapMemory(c *testing.T) {
   195  	testRequires(c, DaemonIsLinux)
   196  	testRequires(c, memoryLimitSupport)
   197  	testRequires(c, swapMemorySupport)
   198  
   199  	name := "test-update-container"
   200  	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "busybox", "top")
   201  	out, _, err := dockerCmdWithError("update", "--memory", "800M", name)
   202  	assert.ErrorContains(c, err, "")
   203  	assert.Assert(c, strings.Contains(out, "Memory limit should be smaller than already set memoryswap limit"))
   204  
   205  	dockerCmd(c, "update", "--memory", "800M", "--memory-swap", "1000M", name)
   206  }
   207  
   208  func (s *DockerSuite) TestUpdateNotAffectMonitorRestartPolicy(c *testing.T) {
   209  	testRequires(c, DaemonIsLinux, cpuShare)
   210  
   211  	out, _ := dockerCmd(c, "run", "-tid", "--restart=always", "busybox", "sh")
   212  	id := strings.TrimSpace(out)
   213  	dockerCmd(c, "update", "--cpu-shares", "512", id)
   214  
   215  	cpty, tty, err := pty.Open()
   216  	assert.NilError(c, err)
   217  	defer cpty.Close()
   218  
   219  	cmd := exec.Command(dockerBinary, "attach", id)
   220  	cmd.Stdin = tty
   221  
   222  	assert.NilError(c, cmd.Start())
   223  	defer cmd.Process.Kill()
   224  
   225  	_, err = cpty.Write([]byte("exit\n"))
   226  	assert.NilError(c, err)
   227  
   228  	assert.NilError(c, cmd.Wait())
   229  
   230  	// container should restart again and keep running
   231  	err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
   232  	assert.NilError(c, err)
   233  	assert.NilError(c, waitRun(id))
   234  }
   235  
   236  func (s *DockerSuite) TestUpdateWithNanoCPUs(c *testing.T) {
   237  	testRequires(c, cpuCfsQuota, cpuCfsPeriod)
   238  
   239  	file1 := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
   240  	file2 := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
   241  
   242  	out, _ := dockerCmd(c, "run", "-d", "--cpus", "0.5", "--name", "top", "busybox", "top")
   243  	assert.Assert(c, strings.TrimSpace(out) != "")
   244  
   245  	out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
   246  	assert.Equal(c, strings.TrimSpace(out), "50000\n100000")
   247  
   248  	clt, err := client.NewClientWithOpts(client.FromEnv)
   249  	assert.NilError(c, err)
   250  	inspect, err := clt.ContainerInspect(context.Background(), "top")
   251  	assert.NilError(c, err)
   252  	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(500000000))
   253  
   254  	out = inspectField(c, "top", "HostConfig.CpuQuota")
   255  	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
   256  	out = inspectField(c, "top", "HostConfig.CpuPeriod")
   257  	assert.Equal(c, out, "0", "CPU CFS period should be 0")
   258  
   259  	out, _, err = dockerCmdWithError("update", "--cpu-quota", "80000", "top")
   260  	assert.ErrorContains(c, err, "")
   261  	assert.Assert(c, strings.Contains(out, "Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set"))
   262  
   263  	dockerCmd(c, "update", "--cpus", "0.8", "top")
   264  	inspect, err = clt.ContainerInspect(context.Background(), "top")
   265  	assert.NilError(c, err)
   266  	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(800000000))
   267  
   268  	out = inspectField(c, "top", "HostConfig.CpuQuota")
   269  	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
   270  	out = inspectField(c, "top", "HostConfig.CpuPeriod")
   271  	assert.Equal(c, out, "0", "CPU CFS period should be 0")
   272  
   273  	out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
   274  	assert.Equal(c, strings.TrimSpace(out), "80000\n100000")
   275  }