github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration-cli/docker_cli_update_unix_test.go (about)

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