Move LXC into a sub pkg and provide initial execution driver interface
| ... | ... |
@@ -657,16 +657,16 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r |
| 657 | 657 |
for scanner.Scan() {
|
| 658 | 658 |
out.Warnings = append(out.Warnings, scanner.Text()) |
| 659 | 659 |
} |
| 660 |
- if job.GetenvInt("Memory") > 0 && !srv.runtime.capabilities.MemoryLimit {
|
|
| 660 |
+ if job.GetenvInt("Memory") > 0 && !srv.runtime.sysInfo.MemoryLimit {
|
|
| 661 | 661 |
log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
|
| 662 | 662 |
out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") |
| 663 | 663 |
} |
| 664 |
- if job.GetenvInt("Memory") > 0 && !srv.runtime.capabilities.SwapLimit {
|
|
| 664 |
+ if job.GetenvInt("Memory") > 0 && !srv.runtime.sysInfo.SwapLimit {
|
|
| 665 | 665 |
log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.")
|
| 666 | 666 |
out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.") |
| 667 | 667 |
} |
| 668 | 668 |
|
| 669 |
- if !job.GetenvBool("NetworkDisabled") && srv.runtime.capabilities.IPv4ForwardingDisabled {
|
|
| 669 |
+ if !job.GetenvBool("NetworkDisabled") && srv.runtime.sysInfo.IPv4ForwardingDisabled {
|
|
| 670 | 670 |
log.Println("Warning: IPv4 forwarding is disabled.")
|
| 671 | 671 |
out.Warnings = append(out.Warnings, "IPv4 forwarding is disabled.") |
| 672 | 672 |
} |
| ... | ... |
@@ -12,8 +12,13 @@ import ( |
| 12 | 12 |
"strings" |
| 13 | 13 |
) |
| 14 | 14 |
|
| 15 |
-// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt |
|
| 15 |
+type Values struct {
|
|
| 16 |
+ Memory int64 `json:"memory"` |
|
| 17 |
+ MemorySwap int64 `json:"memory_swap"` |
|
| 18 |
+ CpuShares int64 `json:"cpu_shares"` |
|
| 19 |
+} |
|
| 16 | 20 |
|
| 21 |
+// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt |
|
| 17 | 22 |
func FindCgroupMountpoint(subsystem string) (string, error) {
|
| 18 | 23 |
mounts, err := mount.GetMounts() |
| 19 | 24 |
if err != nil {
|
| ... | ... |
@@ -12,6 +12,7 @@ import ( |
| 12 | 12 |
"github.com/dotcloud/docker/auth" |
| 13 | 13 |
"github.com/dotcloud/docker/engine" |
| 14 | 14 |
flag "github.com/dotcloud/docker/pkg/mflag" |
| 15 |
+ "github.com/dotcloud/docker/pkg/sysinfo" |
|
| 15 | 16 |
"github.com/dotcloud/docker/pkg/term" |
| 16 | 17 |
"github.com/dotcloud/docker/registry" |
| 17 | 18 |
"github.com/dotcloud/docker/utils" |
| ... | ... |
@@ -470,7 +471,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
| 470 | 470 |
fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
|
| 471 | 471 |
fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
|
| 472 | 472 |
fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
|
| 473 |
- fmt.Fprintf(cli.out, "LXC Version: %s\n", remoteInfo.Get("LXCVersion"))
|
|
| 473 |
+ fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
|
|
| 474 | 474 |
fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
|
| 475 | 475 |
fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
|
| 476 | 476 |
|
| ... | ... |
@@ -1745,14 +1746,14 @@ func (cli *DockerCli) CmdTag(args ...string) error {
|
| 1745 | 1745 |
} |
| 1746 | 1746 |
|
| 1747 | 1747 |
//FIXME Only used in tests |
| 1748 |
-func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
|
|
| 1748 |
+func ParseRun(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
|
|
| 1749 | 1749 |
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
|
| 1750 | 1750 |
cmd.SetOutput(ioutil.Discard) |
| 1751 | 1751 |
cmd.Usage = nil |
| 1752 |
- return parseRun(cmd, args, capabilities) |
|
| 1752 |
+ return parseRun(cmd, args, sysInfo) |
|
| 1753 | 1753 |
} |
| 1754 | 1754 |
|
| 1755 |
-func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
|
|
| 1755 |
+func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
|
|
| 1756 | 1756 |
var ( |
| 1757 | 1757 |
// FIXME: use utils.ListOpts for attach and volumes? |
| 1758 | 1758 |
flAttach = NewListOpts(ValidateAttach) |
| ... | ... |
@@ -1802,7 +1803,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co |
| 1802 | 1802 |
} |
| 1803 | 1803 |
|
| 1804 | 1804 |
// Check if the kernel supports memory limit cgroup. |
| 1805 |
- if capabilities != nil && *flMemoryString != "" && !capabilities.MemoryLimit {
|
|
| 1805 |
+ if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
|
|
| 1806 | 1806 |
*flMemoryString = "" |
| 1807 | 1807 |
} |
| 1808 | 1808 |
|
| ... | ... |
@@ -1934,7 +1935,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co |
| 1934 | 1934 |
PublishAllPorts: *flPublishAll, |
| 1935 | 1935 |
} |
| 1936 | 1936 |
|
| 1937 |
- if capabilities != nil && flMemory > 0 && !capabilities.SwapLimit {
|
|
| 1937 |
+ if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
|
|
| 1938 | 1938 |
//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n") |
| 1939 | 1939 |
config.MemorySwap = -1 |
| 1940 | 1940 |
} |
| ... | ... |
@@ -1,11 +1,12 @@ |
| 1 | 1 |
package docker |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "bytes" |
|
| 5 | 4 |
"encoding/json" |
| 6 | 5 |
"errors" |
| 7 | 6 |
"fmt" |
| 8 | 7 |
"github.com/dotcloud/docker/archive" |
| 8 |
+ "github.com/dotcloud/docker/cgroups" |
|
| 9 |
+ "github.com/dotcloud/docker/execdriver" |
|
| 9 | 10 |
"github.com/dotcloud/docker/graphdriver" |
| 10 | 11 |
"github.com/dotcloud/docker/mount" |
| 11 | 12 |
"github.com/dotcloud/docker/pkg/term" |
| ... | ... |
@@ -16,9 +17,7 @@ import ( |
| 16 | 16 |
"log" |
| 17 | 17 |
"net" |
| 18 | 18 |
"os" |
| 19 |
- "os/exec" |
|
| 20 | 19 |
"path" |
| 21 |
- "strconv" |
|
| 22 | 20 |
"strings" |
| 23 | 21 |
"sync" |
| 24 | 22 |
"syscall" |
| ... | ... |
@@ -55,7 +54,7 @@ type Container struct {
|
| 55 | 55 |
Name string |
| 56 | 56 |
Driver string |
| 57 | 57 |
|
| 58 |
- cmd *exec.Cmd |
|
| 58 |
+ process *execdriver.Process |
|
| 59 | 59 |
stdout *utils.WriteBroadcaster |
| 60 | 60 |
stderr *utils.WriteBroadcaster |
| 61 | 61 |
stdin io.ReadCloser |
| ... | ... |
@@ -235,10 +234,6 @@ func (container *Container) Inject(file io.Reader, pth string) error {
|
| 235 | 235 |
return nil |
| 236 | 236 |
} |
| 237 | 237 |
|
| 238 |
-func (container *Container) Cmd() *exec.Cmd {
|
|
| 239 |
- return container.cmd |
|
| 240 |
-} |
|
| 241 |
- |
|
| 242 | 238 |
func (container *Container) When() time.Time {
|
| 243 | 239 |
return container.Created |
| 244 | 240 |
} |
| ... | ... |
@@ -305,23 +300,14 @@ func (container *Container) generateEnvConfig(env []string) error {
|
| 305 | 305 |
return nil |
| 306 | 306 |
} |
| 307 | 307 |
|
| 308 |
-func (container *Container) generateLXCConfig() error {
|
|
| 309 |
- fo, err := os.Create(container.lxcConfigPath()) |
|
| 310 |
- if err != nil {
|
|
| 311 |
- return err |
|
| 312 |
- } |
|
| 313 |
- defer fo.Close() |
|
| 314 |
- return LxcTemplateCompiled.Execute(fo, container) |
|
| 315 |
-} |
|
| 316 |
- |
|
| 317 |
-func (container *Container) startPty() error {
|
|
| 308 |
+func (container *Container) setupPty() error {
|
|
| 318 | 309 |
ptyMaster, ptySlave, err := pty.Open() |
| 319 | 310 |
if err != nil {
|
| 320 | 311 |
return err |
| 321 | 312 |
} |
| 322 | 313 |
container.ptyMaster = ptyMaster |
| 323 |
- container.cmd.Stdout = ptySlave |
|
| 324 |
- container.cmd.Stderr = ptySlave |
|
| 314 |
+ container.process.Stdout = ptySlave |
|
| 315 |
+ container.process.Stderr = ptySlave |
|
| 325 | 316 |
|
| 326 | 317 |
// Copy the PTYs to our broadcasters |
| 327 | 318 |
go func() {
|
| ... | ... |
@@ -333,8 +319,8 @@ func (container *Container) startPty() error {
|
| 333 | 333 |
|
| 334 | 334 |
// stdin |
| 335 | 335 |
if container.Config.OpenStdin {
|
| 336 |
- container.cmd.Stdin = ptySlave |
|
| 337 |
- container.cmd.SysProcAttr.Setctty = true |
|
| 336 |
+ container.process.Stdin = ptySlave |
|
| 337 |
+ container.process.SysProcAttr.Setctty = true |
|
| 338 | 338 |
go func() {
|
| 339 | 339 |
defer container.stdin.Close() |
| 340 | 340 |
utils.Debugf("startPty: begin of stdin pipe")
|
| ... | ... |
@@ -342,18 +328,14 @@ func (container *Container) startPty() error {
|
| 342 | 342 |
utils.Debugf("startPty: end of stdin pipe")
|
| 343 | 343 |
}() |
| 344 | 344 |
} |
| 345 |
- if err := container.cmd.Start(); err != nil {
|
|
| 346 |
- return err |
|
| 347 |
- } |
|
| 348 |
- ptySlave.Close() |
|
| 349 | 345 |
return nil |
| 350 | 346 |
} |
| 351 | 347 |
|
| 352 |
-func (container *Container) start() error {
|
|
| 353 |
- container.cmd.Stdout = container.stdout |
|
| 354 |
- container.cmd.Stderr = container.stderr |
|
| 348 |
+func (container *Container) setupStd() error {
|
|
| 349 |
+ container.process.Stdout = container.stdout |
|
| 350 |
+ container.process.Stderr = container.stderr |
|
| 355 | 351 |
if container.Config.OpenStdin {
|
| 356 |
- stdin, err := container.cmd.StdinPipe() |
|
| 352 |
+ stdin, err := container.process.StdinPipe() |
|
| 357 | 353 |
if err != nil {
|
| 358 | 354 |
return err |
| 359 | 355 |
} |
| ... | ... |
@@ -364,7 +346,7 @@ func (container *Container) start() error {
|
| 364 | 364 |
utils.Debugf("start: end of stdin pipe")
|
| 365 | 365 |
}() |
| 366 | 366 |
} |
| 367 |
- return container.cmd.Start() |
|
| 367 |
+ return nil |
|
| 368 | 368 |
} |
| 369 | 369 |
|
| 370 | 370 |
func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
|
| ... | ... |
@@ -384,12 +366,14 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
| 384 | 384 |
if container.Config.StdinOnce && !container.Config.Tty {
|
| 385 | 385 |
defer cStdin.Close() |
| 386 | 386 |
} else {
|
| 387 |
- if cStdout != nil {
|
|
| 388 |
- defer cStdout.Close() |
|
| 389 |
- } |
|
| 390 |
- if cStderr != nil {
|
|
| 391 |
- defer cStderr.Close() |
|
| 392 |
- } |
|
| 387 |
+ defer func() {
|
|
| 388 |
+ if cStdout != nil {
|
|
| 389 |
+ cStdout.Close() |
|
| 390 |
+ } |
|
| 391 |
+ if cStderr != nil {
|
|
| 392 |
+ cStderr.Close() |
|
| 393 |
+ } |
|
| 394 |
+ }() |
|
| 393 | 395 |
} |
| 394 | 396 |
if container.Config.Tty {
|
| 395 | 397 |
_, err = utils.CopyEscapable(cStdin, stdin) |
| ... | ... |
@@ -485,12 +469,15 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
| 485 | 485 |
} |
| 486 | 486 |
|
| 487 | 487 |
return utils.Go(func() error {
|
| 488 |
- if cStdout != nil {
|
|
| 489 |
- defer cStdout.Close() |
|
| 490 |
- } |
|
| 491 |
- if cStderr != nil {
|
|
| 492 |
- defer cStderr.Close() |
|
| 493 |
- } |
|
| 488 |
+ defer func() {
|
|
| 489 |
+ if cStdout != nil {
|
|
| 490 |
+ cStdout.Close() |
|
| 491 |
+ } |
|
| 492 |
+ if cStderr != nil {
|
|
| 493 |
+ cStderr.Close() |
|
| 494 |
+ } |
|
| 495 |
+ }() |
|
| 496 |
+ |
|
| 494 | 497 |
// FIXME: how to clean up the stdin goroutine without the unwanted side effect |
| 495 | 498 |
// of closing the passed stdin? Add an intermediary io.Pipe? |
| 496 | 499 |
for i := 0; i < nJobs; i += 1 {
|
| ... | ... |
@@ -532,16 +519,16 @@ func (container *Container) Start() (err error) {
|
| 532 | 532 |
} |
| 533 | 533 |
|
| 534 | 534 |
// Make sure the config is compatible with the current kernel |
| 535 |
- if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit {
|
|
| 535 |
+ if container.Config.Memory > 0 && !container.runtime.sysInfo.MemoryLimit {
|
|
| 536 | 536 |
log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
|
| 537 | 537 |
container.Config.Memory = 0 |
| 538 | 538 |
} |
| 539 |
- if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit {
|
|
| 539 |
+ if container.Config.Memory > 0 && !container.runtime.sysInfo.SwapLimit {
|
|
| 540 | 540 |
log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
|
| 541 | 541 |
container.Config.MemorySwap = -1 |
| 542 | 542 |
} |
| 543 | 543 |
|
| 544 |
- if container.runtime.capabilities.IPv4ForwardingDisabled {
|
|
| 544 |
+ if container.runtime.sysInfo.IPv4ForwardingDisabled {
|
|
| 545 | 545 |
log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
|
| 546 | 546 |
} |
| 547 | 547 |
|
| ... | ... |
@@ -559,38 +546,6 @@ func (container *Container) Start() (err error) {
|
| 559 | 559 |
return err |
| 560 | 560 |
} |
| 561 | 561 |
|
| 562 |
- if err := container.generateLXCConfig(); err != nil {
|
|
| 563 |
- return err |
|
| 564 |
- } |
|
| 565 |
- |
|
| 566 |
- var lxcStart string = "lxc-start" |
|
| 567 |
- if container.hostConfig.Privileged && container.runtime.capabilities.AppArmor {
|
|
| 568 |
- lxcStart = path.Join(container.runtime.config.Root, "lxc-start-unconfined") |
|
| 569 |
- } |
|
| 570 |
- |
|
| 571 |
- params := []string{
|
|
| 572 |
- lxcStart, |
|
| 573 |
- "-n", container.ID, |
|
| 574 |
- "-f", container.lxcConfigPath(), |
|
| 575 |
- "--", |
|
| 576 |
- "/.dockerinit", |
|
| 577 |
- } |
|
| 578 |
- |
|
| 579 |
- // Networking |
|
| 580 |
- if !container.Config.NetworkDisabled {
|
|
| 581 |
- network := container.NetworkSettings |
|
| 582 |
- params = append(params, |
|
| 583 |
- "-g", network.Gateway, |
|
| 584 |
- "-i", fmt.Sprintf("%s/%d", network.IPAddress, network.IPPrefixLen),
|
|
| 585 |
- "-mtu", strconv.Itoa(container.runtime.config.Mtu), |
|
| 586 |
- ) |
|
| 587 |
- } |
|
| 588 |
- |
|
| 589 |
- // User |
|
| 590 |
- if container.Config.User != "" {
|
|
| 591 |
- params = append(params, "-u", container.Config.User) |
|
| 592 |
- } |
|
| 593 |
- |
|
| 594 | 562 |
// Setup environment |
| 595 | 563 |
env := []string{
|
| 596 | 564 |
"HOME=/", |
| ... | ... |
@@ -602,10 +557,6 @@ func (container *Container) Start() (err error) {
|
| 602 | 602 |
env = append(env, "TERM=xterm") |
| 603 | 603 |
} |
| 604 | 604 |
|
| 605 |
- if container.hostConfig.Privileged {
|
|
| 606 |
- params = append(params, "-privileged") |
|
| 607 |
- } |
|
| 608 |
- |
|
| 609 | 605 |
// Init any links between the parent and children |
| 610 | 606 |
runtime := container.runtime |
| 611 | 607 |
|
| ... | ... |
@@ -653,37 +604,12 @@ func (container *Container) Start() (err error) {
|
| 653 | 653 |
return err |
| 654 | 654 |
} |
| 655 | 655 |
|
| 656 |
+ var workingDir string |
|
| 656 | 657 |
if container.Config.WorkingDir != "" {
|
| 657 |
- workingDir := path.Clean(container.Config.WorkingDir) |
|
| 658 |
- utils.Debugf("[working dir] working dir is %s", workingDir)
|
|
| 659 |
- |
|
| 658 |
+ workingDir = path.Clean(container.Config.WorkingDir) |
|
| 660 | 659 |
if err := os.MkdirAll(path.Join(container.RootfsPath(), workingDir), 0755); err != nil {
|
| 661 | 660 |
return nil |
| 662 | 661 |
} |
| 663 |
- |
|
| 664 |
- params = append(params, |
|
| 665 |
- "-w", workingDir, |
|
| 666 |
- ) |
|
| 667 |
- } |
|
| 668 |
- |
|
| 669 |
- // Program |
|
| 670 |
- params = append(params, "--", container.Path) |
|
| 671 |
- params = append(params, container.Args...) |
|
| 672 |
- |
|
| 673 |
- if RootIsShared() {
|
|
| 674 |
- // lxc-start really needs / to be non-shared, or all kinds of stuff break |
|
| 675 |
- // when lxc-start unmount things and those unmounts propagate to the main |
|
| 676 |
- // mount namespace. |
|
| 677 |
- // What we really want is to clone into a new namespace and then |
|
| 678 |
- // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork |
|
| 679 |
- // without exec in go we have to do this horrible shell hack... |
|
| 680 |
- shellString := |
|
| 681 |
- "mount --make-rslave /; exec " + |
|
| 682 |
- utils.ShellQuoteArguments(params) |
|
| 683 |
- |
|
| 684 |
- params = []string{
|
|
| 685 |
- "unshare", "-m", "--", "/bin/sh", "-c", shellString, |
|
| 686 |
- } |
|
| 687 | 662 |
} |
| 688 | 663 |
|
| 689 | 664 |
root := container.RootfsPath() |
| ... | ... |
@@ -713,7 +639,6 @@ func (container *Container) Start() (err error) {
|
| 713 | 713 |
} |
| 714 | 714 |
|
| 715 | 715 |
// Mount user specified volumes |
| 716 |
- |
|
| 717 | 716 |
for r, v := range container.Volumes {
|
| 718 | 717 |
mountAs := "ro" |
| 719 | 718 |
if container.VolumesRW[r] {
|
| ... | ... |
@@ -725,7 +650,48 @@ func (container *Container) Start() (err error) {
|
| 725 | 725 |
} |
| 726 | 726 |
} |
| 727 | 727 |
|
| 728 |
- container.cmd = exec.Command(params[0], params[1:]...) |
|
| 728 |
+ var ( |
|
| 729 |
+ en *execdriver.Network |
|
| 730 |
+ driverConfig []string |
|
| 731 |
+ ) |
|
| 732 |
+ |
|
| 733 |
+ if !container.Config.NetworkDisabled {
|
|
| 734 |
+ network := container.NetworkSettings |
|
| 735 |
+ en = &execdriver.Network{
|
|
| 736 |
+ Gateway: network.Gateway, |
|
| 737 |
+ Bridge: network.Bridge, |
|
| 738 |
+ IPAddress: network.IPAddress, |
|
| 739 |
+ IPPrefixLen: network.IPPrefixLen, |
|
| 740 |
+ Mtu: container.runtime.config.Mtu, |
|
| 741 |
+ } |
|
| 742 |
+ } |
|
| 743 |
+ |
|
| 744 |
+ if lxcConf := container.hostConfig.LxcConf; lxcConf != nil {
|
|
| 745 |
+ for _, pair := range lxcConf {
|
|
| 746 |
+ driverConfig = append(driverConfig, fmt.Sprintf("%s = %s", pair.Key, pair.Value))
|
|
| 747 |
+ } |
|
| 748 |
+ } |
|
| 749 |
+ cgroupValues := &cgroups.Values{
|
|
| 750 |
+ Memory: container.Config.Memory, |
|
| 751 |
+ MemorySwap: container.Config.MemorySwap, |
|
| 752 |
+ CpuShares: container.Config.CpuShares, |
|
| 753 |
+ } |
|
| 754 |
+ |
|
| 755 |
+ container.process = &execdriver.Process{
|
|
| 756 |
+ ID: container.ID, |
|
| 757 |
+ Privileged: container.hostConfig.Privileged, |
|
| 758 |
+ Rootfs: root, |
|
| 759 |
+ InitPath: "/.dockerinit", |
|
| 760 |
+ Entrypoint: container.Path, |
|
| 761 |
+ Arguments: container.Args, |
|
| 762 |
+ WorkingDir: workingDir, |
|
| 763 |
+ Network: en, |
|
| 764 |
+ Tty: container.Config.Tty, |
|
| 765 |
+ User: container.Config.User, |
|
| 766 |
+ Config: driverConfig, |
|
| 767 |
+ Cgroups: cgroupValues, |
|
| 768 |
+ } |
|
| 769 |
+ container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
|
| 729 | 770 |
|
| 730 | 771 |
// Setup logging of stdout and stderr to disk |
| 731 | 772 |
if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
|
| ... | ... |
@@ -734,59 +700,47 @@ func (container *Container) Start() (err error) {
|
| 734 | 734 |
if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
|
| 735 | 735 |
return err |
| 736 | 736 |
} |
| 737 |
+ container.waitLock = make(chan struct{})
|
|
| 737 | 738 |
|
| 738 |
- container.cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
|
| 739 |
- |
|
| 739 |
+ // Setuping pipes and/or Pty |
|
| 740 |
+ var setup func() error |
|
| 740 | 741 |
if container.Config.Tty {
|
| 741 |
- err = container.startPty() |
|
| 742 |
+ setup = container.setupPty |
|
| 742 | 743 |
} else {
|
| 743 |
- err = container.start() |
|
| 744 |
+ setup = container.setupStd |
|
| 744 | 745 |
} |
| 745 |
- if err != nil {
|
|
| 746 |
+ if err := setup(); err != nil {
|
|
| 746 | 747 |
return err |
| 747 | 748 |
} |
| 748 |
- // FIXME: save state on disk *first*, then converge |
|
| 749 |
- // this way disk state is used as a journal, eg. we can restore after crash etc. |
|
| 750 |
- container.State.SetRunning(container.cmd.Process.Pid) |
|
| 751 |
- |
|
| 752 |
- // Init the lock |
|
| 753 |
- container.waitLock = make(chan struct{})
|
|
| 754 | 749 |
|
| 755 |
- container.ToDisk() |
|
| 756 |
- go container.monitor() |
|
| 757 |
- |
|
| 758 |
- defer utils.Debugf("Container running: %v", container.State.IsRunning())
|
|
| 759 |
- // We wait for the container to be fully running. |
|
| 760 |
- // Timeout after 5 seconds. In case of broken pipe, just retry. |
|
| 761 |
- // Note: The container can run and finish correctly before |
|
| 762 |
- // the end of this loop |
|
| 763 |
- for now := time.Now(); time.Since(now) < 5*time.Second; {
|
|
| 764 |
- // If the container dies while waiting for it, just return |
|
| 765 |
- if !container.State.IsRunning() {
|
|
| 766 |
- return nil |
|
| 767 |
- } |
|
| 768 |
- output, err := exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput()
|
|
| 769 |
- if err != nil {
|
|
| 770 |
- utils.Debugf("Error with lxc-info: %s (%s)", err, output)
|
|
| 771 |
- |
|
| 772 |
- output, err = exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput()
|
|
| 773 |
- if err != nil {
|
|
| 774 |
- utils.Debugf("Second Error with lxc-info: %s (%s)", err, output)
|
|
| 775 |
- return err |
|
| 750 |
+ callbackLock := make(chan struct{})
|
|
| 751 |
+ callback := func(process *execdriver.Process) {
|
|
| 752 |
+ container.State.SetRunning(process.Pid()) |
|
| 753 |
+ if process.Tty {
|
|
| 754 |
+ // The callback is called after the process Start() |
|
| 755 |
+ // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace |
|
| 756 |
+ // which we close here. |
|
| 757 |
+ if c, ok := process.Stdout.(io.Closer); ok {
|
|
| 758 |
+ c.Close() |
|
| 776 | 759 |
} |
| 777 |
- |
|
| 778 | 760 |
} |
| 779 |
- if strings.Contains(string(output), "RUNNING") {
|
|
| 780 |
- return nil |
|
| 761 |
+ if err := container.ToDisk(); err != nil {
|
|
| 762 |
+ utils.Debugf("%s", err)
|
|
| 781 | 763 |
} |
| 782 |
- utils.Debugf("Waiting for the container to start (running: %v): %s", container.State.IsRunning(), bytes.TrimSpace(output))
|
|
| 783 |
- time.Sleep(50 * time.Millisecond) |
|
| 764 |
+ close(callbackLock) |
|
| 784 | 765 |
} |
| 785 | 766 |
|
| 786 |
- if container.State.IsRunning() {
|
|
| 787 |
- return ErrContainerStartTimeout |
|
| 767 |
+ // We use a callback here instead of a goroutine and an chan for |
|
| 768 |
+ // syncronization purposes |
|
| 769 |
+ cErr := utils.Go(func() error { return container.monitor(callback) })
|
|
| 770 |
+ |
|
| 771 |
+ // Start should not return until the process is actually running |
|
| 772 |
+ select {
|
|
| 773 |
+ case <-callbackLock: |
|
| 774 |
+ case err := <-cErr: |
|
| 775 |
+ return err |
|
| 788 | 776 |
} |
| 789 |
- return ErrContainerStart |
|
| 777 |
+ return nil |
|
| 790 | 778 |
} |
| 791 | 779 |
|
| 792 | 780 |
func (container *Container) getBindMap() (map[string]BindMap, error) {
|
| ... | ... |
@@ -1159,47 +1113,23 @@ func (container *Container) releaseNetwork() {
|
| 1159 | 1159 |
container.NetworkSettings = &NetworkSettings{}
|
| 1160 | 1160 |
} |
| 1161 | 1161 |
|
| 1162 |
-// FIXME: replace this with a control socket within dockerinit |
|
| 1163 |
-func (container *Container) waitLxc() error {
|
|
| 1164 |
- for {
|
|
| 1165 |
- output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
|
|
| 1166 |
- if err != nil {
|
|
| 1167 |
- return err |
|
| 1168 |
- } |
|
| 1169 |
- if !strings.Contains(string(output), "RUNNING") {
|
|
| 1170 |
- return nil |
|
| 1171 |
- } |
|
| 1172 |
- time.Sleep(500 * time.Millisecond) |
|
| 1173 |
- } |
|
| 1174 |
-} |
|
| 1175 |
- |
|
| 1176 |
-func (container *Container) monitor() {
|
|
| 1177 |
- // Wait for the program to exit |
|
| 1162 |
+func (container *Container) monitor(callback execdriver.StartCallback) error {
|
|
| 1163 |
+ var ( |
|
| 1164 |
+ err error |
|
| 1165 |
+ exitCode int |
|
| 1166 |
+ ) |
|
| 1178 | 1167 |
|
| 1179 |
- // If the command does not exist, try to wait via lxc |
|
| 1180 |
- // (This probably happens only for ghost containers, i.e. containers that were running when Docker started) |
|
| 1181 |
- if container.cmd == nil {
|
|
| 1182 |
- utils.Debugf("monitor: waiting for container %s using waitLxc", container.ID)
|
|
| 1183 |
- if err := container.waitLxc(); err != nil {
|
|
| 1184 |
- utils.Errorf("monitor: while waiting for container %s, waitLxc had a problem: %s", container.ID, err)
|
|
| 1185 |
- } |
|
| 1168 |
+ if container.process == nil {
|
|
| 1169 |
+ // This happends when you have a GHOST container with lxc |
|
| 1170 |
+ err = container.runtime.WaitGhost(container) |
|
| 1186 | 1171 |
} else {
|
| 1187 |
- utils.Debugf("monitor: waiting for container %s using cmd.Wait", container.ID)
|
|
| 1188 |
- if err := container.cmd.Wait(); err != nil {
|
|
| 1189 |
- // Since non-zero exit status and signal terminations will cause err to be non-nil, |
|
| 1190 |
- // we have to actually discard it. Still, log it anyway, just in case. |
|
| 1191 |
- utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID)
|
|
| 1192 |
- } |
|
| 1193 |
- } |
|
| 1194 |
- utils.Debugf("monitor: container %s finished", container.ID)
|
|
| 1195 |
- |
|
| 1196 |
- exitCode := -1 |
|
| 1197 |
- if container.cmd != nil {
|
|
| 1198 |
- exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() |
|
| 1172 |
+ exitCode, err = container.runtime.Run(container, callback) |
|
| 1199 | 1173 |
} |
| 1200 | 1174 |
|
| 1201 |
- if container.runtime != nil && container.runtime.srv != nil {
|
|
| 1202 |
- container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
|
|
| 1175 |
+ if err != nil {
|
|
| 1176 |
+ if container.runtime != nil && container.runtime.srv != nil {
|
|
| 1177 |
+ container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
|
|
| 1178 |
+ } |
|
| 1203 | 1179 |
} |
| 1204 | 1180 |
|
| 1205 | 1181 |
// Cleanup |
| ... | ... |
@@ -1210,21 +1140,20 @@ func (container *Container) monitor() {
|
| 1210 | 1210 |
container.stdin, container.stdinPipe = io.Pipe() |
| 1211 | 1211 |
} |
| 1212 | 1212 |
|
| 1213 |
- // Report status back |
|
| 1214 | 1213 |
container.State.SetStopped(exitCode) |
| 1215 | 1214 |
|
| 1216 |
- // Release the lock |
|
| 1217 | 1215 |
close(container.waitLock) |
| 1218 | 1216 |
|
| 1219 |
- if err := container.ToDisk(); err != nil {
|
|
| 1220 |
- // FIXME: there is a race condition here which causes this to fail during the unit tests. |
|
| 1221 |
- // If another goroutine was waiting for Wait() to return before removing the container's root |
|
| 1222 |
- // from the filesystem... At this point it may already have done so. |
|
| 1223 |
- // This is because State.setStopped() has already been called, and has caused Wait() |
|
| 1224 |
- // to return. |
|
| 1225 |
- // FIXME: why are we serializing running state to disk in the first place? |
|
| 1226 |
- //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
|
|
| 1227 |
- } |
|
| 1217 |
+ // FIXME: there is a race condition here which causes this to fail during the unit tests. |
|
| 1218 |
+ // If another goroutine was waiting for Wait() to return before removing the container's root |
|
| 1219 |
+ // from the filesystem... At this point it may already have done so. |
|
| 1220 |
+ // This is because State.setStopped() has already been called, and has caused Wait() |
|
| 1221 |
+ // to return. |
|
| 1222 |
+ // FIXME: why are we serializing running state to disk in the first place? |
|
| 1223 |
+ //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
|
|
| 1224 |
+ container.ToDisk() |
|
| 1225 |
+ |
|
| 1226 |
+ return err |
|
| 1228 | 1227 |
} |
| 1229 | 1228 |
|
| 1230 | 1229 |
func (container *Container) cleanup() {
|
| ... | ... |
@@ -1267,13 +1196,7 @@ func (container *Container) kill(sig int) error {
|
| 1267 | 1267 |
if !container.State.IsRunning() {
|
| 1268 | 1268 |
return nil |
| 1269 | 1269 |
} |
| 1270 |
- |
|
| 1271 |
- if output, err := exec.Command("lxc-kill", "-n", container.ID, strconv.Itoa(sig)).CombinedOutput(); err != nil {
|
|
| 1272 |
- log.Printf("error killing container %s (%s, %s)", utils.TruncateID(container.ID), output, err)
|
|
| 1273 |
- return err |
|
| 1274 |
- } |
|
| 1275 |
- |
|
| 1276 |
- return nil |
|
| 1270 |
+ return container.runtime.Kill(container, sig) |
|
| 1277 | 1271 |
} |
| 1278 | 1272 |
|
| 1279 | 1273 |
func (container *Container) Kill() error {
|
| ... | ... |
@@ -1288,11 +1211,11 @@ func (container *Container) Kill() error {
|
| 1288 | 1288 |
|
| 1289 | 1289 |
// 2. Wait for the process to die, in last resort, try to kill the process directly |
| 1290 | 1290 |
if err := container.WaitTimeout(10 * time.Second); err != nil {
|
| 1291 |
- if container.cmd == nil {
|
|
| 1291 |
+ if container.process == nil {
|
|
| 1292 | 1292 |
return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", utils.TruncateID(container.ID))
|
| 1293 | 1293 |
} |
| 1294 | 1294 |
log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", utils.TruncateID(container.ID))
|
| 1295 |
- if err := container.cmd.Process.Kill(); err != nil {
|
|
| 1295 |
+ if err := container.runtime.Kill(container, 9); err != nil {
|
|
| 1296 | 1296 |
return err |
| 1297 | 1297 |
} |
| 1298 | 1298 |
} |
| ... | ... |
@@ -1463,10 +1386,6 @@ func (container *Container) EnvConfigPath() (string, error) {
|
| 1463 | 1463 |
return p, nil |
| 1464 | 1464 |
} |
| 1465 | 1465 |
|
| 1466 |
-func (container *Container) lxcConfigPath() string {
|
|
| 1467 |
- return path.Join(container.root, "config.lxc") |
|
| 1468 |
-} |
|
| 1469 |
- |
|
| 1470 | 1466 |
// This method must be exported to be used from the lxc template |
| 1471 | 1467 |
func (container *Container) RootfsPath() string {
|
| 1472 | 1468 |
return container.rootfs |
| 0 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,87 @@ |
| 0 |
+package chroot |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/execdriver" |
|
| 5 |
+ "github.com/dotcloud/docker/mount" |
|
| 6 |
+ "os" |
|
| 7 |
+ "os/exec" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+const ( |
|
| 11 |
+ DriverName = "chroot" |
|
| 12 |
+ Version = "0.1" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+func init() {
|
|
| 16 |
+ execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
|
| 17 |
+ if err := mount.ForceMount("proc", "proc", "proc", ""); err != nil {
|
|
| 18 |
+ return err |
|
| 19 |
+ } |
|
| 20 |
+ defer mount.ForceUnmount("proc")
|
|
| 21 |
+ cmd := exec.Command(args.Args[0], args.Args[1:]...) |
|
| 22 |
+ |
|
| 23 |
+ cmd.Stderr = os.Stderr |
|
| 24 |
+ cmd.Stdout = os.Stdout |
|
| 25 |
+ cmd.Stdin = os.Stdin |
|
| 26 |
+ |
|
| 27 |
+ return cmd.Run() |
|
| 28 |
+ }) |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+type driver struct {
|
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func NewDriver() (*driver, error) {
|
|
| 35 |
+ return &driver{}, nil
|
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) {
|
|
| 39 |
+ params := []string{
|
|
| 40 |
+ "chroot", |
|
| 41 |
+ c.Rootfs, |
|
| 42 |
+ "/.dockerinit", |
|
| 43 |
+ "-driver", |
|
| 44 |
+ DriverName, |
|
| 45 |
+ } |
|
| 46 |
+ params = append(params, c.Entrypoint) |
|
| 47 |
+ params = append(params, c.Arguments...) |
|
| 48 |
+ |
|
| 49 |
+ var ( |
|
| 50 |
+ name = params[0] |
|
| 51 |
+ arg = params[1:] |
|
| 52 |
+ ) |
|
| 53 |
+ aname, err := exec.LookPath(name) |
|
| 54 |
+ if err != nil {
|
|
| 55 |
+ aname = name |
|
| 56 |
+ } |
|
| 57 |
+ c.Path = aname |
|
| 58 |
+ c.Args = append([]string{name}, arg...)
|
|
| 59 |
+ |
|
| 60 |
+ if err := c.Start(); err != nil {
|
|
| 61 |
+ return -1, err |
|
| 62 |
+ } |
|
| 63 |
+ |
|
| 64 |
+ if startCallback != nil {
|
|
| 65 |
+ startCallback(c) |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ err = c.Wait() |
|
| 69 |
+ return c.GetExitCode(), err |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+func (d *driver) Kill(p *execdriver.Process, sig int) error {
|
|
| 73 |
+ return p.Process.Kill() |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+func (d *driver) Wait(id string) error {
|
|
| 77 |
+ panic("Not Implemented")
|
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+func (d *driver) Info(id string) execdriver.Info {
|
|
| 81 |
+ panic("Not implemented")
|
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+func (d *driver) Name() string {
|
|
| 85 |
+ return fmt.Sprintf("%s-%s", DriverName, Version)
|
|
| 86 |
+} |
| 0 | 87 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,115 @@ |
| 0 |
+package execdriver |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "github.com/dotcloud/docker/cgroups" |
|
| 5 |
+ "os/exec" |
|
| 6 |
+ "syscall" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+var ( |
|
| 10 |
+ ErrNotRunning = errors.New("Process could not be started")
|
|
| 11 |
+ ErrWaitTimeoutReached = errors.New("Wait timeout reached")
|
|
| 12 |
+ ErrDriverAlreadyRegistered = errors.New("A driver already registered this docker init function")
|
|
| 13 |
+ ErrDriverNotFound = errors.New("The requested docker init has not been found")
|
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+var dockerInitFcts map[string]InitFunc |
|
| 17 |
+ |
|
| 18 |
+type ( |
|
| 19 |
+ StartCallback func(*Process) |
|
| 20 |
+ InitFunc func(i *InitArgs) error |
|
| 21 |
+) |
|
| 22 |
+ |
|
| 23 |
+func RegisterInitFunc(name string, fct InitFunc) error {
|
|
| 24 |
+ if dockerInitFcts == nil {
|
|
| 25 |
+ dockerInitFcts = make(map[string]InitFunc) |
|
| 26 |
+ } |
|
| 27 |
+ if _, ok := dockerInitFcts[name]; ok {
|
|
| 28 |
+ return ErrDriverAlreadyRegistered |
|
| 29 |
+ } |
|
| 30 |
+ dockerInitFcts[name] = fct |
|
| 31 |
+ return nil |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func GetInitFunc(name string) (InitFunc, error) {
|
|
| 35 |
+ fct, ok := dockerInitFcts[name] |
|
| 36 |
+ if !ok {
|
|
| 37 |
+ return nil, ErrDriverNotFound |
|
| 38 |
+ } |
|
| 39 |
+ return fct, nil |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+// Args provided to the init function for a driver |
|
| 43 |
+type InitArgs struct {
|
|
| 44 |
+ User string |
|
| 45 |
+ Gateway string |
|
| 46 |
+ Ip string |
|
| 47 |
+ WorkDir string |
|
| 48 |
+ Privileged bool |
|
| 49 |
+ Env []string |
|
| 50 |
+ Args []string |
|
| 51 |
+ Mtu int |
|
| 52 |
+ Driver string |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+// Driver specific information based on |
|
| 56 |
+// processes registered with the driver |
|
| 57 |
+type Info interface {
|
|
| 58 |
+ IsRunning() bool |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+type Driver interface {
|
|
| 62 |
+ Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code |
|
| 63 |
+ Kill(c *Process, sig int) error |
|
| 64 |
+ Wait(id string) error // Wait on an out of process...process - lxc ghosts TODO: Rename to reattach, reconnect |
|
| 65 |
+ Name() string // Driver name |
|
| 66 |
+ Info(id string) Info // "temporary" hack (until we move state from core to plugins) |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+// Network settings of the container |
|
| 70 |
+type Network struct {
|
|
| 71 |
+ Gateway string `json:"gateway"` |
|
| 72 |
+ IPAddress string `json:"ip"` |
|
| 73 |
+ Bridge string `json:"bridge"` |
|
| 74 |
+ IPPrefixLen int `json:"ip_prefix_len"` |
|
| 75 |
+ Mtu int `json:"mtu"` |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+// Process wrapps an os/exec.Cmd to add more metadata |
|
| 79 |
+// TODO: Rename to Command |
|
| 80 |
+type Process struct {
|
|
| 81 |
+ exec.Cmd |
|
| 82 |
+ |
|
| 83 |
+ ID string `json:"id"` |
|
| 84 |
+ Privileged bool `json:"privileged"` |
|
| 85 |
+ User string `json:"user"` |
|
| 86 |
+ Rootfs string `json:"rootfs"` // root fs of the container |
|
| 87 |
+ InitPath string `json:"initpath"` // dockerinit |
|
| 88 |
+ Entrypoint string `json:"entrypoint"` |
|
| 89 |
+ Arguments []string `json:"arguments"` |
|
| 90 |
+ WorkingDir string `json:"working_dir"` |
|
| 91 |
+ ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver |
|
| 92 |
+ Tty bool `json:"tty"` |
|
| 93 |
+ Network *Network `json:"network"` // if network is nil then networking is disabled |
|
| 94 |
+ Config []string `json:"config"` // generic values that specific drivers can consume |
|
| 95 |
+ Cgroups *cgroups.Values `json:"cgroups"` |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// Return the pid of the process |
|
| 99 |
+// If the process is nil -1 will be returned |
|
| 100 |
+func (c *Process) Pid() int {
|
|
| 101 |
+ if c.Process == nil {
|
|
| 102 |
+ return -1 |
|
| 103 |
+ } |
|
| 104 |
+ return c.Process.Pid |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+// Return the exit code of the process |
|
| 108 |
+// if the process has not exited -1 will be returned |
|
| 109 |
+func (c *Process) GetExitCode() int {
|
|
| 110 |
+ if c.ProcessState == nil {
|
|
| 111 |
+ return -1 |
|
| 112 |
+ } |
|
| 113 |
+ return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() |
|
| 114 |
+} |
| 0 | 115 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,323 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/execdriver" |
|
| 5 |
+ "github.com/dotcloud/docker/utils" |
|
| 6 |
+ "io/ioutil" |
|
| 7 |
+ "log" |
|
| 8 |
+ "os" |
|
| 9 |
+ "os/exec" |
|
| 10 |
+ "path" |
|
| 11 |
+ "strconv" |
|
| 12 |
+ "strings" |
|
| 13 |
+ "syscall" |
|
| 14 |
+ "time" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+const DriverName = "lxc" |
|
| 18 |
+ |
|
| 19 |
+func init() {
|
|
| 20 |
+ execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
|
| 21 |
+ if err := setupHostname(args); err != nil {
|
|
| 22 |
+ return err |
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ if err := setupNetworking(args); err != nil {
|
|
| 26 |
+ return err |
|
| 27 |
+ } |
|
| 28 |
+ |
|
| 29 |
+ if err := setupCapabilities(args); err != nil {
|
|
| 30 |
+ return err |
|
| 31 |
+ } |
|
| 32 |
+ if err := setupWorkingDirectory(args); err != nil {
|
|
| 33 |
+ return err |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ if err := changeUser(args); err != nil {
|
|
| 37 |
+ return err |
|
| 38 |
+ } |
|
| 39 |
+ path, err := exec.LookPath(args.Args[0]) |
|
| 40 |
+ if err != nil {
|
|
| 41 |
+ log.Printf("Unable to locate %v", args.Args[0])
|
|
| 42 |
+ os.Exit(127) |
|
| 43 |
+ } |
|
| 44 |
+ if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
|
|
| 45 |
+ return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
|
|
| 46 |
+ } |
|
| 47 |
+ panic("Unreachable")
|
|
| 48 |
+ }) |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+type driver struct {
|
|
| 52 |
+ root string // root path for the driver to use |
|
| 53 |
+ apparmor bool |
|
| 54 |
+ sharedRoot bool |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+func NewDriver(root string, apparmor bool) (*driver, error) {
|
|
| 58 |
+ // setup unconfined symlink |
|
| 59 |
+ if err := linkLxcStart(root); err != nil {
|
|
| 60 |
+ return nil, err |
|
| 61 |
+ } |
|
| 62 |
+ return &driver{
|
|
| 63 |
+ apparmor: apparmor, |
|
| 64 |
+ root: root, |
|
| 65 |
+ sharedRoot: rootIsShared(), |
|
| 66 |
+ }, nil |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+func (d *driver) Name() string {
|
|
| 70 |
+ version := d.version() |
|
| 71 |
+ return fmt.Sprintf("%s-%s", DriverName, version)
|
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) {
|
|
| 75 |
+ configPath, err := d.generateLXCConfig(c) |
|
| 76 |
+ if err != nil {
|
|
| 77 |
+ return -1, err |
|
| 78 |
+ } |
|
| 79 |
+ params := []string{
|
|
| 80 |
+ "lxc-start", |
|
| 81 |
+ "-n", c.ID, |
|
| 82 |
+ "-f", configPath, |
|
| 83 |
+ "--", |
|
| 84 |
+ c.InitPath, |
|
| 85 |
+ "-driver", |
|
| 86 |
+ DriverName, |
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ if c.Network != nil {
|
|
| 90 |
+ params = append(params, |
|
| 91 |
+ "-g", c.Network.Gateway, |
|
| 92 |
+ "-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
|
|
| 93 |
+ "-mtu", strconv.Itoa(c.Network.Mtu), |
|
| 94 |
+ ) |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ if c.User != "" {
|
|
| 98 |
+ params = append(params, "-u", c.User) |
|
| 99 |
+ } |
|
| 100 |
+ |
|
| 101 |
+ if c.Privileged {
|
|
| 102 |
+ if d.apparmor {
|
|
| 103 |
+ params[0] = path.Join(d.root, "lxc-start-unconfined") |
|
| 104 |
+ |
|
| 105 |
+ } |
|
| 106 |
+ params = append(params, "-privileged") |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ if c.WorkingDir != "" {
|
|
| 110 |
+ params = append(params, "-w", c.WorkingDir) |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 113 |
+ if d.sharedRoot {
|
|
| 114 |
+ // lxc-start really needs / to be non-shared, or all kinds of stuff break |
|
| 115 |
+ // when lxc-start unmount things and those unmounts propagate to the main |
|
| 116 |
+ // mount namespace. |
|
| 117 |
+ // What we really want is to clone into a new namespace and then |
|
| 118 |
+ // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork |
|
| 119 |
+ // without exec in go we have to do this horrible shell hack... |
|
| 120 |
+ shellString := |
|
| 121 |
+ "mount --make-rslave /; exec " + |
|
| 122 |
+ utils.ShellQuoteArguments(params) |
|
| 123 |
+ |
|
| 124 |
+ params = []string{
|
|
| 125 |
+ "unshare", "-m", "--", "/bin/sh", "-c", shellString, |
|
| 126 |
+ } |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ params = append(params, "--", c.Entrypoint) |
|
| 130 |
+ params = append(params, c.Arguments...) |
|
| 131 |
+ |
|
| 132 |
+ var ( |
|
| 133 |
+ name = params[0] |
|
| 134 |
+ arg = params[1:] |
|
| 135 |
+ ) |
|
| 136 |
+ aname, err := exec.LookPath(name) |
|
| 137 |
+ if err != nil {
|
|
| 138 |
+ aname = name |
|
| 139 |
+ } |
|
| 140 |
+ c.Path = aname |
|
| 141 |
+ c.Args = append([]string{name}, arg...)
|
|
| 142 |
+ |
|
| 143 |
+ if err := c.Start(); err != nil {
|
|
| 144 |
+ return -1, err |
|
| 145 |
+ } |
|
| 146 |
+ |
|
| 147 |
+ var ( |
|
| 148 |
+ waitErr error |
|
| 149 |
+ waitLock = make(chan struct{})
|
|
| 150 |
+ ) |
|
| 151 |
+ go func() {
|
|
| 152 |
+ if err := c.Wait(); err != nil {
|
|
| 153 |
+ waitErr = err |
|
| 154 |
+ } |
|
| 155 |
+ close(waitLock) |
|
| 156 |
+ }() |
|
| 157 |
+ |
|
| 158 |
+ // Poll lxc for RUNNING status |
|
| 159 |
+ if err := d.waitForStart(c, waitLock); err != nil {
|
|
| 160 |
+ return -1, err |
|
| 161 |
+ } |
|
| 162 |
+ |
|
| 163 |
+ if startCallback != nil {
|
|
| 164 |
+ startCallback(c) |
|
| 165 |
+ } |
|
| 166 |
+ |
|
| 167 |
+ <-waitLock |
|
| 168 |
+ |
|
| 169 |
+ return c.GetExitCode(), waitErr |
|
| 170 |
+} |
|
| 171 |
+ |
|
| 172 |
+func (d *driver) Kill(c *execdriver.Process, sig int) error {
|
|
| 173 |
+ return d.kill(c, sig) |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+func (d *driver) Wait(id string) error {
|
|
| 177 |
+ for {
|
|
| 178 |
+ output, err := exec.Command("lxc-info", "-n", id).CombinedOutput()
|
|
| 179 |
+ if err != nil {
|
|
| 180 |
+ return err |
|
| 181 |
+ } |
|
| 182 |
+ if !strings.Contains(string(output), "RUNNING") {
|
|
| 183 |
+ return nil |
|
| 184 |
+ } |
|
| 185 |
+ time.Sleep(500 * time.Millisecond) |
|
| 186 |
+ } |
|
| 187 |
+} |
|
| 188 |
+ |
|
| 189 |
+func (d *driver) version() string {
|
|
| 190 |
+ version := "" |
|
| 191 |
+ if output, err := exec.Command("lxc-version").CombinedOutput(); err == nil {
|
|
| 192 |
+ outputStr := string(output) |
|
| 193 |
+ if len(strings.SplitN(outputStr, ":", 2)) == 2 {
|
|
| 194 |
+ version = strings.TrimSpace(strings.SplitN(outputStr, ":", 2)[1]) |
|
| 195 |
+ } |
|
| 196 |
+ } |
|
| 197 |
+ return version |
|
| 198 |
+} |
|
| 199 |
+ |
|
| 200 |
+func (d *driver) kill(c *execdriver.Process, sig int) error {
|
|
| 201 |
+ output, err := exec.Command("lxc-kill", "-n", c.ID, strconv.Itoa(sig)).CombinedOutput()
|
|
| 202 |
+ if err != nil {
|
|
| 203 |
+ return fmt.Errorf("Err: %s Output: %s", err, output)
|
|
| 204 |
+ } |
|
| 205 |
+ return nil |
|
| 206 |
+} |
|
| 207 |
+ |
|
| 208 |
+func (d *driver) waitForStart(c *execdriver.Process, waitLock chan struct{}) error {
|
|
| 209 |
+ var ( |
|
| 210 |
+ err error |
|
| 211 |
+ output []byte |
|
| 212 |
+ ) |
|
| 213 |
+ // We wait for the container to be fully running. |
|
| 214 |
+ // Timeout after 5 seconds. In case of broken pipe, just retry. |
|
| 215 |
+ // Note: The container can run and finish correctly before |
|
| 216 |
+ // the end of this loop |
|
| 217 |
+ for now := time.Now(); time.Since(now) < 5*time.Second; {
|
|
| 218 |
+ select {
|
|
| 219 |
+ case <-waitLock: |
|
| 220 |
+ // If the process dies while waiting for it, just return |
|
| 221 |
+ return nil |
|
| 222 |
+ if c.ProcessState != nil && c.ProcessState.Exited() {
|
|
| 223 |
+ return nil |
|
| 224 |
+ } |
|
| 225 |
+ default: |
|
| 226 |
+ } |
|
| 227 |
+ |
|
| 228 |
+ output, err = d.getInfo(c.ID) |
|
| 229 |
+ if err != nil {
|
|
| 230 |
+ output, err = d.getInfo(c.ID) |
|
| 231 |
+ if err != nil {
|
|
| 232 |
+ return err |
|
| 233 |
+ } |
|
| 234 |
+ } |
|
| 235 |
+ if strings.Contains(string(output), "RUNNING") {
|
|
| 236 |
+ return nil |
|
| 237 |
+ } |
|
| 238 |
+ time.Sleep(50 * time.Millisecond) |
|
| 239 |
+ } |
|
| 240 |
+ return execdriver.ErrNotRunning |
|
| 241 |
+} |
|
| 242 |
+ |
|
| 243 |
+func (d *driver) getInfo(id string) ([]byte, error) {
|
|
| 244 |
+ return exec.Command("lxc-info", "-s", "-n", id).CombinedOutput()
|
|
| 245 |
+} |
|
| 246 |
+ |
|
| 247 |
+type info struct {
|
|
| 248 |
+ ID string |
|
| 249 |
+ driver *driver |
|
| 250 |
+} |
|
| 251 |
+ |
|
| 252 |
+func (i *info) IsRunning() bool {
|
|
| 253 |
+ var running bool |
|
| 254 |
+ |
|
| 255 |
+ output, err := i.driver.getInfo(i.ID) |
|
| 256 |
+ if err != nil {
|
|
| 257 |
+ panic(err) |
|
| 258 |
+ } |
|
| 259 |
+ if strings.Contains(string(output), "RUNNING") {
|
|
| 260 |
+ running = true |
|
| 261 |
+ } |
|
| 262 |
+ return running |
|
| 263 |
+} |
|
| 264 |
+ |
|
| 265 |
+func (d *driver) Info(id string) execdriver.Info {
|
|
| 266 |
+ return &info{
|
|
| 267 |
+ ID: id, |
|
| 268 |
+ driver: d, |
|
| 269 |
+ } |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+func linkLxcStart(root string) error {
|
|
| 273 |
+ sourcePath, err := exec.LookPath("lxc-start")
|
|
| 274 |
+ if err != nil {
|
|
| 275 |
+ return err |
|
| 276 |
+ } |
|
| 277 |
+ targetPath := path.Join(root, "lxc-start-unconfined") |
|
| 278 |
+ |
|
| 279 |
+ if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
|
|
| 280 |
+ return err |
|
| 281 |
+ } else if err == nil {
|
|
| 282 |
+ if err := os.Remove(targetPath); err != nil {
|
|
| 283 |
+ return err |
|
| 284 |
+ } |
|
| 285 |
+ } |
|
| 286 |
+ return os.Symlink(sourcePath, targetPath) |
|
| 287 |
+} |
|
| 288 |
+ |
|
| 289 |
+// TODO: This can be moved to the mountinfo reader in the mount pkg |
|
| 290 |
+func rootIsShared() bool {
|
|
| 291 |
+ if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
|
|
| 292 |
+ for _, line := range strings.Split(string(data), "\n") {
|
|
| 293 |
+ cols := strings.Split(line, " ") |
|
| 294 |
+ if len(cols) >= 6 && cols[4] == "/" {
|
|
| 295 |
+ return strings.HasPrefix(cols[6], "shared") |
|
| 296 |
+ } |
|
| 297 |
+ } |
|
| 298 |
+ } |
|
| 299 |
+ |
|
| 300 |
+ // No idea, probably safe to assume so |
|
| 301 |
+ return true |
|
| 302 |
+} |
|
| 303 |
+ |
|
| 304 |
+func (d *driver) generateLXCConfig(p *execdriver.Process) (string, error) {
|
|
| 305 |
+ root := path.Join(d.root, "containers", p.ID, "config.lxc") |
|
| 306 |
+ fo, err := os.Create(root) |
|
| 307 |
+ if err != nil {
|
|
| 308 |
+ return "", err |
|
| 309 |
+ } |
|
| 310 |
+ defer fo.Close() |
|
| 311 |
+ |
|
| 312 |
+ if err := LxcTemplateCompiled.Execute(fo, struct {
|
|
| 313 |
+ *execdriver.Process |
|
| 314 |
+ AppArmor bool |
|
| 315 |
+ }{
|
|
| 316 |
+ Process: p, |
|
| 317 |
+ AppArmor: d.apparmor, |
|
| 318 |
+ }); err != nil {
|
|
| 319 |
+ return "", err |
|
| 320 |
+ } |
|
| 321 |
+ return root, nil |
|
| 322 |
+} |
| 0 | 323 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,153 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/execdriver" |
|
| 5 |
+ "github.com/dotcloud/docker/pkg/netlink" |
|
| 6 |
+ "github.com/dotcloud/docker/utils" |
|
| 7 |
+ "github.com/syndtr/gocapability/capability" |
|
| 8 |
+ "net" |
|
| 9 |
+ "os" |
|
| 10 |
+ "strconv" |
|
| 11 |
+ "strings" |
|
| 12 |
+ "syscall" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+func setupHostname(args *execdriver.InitArgs) error {
|
|
| 16 |
+ hostname := getEnv(args, "HOSTNAME") |
|
| 17 |
+ if hostname == "" {
|
|
| 18 |
+ return nil |
|
| 19 |
+ } |
|
| 20 |
+ return setHostname(hostname) |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// Setup networking |
|
| 24 |
+func setupNetworking(args *execdriver.InitArgs) error {
|
|
| 25 |
+ if args.Ip != "" {
|
|
| 26 |
+ // eth0 |
|
| 27 |
+ iface, err := net.InterfaceByName("eth0")
|
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 30 |
+ } |
|
| 31 |
+ ip, ipNet, err := net.ParseCIDR(args.Ip) |
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 34 |
+ } |
|
| 35 |
+ if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
|
| 36 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 37 |
+ } |
|
| 38 |
+ if err := netlink.NetworkSetMTU(iface, args.Mtu); err != nil {
|
|
| 39 |
+ return fmt.Errorf("Unable to set MTU: %v", err)
|
|
| 40 |
+ } |
|
| 41 |
+ if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 42 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ // loopback |
|
| 46 |
+ iface, err = net.InterfaceByName("lo")
|
|
| 47 |
+ if err != nil {
|
|
| 48 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 49 |
+ } |
|
| 50 |
+ if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 51 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 52 |
+ } |
|
| 53 |
+ } |
|
| 54 |
+ if args.Gateway != "" {
|
|
| 55 |
+ gw := net.ParseIP(args.Gateway) |
|
| 56 |
+ if gw == nil {
|
|
| 57 |
+ return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.Gateway)
|
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ if err := netlink.AddDefaultGw(gw); err != nil {
|
|
| 61 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 62 |
+ } |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ return nil |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// Setup working directory |
|
| 69 |
+func setupWorkingDirectory(args *execdriver.InitArgs) error {
|
|
| 70 |
+ if args.WorkDir == "" {
|
|
| 71 |
+ return nil |
|
| 72 |
+ } |
|
| 73 |
+ if err := syscall.Chdir(args.WorkDir); err != nil {
|
|
| 74 |
+ return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err)
|
|
| 75 |
+ } |
|
| 76 |
+ return nil |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Takes care of dropping privileges to the desired user |
|
| 80 |
+func changeUser(args *execdriver.InitArgs) error {
|
|
| 81 |
+ if args.User == "" {
|
|
| 82 |
+ return nil |
|
| 83 |
+ } |
|
| 84 |
+ userent, err := utils.UserLookup(args.User) |
|
| 85 |
+ if err != nil {
|
|
| 86 |
+ return fmt.Errorf("Unable to find user %v: %v", args.User, err)
|
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ uid, err := strconv.Atoi(userent.Uid) |
|
| 90 |
+ if err != nil {
|
|
| 91 |
+ return fmt.Errorf("Invalid uid: %v", userent.Uid)
|
|
| 92 |
+ } |
|
| 93 |
+ gid, err := strconv.Atoi(userent.Gid) |
|
| 94 |
+ if err != nil {
|
|
| 95 |
+ return fmt.Errorf("Invalid gid: %v", userent.Gid)
|
|
| 96 |
+ } |
|
| 97 |
+ |
|
| 98 |
+ if err := syscall.Setgid(gid); err != nil {
|
|
| 99 |
+ return fmt.Errorf("setgid failed: %v", err)
|
|
| 100 |
+ } |
|
| 101 |
+ if err := syscall.Setuid(uid); err != nil {
|
|
| 102 |
+ return fmt.Errorf("setuid failed: %v", err)
|
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 105 |
+ return nil |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+func setupCapabilities(args *execdriver.InitArgs) error {
|
|
| 109 |
+ |
|
| 110 |
+ if args.Privileged {
|
|
| 111 |
+ return nil |
|
| 112 |
+ } |
|
| 113 |
+ |
|
| 114 |
+ drop := []capability.Cap{
|
|
| 115 |
+ capability.CAP_SETPCAP, |
|
| 116 |
+ capability.CAP_SYS_MODULE, |
|
| 117 |
+ capability.CAP_SYS_RAWIO, |
|
| 118 |
+ capability.CAP_SYS_PACCT, |
|
| 119 |
+ capability.CAP_SYS_ADMIN, |
|
| 120 |
+ capability.CAP_SYS_NICE, |
|
| 121 |
+ capability.CAP_SYS_RESOURCE, |
|
| 122 |
+ capability.CAP_SYS_TIME, |
|
| 123 |
+ capability.CAP_SYS_TTY_CONFIG, |
|
| 124 |
+ capability.CAP_MKNOD, |
|
| 125 |
+ capability.CAP_AUDIT_WRITE, |
|
| 126 |
+ capability.CAP_AUDIT_CONTROL, |
|
| 127 |
+ capability.CAP_MAC_OVERRIDE, |
|
| 128 |
+ capability.CAP_MAC_ADMIN, |
|
| 129 |
+ } |
|
| 130 |
+ |
|
| 131 |
+ c, err := capability.NewPid(os.Getpid()) |
|
| 132 |
+ if err != nil {
|
|
| 133 |
+ return err |
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ c.Unset(capability.CAPS|capability.BOUNDS, drop...) |
|
| 137 |
+ |
|
| 138 |
+ if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
|
|
| 139 |
+ return err |
|
| 140 |
+ } |
|
| 141 |
+ return nil |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+func getEnv(args *execdriver.InitArgs, key string) string {
|
|
| 145 |
+ for _, kv := range args.Env {
|
|
| 146 |
+ parts := strings.SplitN(kv, "=", 2) |
|
| 147 |
+ if parts[0] == key && len(parts) == 2 {
|
|
| 148 |
+ return parts[1] |
|
| 149 |
+ } |
|
| 150 |
+ } |
|
| 151 |
+ return "" |
|
| 152 |
+} |
| 0 | 9 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,141 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/cgroups" |
|
| 4 |
+ "strings" |
|
| 5 |
+ "text/template" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+const LxcTemplate = ` |
|
| 9 |
+{{if .Network}}
|
|
| 10 |
+# network configuration |
|
| 11 |
+lxc.network.type = veth |
|
| 12 |
+lxc.network.link = {{.Network.Bridge}}
|
|
| 13 |
+lxc.network.name = eth0 |
|
| 14 |
+{{else}}
|
|
| 15 |
+# network is disabled (-n=false) |
|
| 16 |
+lxc.network.type = empty |
|
| 17 |
+{{end}}
|
|
| 18 |
+ |
|
| 19 |
+# root filesystem |
|
| 20 |
+{{$ROOTFS := .Rootfs}}
|
|
| 21 |
+lxc.rootfs = {{$ROOTFS}}
|
|
| 22 |
+ |
|
| 23 |
+# use a dedicated pts for the container (and limit the number of pseudo terminal |
|
| 24 |
+# available) |
|
| 25 |
+lxc.pts = 1024 |
|
| 26 |
+ |
|
| 27 |
+# disable the main console |
|
| 28 |
+lxc.console = none |
|
| 29 |
+ |
|
| 30 |
+# no controlling tty at all |
|
| 31 |
+lxc.tty = 1 |
|
| 32 |
+ |
|
| 33 |
+{{if .Privileged}}
|
|
| 34 |
+lxc.cgroup.devices.allow = a |
|
| 35 |
+{{else}}
|
|
| 36 |
+# no implicit access to devices |
|
| 37 |
+lxc.cgroup.devices.deny = a |
|
| 38 |
+ |
|
| 39 |
+# /dev/null and zero |
|
| 40 |
+lxc.cgroup.devices.allow = c 1:3 rwm |
|
| 41 |
+lxc.cgroup.devices.allow = c 1:5 rwm |
|
| 42 |
+ |
|
| 43 |
+# consoles |
|
| 44 |
+lxc.cgroup.devices.allow = c 5:1 rwm |
|
| 45 |
+lxc.cgroup.devices.allow = c 5:0 rwm |
|
| 46 |
+lxc.cgroup.devices.allow = c 4:0 rwm |
|
| 47 |
+lxc.cgroup.devices.allow = c 4:1 rwm |
|
| 48 |
+ |
|
| 49 |
+# /dev/urandom,/dev/random |
|
| 50 |
+lxc.cgroup.devices.allow = c 1:9 rwm |
|
| 51 |
+lxc.cgroup.devices.allow = c 1:8 rwm |
|
| 52 |
+ |
|
| 53 |
+# /dev/pts/ - pts namespaces are "coming soon" |
|
| 54 |
+lxc.cgroup.devices.allow = c 136:* rwm |
|
| 55 |
+lxc.cgroup.devices.allow = c 5:2 rwm |
|
| 56 |
+ |
|
| 57 |
+# tuntap |
|
| 58 |
+lxc.cgroup.devices.allow = c 10:200 rwm |
|
| 59 |
+ |
|
| 60 |
+# fuse |
|
| 61 |
+#lxc.cgroup.devices.allow = c 10:229 rwm |
|
| 62 |
+ |
|
| 63 |
+# rtc |
|
| 64 |
+#lxc.cgroup.devices.allow = c 254:0 rwm |
|
| 65 |
+{{end}}
|
|
| 66 |
+ |
|
| 67 |
+# standard mount point |
|
| 68 |
+# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385 |
|
| 69 |
+lxc.pivotdir = lxc_putold |
|
| 70 |
+ |
|
| 71 |
+# NOTICE: These mounts must be applied within the namespace |
|
| 72 |
+ |
|
| 73 |
+# WARNING: procfs is a known attack vector and should probably be disabled |
|
| 74 |
+# if your userspace allows it. eg. see http://blog.zx2c4.com/749 |
|
| 75 |
+lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
|
|
| 76 |
+ |
|
| 77 |
+# WARNING: sysfs is a known attack vector and should probably be disabled |
|
| 78 |
+# if your userspace allows it. eg. see http://bit.ly/T9CkqJ |
|
| 79 |
+lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
|
|
| 80 |
+ |
|
| 81 |
+lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
|
|
| 82 |
+lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
|
|
| 83 |
+ |
|
| 84 |
+{{if .Privileged}}
|
|
| 85 |
+{{if .AppArmor}}
|
|
| 86 |
+lxc.aa_profile = unconfined |
|
| 87 |
+{{else}}
|
|
| 88 |
+#lxc.aa_profile = unconfined |
|
| 89 |
+{{end}}
|
|
| 90 |
+{{end}}
|
|
| 91 |
+ |
|
| 92 |
+# limits |
|
| 93 |
+{{if .Cgroups}}
|
|
| 94 |
+{{if .Cgroups.Memory}}
|
|
| 95 |
+lxc.cgroup.memory.limit_in_bytes = {{.Cgroups.Memory}}
|
|
| 96 |
+lxc.cgroup.memory.soft_limit_in_bytes = {{.Cgroups.Memory}}
|
|
| 97 |
+{{with $memSwap := getMemorySwap .Cgroups}}
|
|
| 98 |
+lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
|
|
| 99 |
+{{end}}
|
|
| 100 |
+{{end}}
|
|
| 101 |
+{{if .Cgroups.CpuShares}}
|
|
| 102 |
+lxc.cgroup.cpu.shares = {{.Cgroups.CpuShares}}
|
|
| 103 |
+{{end}}
|
|
| 104 |
+{{end}}
|
|
| 105 |
+ |
|
| 106 |
+{{if .Config}}
|
|
| 107 |
+{{range $value := .Config}}
|
|
| 108 |
+{{$value}}
|
|
| 109 |
+{{end}}
|
|
| 110 |
+{{end}}
|
|
| 111 |
+` |
|
| 112 |
+ |
|
| 113 |
+var LxcTemplateCompiled *template.Template |
|
| 114 |
+ |
|
| 115 |
+// Escape spaces in strings according to the fstab documentation, which is the |
|
| 116 |
+// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab". |
|
| 117 |
+func escapeFstabSpaces(field string) string {
|
|
| 118 |
+ return strings.Replace(field, " ", "\\040", -1) |
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+func getMemorySwap(v *cgroups.Values) int64 {
|
|
| 122 |
+ // By default, MemorySwap is set to twice the size of RAM. |
|
| 123 |
+ // If you want to omit MemorySwap, set it to `-1'. |
|
| 124 |
+ if v.MemorySwap < 0 {
|
|
| 125 |
+ return 0 |
|
| 126 |
+ } |
|
| 127 |
+ return v.Memory * 2 |
|
| 128 |
+} |
|
| 129 |
+ |
|
| 130 |
+func init() {
|
|
| 131 |
+ var err error |
|
| 132 |
+ funcMap := template.FuncMap{
|
|
| 133 |
+ "getMemorySwap": getMemorySwap, |
|
| 134 |
+ "escapeFstabSpaces": escapeFstabSpaces, |
|
| 135 |
+ } |
|
| 136 |
+ LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
|
|
| 137 |
+ if err != nil {
|
|
| 138 |
+ panic(err) |
|
| 139 |
+ } |
|
| 140 |
+} |
| 0 | 141 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,126 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "github.com/dotcloud/docker/cgroups" |
|
| 6 |
+ "github.com/dotcloud/docker/execdriver" |
|
| 7 |
+ "io/ioutil" |
|
| 8 |
+ "math/rand" |
|
| 9 |
+ "os" |
|
| 10 |
+ "path" |
|
| 11 |
+ "strings" |
|
| 12 |
+ "testing" |
|
| 13 |
+ "time" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+func TestLXCConfig(t *testing.T) {
|
|
| 17 |
+ root, err := ioutil.TempDir("", "TestLXCConfig")
|
|
| 18 |
+ if err != nil {
|
|
| 19 |
+ t.Fatal(err) |
|
| 20 |
+ } |
|
| 21 |
+ defer os.RemoveAll(root) |
|
| 22 |
+ |
|
| 23 |
+ os.MkdirAll(path.Join(root, "containers", "1"), 0777) |
|
| 24 |
+ |
|
| 25 |
+ // Memory is allocated randomly for testing |
|
| 26 |
+ rand.Seed(time.Now().UTC().UnixNano()) |
|
| 27 |
+ var ( |
|
| 28 |
+ memMin = 33554432 |
|
| 29 |
+ memMax = 536870912 |
|
| 30 |
+ mem = memMin + rand.Intn(memMax-memMin) |
|
| 31 |
+ cpuMin = 100 |
|
| 32 |
+ cpuMax = 10000 |
|
| 33 |
+ cpu = cpuMin + rand.Intn(cpuMax-cpuMin) |
|
| 34 |
+ ) |
|
| 35 |
+ |
|
| 36 |
+ driver, err := NewDriver(root, false) |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ t.Fatal(err) |
|
| 39 |
+ } |
|
| 40 |
+ process := &execdriver.Process{
|
|
| 41 |
+ ID: "1", |
|
| 42 |
+ Cgroups: &cgroups.Values{
|
|
| 43 |
+ Memory: int64(mem), |
|
| 44 |
+ CpuShares: int64(cpu), |
|
| 45 |
+ }, |
|
| 46 |
+ } |
|
| 47 |
+ p, err := driver.generateLXCConfig(process) |
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ t.Fatal(err) |
|
| 50 |
+ } |
|
| 51 |
+ grepFile(t, p, |
|
| 52 |
+ fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
|
|
| 53 |
+ |
|
| 54 |
+ grepFile(t, p, |
|
| 55 |
+ fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
|
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func TestCustomLxcConfig(t *testing.T) {
|
|
| 59 |
+ root, err := ioutil.TempDir("", "TestCustomLxcConfig")
|
|
| 60 |
+ if err != nil {
|
|
| 61 |
+ t.Fatal(err) |
|
| 62 |
+ } |
|
| 63 |
+ defer os.RemoveAll(root) |
|
| 64 |
+ |
|
| 65 |
+ os.MkdirAll(path.Join(root, "containers", "1"), 0777) |
|
| 66 |
+ |
|
| 67 |
+ driver, err := NewDriver(root, false) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ t.Fatal(err) |
|
| 70 |
+ } |
|
| 71 |
+ process := &execdriver.Process{
|
|
| 72 |
+ ID: "1", |
|
| 73 |
+ Privileged: false, |
|
| 74 |
+ Config: []string{
|
|
| 75 |
+ "lxc.utsname = docker", |
|
| 76 |
+ "lxc.cgroup.cpuset.cpus = 0,1", |
|
| 77 |
+ }, |
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 80 |
+ p, err := driver.generateLXCConfig(process) |
|
| 81 |
+ if err != nil {
|
|
| 82 |
+ t.Fatal(err) |
|
| 83 |
+ } |
|
| 84 |
+ |
|
| 85 |
+ grepFile(t, p, "lxc.utsname = docker") |
|
| 86 |
+ grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1") |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+func grepFile(t *testing.T, path string, pattern string) {
|
|
| 90 |
+ f, err := os.Open(path) |
|
| 91 |
+ if err != nil {
|
|
| 92 |
+ t.Fatal(err) |
|
| 93 |
+ } |
|
| 94 |
+ defer f.Close() |
|
| 95 |
+ r := bufio.NewReader(f) |
|
| 96 |
+ var ( |
|
| 97 |
+ line string |
|
| 98 |
+ ) |
|
| 99 |
+ err = nil |
|
| 100 |
+ for err == nil {
|
|
| 101 |
+ line, err = r.ReadString('\n')
|
|
| 102 |
+ if strings.Contains(line, pattern) == true {
|
|
| 103 |
+ return |
|
| 104 |
+ } |
|
| 105 |
+ } |
|
| 106 |
+ t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
|
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+func TestEscapeFstabSpaces(t *testing.T) {
|
|
| 110 |
+ var testInputs = map[string]string{
|
|
| 111 |
+ " ": "\\040", |
|
| 112 |
+ "": "", |
|
| 113 |
+ "/double space": "/double\\040\\040space", |
|
| 114 |
+ "/some long test string": "/some\\040long\\040test\\040string", |
|
| 115 |
+ "/var/lib/docker": "/var/lib/docker", |
|
| 116 |
+ " leading": "\\040leading", |
|
| 117 |
+ "trailing ": "trailing\\040", |
|
| 118 |
+ } |
|
| 119 |
+ for in, exp := range testInputs {
|
|
| 120 |
+ if out := escapeFstabSpaces(in); exp != out {
|
|
| 121 |
+ t.Logf("Expected %s got %s", exp, out)
|
|
| 122 |
+ t.Fail() |
|
| 123 |
+ } |
|
| 124 |
+ } |
|
| 125 |
+} |
| 45 | 44 |
deleted file mode 100644 |
| ... | ... |
@@ -1,148 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "strings" |
|
| 5 |
- "text/template" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-const LxcTemplate = ` |
|
| 9 |
-{{if .Config.NetworkDisabled}}
|
|
| 10 |
-# network is disabled (-n=false) |
|
| 11 |
-lxc.network.type = empty |
|
| 12 |
-{{else}}
|
|
| 13 |
-# network configuration |
|
| 14 |
-lxc.network.type = veth |
|
| 15 |
-lxc.network.link = {{.NetworkSettings.Bridge}}
|
|
| 16 |
-lxc.network.name = eth0 |
|
| 17 |
-{{end}}
|
|
| 18 |
- |
|
| 19 |
-# root filesystem |
|
| 20 |
-{{$ROOTFS := .RootfsPath}}
|
|
| 21 |
-lxc.rootfs = {{$ROOTFS}}
|
|
| 22 |
- |
|
| 23 |
-# use a dedicated pts for the container (and limit the number of pseudo terminal |
|
| 24 |
-# available) |
|
| 25 |
-lxc.pts = 1024 |
|
| 26 |
- |
|
| 27 |
-# disable the main console |
|
| 28 |
-lxc.console = none |
|
| 29 |
- |
|
| 30 |
-# no controlling tty at all |
|
| 31 |
-lxc.tty = 1 |
|
| 32 |
- |
|
| 33 |
-{{if (getHostConfig .).Privileged}}
|
|
| 34 |
-lxc.cgroup.devices.allow = a |
|
| 35 |
-{{else}}
|
|
| 36 |
-# no implicit access to devices |
|
| 37 |
-lxc.cgroup.devices.deny = a |
|
| 38 |
- |
|
| 39 |
-# /dev/null and zero |
|
| 40 |
-lxc.cgroup.devices.allow = c 1:3 rwm |
|
| 41 |
-lxc.cgroup.devices.allow = c 1:5 rwm |
|
| 42 |
- |
|
| 43 |
-# consoles |
|
| 44 |
-lxc.cgroup.devices.allow = c 5:1 rwm |
|
| 45 |
-lxc.cgroup.devices.allow = c 5:0 rwm |
|
| 46 |
-lxc.cgroup.devices.allow = c 4:0 rwm |
|
| 47 |
-lxc.cgroup.devices.allow = c 4:1 rwm |
|
| 48 |
- |
|
| 49 |
-# /dev/urandom,/dev/random |
|
| 50 |
-lxc.cgroup.devices.allow = c 1:9 rwm |
|
| 51 |
-lxc.cgroup.devices.allow = c 1:8 rwm |
|
| 52 |
- |
|
| 53 |
-# /dev/pts/ - pts namespaces are "coming soon" |
|
| 54 |
-lxc.cgroup.devices.allow = c 136:* rwm |
|
| 55 |
-lxc.cgroup.devices.allow = c 5:2 rwm |
|
| 56 |
- |
|
| 57 |
-# tuntap |
|
| 58 |
-lxc.cgroup.devices.allow = c 10:200 rwm |
|
| 59 |
- |
|
| 60 |
-# fuse |
|
| 61 |
-#lxc.cgroup.devices.allow = c 10:229 rwm |
|
| 62 |
- |
|
| 63 |
-# rtc |
|
| 64 |
-#lxc.cgroup.devices.allow = c 254:0 rwm |
|
| 65 |
-{{end}}
|
|
| 66 |
- |
|
| 67 |
-# standard mount point |
|
| 68 |
-# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385 |
|
| 69 |
-lxc.pivotdir = lxc_putold |
|
| 70 |
- |
|
| 71 |
-# NOTICE: These mounts must be applied within the namespace |
|
| 72 |
- |
|
| 73 |
-# WARNING: procfs is a known attack vector and should probably be disabled |
|
| 74 |
-# if your userspace allows it. eg. see http://blog.zx2c4.com/749 |
|
| 75 |
-lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
|
|
| 76 |
- |
|
| 77 |
-# WARNING: sysfs is a known attack vector and should probably be disabled |
|
| 78 |
-# if your userspace allows it. eg. see http://bit.ly/T9CkqJ |
|
| 79 |
-lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
|
|
| 80 |
- |
|
| 81 |
-lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
|
|
| 82 |
-lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
|
|
| 83 |
- |
|
| 84 |
-{{if (getHostConfig .).Privileged}}
|
|
| 85 |
-{{if (getCapabilities .).AppArmor}}
|
|
| 86 |
-lxc.aa_profile = unconfined |
|
| 87 |
-{{else}}
|
|
| 88 |
-#lxc.aa_profile = unconfined |
|
| 89 |
-{{end}}
|
|
| 90 |
-{{end}}
|
|
| 91 |
- |
|
| 92 |
-# limits |
|
| 93 |
-{{if .Config.Memory}}
|
|
| 94 |
-lxc.cgroup.memory.limit_in_bytes = {{.Config.Memory}}
|
|
| 95 |
-lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Memory}}
|
|
| 96 |
-{{with $memSwap := getMemorySwap .Config}}
|
|
| 97 |
-lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
|
|
| 98 |
-{{end}}
|
|
| 99 |
-{{end}}
|
|
| 100 |
-{{if .Config.CpuShares}}
|
|
| 101 |
-lxc.cgroup.cpu.shares = {{.Config.CpuShares}}
|
|
| 102 |
-{{end}}
|
|
| 103 |
- |
|
| 104 |
-{{if (getHostConfig .).LxcConf}}
|
|
| 105 |
-{{range $pair := (getHostConfig .).LxcConf}}
|
|
| 106 |
-{{$pair.Key}} = {{$pair.Value}}
|
|
| 107 |
-{{end}}
|
|
| 108 |
-{{end}}
|
|
| 109 |
-` |
|
| 110 |
- |
|
| 111 |
-var LxcTemplateCompiled *template.Template |
|
| 112 |
- |
|
| 113 |
-// Escape spaces in strings according to the fstab documentation, which is the |
|
| 114 |
-// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab". |
|
| 115 |
-func escapeFstabSpaces(field string) string {
|
|
| 116 |
- return strings.Replace(field, " ", "\\040", -1) |
|
| 117 |
-} |
|
| 118 |
- |
|
| 119 |
-func getMemorySwap(config *Config) int64 {
|
|
| 120 |
- // By default, MemorySwap is set to twice the size of RAM. |
|
| 121 |
- // If you want to omit MemorySwap, set it to `-1'. |
|
| 122 |
- if config.MemorySwap < 0 {
|
|
| 123 |
- return 0 |
|
| 124 |
- } |
|
| 125 |
- return config.Memory * 2 |
|
| 126 |
-} |
|
| 127 |
- |
|
| 128 |
-func getHostConfig(container *Container) *HostConfig {
|
|
| 129 |
- return container.hostConfig |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-func getCapabilities(container *Container) *Capabilities {
|
|
| 133 |
- return container.runtime.capabilities |
|
| 134 |
-} |
|
| 135 |
- |
|
| 136 |
-func init() {
|
|
| 137 |
- var err error |
|
| 138 |
- funcMap := template.FuncMap{
|
|
| 139 |
- "getMemorySwap": getMemorySwap, |
|
| 140 |
- "getHostConfig": getHostConfig, |
|
| 141 |
- "getCapabilities": getCapabilities, |
|
| 142 |
- "escapeFstabSpaces": escapeFstabSpaces, |
|
| 143 |
- } |
|
| 144 |
- LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
|
|
| 145 |
- if err != nil {
|
|
| 146 |
- panic(err) |
|
| 147 |
- } |
|
| 148 |
-} |
| 149 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,118 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io/ioutil" |
|
| 7 |
- "math/rand" |
|
| 8 |
- "os" |
|
| 9 |
- "strings" |
|
| 10 |
- "testing" |
|
| 11 |
- "time" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-func TestLXCConfig(t *testing.T) {
|
|
| 15 |
- root, err := ioutil.TempDir("", "TestLXCConfig")
|
|
| 16 |
- if err != nil {
|
|
| 17 |
- t.Fatal(err) |
|
| 18 |
- } |
|
| 19 |
- defer os.RemoveAll(root) |
|
| 20 |
- // Memory is allocated randomly for testing |
|
| 21 |
- rand.Seed(time.Now().UTC().UnixNano()) |
|
| 22 |
- memMin := 33554432 |
|
| 23 |
- memMax := 536870912 |
|
| 24 |
- mem := memMin + rand.Intn(memMax-memMin) |
|
| 25 |
- // CPU shares as well |
|
| 26 |
- cpuMin := 100 |
|
| 27 |
- cpuMax := 10000 |
|
| 28 |
- cpu := cpuMin + rand.Intn(cpuMax-cpuMin) |
|
| 29 |
- container := &Container{
|
|
| 30 |
- root: root, |
|
| 31 |
- Config: &Config{
|
|
| 32 |
- Memory: int64(mem), |
|
| 33 |
- CpuShares: int64(cpu), |
|
| 34 |
- NetworkDisabled: true, |
|
| 35 |
- }, |
|
| 36 |
- hostConfig: &HostConfig{
|
|
| 37 |
- Privileged: false, |
|
| 38 |
- }, |
|
| 39 |
- } |
|
| 40 |
- if err := container.generateLXCConfig(); err != nil {
|
|
| 41 |
- t.Fatal(err) |
|
| 42 |
- } |
|
| 43 |
- grepFile(t, container.lxcConfigPath(), |
|
| 44 |
- fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
|
|
| 45 |
- grepFile(t, container.lxcConfigPath(), |
|
| 46 |
- fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
|
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-func TestCustomLxcConfig(t *testing.T) {
|
|
| 50 |
- root, err := ioutil.TempDir("", "TestCustomLxcConfig")
|
|
| 51 |
- if err != nil {
|
|
| 52 |
- t.Fatal(err) |
|
| 53 |
- } |
|
| 54 |
- defer os.RemoveAll(root) |
|
| 55 |
- container := &Container{
|
|
| 56 |
- root: root, |
|
| 57 |
- Config: &Config{
|
|
| 58 |
- Hostname: "foobar", |
|
| 59 |
- NetworkDisabled: true, |
|
| 60 |
- }, |
|
| 61 |
- hostConfig: &HostConfig{
|
|
| 62 |
- Privileged: false, |
|
| 63 |
- LxcConf: []KeyValuePair{
|
|
| 64 |
- {
|
|
| 65 |
- Key: "lxc.utsname", |
|
| 66 |
- Value: "docker", |
|
| 67 |
- }, |
|
| 68 |
- {
|
|
| 69 |
- Key: "lxc.cgroup.cpuset.cpus", |
|
| 70 |
- Value: "0,1", |
|
| 71 |
- }, |
|
| 72 |
- }, |
|
| 73 |
- }, |
|
| 74 |
- } |
|
| 75 |
- if err := container.generateLXCConfig(); err != nil {
|
|
| 76 |
- t.Fatal(err) |
|
| 77 |
- } |
|
| 78 |
- grepFile(t, container.lxcConfigPath(), "lxc.utsname = docker") |
|
| 79 |
- grepFile(t, container.lxcConfigPath(), "lxc.cgroup.cpuset.cpus = 0,1") |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func grepFile(t *testing.T, path string, pattern string) {
|
|
| 83 |
- f, err := os.Open(path) |
|
| 84 |
- if err != nil {
|
|
| 85 |
- t.Fatal(err) |
|
| 86 |
- } |
|
| 87 |
- defer f.Close() |
|
| 88 |
- r := bufio.NewReader(f) |
|
| 89 |
- var ( |
|
| 90 |
- line string |
|
| 91 |
- ) |
|
| 92 |
- err = nil |
|
| 93 |
- for err == nil {
|
|
| 94 |
- line, err = r.ReadString('\n')
|
|
| 95 |
- if strings.Contains(line, pattern) == true {
|
|
| 96 |
- return |
|
| 97 |
- } |
|
| 98 |
- } |
|
| 99 |
- t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
|
|
| 100 |
-} |
|
| 101 |
- |
|
| 102 |
-func TestEscapeFstabSpaces(t *testing.T) {
|
|
| 103 |
- var testInputs = map[string]string{
|
|
| 104 |
- " ": "\\040", |
|
| 105 |
- "": "", |
|
| 106 |
- "/double space": "/double\\040\\040space", |
|
| 107 |
- "/some long test string": "/some\\040long\\040test\\040string", |
|
| 108 |
- "/var/lib/docker": "/var/lib/docker", |
|
| 109 |
- " leading": "\\040leading", |
|
| 110 |
- "trailing ": "trailing\\040", |
|
| 111 |
- } |
|
| 112 |
- for in, exp := range testInputs {
|
|
| 113 |
- if out := escapeFstabSpaces(in); exp != out {
|
|
| 114 |
- t.Logf("Expected %s got %s", exp, out)
|
|
| 115 |
- t.Fail() |
|
| 116 |
- } |
|
| 117 |
- } |
|
| 118 |
-} |
| ... | ... |
@@ -25,27 +25,37 @@ func Mounted(mountpoint string) (bool, error) {
|
| 25 | 25 |
return false, nil |
| 26 | 26 |
} |
| 27 | 27 |
|
| 28 |
-// Mount the specified options at the target path |
|
| 28 |
+// Mount the specified options at the target path only if |
|
| 29 |
+// the target is not mounted |
|
| 29 | 30 |
// Options must be specified as fstab style |
| 30 | 31 |
func Mount(device, target, mType, options string) error {
|
| 31 | 32 |
if mounted, err := Mounted(target); err != nil || mounted {
|
| 32 | 33 |
return err |
| 33 | 34 |
} |
| 35 |
+ return ForceMount(device, target, mType, options) |
|
| 36 |
+} |
|
| 34 | 37 |
|
| 38 |
+// Mount the specified options at the target path |
|
| 39 |
+// reguardless if the target is mounted or not |
|
| 40 |
+// Options must be specified as fstab style |
|
| 41 |
+func ForceMount(device, target, mType, options string) error {
|
|
| 35 | 42 |
flag, data := parseOptions(options) |
| 36 | 43 |
if err := mount(device, target, mType, uintptr(flag), data); err != nil {
|
| 37 | 44 |
return err |
| 38 | 45 |
} |
| 39 | 46 |
return nil |
| 40 |
- |
|
| 41 | 47 |
} |
| 42 | 48 |
|
| 43 | 49 |
// Unmount the target only if it is mounted |
| 44 |
-func Unmount(target string) (err error) {
|
|
| 50 |
+func Unmount(target string) error {
|
|
| 45 | 51 |
if mounted, err := Mounted(target); err != nil || !mounted {
|
| 46 | 52 |
return err |
| 47 | 53 |
} |
| 54 |
+ return ForceUnmount(target) |
|
| 55 |
+} |
|
| 48 | 56 |
|
| 57 |
+// Unmount the target reguardless if it is mounted or not |
|
| 58 |
+func ForceUnmount(target string) (err error) {
|
|
| 49 | 59 |
// Simple retry logic for unmount |
| 50 | 60 |
for i := 0; i < 10; i++ {
|
| 51 | 61 |
if err = unmount(target, 0); err == nil {
|
| 52 | 62 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,55 @@ |
| 0 |
+package sysinfo |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/cgroups" |
|
| 4 |
+ "github.com/dotcloud/docker/utils" |
|
| 5 |
+ "io/ioutil" |
|
| 6 |
+ "log" |
|
| 7 |
+ "os" |
|
| 8 |
+ "path" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+type SysInfo struct {
|
|
| 12 |
+ MemoryLimit bool |
|
| 13 |
+ SwapLimit bool |
|
| 14 |
+ IPv4ForwardingDisabled bool |
|
| 15 |
+ AppArmor bool |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func New(quiet bool) *SysInfo {
|
|
| 19 |
+ sysInfo := &SysInfo{}
|
|
| 20 |
+ if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil {
|
|
| 21 |
+ if !quiet {
|
|
| 22 |
+ log.Printf("WARNING: %s\n", err)
|
|
| 23 |
+ } |
|
| 24 |
+ } else {
|
|
| 25 |
+ _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes")) |
|
| 26 |
+ _, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes")) |
|
| 27 |
+ sysInfo.MemoryLimit = err1 == nil && err2 == nil |
|
| 28 |
+ if !sysInfo.MemoryLimit && !quiet {
|
|
| 29 |
+ log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
|
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) |
|
| 33 |
+ sysInfo.SwapLimit = err == nil |
|
| 34 |
+ if !sysInfo.SwapLimit && !quiet {
|
|
| 35 |
+ log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
|
|
| 36 |
+ } |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ content, err3 := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward")
|
|
| 40 |
+ sysInfo.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1' |
|
| 41 |
+ if sysInfo.IPv4ForwardingDisabled && !quiet {
|
|
| 42 |
+ log.Printf("WARNING: IPv4 forwarding is disabled.")
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ // Check if AppArmor seems to be enabled on this system. |
|
| 46 |
+ if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
|
|
| 47 |
+ utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.")
|
|
| 48 |
+ sysInfo.AppArmor = false |
|
| 49 |
+ } else {
|
|
| 50 |
+ utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.")
|
|
| 51 |
+ sysInfo.AppArmor = true |
|
| 52 |
+ } |
|
| 53 |
+ return sysInfo |
|
| 54 |
+} |
| ... | ... |
@@ -4,18 +4,19 @@ import ( |
| 4 | 4 |
"container/list" |
| 5 | 5 |
"fmt" |
| 6 | 6 |
"github.com/dotcloud/docker/archive" |
| 7 |
- "github.com/dotcloud/docker/cgroups" |
|
| 7 |
+ "github.com/dotcloud/docker/execdriver" |
|
| 8 |
+ "github.com/dotcloud/docker/execdriver/chroot" |
|
| 9 |
+ "github.com/dotcloud/docker/execdriver/lxc" |
|
| 8 | 10 |
"github.com/dotcloud/docker/graphdriver" |
| 9 | 11 |
"github.com/dotcloud/docker/graphdriver/aufs" |
| 10 | 12 |
_ "github.com/dotcloud/docker/graphdriver/devmapper" |
| 11 | 13 |
_ "github.com/dotcloud/docker/graphdriver/vfs" |
| 12 | 14 |
"github.com/dotcloud/docker/pkg/graphdb" |
| 15 |
+ "github.com/dotcloud/docker/pkg/sysinfo" |
|
| 13 | 16 |
"github.com/dotcloud/docker/utils" |
| 14 | 17 |
"io" |
| 15 | 18 |
"io/ioutil" |
| 16 |
- "log" |
|
| 17 | 19 |
"os" |
| 18 |
- "os/exec" |
|
| 19 | 20 |
"path" |
| 20 | 21 |
"regexp" |
| 21 | 22 |
"sort" |
| ... | ... |
@@ -35,13 +36,6 @@ var ( |
| 35 | 35 |
validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`) |
| 36 | 36 |
) |
| 37 | 37 |
|
| 38 |
-type Capabilities struct {
|
|
| 39 |
- MemoryLimit bool |
|
| 40 |
- SwapLimit bool |
|
| 41 |
- IPv4ForwardingDisabled bool |
|
| 42 |
- AppArmor bool |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 | 38 |
type Runtime struct {
|
| 46 | 39 |
repository string |
| 47 | 40 |
sysInitPath string |
| ... | ... |
@@ -50,12 +44,13 @@ type Runtime struct {
|
| 50 | 50 |
graph *Graph |
| 51 | 51 |
repositories *TagStore |
| 52 | 52 |
idIndex *utils.TruncIndex |
| 53 |
- capabilities *Capabilities |
|
| 53 |
+ sysInfo *sysinfo.SysInfo |
|
| 54 | 54 |
volumes *Graph |
| 55 | 55 |
srv *Server |
| 56 | 56 |
config *DaemonConfig |
| 57 | 57 |
containerGraph *graphdb.Database |
| 58 | 58 |
driver graphdriver.Driver |
| 59 |
+ execDriver execdriver.Driver |
|
| 59 | 60 |
} |
| 60 | 61 |
|
| 61 | 62 |
// List returns an array of all containers registered in the runtime. |
| ... | ... |
@@ -160,11 +155,9 @@ func (runtime *Runtime) Register(container *Container) error {
|
| 160 | 160 |
// if so, then we need to restart monitor and init a new lock |
| 161 | 161 |
// If the container is supposed to be running, make sure of it |
| 162 | 162 |
if container.State.IsRunning() {
|
| 163 |
- output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
|
|
| 164 |
- if err != nil {
|
|
| 165 |
- return err |
|
| 166 |
- } |
|
| 167 |
- if !strings.Contains(string(output), "RUNNING") {
|
|
| 163 |
+ info := runtime.execDriver.Info(container.ID) |
|
| 164 |
+ |
|
| 165 |
+ if !info.IsRunning() {
|
|
| 168 | 166 |
utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
|
| 169 | 167 |
if runtime.config.AutoRestart {
|
| 170 | 168 |
utils.Debugf("Restarting")
|
| ... | ... |
@@ -188,7 +181,7 @@ func (runtime *Runtime) Register(container *Container) error {
|
| 188 | 188 |
} |
| 189 | 189 |
|
| 190 | 190 |
container.waitLock = make(chan struct{})
|
| 191 |
- go container.monitor() |
|
| 191 |
+ go container.monitor(nil) |
|
| 192 | 192 |
} |
| 193 | 193 |
} |
| 194 | 194 |
return nil |
| ... | ... |
@@ -331,43 +324,6 @@ func (runtime *Runtime) restore() error {
|
| 331 | 331 |
return nil |
| 332 | 332 |
} |
| 333 | 333 |
|
| 334 |
-// FIXME: comment please! |
|
| 335 |
-func (runtime *Runtime) UpdateCapabilities(quiet bool) {
|
|
| 336 |
- if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil {
|
|
| 337 |
- if !quiet {
|
|
| 338 |
- log.Printf("WARNING: %s\n", err)
|
|
| 339 |
- } |
|
| 340 |
- } else {
|
|
| 341 |
- _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes")) |
|
| 342 |
- _, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes")) |
|
| 343 |
- runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil |
|
| 344 |
- if !runtime.capabilities.MemoryLimit && !quiet {
|
|
| 345 |
- log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
|
|
| 346 |
- } |
|
| 347 |
- |
|
| 348 |
- _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) |
|
| 349 |
- runtime.capabilities.SwapLimit = err == nil |
|
| 350 |
- if !runtime.capabilities.SwapLimit && !quiet {
|
|
| 351 |
- log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
|
|
| 352 |
- } |
|
| 353 |
- } |
|
| 354 |
- |
|
| 355 |
- content, err3 := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward")
|
|
| 356 |
- runtime.capabilities.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1' |
|
| 357 |
- if runtime.capabilities.IPv4ForwardingDisabled && !quiet {
|
|
| 358 |
- log.Printf("WARNING: IPv4 forwarding is disabled.")
|
|
| 359 |
- } |
|
| 360 |
- |
|
| 361 |
- // Check if AppArmor seems to be enabled on this system. |
|
| 362 |
- if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
|
|
| 363 |
- utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.")
|
|
| 364 |
- runtime.capabilities.AppArmor = false |
|
| 365 |
- } else {
|
|
| 366 |
- utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.")
|
|
| 367 |
- runtime.capabilities.AppArmor = true |
|
| 368 |
- } |
|
| 369 |
-} |
|
| 370 |
- |
|
| 371 | 334 |
// Create creates a new container from the given configuration with a given name. |
| 372 | 335 |
func (runtime *Runtime) Create(config *Config, name string) (*Container, []string, error) {
|
| 373 | 336 |
// Lookup image |
| ... | ... |
@@ -646,7 +602,6 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
|
| 646 | 646 |
if err != nil {
|
| 647 | 647 |
return nil, err |
| 648 | 648 |
} |
| 649 |
- runtime.UpdateCapabilities(false) |
|
| 650 | 649 |
return runtime, nil |
| 651 | 650 |
} |
| 652 | 651 |
|
| ... | ... |
@@ -675,10 +630,6 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
| 675 | 675 |
} |
| 676 | 676 |
} |
| 677 | 677 |
|
| 678 |
- utils.Debugf("Escaping AppArmor confinement")
|
|
| 679 |
- if err := linkLxcStart(config.Root); err != nil {
|
|
| 680 |
- return nil, err |
|
| 681 |
- } |
|
| 682 | 678 |
utils.Debugf("Creating images graph")
|
| 683 | 679 |
g, err := NewGraph(path.Join(config.Root, "graph"), driver) |
| 684 | 680 |
if err != nil {
|
| ... | ... |
@@ -735,6 +686,26 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
| 735 | 735 |
sysInitPath = localCopy |
| 736 | 736 |
} |
| 737 | 737 |
|
| 738 |
+ sysInfo := sysinfo.New(false) |
|
| 739 |
+ |
|
| 740 |
+ /* |
|
| 741 |
+ temporarilly disabled. |
|
| 742 |
+ */ |
|
| 743 |
+ if false {
|
|
| 744 |
+ var ed execdriver.Driver |
|
| 745 |
+ if driver := os.Getenv("EXEC_DRIVER"); driver == "lxc" {
|
|
| 746 |
+ ed, err = lxc.NewDriver(config.Root, sysInfo.AppArmor) |
|
| 747 |
+ } else {
|
|
| 748 |
+ ed, err = chroot.NewDriver() |
|
| 749 |
+ } |
|
| 750 |
+ if ed != nil {
|
|
| 751 |
+ } |
|
| 752 |
+ } |
|
| 753 |
+ ed, err := lxc.NewDriver(config.Root, sysInfo.AppArmor) |
|
| 754 |
+ if err != nil {
|
|
| 755 |
+ return nil, err |
|
| 756 |
+ } |
|
| 757 |
+ |
|
| 738 | 758 |
runtime := &Runtime{
|
| 739 | 759 |
repository: runtimeRepo, |
| 740 | 760 |
containers: list.New(), |
| ... | ... |
@@ -742,12 +713,13 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
| 742 | 742 |
graph: g, |
| 743 | 743 |
repositories: repositories, |
| 744 | 744 |
idIndex: utils.NewTruncIndex(), |
| 745 |
- capabilities: &Capabilities{},
|
|
| 745 |
+ sysInfo: sysInfo, |
|
| 746 | 746 |
volumes: volumes, |
| 747 | 747 |
config: config, |
| 748 | 748 |
containerGraph: graph, |
| 749 | 749 |
driver: driver, |
| 750 | 750 |
sysInitPath: sysInitPath, |
| 751 |
+ execDriver: ed, |
|
| 751 | 752 |
} |
| 752 | 753 |
|
| 753 | 754 |
if err := runtime.restore(); err != nil {
|
| ... | ... |
@@ -829,6 +801,18 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
|
| 829 | 829 |
return archive.ExportChanges(cDir, changes) |
| 830 | 830 |
} |
| 831 | 831 |
|
| 832 |
+func (runtime *Runtime) Run(c *Container, startCallback execdriver.StartCallback) (int, error) {
|
|
| 833 |
+ return runtime.execDriver.Run(c.process, startCallback) |
|
| 834 |
+} |
|
| 835 |
+ |
|
| 836 |
+func (runtime *Runtime) Kill(c *Container, sig int) error {
|
|
| 837 |
+ return runtime.execDriver.Kill(c.process, sig) |
|
| 838 |
+} |
|
| 839 |
+ |
|
| 840 |
+func (runtime *Runtime) WaitGhost(c *Container) error {
|
|
| 841 |
+ return runtime.execDriver.Wait(c.ID) |
|
| 842 |
+} |
|
| 843 |
+ |
|
| 832 | 844 |
// Nuke kills all containers then removes all content |
| 833 | 845 |
// from the content root, including images, volumes and |
| 834 | 846 |
// container filesystems. |
| ... | ... |
@@ -848,23 +832,6 @@ func (runtime *Runtime) Nuke() error {
|
| 848 | 848 |
return os.RemoveAll(runtime.config.Root) |
| 849 | 849 |
} |
| 850 | 850 |
|
| 851 |
-func linkLxcStart(root string) error {
|
|
| 852 |
- sourcePath, err := exec.LookPath("lxc-start")
|
|
| 853 |
- if err != nil {
|
|
| 854 |
- return err |
|
| 855 |
- } |
|
| 856 |
- targetPath := path.Join(root, "lxc-start-unconfined") |
|
| 857 |
- |
|
| 858 |
- if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
|
|
| 859 |
- return err |
|
| 860 |
- } else if err == nil {
|
|
| 861 |
- if err := os.Remove(targetPath); err != nil {
|
|
| 862 |
- return err |
|
| 863 |
- } |
|
| 864 |
- } |
|
| 865 |
- return os.Symlink(sourcePath, targetPath) |
|
| 866 |
-} |
|
| 867 |
- |
|
| 868 | 851 |
// FIXME: this is a convenience function for integration tests |
| 869 | 852 |
// which need direct access to runtime.graph. |
| 870 | 853 |
// Once the tests switch to using engine and jobs, this method |
| ... | ... |
@@ -516,7 +516,7 @@ func (srv *Server) ImageInsert(job *engine.Job) engine.Status {
|
| 516 | 516 |
} |
| 517 | 517 |
defer file.Body.Close() |
| 518 | 518 |
|
| 519 |
- config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities)
|
|
| 519 |
+ config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo)
|
|
| 520 | 520 |
if err != nil {
|
| 521 | 521 |
job.Error(err) |
| 522 | 522 |
return engine.StatusErr |
| ... | ... |
@@ -661,13 +661,6 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status {
|
| 661 | 661 |
} else {
|
| 662 | 662 |
imgcount = len(images) |
| 663 | 663 |
} |
| 664 |
- lxcVersion := "" |
|
| 665 |
- if output, err := exec.Command("lxc-version").CombinedOutput(); err == nil {
|
|
| 666 |
- outputStr := string(output) |
|
| 667 |
- if len(strings.SplitN(outputStr, ":", 2)) == 2 {
|
|
| 668 |
- lxcVersion = strings.TrimSpace(strings.SplitN(string(output), ":", 2)[1]) |
|
| 669 |
- } |
|
| 670 |
- } |
|
| 671 | 664 |
kernelVersion := "<unknown>" |
| 672 | 665 |
if kv, err := utils.GetKernelVersion(); err == nil {
|
| 673 | 666 |
kernelVersion = kv.String() |
| ... | ... |
@@ -685,13 +678,13 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status {
|
| 685 | 685 |
v.SetInt("Images", imgcount)
|
| 686 | 686 |
v.Set("Driver", srv.runtime.driver.String())
|
| 687 | 687 |
v.SetJson("DriverStatus", srv.runtime.driver.Status())
|
| 688 |
- v.SetBool("MemoryLimit", srv.runtime.capabilities.MemoryLimit)
|
|
| 689 |
- v.SetBool("SwapLimit", srv.runtime.capabilities.SwapLimit)
|
|
| 690 |
- v.SetBool("IPv4Forwarding", !srv.runtime.capabilities.IPv4ForwardingDisabled)
|
|
| 688 |
+ v.SetBool("MemoryLimit", srv.runtime.sysInfo.MemoryLimit)
|
|
| 689 |
+ v.SetBool("SwapLimit", srv.runtime.sysInfo.SwapLimit)
|
|
| 690 |
+ v.SetBool("IPv4Forwarding", !srv.runtime.sysInfo.IPv4ForwardingDisabled)
|
|
| 691 | 691 |
v.SetBool("Debug", os.Getenv("DEBUG") != "")
|
| 692 | 692 |
v.SetInt("NFd", utils.GetTotalUsedFds())
|
| 693 | 693 |
v.SetInt("NGoroutines", runtime.NumGoroutine())
|
| 694 |
- v.Set("LXCVersion", lxcVersion)
|
|
| 694 |
+ v.Set("ExecutionDriver", srv.runtime.execDriver.Name())
|
|
| 695 | 695 |
v.SetInt("NEventsListener", len(srv.events))
|
| 696 | 696 |
v.Set("KernelVersion", kernelVersion)
|
| 697 | 697 |
v.Set("IndexServerAddress", auth.IndexServerAddress())
|
| ... | ... |
@@ -1477,10 +1470,10 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status {
|
| 1477 | 1477 |
job.Errorf("Minimum memory limit allowed is 512k")
|
| 1478 | 1478 |
return engine.StatusErr |
| 1479 | 1479 |
} |
| 1480 |
- if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
|
|
| 1480 |
+ if config.Memory > 0 && !srv.runtime.sysInfo.MemoryLimit {
|
|
| 1481 | 1481 |
config.Memory = 0 |
| 1482 | 1482 |
} |
| 1483 |
- if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
|
|
| 1483 |
+ if config.Memory > 0 && !srv.runtime.sysInfo.SwapLimit {
|
|
| 1484 | 1484 |
config.MemorySwap = -1 |
| 1485 | 1485 |
} |
| 1486 | 1486 |
container, buildWarnings, err := srv.runtime.Create(&config, name) |
| ... | ... |
@@ -4,163 +4,19 @@ import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"flag" |
| 6 | 6 |
"fmt" |
| 7 |
- "github.com/dotcloud/docker/pkg/netlink" |
|
| 8 |
- "github.com/dotcloud/docker/utils" |
|
| 9 |
- "github.com/syndtr/gocapability/capability" |
|
| 7 |
+ "github.com/dotcloud/docker/execdriver" |
|
| 8 |
+ _ "github.com/dotcloud/docker/execdriver/chroot" |
|
| 9 |
+ _ "github.com/dotcloud/docker/execdriver/lxc" |
|
| 10 | 10 |
"io/ioutil" |
| 11 | 11 |
"log" |
| 12 |
- "net" |
|
| 13 | 12 |
"os" |
| 14 |
- "os/exec" |
|
| 15 |
- "strconv" |
|
| 16 | 13 |
"strings" |
| 17 |
- "syscall" |
|
| 18 | 14 |
) |
| 19 | 15 |
|
| 20 |
-type DockerInitArgs struct {
|
|
| 21 |
- user string |
|
| 22 |
- gateway string |
|
| 23 |
- ip string |
|
| 24 |
- workDir string |
|
| 25 |
- privileged bool |
|
| 26 |
- env []string |
|
| 27 |
- args []string |
|
| 28 |
- mtu int |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-func setupHostname(args *DockerInitArgs) error {
|
|
| 32 |
- hostname := getEnv(args, "HOSTNAME") |
|
| 33 |
- if hostname == "" {
|
|
| 34 |
- return nil |
|
| 35 |
- } |
|
| 36 |
- return setHostname(hostname) |
|
| 37 |
-} |
|
| 38 |
- |
|
| 39 |
-// Setup networking |
|
| 40 |
-func setupNetworking(args *DockerInitArgs) error {
|
|
| 41 |
- if args.ip != "" {
|
|
| 42 |
- // eth0 |
|
| 43 |
- iface, err := net.InterfaceByName("eth0")
|
|
| 44 |
- if err != nil {
|
|
| 45 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 46 |
- } |
|
| 47 |
- ip, ipNet, err := net.ParseCIDR(args.ip) |
|
| 48 |
- if err != nil {
|
|
| 49 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 50 |
- } |
|
| 51 |
- if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
|
| 52 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 53 |
- } |
|
| 54 |
- if err := netlink.NetworkSetMTU(iface, args.mtu); err != nil {
|
|
| 55 |
- return fmt.Errorf("Unable to set MTU: %v", err)
|
|
| 56 |
- } |
|
| 57 |
- if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 58 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 59 |
- } |
|
| 60 |
- |
|
| 61 |
- // loopback |
|
| 62 |
- iface, err = net.InterfaceByName("lo")
|
|
| 63 |
- if err != nil {
|
|
| 64 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 65 |
- } |
|
| 66 |
- if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 67 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 68 |
- } |
|
| 69 |
- } |
|
| 70 |
- if args.gateway != "" {
|
|
| 71 |
- gw := net.ParseIP(args.gateway) |
|
| 72 |
- if gw == nil {
|
|
| 73 |
- return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.gateway)
|
|
| 74 |
- } |
|
| 75 |
- |
|
| 76 |
- if err := netlink.AddDefaultGw(gw); err != nil {
|
|
| 77 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 78 |
- } |
|
| 79 |
- } |
|
| 80 |
- |
|
| 81 |
- return nil |
|
| 82 |
-} |
|
| 83 |
- |
|
| 84 |
-// Setup working directory |
|
| 85 |
-func setupWorkingDirectory(args *DockerInitArgs) error {
|
|
| 86 |
- if args.workDir == "" {
|
|
| 87 |
- return nil |
|
| 88 |
- } |
|
| 89 |
- if err := syscall.Chdir(args.workDir); err != nil {
|
|
| 90 |
- return fmt.Errorf("Unable to change dir to %v: %v", args.workDir, err)
|
|
| 91 |
- } |
|
| 92 |
- return nil |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-// Takes care of dropping privileges to the desired user |
|
| 96 |
-func changeUser(args *DockerInitArgs) error {
|
|
| 97 |
- if args.user == "" {
|
|
| 98 |
- return nil |
|
| 99 |
- } |
|
| 100 |
- userent, err := utils.UserLookup(args.user) |
|
| 101 |
- if err != nil {
|
|
| 102 |
- return fmt.Errorf("Unable to find user %v: %v", args.user, err)
|
|
| 103 |
- } |
|
| 104 |
- |
|
| 105 |
- uid, err := strconv.Atoi(userent.Uid) |
|
| 106 |
- if err != nil {
|
|
| 107 |
- return fmt.Errorf("Invalid uid: %v", userent.Uid)
|
|
| 108 |
- } |
|
| 109 |
- gid, err := strconv.Atoi(userent.Gid) |
|
| 110 |
- if err != nil {
|
|
| 111 |
- return fmt.Errorf("Invalid gid: %v", userent.Gid)
|
|
| 112 |
- } |
|
| 113 |
- |
|
| 114 |
- if err := syscall.Setgid(gid); err != nil {
|
|
| 115 |
- return fmt.Errorf("setgid failed: %v", err)
|
|
| 116 |
- } |
|
| 117 |
- if err := syscall.Setuid(uid); err != nil {
|
|
| 118 |
- return fmt.Errorf("setuid failed: %v", err)
|
|
| 119 |
- } |
|
| 120 |
- |
|
| 121 |
- return nil |
|
| 122 |
-} |
|
| 123 |
- |
|
| 124 |
-func setupCapabilities(args *DockerInitArgs) error {
|
|
| 125 |
- |
|
| 126 |
- if args.privileged {
|
|
| 127 |
- return nil |
|
| 128 |
- } |
|
| 129 |
- |
|
| 130 |
- drop := []capability.Cap{
|
|
| 131 |
- capability.CAP_SETPCAP, |
|
| 132 |
- capability.CAP_SYS_MODULE, |
|
| 133 |
- capability.CAP_SYS_RAWIO, |
|
| 134 |
- capability.CAP_SYS_PACCT, |
|
| 135 |
- capability.CAP_SYS_ADMIN, |
|
| 136 |
- capability.CAP_SYS_NICE, |
|
| 137 |
- capability.CAP_SYS_RESOURCE, |
|
| 138 |
- capability.CAP_SYS_TIME, |
|
| 139 |
- capability.CAP_SYS_TTY_CONFIG, |
|
| 140 |
- capability.CAP_MKNOD, |
|
| 141 |
- capability.CAP_AUDIT_WRITE, |
|
| 142 |
- capability.CAP_AUDIT_CONTROL, |
|
| 143 |
- capability.CAP_MAC_OVERRIDE, |
|
| 144 |
- capability.CAP_MAC_ADMIN, |
|
| 145 |
- } |
|
| 146 |
- |
|
| 147 |
- c, err := capability.NewPid(os.Getpid()) |
|
| 148 |
- if err != nil {
|
|
| 149 |
- return err |
|
| 150 |
- } |
|
| 151 |
- |
|
| 152 |
- c.Unset(capability.CAPS|capability.BOUNDS, drop...) |
|
| 153 |
- |
|
| 154 |
- if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
|
|
| 155 |
- return err |
|
| 156 |
- } |
|
| 157 |
- return nil |
|
| 158 |
-} |
|
| 159 |
- |
|
| 160 | 16 |
// Clear environment pollution introduced by lxc-start |
| 161 |
-func setupEnv(args *DockerInitArgs) {
|
|
| 17 |
+func setupEnv(args *execdriver.InitArgs) {
|
|
| 162 | 18 |
os.Clearenv() |
| 163 |
- for _, kv := range args.env {
|
|
| 19 |
+ for _, kv := range args.Env {
|
|
| 164 | 20 |
parts := strings.SplitN(kv, "=", 2) |
| 165 | 21 |
if len(parts) == 1 {
|
| 166 | 22 |
parts = append(parts, "") |
| ... | ... |
@@ -169,50 +25,19 @@ func setupEnv(args *DockerInitArgs) {
|
| 169 | 169 |
} |
| 170 | 170 |
} |
| 171 | 171 |
|
| 172 |
-func getEnv(args *DockerInitArgs, key string) string {
|
|
| 173 |
- for _, kv := range args.env {
|
|
| 174 |
- parts := strings.SplitN(kv, "=", 2) |
|
| 175 |
- if parts[0] == key && len(parts) == 2 {
|
|
| 176 |
- return parts[1] |
|
| 177 |
- } |
|
| 178 |
- } |
|
| 179 |
- return "" |
|
| 180 |
-} |
|
| 181 |
- |
|
| 182 |
-func executeProgram(args *DockerInitArgs) error {
|
|
| 172 |
+func executeProgram(args *execdriver.InitArgs) error {
|
|
| 183 | 173 |
setupEnv(args) |
| 184 |
- |
|
| 185 |
- if err := setupHostname(args); err != nil {
|
|
| 186 |
- return err |
|
| 187 |
- } |
|
| 188 |
- |
|
| 189 |
- if err := setupNetworking(args); err != nil {
|
|
| 190 |
- return err |
|
| 191 |
- } |
|
| 192 |
- |
|
| 193 |
- if err := setupCapabilities(args); err != nil {
|
|
| 194 |
- return err |
|
| 195 |
- } |
|
| 196 |
- |
|
| 197 |
- if err := setupWorkingDirectory(args); err != nil {
|
|
| 198 |
- return err |
|
| 199 |
- } |
|
| 200 |
- |
|
| 201 |
- if err := changeUser(args); err != nil {
|
|
| 202 |
- return err |
|
| 203 |
- } |
|
| 204 |
- |
|
| 205 |
- path, err := exec.LookPath(args.args[0]) |
|
| 174 |
+ dockerInitFct, err := execdriver.GetInitFunc(args.Driver) |
|
| 206 | 175 |
if err != nil {
|
| 207 |
- log.Printf("Unable to locate %v", args.args[0])
|
|
| 208 |
- os.Exit(127) |
|
| 176 |
+ panic(err) |
|
| 209 | 177 |
} |
| 178 |
+ return dockerInitFct(args) |
|
| 210 | 179 |
|
| 211 |
- if err := syscall.Exec(path, args.args, os.Environ()); err != nil {
|
|
| 212 |
- return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
|
|
| 180 |
+ if args.Driver == "lxc" {
|
|
| 181 |
+ // Will never reach |
|
| 182 |
+ } else if args.Driver == "chroot" {
|
|
| 213 | 183 |
} |
| 214 | 184 |
|
| 215 |
- // Will never reach here |
|
| 216 | 185 |
return nil |
| 217 | 186 |
} |
| 218 | 187 |
|
| ... | ... |
@@ -232,6 +57,7 @@ func SysInit() {
|
| 232 | 232 |
workDir := flag.String("w", "", "workdir")
|
| 233 | 233 |
privileged := flag.Bool("privileged", false, "privileged mode")
|
| 234 | 234 |
mtu := flag.Int("mtu", 1500, "interface mtu")
|
| 235 |
+ driver := flag.String("driver", "", "exec driver")
|
|
| 235 | 236 |
flag.Parse() |
| 236 | 237 |
|
| 237 | 238 |
// Get env |
| ... | ... |
@@ -247,15 +73,16 @@ func SysInit() {
|
| 247 | 247 |
// Propagate the plugin-specific container env variable |
| 248 | 248 |
env = append(env, "container="+os.Getenv("container"))
|
| 249 | 249 |
|
| 250 |
- args := &DockerInitArgs{
|
|
| 251 |
- user: *user, |
|
| 252 |
- gateway: *gateway, |
|
| 253 |
- ip: *ip, |
|
| 254 |
- workDir: *workDir, |
|
| 255 |
- privileged: *privileged, |
|
| 256 |
- env: env, |
|
| 257 |
- args: flag.Args(), |
|
| 258 |
- mtu: *mtu, |
|
| 250 |
+ args := &execdriver.InitArgs{
|
|
| 251 |
+ User: *user, |
|
| 252 |
+ Gateway: *gateway, |
|
| 253 |
+ Ip: *ip, |
|
| 254 |
+ WorkDir: *workDir, |
|
| 255 |
+ Privileged: *privileged, |
|
| 256 |
+ Env: env, |
|
| 257 |
+ Args: flag.Args(), |
|
| 258 |
+ Mtu: *mtu, |
|
| 259 |
+ Driver: *driver, |
|
| 259 | 260 |
} |
| 260 | 261 |
|
| 261 | 262 |
if err := executeProgram(args); err != nil {
|
| ... | ... |
@@ -5,7 +5,6 @@ import ( |
| 5 | 5 |
"github.com/dotcloud/docker/archive" |
| 6 | 6 |
"github.com/dotcloud/docker/pkg/namesgenerator" |
| 7 | 7 |
"github.com/dotcloud/docker/utils" |
| 8 |
- "io/ioutil" |
|
| 9 | 8 |
"strconv" |
| 10 | 9 |
"strings" |
| 11 | 10 |
) |
| ... | ... |
@@ -328,20 +327,6 @@ func parseLink(rawLink string) (map[string]string, error) {
|
| 328 | 328 |
return utils.PartParser("name:alias", rawLink)
|
| 329 | 329 |
} |
| 330 | 330 |
|
| 331 |
-func RootIsShared() bool {
|
|
| 332 |
- if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
|
|
| 333 |
- for _, line := range strings.Split(string(data), "\n") {
|
|
| 334 |
- cols := strings.Split(line, " ") |
|
| 335 |
- if len(cols) >= 6 && cols[4] == "/" {
|
|
| 336 |
- return strings.HasPrefix(cols[6], "shared") |
|
| 337 |
- } |
|
| 338 |
- } |
|
| 339 |
- } |
|
| 340 |
- |
|
| 341 |
- // No idea, probably safe to assume so |
|
| 342 |
- return true |
|
| 343 |
-} |
|
| 344 |
- |
|
| 345 | 331 |
type checker struct {
|
| 346 | 332 |
runtime *Runtime |
| 347 | 333 |
} |