Browse code

Merge remote-tracking branch 'origin/check_kernel_capabilities'

Solomon Hykes authored on 2013/04/21 09:40:25
Showing 8 changed files
... ...
@@ -13,10 +13,7 @@ endif
13 13
 GIT_COMMIT = $(shell git rev-parse --short HEAD)
14 14
 GIT_STATUS = $(shell test -n "`git status --porcelain`" && echo "+CHANGES")
15 15
 
16
-NO_MEMORY_LIMIT ?= 0
17
-export NO_MEMORY_LIMIT
18
-
19
-BUILD_OPTIONS = -ldflags "-X main.GIT_COMMIT $(GIT_COMMIT)$(GIT_STATUS) -X main.NO_MEMORY_LIMIT $(NO_MEMORY_LIMIT)"
16
+BUILD_OPTIONS = -ldflags "-X main.GIT_COMMIT $(GIT_COMMIT)$(GIT_STATUS)"
20 17
 
21 18
 SRC_DIR := $(GOPATH)/src
22 19
 
... ...
@@ -21,8 +21,7 @@ import (
21 21
 const VERSION = "0.1.7"
22 22
 
23 23
 var (
24
-	GIT_COMMIT      string
25
-	NO_MEMORY_LIMIT bool
24
+	GIT_COMMIT string
26 25
 )
27 26
 
28 27
 func (srv *Server) Name() string {
... ...
@@ -184,10 +183,14 @@ func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string
184 184
 
185 185
 // 'docker version': show version information
186 186
 func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
187
-	fmt.Fprintf(stdout, "Version:%s\n", VERSION)
188
-	fmt.Fprintf(stdout, "Git Commit:%s\n", GIT_COMMIT)
189
-	if NO_MEMORY_LIMIT {
190
-		fmt.Fprintf(stdout, "Memory limit disabled\n")
187
+	fmt.Fprintf(stdout, "Version: %s\n", VERSION)
188
+	fmt.Fprintf(stdout, "Git Commit: %s\n", GIT_COMMIT)
189
+	fmt.Fprintf(stdout, "Kernel: %s\n", srv.runtime.kernelVersion)
190
+	if !srv.runtime.capabilities.MemoryLimit {
191
+		fmt.Fprintf(stdout, "WARNING: No memory limit support\n")
192
+	}
193
+	if !srv.runtime.capabilities.SwapLimit {
194
+		fmt.Fprintf(stdout, "WARNING: No swap limit support\n")
191 195
 	}
192 196
 	return nil
193 197
 }
... ...
@@ -910,7 +913,7 @@ func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string)
910 910
 }
911 911
 
912 912
 func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
913
-	config, err := ParseRun(args, stdout)
913
+	config, err := ParseRun(args, stdout, srv.runtime.capabilities)
914 914
 	if err != nil {
915 915
 		return err
916 916
 	}
... ...
@@ -68,7 +68,7 @@ type Config struct {
68 68
 	Image        string // Name of the image as it was passed by the operator (eg. could be symbolic)
69 69
 }
70 70
 
