Browse code

fix shm size handling

Signed-off-by: Antonio Murdaca <runcom@redhat.com>

Antonio Murdaca authored on 2015/11/26 21:14:09
Showing 10 changed files
... ...
@@ -211,18 +211,6 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
211 211
 		}
212 212
 	}
213 213
 
214
-	var shmSize int64 = 67108864 // initial SHM size is 64MB
215
-	if *flShmSize != "" {
216
-		parsedShmSize, err := units.RAMInBytes(*flShmSize)
217
-		if err != nil {
218
-			return err
219
-		}
220
-		if parsedShmSize <= 0 {
221
-			return fmt.Errorf("--shm-size: SHM size must be greater than 0 . You specified: %v ", parsedShmSize)
222
-		}
223
-		shmSize = parsedShmSize
224
-	}
225
-
226 214
 	// Send the build context
227 215
 	v := url.Values{
228 216
 		"t": flTags.GetAll(),
... ...
@@ -261,9 +249,16 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
261 261
 	v.Set("cpuperiod", strconv.FormatInt(*flCPUPeriod, 10))
262 262
 	v.Set("memory", strconv.FormatInt(memory, 10))
263 263
 	v.Set("memswap", strconv.FormatInt(memorySwap, 10))
264
-	v.Set("shmsize", strconv.FormatInt(shmSize, 10))
265 264
 	v.Set("cgroupparent", *flCgroupParent)
266 265
 
266
+	if *flShmSize != "" {
267
+		parsedShmSize, err := units.RAMInBytes(*flShmSize)
268
+		if err != nil {
269
+			return err
270
+		}
271
+		v.Set("shmsize", strconv.FormatInt(parsedShmSize, 10))
272
+	}
273
+
267 274
 	v.Set("dockerfile", relDockerfile)
268 275
 
269 276
 	ulimitsVar := flUlimits.GetList()
... ...
@@ -357,7 +357,11 @@ func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
357 357
 	buildConfig.ForceRemove = httputils.BoolValue(r, "forcerm")
358 358
 	buildConfig.MemorySwap = httputils.Int64ValueOrZero(r, "memswap")
359 359
 	buildConfig.Memory = httputils.Int64ValueOrZero(r, "memory")
360
-	buildConfig.ShmSize = httputils.Int64ValueOrZero(r, "shmsize")
360
+	shmSize, err := httputils.Int64ValueOrDefault(r, "shmsize", runconfig.DefaultSHMSize)
361
+	if err != nil {
362
+		return errf(err)
363
+	}
364
+	buildConfig.ShmSize = &shmSize
361 365
 	buildConfig.CPUShares = httputils.Int64ValueOrZero(r, "cpushares")
362 366
 	buildConfig.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod")
363 367
 	buildConfig.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota")
... ...
@@ -60,7 +60,7 @@ type Config struct {
60 60
 
61 61
 	Memory       int64
62 62
 	MemorySwap   int64
63
-	ShmSize      int64
63
+	ShmSize      *int64
64 64
 	CPUShares    int64
65 65
 	CPUPeriod    int64
66 66
 	CPUQuota     int64
... ...
@@ -1358,11 +1358,12 @@ func (daemon *Daemon) setupIpcDirs(container *Container) error {
1358 1358
 			return err
1359 1359
 		}
1360 1360
 
1361
-		// When ShmSize is 0 or less, the SHM size is set to 64MB.
1362
-		if container.hostConfig.ShmSize <= 0 {
1363
-			container.hostConfig.ShmSize = 67108864
1361
+		shmSize := runconfig.DefaultSHMSize
1362
+		if container.hostConfig.ShmSize != nil {
1363
+			shmSize = *container.hostConfig.ShmSize
1364 1364
 		}
1365
-		shmproperty := "mode=1777,size=" + strconv.FormatInt(container.hostConfig.ShmSize, 10)
1365
+
1366
+		shmproperty := "mode=1777,size=" + strconv.FormatInt(shmSize, 10)
1366 1367
 		if err := syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel(shmproperty, container.getMountLabel())); err != nil {
1367 1368
 			return fmt.Errorf("mounting shm tmpfs: %s", err)
1368 1369
 		}
... ...
@@ -131,6 +131,10 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *runconfig.HostConfig, a
131 131
 		// By default, MemorySwap is set to twice the size of Memory.
132 132
 		hostConfig.MemorySwap = hostConfig.Memory * 2
133 133
 	}
134
+	if hostConfig.ShmSize == nil {
135
+		shmSize := runconfig.DefaultSHMSize
136
+		hostConfig.ShmSize = &shmSize
137
+	}
134 138
 }
