Fixes #12015
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
| ... | ... |
@@ -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()) |