71
-func ParseRun(args []string, stdout io.Writer) (*Config, error) {
71
+func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Config, error) {
72 72
 	cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
73 73
 	if len(args) > 0 && args[0] != "--help" {
74 74
 		cmd.SetOutput(ioutil.Discard)
... ...
@@ -83,8 +83,8 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) {
83 83
 	flTty := cmd.Bool("t", false, "Allocate a pseudo-tty")
84 84
 	flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)")
85 85
 
86
-	if *flMemory > 0 && NO_MEMORY_LIMIT {
87
-		fmt.Fprintf(stdout, "WARNING: This version of docker has been compiled without memory limit support. Discarding -m.")
86
+	if *flMemory > 0 && !capabilities.MemoryLimit {
87
+		fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
88 88
 		*flMemory = 0
89 89
 	}
90 90
 
... ...
@@ -137,6 +137,12 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) {
137 137
 		Dns:          flDns,
138 138
 		Image:        image,
139 139
 	}
140
+
141
+	if *flMemory > 0 && !capabilities.SwapLimit {
142
+		fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
143
+		config.MemorySwap = -1
144
+	}
145
+
140 146
 	// When allocating stdin in attached mode, close stdin at client disconnect
141 147
 	if config.OpenStdin && config.AttachStdin {
142 148
 		config.StdinOnce = true
... ...
@@ -379,10 +385,15 @@ func (container *Container) Start() error {
379 379
 		return err
380 380
 	}
381 381
 
382
-	if container.Config.Memory > 0 && NO_MEMORY_LIMIT {
383
-		log.Printf("WARNING: This version of docker has been compiled without memory limit support. Discarding the limit.")
382
+	// Make sure the config is compatible with the current kernel
383
+	if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit {
384
+		log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
384 385
 		container.Config.Memory = 0
385 386
 	}
387
+	if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit {
388
+		log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
389
+		container.Config.MemorySwap = -1
390
+	}
386 391
 
387 392
 	if err := container.generateLXCConfig(); err != nil {
388 393
 		return err
... ...
@@ -14,8 +14,7 @@ import (
14 14
 )
15 15
 
16 16
 var (
17
-	GIT_COMMIT      string
18
-	NO_MEMORY_LIMIT string
17
+	GIT_COMMIT string
19 18
 )
20 19
 
21 20
 func main() {
... ...
@@ -39,15 +38,11 @@ func main() {
39 39
 		os.Setenv("DEBUG", "1")
40 40
 	}
41 41
 	docker.GIT_COMMIT = GIT_COMMIT
42
-	docker.NO_MEMORY_LIMIT = NO_MEMORY_LIMIT == "1"
43 42
 	if *flDaemon {
44 43
 		if flag.NArg() != 0 {
45 44
 			flag.Usage()
46 45
 			return
47 46
 		}
48
-		if NO_MEMORY_LIMIT == "1" {
49
-			log.Printf("WARNING: This version of docker has been compiled without memory limit support.")
50
-		}
51 47
 		if err := daemon(*pidfile); err != nil {
52 48
 			log.Fatal(err)
53 49
 		}
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"github.com/dotcloud/docker/auth"
7 7
 	"io"
8 8
 	"io/ioutil"
9
+	"log"
9 10
 	"os"
10 11
 	"os/exec"
11 12
 	"path"
... ...
@@ -14,6 +15,11 @@ import (
14 14
 	"time"
15 15
 )
16 16
 
17
+type Capabilities struct {
18
+	MemoryLimit bool
19
+	SwapLimit   bool
20
+}
21
+
17 22
 type Runtime struct {
18 23
 	root           string
19 24
 	repository     string
... ...
@@ -23,6 +29,8 @@ type Runtime struct {
23 23
 	repositories   *TagStore
24 24
 	authConfig     *auth.AuthConfig
25 25
 	idIndex        *TruncIndex
26
+	capabilities   *Capabilities
27
+	kernelVersion  *KernelVersionInfo
26 28
 }
27 29
 
28 30
 var sysInitPath string
... ...
@@ -282,7 +290,34 @@ func (runtime *Runtime) restore() error {
282 282
 
283 283
 // FIXME: harmonize with NewGraph()
284 284
 func NewRuntime() (*Runtime, error) {
285
-	return NewRuntimeFromDirectory("/var/lib/docker")
285
+	runtime, err := NewRuntimeFromDirectory("/var/lib/docker")
286
+	if err != nil {
287
+		return nil, err
288
+	}
289
+
290
+	k, err := GetKernelVersion()
291
+	if err != nil {
292
+		return nil, err
293
+	}
294
+	runtime.kernelVersion = k
295
+
296
+	if CompareKernelVersion(k, &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
297
+		log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
298
+	}
299
+
300
+	cgroupMemoryMountpoint, err := FindCgroupMountpoint("memory")
301
+	if err != nil {
302
+		return nil, err
303
+	}
304
+
305
+	_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "/memory.limit_in_bytes"))
306
+	_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
307
+	runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
308
+
309
+	_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memeory.memsw.limit_in_bytes"))
310
+	runtime.capabilities.SwapLimit = err == nil
311
+
312
+	return runtime, nil
286 313
 }
287 314
 
288 315
 func NewRuntimeFromDirectory(root string) (*Runtime, error) {
... ...
@@ -321,6 +356,7 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
321 321
 		repositories:   repositories,
322 322
 		authConfig:     authConfig,
323 323
 		idIndex:        NewTruncIndex(),
324
+		capabilities:   &Capabilities{},
324 325
 	}
325 326
 
326 327
 	if err := runtime.restore(); err != nil {
... ...
@@ -48,8 +48,6 @@ func layerArchive(tarfile string) (io.Reader, error) {
48 48
 }
49 49
 
50 50
 func init() {
51
-	NO_MEMORY_LIMIT = os.Getenv("NO_MEMORY_LIMIT") == "1"
52
-
53 51
 	// Hack to run sys init during unit testing
54 52
 	if SelfPath() == "/sbin/init" {
55 53
 		SysInit()
... ...
@@ -12,9 +12,12 @@ import (
12 12
 	"os"
13 13
 	"os/exec"
14 14
 	"path/filepath"
15
+	"regexp"
15 16
 	"runtime"
17
+	"strconv"
16 18
 	"strings"
17 19
 	"sync"
20
+	"syscall"
18 21
 	"time"
19 22
 )
20 23
 
... ...
@@ -384,3 +387,112 @@ func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
384 384
 	}
385 385
 	return written, err
386 386
 }
387
+
388
+type KernelVersionInfo struct {
389
+	Kernel   int
390
+	Major    int
391
+	Minor    int
392
+	Specific int
393
+}
394
+
395
+func GetKernelVersion() (*KernelVersionInfo, error) {
396
+	var uts syscall.Utsname
397
+
398
+	if err := syscall.Uname(&uts); err != nil {
399
+		return nil, err
400
+	}
401
+
402
+	release := make([]byte, len(uts.Release))
403
+
404
+	i := 0
405
+	for _, c := range uts.Release {
406
+		release[i] = byte(c)
407
+		i++
408
+	}
409
+
410
+	tmp := strings.SplitN(string(release), "-", 2)
411
+	if len(tmp) != 2 {
412
+		return nil, fmt.Errorf("Unrecognized kernel version")
413
+	}
414
+	tmp2 := strings.SplitN(tmp[0], ".", 3)
415
+	if len(tmp2) != 3 {
416
+		return nil, fmt.Errorf("Unrecognized kernel version")
417
+	}
418
+
419
+	kernel, err := strconv.Atoi(tmp2[0])
420
+	if err != nil {
421
+		return nil, err
422
+	}
423
+
424
+	major, err := strconv.Atoi(tmp2[1])
425
+	if err != nil {
426
+		return nil, err
427
+	}
428
+
429
+	minor, err := strconv.Atoi(tmp2[2])
430
+	if err != nil {
431
+		return nil, err
432
+	}
433
+
434
+	specific, err := strconv.Atoi(strings.Split(tmp[1], "-")[0])
435
+	if err != nil {
436
+		return nil, err
437
+	}
438
+
439
+	return &KernelVersionInfo{
440
+		Kernel:   kernel,
441
+		Major:    major,
442
+		Minor:    minor,
443
+		Specific: specific,
444
+	}, nil
445
+}
446
+
447
+func (k *KernelVersionInfo) String() string {
448
+	return fmt.Sprintf("%d.%d.%d-%d", k.Kernel, k.Major, k.Minor, k.Specific)
449
+}
450
+
451
+// Compare two KernelVersionInfo struct.
452
+// Returns -1 if a < b, = if a == b, 1 it a > b
453
+func CompareKernelVersion(a, b *KernelVersionInfo) int {
454
+	if a.Kernel < b.Kernel {
455
+		return -1
456
+	} else if a.Kernel > b.Kernel {
457
+		return 1
458
+	}
459
+
460
+	if a.Major < b.Major {
461
+		return -1
462
+	} else if a.Major > b.Major {
463
+		return 1
464
+	}
465
+
466
+	if a.Minor < b.Minor {
467
+		return -1
468
+	} else if a.Minor > b.Minor {
469
+		return 1
470
+	}
471
+
472
+	if a.Specific < b.Specific {
473
+		return -1
474
+	} else if a.Specific > b.Specific {
475
+		return 1
476
+	}
477
+	return 0
478
+}
479
+
480
+func FindCgroupMountpoint(cgroupType string) (string, error) {
481
+	output, err := exec.Command("mount").CombinedOutput()
482
+	if err != nil {
483
+		return "", err
484
+	}
485
+
486
+	reg := regexp.MustCompile(`^cgroup on (.*) type cgroup \(.*` + cgroupType + `[,\)]`)
487
+	for _, line := range strings.Split(string(output), "\n") {
488
+		r := reg.FindStringSubmatch(line)
489
+		if len(r) == 2 {
490
+			return r[1], nil
491
+		}
492
+		fmt.Printf("line: %s (%d)\n", line, len(r))
493
+	}
494
+	return "", fmt.Errorf("cgroup mountpoint not found")
495
+}
... ...
@@ -228,3 +228,36 @@ func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult strin
228 228
 		t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
229 229
 	}
230 230
 }
231
+
232
+func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
233
+	if r := CompareKernelVersion(a, b); r != result {
234
+		t.Fatalf("Unepected kernel version comparaison result. Found %d, expected %d", r, result)
235
+	}
236
+}
237
+
238
+func TestCompareKernelVersion(t *testing.T) {
239
+	assertKernelVersion(t,
240
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
241
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
242
+		0)
243
+	assertKernelVersion(t,
244
+		&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0, Specific: 0},
245
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
246
+		-1)
247
+	assertKernelVersion(t,
248
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
249
+		&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0, Specific: 0},
250
+		1)
251
+	assertKernelVersion(t,
252
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
253
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 16},
254
+		-1)
255
+	assertKernelVersion(t,
256
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5, Specific: 0},
257
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
258
+		1)
259
+	assertKernelVersion(t,
260
+		&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20, Specific: 25},
261
+		&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Specific: 0},
262
+		-1)
263
+}