update_linux_test.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package container
  2. import (
  3. "bytes"
  4. "context"
  5. "fmt"
  6. "io/ioutil"
  7. "strconv"
  8. "strings"
  9. "testing"
  10. "time"
  11. "github.com/docker/docker/api/types"
  12. "github.com/docker/docker/api/types/container"
  13. "github.com/docker/docker/api/types/strslice"
  14. "github.com/docker/docker/client"
  15. "github.com/docker/docker/integration/util/request"
  16. "github.com/docker/docker/pkg/stdcopy"
  17. "github.com/gotestyourself/gotestyourself/poll"
  18. "github.com/gotestyourself/gotestyourself/skip"
  19. "github.com/stretchr/testify/assert"
  20. "github.com/stretchr/testify/require"
  21. )
  22. func TestUpdateMemory(t *testing.T) {
  23. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  24. skip.If(t, !testEnv.DaemonInfo.MemoryLimit)
  25. skip.If(t, !testEnv.DaemonInfo.SwapLimit)
  26. defer setupTest(t)()
  27. client := request.NewAPIClient(t)
  28. ctx := context.Background()
  29. c, err := client.ContainerCreate(ctx,
  30. &container.Config{
  31. Cmd: []string{"top"},
  32. Image: "busybox",
  33. },
  34. &container.HostConfig{
  35. Resources: container.Resources{
  36. Memory: 200 * 1024 * 1024,
  37. },
  38. },
  39. nil,
  40. "",
  41. )
  42. require.NoError(t, err)
  43. err = client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{})
  44. require.NoError(t, err)
  45. poll.WaitOn(t, containerIsInState(ctx, client, c.ID, "running"), poll.WithDelay(100*time.Millisecond))
  46. _, err = client.ContainerUpdate(ctx, c.ID, container.UpdateConfig{
  47. Resources: container.Resources{
  48. Memory: 314572800,
  49. MemorySwap: 524288000,
  50. },
  51. })
  52. require.NoError(t, err)
  53. inspect, err := client.ContainerInspect(ctx, c.ID)
  54. require.NoError(t, err)
  55. assert.Equal(t, inspect.HostConfig.Memory, int64(314572800))
  56. assert.Equal(t, inspect.HostConfig.MemorySwap, int64(524288000))
  57. body, err := getContainerSysFSValue(ctx, client, c.ID, "/sys/fs/cgroup/memory/memory.limit_in_bytes")
  58. require.NoError(t, err)
  59. assert.Equal(t, strings.TrimSpace(body), "314572800")
  60. body, err = getContainerSysFSValue(ctx, client, c.ID, "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes")
  61. require.NoError(t, err)
  62. assert.Equal(t, strings.TrimSpace(body), "524288000")
  63. }
  64. func TestUpdateCPUQUota(t *testing.T) {
  65. t.Parallel()
  66. client := request.NewAPIClient(t)
  67. ctx := context.Background()
  68. c, err := client.ContainerCreate(ctx, &container.Config{
  69. Image: "busybox",
  70. Cmd: []string{"top"},
  71. }, nil, nil, "")
  72. if err != nil {
  73. t.Fatal(err)
  74. }
  75. defer func() {
  76. if err := client.ContainerRemove(ctx, c.ID, types.ContainerRemoveOptions{Force: true}); err != nil {
  77. panic(fmt.Sprintf("failed to clean up after test: %v", err))
  78. }
  79. }()
  80. if err := client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{}); err != nil {
  81. t.Fatal(err)
  82. }
  83. for _, test := range []struct {
  84. desc string
  85. update int64
  86. }{
  87. {desc: "some random value", update: 15000},
  88. {desc: "a higher value", update: 20000},
  89. {desc: "a lower value", update: 10000},
  90. {desc: "unset value", update: -1},
  91. } {
  92. if _, err := client.ContainerUpdate(ctx, c.ID, container.UpdateConfig{
  93. Resources: container.Resources{
  94. CPUQuota: test.update,
  95. },
  96. }); err != nil {
  97. t.Fatal(err)
  98. }
  99. inspect, err := client.ContainerInspect(ctx, c.ID)
  100. if err != nil {
  101. t.Fatal(err)
  102. }
  103. if inspect.HostConfig.CPUQuota != test.update {
  104. t.Fatalf("quota not updated in the API, expected %d, got: %d", test.update, inspect.HostConfig.CPUQuota)
  105. }
  106. execCreate, err := client.ContainerExecCreate(ctx, c.ID, types.ExecConfig{
  107. Cmd: []string{"/bin/cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"},
  108. AttachStdout: true,
  109. AttachStderr: true,
  110. })
  111. if err != nil {
  112. t.Fatal(err)
  113. }
  114. attach, err := client.ContainerExecAttach(ctx, execCreate.ID, types.ExecStartCheck{})
  115. if err != nil {
  116. t.Fatal(err)
  117. }
  118. if err := client.ContainerExecStart(ctx, execCreate.ID, types.ExecStartCheck{}); err != nil {
  119. t.Fatal(err)
  120. }
  121. buf := bytes.NewBuffer(nil)
  122. ready := make(chan error)
  123. go func() {
  124. _, err := stdcopy.StdCopy(buf, buf, attach.Reader)
  125. ready <- err
  126. }()
  127. select {
  128. case <-time.After(60 * time.Second):
  129. t.Fatal("timeout waiting for exec to complete")
  130. case err := <-ready:
  131. if err != nil {
  132. t.Fatal(err)
  133. }
  134. }
  135. actual := strings.TrimSpace(buf.String())
  136. if actual != strconv.Itoa(int(test.update)) {
  137. t.Fatalf("expected cgroup value %d, got: %s", test.update, actual)
  138. }
  139. }
  140. }
  141. func getContainerSysFSValue(ctx context.Context, client client.APIClient, cID string, path string) (string, error) {
  142. var b bytes.Buffer
  143. ex, err := client.ContainerExecCreate(ctx, cID,
  144. types.ExecConfig{
  145. AttachStdout: true,
  146. Cmd: strslice.StrSlice([]string{"cat", path}),
  147. },
  148. )
  149. if err != nil {
  150. return "", err
  151. }
  152. resp, err := client.ContainerExecAttach(ctx, ex.ID,
  153. types.ExecStartCheck{
  154. Detach: false,
  155. Tty: false,
  156. },
  157. )
  158. if err != nil {
  159. return "", err
  160. }
  161. defer resp.Close()
  162. b.Reset()
  163. _, err = stdcopy.StdCopy(&b, ioutil.Discard, resp.Reader)
  164. return b.String(), err
  165. }