135 139
 
136 140
 // verifyPlatformContainerSettings performs platform-specific validation of the
... ...
@@ -144,6 +148,10 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *runconfig.HostC
144 144
 		return warnings, err
145 145
 	}
146 146
 
147
+	if hostConfig.ShmSize != nil && *hostConfig.ShmSize <= 0 {
148
+		return warnings, fmt.Errorf("SHM size must be greater then 0")
149
+	}
150
+
147 151
 	// memory subsystem checks and adjustments
148 152
 	if hostConfig.Memory != 0 && hostConfig.Memory < 4194304 {
149 153
 		return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB")
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"net/http/httputil"
11 11
 	"net/url"
12 12
 	"os"
13
+	"regexp"
13 14
 	"strconv"
14 15
 	"strings"
15 16
 	"time"
... ...
@@ -1388,3 +1389,138 @@ func (s *DockerSuite) TestStartWithNilDNS(c *check.C) {
1388 1388
 	c.Assert(err, checker.IsNil)
1389 1389
 	c.Assert(dns, checker.Equals, "[]")
1390 1390
 }
1391
+
1392
+func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
1393
+	config := map[string]interface{}{
1394
+		"Image":      "busybox",
1395
+		"HostConfig": map[string]interface{}{"ShmSize": -1},
1396
+	}
1397
+
1398
+	status, body, err := sockRequest("POST", "/containers/create", config)
1399
+	c.Assert(err, check.IsNil)
1400
+	c.Assert(status, check.Equals, http.StatusInternalServerError)
1401
+	c.Assert(string(body), checker.Contains, "SHM size must be greater then 0")
1402
+}
1403
+
1404
+func (s *DockerSuite) TestPostContainersCreateShmSizeZero(c *check.C) {
1405
+	config := map[string]interface{}{
1406
+		"Image":      "busybox",
1407
+		"HostConfig": map[string]interface{}{"ShmSize": 0},
1408
+	}
1409
+
1410
+	status, body, err := sockRequest("POST", "/containers/create", config)
1411
+	c.Assert(err, check.IsNil)
1412
+	c.Assert(status, check.Equals, http.StatusInternalServerError)
1413
+	c.Assert(string(body), checker.Contains, "SHM size must be greater then 0")
1414
+}
1415
+
1416
+func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) {
1417
+	config := map[string]interface{}{
1418
+		"Image": "busybox",
1419
+		"Cmd":   "mount",
1420
+	}
1421
+
1422
+	status, body, err := sockRequest("POST", "/containers/create", config)
1423
+	c.Assert(err, check.IsNil)
1424
+	c.Assert(status, check.Equals, http.StatusCreated)
1425
+
1426
+	var container types.ContainerCreateResponse
1427
+	c.Assert(json.Unmarshal(body, &container), check.IsNil)
1428
+
1429
+	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
1430
+	c.Assert(err, check.IsNil)
1431
+	c.Assert(status, check.Equals, http.StatusOK)
1432
+
1433
+	var containerJSON types.ContainerJSON
1434
+	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
1435
+
1436
+	c.Assert(containerJSON.HostConfig.ShmSize, check.IsNil)
1437
+
1438
+	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
1439
+	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
1440
+	if !shmRegexp.MatchString(out) {
1441
+		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
1442
+	}
1443
+}
1444
+
1445
+func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
1446
+	config := map[string]interface{}{
1447
+		"Image":      "busybox",
1448
+		"HostConfig": map[string]interface{}{},
1449
+		"Cmd":        "mount",
1450
+	}
1451
+
1452
+	status, body, err := sockRequest("POST", "/containers/create", config)
1453
+	c.Assert(err, check.IsNil)
1454
+	c.Assert(status, check.Equals, http.StatusCreated)
1455
+
1456
+	var container types.ContainerCreateResponse
1457
+	c.Assert(json.Unmarshal(body, &container), check.IsNil)
1458
+
1459
+	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
1460
+	c.Assert(err, check.IsNil)
1461
+	c.Assert(status, check.Equals, http.StatusOK)
1462
+
1463
+	var containerJSON types.ContainerJSON
1464
+	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
1465
+
1466
+	c.Assert(*containerJSON.HostConfig.ShmSize, check.Equals, runconfig.DefaultSHMSize)
1467
+
1468
+	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
1469
+	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
1470
+	if !shmRegexp.MatchString(out) {
1471
+		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
1472
+	}
1473
+}
1474
+
1475
+func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
1476
+	config := map[string]interface{}{
1477
+		"Image":      "busybox",
1478
+		"Cmd":        "mount",
1479
+		"HostConfig": map[string]interface{}{"ShmSize": 1073741824},
1480
+	}
1481
+
1482
+	status, body, err := sockRequest("POST", "/containers/create", config)
1483
+	c.Assert(err, check.IsNil)
1484
+	c.Assert(status, check.Equals, http.StatusCreated)
1485
+
1486
+	var container types.ContainerCreateResponse
1487
+	c.Assert(json.Unmarshal(body, &container), check.IsNil)
1488
+
1489
+	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
1490
+	c.Assert(err, check.IsNil)
1491
+	c.Assert(status, check.Equals, http.StatusOK)
1492
+
1493
+	var containerJSON types.ContainerJSON
1494
+	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
1495
+
1496
+	c.Assert(*containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824))
1497
+
1498
+	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
1499
+	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
1500
+	if !shmRegex.MatchString(out) {
1501
+		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
1502
+	}
1503
+}
1504
+
1505
+func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) {
1506
+	config := map[string]interface{}{
1507
+		"Image": "busybox",
1508
+	}
1509
+
1510
+	status, body, err := sockRequest("POST", "/containers/create", config)
1511
+	c.Assert(err, check.IsNil)
1512
+	c.Assert(status, check.Equals, http.StatusCreated)
1513
+
1514
+	var container types.ContainerCreateResponse
1515
+	c.Assert(json.Unmarshal(body, &container), check.IsNil)
1516
+
1517
+	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
1518
+	c.Assert(err, check.IsNil)
1519
+	c.Assert(status, check.Equals, http.StatusOK)
1520
+
1521
+	var containerJSON types.ContainerJSON
1522
+	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
1523
+
1524
+	c.Assert(containerJSON.HostConfig.MemorySwappiness, check.IsNil)
1525
+}
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"os"
10 10
 	"os/exec"
