Browse code

Update libcontainer to c8512754166539461fd860451ff

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Michael Crosby authored on 2015/03/31 08:29:00
Showing 9 changed files
... ...
@@ -75,7 +75,7 @@ rm -rf src/github.com/docker/distribution
75 75
 mkdir -p src/github.com/docker/distribution
76 76
 mv tmp-digest src/github.com/docker/distribution/digest
77 77
 
78
-clone git github.com/docker/libcontainer a6044b701c166fe538fc760f9e2dcea3d737cd2a
78
+clone git github.com/docker/libcontainer c8512754166539461fd860451ff1a0af7491c197
79 79
 # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
80 80
 rm -rf src/github.com/docker/libcontainer/vendor
81 81
 eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli' | grep -v 'github.com/Sirupsen/logrus')"
... ...
@@ -220,16 +220,16 @@ func getCgroupData(c *configs.Cgroup, pid int) (*data, error) {
220 220
 	}, nil
221 221
 }
222 222
 
223
-func (raw *data) parent(subsystem string) (string, error) {
223
+func (raw *data) parent(subsystem, mountpoint string) (string, error) {
224 224
 	initPath, err := cgroups.GetInitCgroupDir(subsystem)
225 225
 	if err != nil {
226 226
 		return "", err
227 227
 	}
228
-	return filepath.Join(raw.root, subsystem, initPath), nil
228
+	return filepath.Join(mountpoint, initPath), nil
229 229
 }
230 230
 
231 231
 func (raw *data) path(subsystem string) (string, error) {
232
-	_, err := cgroups.FindCgroupMountpoint(subsystem)
232
+	mnt, err := cgroups.FindCgroupMountpoint(subsystem)
233 233
 	// If we didn't mount the subsystem, there is no point we make the path.
234 234
 	if err != nil {
235 235
 		return "", err
... ...
@@ -240,7 +240,7 @@ func (raw *data) path(subsystem string) (string, error) {
240 240
 		return filepath.Join(raw.root, subsystem, raw.cgroup), nil
241 241
 	}
242 242
 
243
-	parent, err := raw.parent(subsystem)
243
+	parent, err := raw.parent(subsystem, mnt)
244 244
 	if err != nil {
245 245
 		return "", err
246 246
 	}
... ...
@@ -43,6 +43,10 @@ var subsystems = map[string]subsystem{
43 43
 	"freezer":    &fs.FreezerGroup{},
44 44
 }
45 45
 
46
+const (
47
+	testScopeWait = 4
48
+)
49
+
46 50
 var (
47 51
 	connLock                        sync.Mutex
48 52
 	theConn                         *systemd.Conn
... ...
@@ -86,16 +90,41 @@ func UseSystemd() bool {
86 86
 			}
87 87
 		}
88 88
 
89
+		// Ensure the scope name we use doesn't exist. Use the Pid to
90
+		// avoid collisions between multiple libcontainer users on a
91
+		// single host.
92
+		scope := fmt.Sprintf("libcontainer-%d-systemd-test-default-dependencies.scope", os.Getpid())
93
+		testScopeExists := true
94
+		for i := 0; i <= testScopeWait; i++ {
95
+			if _, err := theConn.StopUnit(scope, "replace"); err != nil {
96
+				if dbusError, ok := err.(dbus.Error); ok {
97
+					if strings.Contains(dbusError.Name, "org.freedesktop.systemd1.NoSuchUnit") {
98
+						testScopeExists = false
99
+						break
100
+					}
101
+				}
102
+			}
103
+			time.Sleep(time.Millisecond)
104
+		}
105
+
106
+		// Bail out if we can't kill this scope without testing for DefaultDependencies
107
+		if testScopeExists {
108
+			return hasStartTransientUnit
109
+		}
110
+
89 111
 		// Assume StartTransientUnit on a scope allows DefaultDependencies
90 112
 		hasTransientDefaultDependencies = true
91 113
 		ddf := newProp("DefaultDependencies", false)
92
-		if _, err := theConn.StartTransientUnit("docker-systemd-test-default-dependencies.scope", "replace", ddf); err != nil {
114
+		if _, err := theConn.StartTransientUnit(scope, "replace", ddf); err != nil {
93 115
 			if dbusError, ok := err.(dbus.Error); ok {
94 116
 				if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") {
95 117
 					hasTransientDefaultDependencies = false
96 118
 				}
97 119
 			}
98 120
 		}
121
+
122
+		// Not critical because of the stop unit logic above.
123
+		theConn.StopUnit(scope, "replace")
99 124
 	}
100 125
 	return hasStartTransientUnit
101 126
 }
... ...
@@ -193,12 +193,13 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
193 193
 
194 194
 func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
195 195
 	return &initConfig{
196
-		Config:  c.config,
197
-		Args:    process.Args,
198
-		Env:     process.Env,
199
-		User:    process.User,
200
-		Cwd:     process.Cwd,
201
-		Console: process.consolePath,
196
+		Config:       c.config,
197
+		Args:         process.Args,
198
+		Env:          process.Env,
199
+		User:         process.User,
200
+		Cwd:          process.Cwd,
201
+		Console:      process.consolePath,
202
+		Capabilities: process.Capabilities,
202 203
 	}
203 204
 }
