Fixes #12015
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
(cherry picked from commit d12fef1515cb3f0938ea6ed8cab8351e2df2753e)
Docker-DCO-1.1-Signed-off-by: Jessie Frazelle <jess@docker.com> (github: jfrazelle)
Docker-DCO-1.1-Signed-off-by: Jessie Frazelle <hugs@docker.com> (github: jfrazelle)
... | ... |
@@ -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 c8512754166539461fd860451ff1a0af7491c197 |
|
78 |
+clone git github.com/docker/libcontainer d00b8369852285d6a830a8d3b966608b2ed89705 |
|
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')" |
... | ... |
@@ -67,12 +67,12 @@ func generateProfile(out io.Writer) error { |
67 | 67 |
data := &data{ |
68 | 68 |
Name: "docker-default", |
69 | 69 |
} |
70 |
- if tuntablesExists() { |
|
70 |
+ if tunablesExists() { |
|
71 | 71 |
data.Imports = append(data.Imports, "#include <tunables/global>") |
72 | 72 |
} else { |
73 | 73 |
data.Imports = append(data.Imports, "@{PROC}=/proc/") |
74 | 74 |
} |
75 |
- if abstrctionsEsists() { |
|
75 |
+ if abstractionsExists() { |
|
76 | 76 |
data.InnerImports = append(data.InnerImports, "#include <abstractions/base>") |
77 | 77 |
} |
78 | 78 |
if err := compiled.Execute(out, data); err != nil { |
... | ... |
@@ -82,13 +82,13 @@ func generateProfile(out io.Writer) error { |
82 | 82 |
} |
83 | 83 |
|
84 | 84 |
// check if the tunables/global exist |
85 |
-func tuntablesExists() bool { |
|
85 |
+func tunablesExists() bool { |
|
86 | 86 |
_, err := os.Stat("/etc/apparmor.d/tunables/global") |
87 | 87 |
return err == nil |
88 | 88 |
} |
89 | 89 |
|
90 | 90 |
// check if abstractions/base exist |
91 |
-func abstrctionsEsists() bool { |
|
91 |
+func abstractionsExists() bool { |
|
92 | 92 |
_, err := os.Stat("/etc/apparmor.d/abstractions/base") |
93 | 93 |
return err == nil |
94 | 94 |
} |
... | ... |
@@ -1,6 +1,7 @@ |
1 | 1 |
package fs |
2 | 2 |
|
3 | 3 |
import ( |
4 |
+ "fmt" |
|
4 | 5 |
"strings" |
5 | 6 |
"time" |
6 | 7 |
|
... | ... |
@@ -41,6 +42,10 @@ func (s *FreezerGroup) Set(path string, cgroup *configs.Cgroup) error { |
41 | 41 |
} |
42 | 42 |
time.Sleep(1 * time.Millisecond) |
43 | 43 |
} |
44 |
+ case configs.Undefined: |
|
45 |
+ return nil |
|
46 |
+ default: |
|
47 |
+ return fmt.Errorf("Invalid argument '%s' to freezer.state", string(cgroup.Freezer)) |
|
44 | 48 |
} |
45 | 49 |
|
46 | 50 |
return nil |
47 | 51 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,45 @@ |
0 |
+package fs |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "testing" |
|
4 |
+ |
|
5 |
+ "github.com/docker/libcontainer/configs" |
|
6 |
+) |
|
7 |
+ |
|
8 |
+func TestFreezerSetState(t *testing.T) { |
|
9 |
+ helper := NewCgroupTestUtil("freezer", t) |
|
10 |
+ defer helper.cleanup() |
|
11 |
+ |
|
12 |
+ helper.writeFileContents(map[string]string{ |
|
13 |
+ "freezer.state": string(configs.Frozen), |
|
14 |
+ }) |
|
15 |
+ |
|
16 |
+ helper.CgroupData.c.Freezer = configs.Thawed |
|
17 |
+ freezer := &FreezerGroup{} |
|
18 |
+ if err := freezer.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { |
|
19 |
+ t.Fatal(err) |
|
20 |
+ } |
|
21 |
+ |
|
22 |
+ value, err := getCgroupParamString(helper.CgroupPath, "freezer.state") |
|
23 |
+ if err != nil { |
|
24 |
+ t.Fatalf("Failed to parse freezer.state - %s", err) |
|
25 |
+ } |
|
26 |
+ if value != string(configs.Thawed) { |
|
27 |
+ t.Fatal("Got the wrong value, set freezer.state failed.") |
|
28 |
+ } |
|
29 |
+} |
|
30 |
+ |
|
31 |
+func TestFreezerSetInvalidState(t *testing.T) { |
|
32 |
+ helper := NewCgroupTestUtil("freezer", t) |
|
33 |
+ defer helper.cleanup() |
|
34 |
+ |
|
35 |
+ const ( |
|
36 |
+ invalidArg configs.FreezerState = "Invalid" |
|
37 |
+ ) |
|
38 |
+ |
|
39 |
+ helper.CgroupData.c.Freezer = invalidArg |
|
40 |
+ freezer := &FreezerGroup{} |
|
41 |
+ if err := freezer.Set(helper.CgroupPath, helper.CgroupData.c); err == nil { |
|
42 |
+ t.Fatal("Failed to return invalid argument error") |
|
43 |
+ } |
|
44 |
+} |
... | ... |
@@ -218,16 +218,7 @@ func (m *Manager) Apply(pid int) error { |
218 | 218 |
} |
219 | 219 |
|
220 | 220 |
paths := make(map[string]string) |
221 |
- for _, sysname := range []string{ |
|
222 |
- "devices", |
|
223 |
- "memory", |
|
224 |
- "cpu", |
|
225 |
- "cpuset", |
|
226 |
- "cpuacct", |
|
227 |
- "blkio", |
|
228 |
- "perf_event", |
|
229 |
- "freezer", |
|
230 |
- } { |
|
221 |
+ for sysname := range subsystems { |
|
231 | 222 |
subsystemPath, err := getSubsystemPath(m.Cgroups, sysname) |
232 | 223 |
if err != nil { |
233 | 224 |
// Don't fail if a cgroup hierarchy was not found, just skip this subsystem |
... | ... |
@@ -140,7 +140,9 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec. |
140 | 140 |
cmd.SysProcAttr = &syscall.SysProcAttr{} |
141 | 141 |
} |
142 | 142 |
cmd.ExtraFiles = []*os.File{childPipe} |
143 |
- cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL |
|
143 |
+ // NOTE: when running a container with no PID namespace and the parent process spawning the container is |
|
144 |
+ // PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason |
|
145 |
+ // even with the parent still running. |
|
144 | 146 |
if c.config.ParentDeathSignal > 0 { |
145 | 147 |
cmd.SysProcAttr.Pdeathsig = syscall.Signal(c.config.ParentDeathSignal) |
146 | 148 |
} |
... | ... |
@@ -69,7 +69,8 @@ func newContainerInit(t initType, pipe *os.File) (initer, error) { |
69 | 69 |
}, nil |
70 | 70 |
case initStandard: |
71 | 71 |
return &linuxStandardInit{ |
72 |
- config: config, |
|
72 |
+ parentPid: syscall.Getppid(), |
|
73 |
+ config: config, |
|
73 | 74 |
}, nil |
74 | 75 |
} |
75 | 76 |
return nil, fmt.Errorf("unknown init type %q", t) |
... | ... |
@@ -5,13 +5,15 @@ It is able to spawn new containers or join existing containers. |
5 | 5 |
|
6 | 6 |
### How to build? |
7 | 7 |
|
8 |
-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 |
+First add the `libcontainer/vendor` into your GOPATH. It's because libcontainer |
|
9 |
+vendors all its dependencies, so it can be built predictably. |
|
9 | 10 |
|
10 | 11 |
``` |
11 | 12 |
export GOPATH=$GOPATH:/your/path/to/libcontainer/vendor |
12 | 13 |
``` |
13 | 14 |
|
14 |
-Then get into the nsinit folder and get the imported file. Use `make` command to make the nsinit binary. |
|
15 |
+Then get into the nsinit folder and get the imported file. Use `make` command |
|
16 |
+to make the nsinit binary. |
|
15 | 17 |
|
16 | 18 |
``` |
17 | 19 |
cd libcontainer/nsinit |
... | ... |
@@ -19,7 +21,8 @@ go get |
19 | 19 |
make |
20 | 20 |
``` |
21 | 21 |
|
22 |
-We have finished compiling the nsinit package, but a root filesystem must be provided for use along with a container configuration file. |
|
22 |
+We have finished compiling the nsinit package, but a root filesystem must be |
|
23 |
+provided for use along with a container configuration file. |
|
23 | 24 |
|
24 | 25 |
Choose a proper place to run your container. For example we use `/busybox`. |
25 | 26 |
|
... | ... |
@@ -28,30 +31,37 @@ mkdir /busybox |
28 | 28 |
curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.11/rootfs.tar' | tar -xC /busybox |
29 | 29 |
``` |
30 | 30 |
|
31 |
-Then you may need to write a configure file named `container.json` in the `/busybox` folder. |
|
32 |
-Environment, networking, and different capabilities for the container are specified in this file. |
|
33 |
-The configuration is used for each process executed inside the container |
|
34 |
-See the `sample_configs` folder for examples of what the container configuration should look like. |
|
31 |
+Then you may need to write a configuration file named `container.json` in the |
|
32 |
+`/busybox` folder. Environment, networking, and different capabilities for |
|
33 |
+the container are specified in this file. The configuration is used for each |
|
34 |
+process executed inside the container. |
|
35 |
+ |
|
36 |
+See the `sample_configs` folder for examples of what the container configuration |
|
37 |
+should look like. |
|
35 | 38 |
|
36 | 39 |
``` |
37 | 40 |
cp libcontainer/sample_configs/minimal.json /busybox/container.json |
38 | 41 |
cd /busybox |
39 | 42 |
``` |
40 | 43 |
|
41 |
-Now the nsinit is ready to work. |
|
42 |
-To execute `/bin/bash` in the current directory as a container just run the following **as root**: |
|
44 |
+You can customize `container.json` per your needs. After that, nsinit is |
|
45 |
+ready to work. |
|
46 |
+ |
|
47 |
+To execute `/bin/bash` in the current directory as a container just run the |
|
48 |
+following **as root**: |
|
49 |
+ |
|
43 | 50 |
```bash |
44 |
-nsinit exec --tty /bin/bash |
|
51 |
+nsinit exec --tty --config container.json /bin/bash |
|
45 | 52 |
``` |
46 | 53 |
|
47 |
-If you wish to spawn another process inside the container while your |
|
48 |
-current bash session is running, run the same command again to |
|
49 |
-get another bash shell (or change the command). If the original |
|
50 |
-process (PID 1) dies, all other processes spawned inside the container |
|
51 |
-will be killed and the namespace will be removed. |
|
54 |
+If you wish to spawn another process inside the container while your current |
|
55 |
+bash session is running, run the same command again to get another bash shell |
|
56 |
+(or change the command). If the original process (PID 1) dies, all other |
|
57 |
+processes spawned inside the container will be killed and the namespace will |
|
58 |
+be removed. |
|
52 | 59 |
|
53 |
-You can identify if a process is running in a container by |
|
54 |
-looking to see if `state.json` is in the root of the directory. |
|
60 |
+You can identify if a process is running in a container by looking to see if |
|
61 |
+`state.json` is in the root of the directory. |
|
55 | 62 |
|
56 |
-You may also specify an alternate root place where |
|
57 |
-the `container.json` file is read and where the `state.json` file will be saved. |
|
63 |
+You may also specify an alternate root directory from where the `container.json` |
|
64 |
+file is read and where the `state.json` file will be saved. |
... | ... |
@@ -13,7 +13,8 @@ import ( |
13 | 13 |
) |
14 | 14 |
|
15 | 15 |
type linuxStandardInit struct { |
16 |
- config *initConfig |
|
16 |
+ parentPid int |
|
17 |
+ config *initConfig |
|
17 | 18 |
} |
18 | 19 |
|
19 | 20 |
func (l *linuxStandardInit) Init() error { |
... | ... |
@@ -85,9 +86,10 @@ func (l *linuxStandardInit) Init() error { |
85 | 85 |
if err := pdeath.Restore(); err != nil { |
86 | 86 |
return err |
87 | 87 |
} |
88 |
- // Signal self if parent is already dead. Does nothing if running in a new |
|
89 |
- // PID namespace, as Getppid will always return 0. |
|
90 |
- if syscall.Getppid() == 1 { |
|
88 |
+ // compare the parent from the inital start of the init process and make sure that it did not change. |
|
89 |
+ // if the parent changes that means it died and we were reparened to something else so we should |
|
90 |
+ // just kill ourself and not cause problems for someone else. |
|
91 |
+ if syscall.Getppid() != l.parentPid { |
|
91 | 92 |
return syscall.Kill(syscall.Getpid(), syscall.SIGKILL) |
92 | 93 |
} |
93 | 94 |
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ()) |