11 11
 	"path/filepath"
12
+	"regexp"
12 13
 	"strconv"
13 14
 	"strings"
14 15
 	"time"
... ...
@@ -409,3 +410,31 @@ func (s *DockerSuite) TestRunInvalidCPUShares(c *check.C) {
409 409
 	expected = "The maximum allowed cpu-shares is"
410 410
 	c.Assert(out, checker.Contains, expected)
411 411
 }
412
+
413
+func (s *DockerSuite) TestRunWithDefaultShmSize(c *check.C) {
414
+	testRequires(c, DaemonIsLinux)
415
+
416
+	name := "shm-default"
417
+	out, _ := dockerCmd(c, "run", "--name", name, "busybox", "mount")
418
+	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
419
+	if !shmRegex.MatchString(out) {
420
+		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
421
+	}
422
+	shmSize, err := inspectField(name, "HostConfig.ShmSize")
423
+	c.Assert(err, check.IsNil)
424
+	c.Assert(shmSize, check.Equals, "67108864")
425
+}
426
+
427
+func (s *DockerSuite) TestRunWithShmSize(c *check.C) {
428
+	testRequires(c, DaemonIsLinux)
429
+
430
+	name := "shm"
431
+	out, _ := dockerCmd(c, "run", "--name", name, "--shm-size=1G", "busybox", "mount")
432
+	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
433
+	if !shmRegex.MatchString(out) {
434
+		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
435
+	}
436
+	shmSize, err := inspectField(name, "HostConfig.ShmSize")
437
+	c.Assert(err, check.IsNil)
438
+	c.Assert(shmSize, check.Equals, "1073741824")
439
+}
... ...
@@ -218,7 +218,7 @@ type HostConfig struct {
218 218
 	ReadonlyRootfs  bool                  // Is the container root filesystem in read-only
219 219
 	SecurityOpt     []string              // List of string values to customize labels for MLS systems, such as SELinux.
220 220
 	UTSMode         UTSMode               // UTS namespace to use for the container
221
-	ShmSize         int64                 // Total shm memory usage
221
+	ShmSize         *int64                // Total shm memory usage
222 222
 
223 223
 	// Applicable to Windows
224 224
 	ConsoleSize [2]int         // Initial console size
... ...
@@ -42,6 +42,9 @@ var (
42 42
 	ErrConflictNetworkExposePorts = fmt.Errorf("Conflicting options: --expose and the network mode (--net)")
43 43
 )
44 44
 
45
+// DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container
46
+const DefaultSHMSize int64 = 67108864
47
+
45 48
 // Parse parses the specified args for the specified command and generates a Config,
46 49
 // a HostConfig and returns them with the specified command.
47 50
 // If the specified args are not valid, it will return an error.
... ...
@@ -201,16 +204,13 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
201 201
 		return nil, nil, cmd, fmt.Errorf("Invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
202 202
 	}
203 203
 
204
-	var parsedShm int64 = 67108864 // initial SHM size is 64MB
204
+	var parsedShm *int64
205 205
 	if *flShmSize != "" {
206
-		var err error
207
-		parsedShm, err = units.RAMInBytes(*flShmSize)
206
+		shmSize, err := units.RAMInBytes(*flShmSize)
208 207
 		if err != nil {
209
-			return nil, nil, cmd, fmt.Errorf("--shm-size: invalid SHM size")
210
-		}
211
-		if parsedShm <= 0 {
212
-			return nil, nil, cmd, fmt.Errorf("--shm-size: SHM size must be greater than 0 . You specified: %v ", parsedShm)
208
+			return nil, nil, cmd, err
213 209
 		}
210
+		parsedShm = &shmSize
214 211
 	}
215 212
 
216 213
 	var binds []string
... ...
@@ -525,16 +525,16 @@ func TestParseModes(t *testing.T) {
525 525
 		t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
526 526
 	}
527 527
 	// shm-size ko
528
-	if _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "--shm-size: invalid SHM size" {
529
-		t.Fatalf("Expected an error with message '--shm-size: invalid SHM size', got %v", err)
528
+	if _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
529
+		t.Fatalf("Expected an error with message 'invalid size: a128m', got %v", err)
530 530
 	}
531 531
 	// shm-size ok
532 532
 	_, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
533 533
 	if err != nil {
534 534
 		t.Fatal(err)
535 535
 	}
536
-	if hostconfig.ShmSize != 134217728 {
537
-		t.Fatalf("Expected a valid ShmSize, got %v", hostconfig.ShmSize)
536
+	if *hostconfig.ShmSize != 134217728 {
537
+		t.Fatalf("Expected a valid ShmSize, got %d", *hostconfig.ShmSize)
538 538
 	}
539 539
 }
540 540