204 205
 
... ...
@@ -40,13 +40,14 @@ type network struct {
40 40
 
41 41
 // initConfig is used for transferring parameters from Exec() to Init()
42 42
 type initConfig struct {
43
-	Args     []string        `json:"args"`
44
-	Env      []string        `json:"env"`
45
-	Cwd      string          `json:"cwd"`
46
-	User     string          `json:"user"`
47
-	Config   *configs.Config `json:"config"`
48
-	Console  string          `json:"console"`
49
-	Networks []*network      `json:"network"`
43
+	Args         []string        `json:"args"`
44
+	Env          []string        `json:"env"`
45
+	Cwd          string          `json:"cwd"`
46
+	Capabilities []string        `json:"capabilities"`
47
+	User         string          `json:"user"`
48
+	Config       *configs.Config `json:"config"`
49
+	Console      string          `json:"console"`
50
+	Networks     []*network      `json:"network"`
50 51
 }
51 52
 
52 53
 type initer interface {
... ...
@@ -99,7 +100,12 @@ func finalizeNamespace(config *initConfig) error {
99 99
 	if err := utils.CloseExecFrom(3); err != nil {
100 100
 		return err
101 101
 	}
102
-	w, err := newCapWhitelist(config.Config.Capabilities)
102
+
103
+	capabilities := config.Config.Capabilities
104
+	if config.Capabilities != nil {
105
+		capabilities = config.Capabilities
106
+	}
107
+	w, err := newCapWhitelist(capabilities)
103 108
 	if err != nil {
104 109
 		return err
105 110
 	}
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"bytes"
5 5
 	"io/ioutil"
6 6
 	"os"
7
+	"strconv"
7 8
 	"strings"
8 9
 	"testing"
9 10
 
... ...
@@ -395,6 +396,90 @@ func TestProcessEnv(t *testing.T) {
395 395
 	}
396 396
 }
397 397
 
398
+func TestProcessCaps(t *testing.T) {
399
+	if testing.Short() {
400
+		return
401
+	}
402
+	root, err := newTestRoot()
403
+	if err != nil {
404
+		t.Fatal(err)
405
+	}
406
+	defer os.RemoveAll(root)
407
+
408
+	rootfs, err := newRootfs()
409
+	if err != nil {
410
+		t.Fatal(err)
411
+	}
412
+	defer remove(rootfs)
413
+
414
+	config := newTemplateConfig(rootfs)
415
+
416
+	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
417
+	if err != nil {
418
+		t.Fatal(err)
419
+	}
420
+
421
+	container, err := factory.Create("test", config)
422
+	if err != nil {
423
+		t.Fatal(err)
424
+	}
425
+	defer container.Destroy()
426
+
427
+	processCaps := append(config.Capabilities, "NET_ADMIN")
428
+
429
+	var stdout bytes.Buffer
430
+	pconfig := libcontainer.Process{
431
+		Args:         []string{"sh", "-c", "cat /proc/self/status"},
432
+		Env:          standardEnvironment,
433
+		Capabilities: processCaps,
434
+		Stdin:        nil,
435
+		Stdout:       &stdout,
436
+	}
437
+	err = container.Start(&pconfig)
438
+	if err != nil {
439
+		t.Fatal(err)
440
+	}
441
+
442
+	// Wait for process
443
+	waitProcess(&pconfig, t)
444
+
445
+	outputStatus := string(stdout.Bytes())
446
+	if err != nil {
447
+		t.Fatal(err)
448
+	}
449
+
450
+	lines := strings.Split(outputStatus, "\n")
451
+
452
+	effectiveCapsLine := ""
453
+	for _, l := range lines {
454
+		line := strings.TrimSpace(l)
455
+		if strings.Contains(line, "CapEff:") {
456
+			effectiveCapsLine = line
457
+			break
458
+		}
459
+	}
460
+
461
+	if effectiveCapsLine == "" {
462
+		t.Fatal("Couldn't find effective caps: ", outputStatus)
463
+	}
464
+
465
+	parts := strings.Split(effectiveCapsLine, ":")
466
+	effectiveCapsStr := strings.TrimSpace(parts[1])
467
+
468
+	effectiveCaps, err := strconv.ParseUint(effectiveCapsStr, 16, 64)
469
+	if err != nil {
470
+		t.Fatal("Could not parse effective caps", err)
471
+	}
472
+
473
+	var netAdminMask uint64
474
+	var netAdminBit uint
475
+	netAdminBit = 12 // from capability.h
476
+	netAdminMask = 1 << netAdminBit
477
+	if effectiveCaps&netAdminMask != netAdminMask {
478
+		t.Fatal("CAP_NET_ADMIN is not set as expected")
479
+	}
480
+}
481
+
398 482
 func TestFreeze(t *testing.T) {
399 483
 	if testing.Short() {
400 484
 		return
401 485
new file mode 100644
... ...
@@ -0,0 +1,57 @@
0
+## nsinit
1
+
2
+`nsinit` is a cli application which demonstrates the use of libcontainer.  
3
+It is able to spawn new containers or join existing containers.  
4
+
5
+### How to build?
6
+
7
+First to add the `libcontainer/vendor` into your GOPATH. It's because something related with this [issue](https://github.com/docker/libcontainer/issues/210).
8
+
9
+```
10
+export GOPATH=$GOPATH:/your/path/to/libcontainer/vendor
11
+```
12
+
13
+Then get into the nsinit folder and get the imported file. Use `make` command to make the nsinit binary.
14
+
15
+```
16
+cd libcontainer/nsinit
17
+go get
18
+make
19
+```
20
+
21
+We have finished compiling the nsinit package, but a root filesystem must be provided for use along with a container configuration file.
22
+
23
+Choose a proper place to run your container. For example we use `/busybox`.
24
+
25
+```
26
+mkdir /busybox 
27
+curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.11/rootfs.tar' | tar -xC /busybox
28
+```
29
+
30
+Then you may need to write a configure file named `container.json` in the `/busybox` folder.
31
+Environment, networking, and different capabilities for the container are specified in this file.
32
+The configuration is used for each process executed inside the container
33
+See the `sample_configs` folder for examples of what the container configuration should look like.
34
+
35
+```
36
+cp libcontainer/sample_configs/minimal.json /busybox/container.json
37
+cd /busybox
38
+```
39
+
40
+Now the nsinit is ready to work.
41
+To execute `/bin/bash` in the current directory as a container just run the following **as root**:
42
+```bash
43
+nsinit exec --tty /bin/bash
44
+```
45
+
46
+If you wish to spawn another process inside the container while your 
47
+current bash session is running, run the same command again to 
48
+get another bash shell (or change the command).  If the original 
49
+process (PID 1) dies, all other processes spawned inside the container 
50
+will be killed and the namespace will be removed. 
51
+
52
+You can identify if a process is running in a container by 
53
+looking to see if `state.json` is in the root of the directory.
54
+   
55
+You may also specify an alternate root place where 
56
+the `container.json` file is read and where the `state.json` file will be saved.
... ...
@@ -41,6 +41,10 @@ type Process struct {
41 41
 	// consolePath is the path to the console allocated to the container.
42 42
 	consolePath string
43 43
 
44
+	// Capabilities specify the capabilities to keep when executing the process inside the container
45
+	// All capbilities not specified will be dropped from the processes capability mask
46
+	Capabilities []string
47
+
44 48
 	ops processOperations
45 49
 }
46 50
 
... ...
@@ -4,6 +4,7 @@ package libcontainer
4 4
 
5 5
 import (
6 6
 	"encoding/json"
7
+	"errors"
7 8
 	"io"
8 9
 	"os"
9 10
 	"os/exec"
... ...
@@ -44,8 +45,12 @@ func (p *setnsProcess) startTime() (string, error) {
44 44
 	return system.GetProcessStartTime(p.pid())
45 45
 }
46 46
 
47
-func (p *setnsProcess) signal(s os.Signal) error {
48
-	return p.cmd.Process.Signal(s)
47
+func (p *setnsProcess) signal(sig os.Signal) error {
48
+	s, ok := sig.(syscall.Signal)
49
+	if !ok {
50
+		return errors.New("os: unsupported signal type")
51
+	}
52
+	return syscall.Kill(p.cmd.Process.Pid, s)
49 53
 }
50 54
 
51 55
 func (p *setnsProcess) start() (err error) {
... ...
@@ -235,6 +240,10 @@ func (p *initProcess) createNetworkInterfaces() error {
235 235
 	return nil
236 236
 }
237 237
 
238
-func (p *initProcess) signal(s os.Signal) error {
239
-	return p.cmd.Process.Signal(s)
238
+func (p *initProcess) signal(sig os.Signal) error {
239
+	s, ok := sig.(syscall.Signal)
240
+	if !ok {
241
+		return errors.New("os: unsupported signal type")
242
+	}
243
+	return syscall.Kill(p.cmd.Process.Pid, s)
240 244
 }