Refactor and improve libcontainer and driver
Remove logging for now because it is complicating things Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
parent
0e4d946dc4
commit
6daf56799f
11 changed files with 84 additions and 116 deletions
|
@ -6,6 +6,9 @@ import (
|
|||
"github.com/dotcloud/docker/pkg/libcontainer/utils"
|
||||
)
|
||||
|
||||
// Veth is a network strategy that uses a bridge and creates
|
||||
// a veth pair, one that stays outside on the host and the other
|
||||
// is placed inside the container's namespace
|
||||
type Veth struct {
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,11 @@ import (
|
|||
"syscall"
|
||||
)
|
||||
|
||||
// CommandFactory takes the container's configuration and options passed by the
|
||||
// parent processes and creates an *exec.Cmd that will be used to fork/exec the
|
||||
// namespaced init process
|
||||
type CommandFactory interface {
|
||||
Create(container *libcontainer.Container, console, logFile string, syncFd uintptr, args []string) *exec.Cmd
|
||||
Create(container *libcontainer.Container, console string, syncFd uintptr, args []string) *exec.Cmd
|
||||
}
|
||||
|
||||
type DefaultCommandFactory struct{}
|
||||
|
@ -17,13 +20,12 @@ type DefaultCommandFactory struct{}
|
|||
// Create will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
||||
// defined on the container's configuration and use the current binary as the init with the
|
||||
// args provided
|
||||
func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console, logFile string, pipe uintptr, args []string) *exec.Cmd {
|
||||
func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console string, pipe uintptr, args []string) *exec.Cmd {
|
||||
// get our binary name so we can always reexec ourself
|
||||
name := os.Args[0]
|
||||
command := exec.Command(name, append([]string{
|
||||
"-console", console,
|
||||
"-pipe", fmt.Sprint(pipe),
|
||||
"-log", logFile,
|
||||
"init"}, args...)...)
|
||||
|
||||
command.SysProcAttr = &syscall.SysProcAttr{
|
||||
|
|
|
@ -28,31 +28,27 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
|
|||
}
|
||||
|
||||
if container.Tty {
|
||||
ns.logger.Printf("setting up master and console")
|
||||
master, console, err = CreateMasterAndConsole()
|
||||
master, console, err = system.CreateMasterAndConsole()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
term.SetMaster(master)
|
||||
}
|
||||
|
||||
command := ns.commandFactory.Create(container, console, ns.logFile, syncPipe.child.Fd(), args)
|
||||
command := ns.commandFactory.Create(container, console, syncPipe.child.Fd(), args)
|
||||
if err := term.Attach(command); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer term.Close()
|
||||
|
||||
ns.logger.Printf("staring init")
|
||||
if err := command.Start(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
ns.logger.Printf("writing state file")
|
||||
if err := ns.stateWriter.WritePid(command.Process.Pid); err != nil {
|
||||
command.Process.Kill()
|
||||
return -1, err
|
||||
}
|
||||
defer func() {
|
||||
ns.logger.Printf("removing state file")
|
||||
ns.stateWriter.DeletePid()
|
||||
}()
|
||||
|
||||
|
@ -68,24 +64,18 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
|
|||
}
|
||||
|
||||
// Sync with child
|
||||
ns.logger.Printf("closing sync pipes")
|
||||
syncPipe.Close()
|
||||
|
||||
ns.logger.Printf("waiting on process")
|
||||
if err := command.Wait(); err != nil {
|
||||
if _, ok := err.(*exec.ExitError); !ok {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
exitCode := command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
ns.logger.Printf("process ended with exit code %d", exitCode)
|
||||
return exitCode, nil
|
||||
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||
}
|
||||
|
||||
func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) error {
|
||||
if container.Cgroups != nil {
|
||||
ns.logger.Printf("setting up cgroups")
|
||||
if err := container.Cgroups.Apply(nspid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -95,7 +85,6 @@ func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) er
|
|||
|
||||
func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
|
||||
if container.Network != nil {
|
||||
ns.logger.Printf("creating host network configuration type %s", container.Network.Type)
|
||||
strategy, err := network.GetStrategy(container.Network.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -104,27 +93,9 @@ func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ns.logger.Printf("sending %v as network context", networkContext)
|
||||
if err := pipe.SendToChild(networkContext); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateMasterAndConsole will open /dev/ptmx on the host and retreive the
|
||||
// pts name for use as the pty slave inside the container
|
||||
func CreateMasterAndConsole() (*os.File, string, error) {
|
||||
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
console, err := system.Ptsname(master)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err := system.Unlockpt(master); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return master, console, nil
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []s
|
|||
return -1, err
|
||||
}
|
||||
}
|
||||
fds, err := getNsFds(nspid, container)
|
||||
fds, err := ns.getNsFds(nspid, container)
|
||||
closeFds := func() {
|
||||
for _, f := range fds {
|
||||
system.Closefd(f)
|
||||
|
@ -75,13 +75,13 @@ dropAndExec:
|
|||
if err := capabilities.DropCapabilities(container); err != nil {
|
||||
return -1, fmt.Errorf("drop capabilities %s", err)
|
||||
}
|
||||
if err := system.Exec(args[0], args[0:], container.Env); err != nil {
|
||||
if err := system.Execv(args[0], args[0:], container.Env); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
|
||||
func (ns *linuxNs) getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
|
||||
fds := make([]uintptr, len(container.Namespaces))
|
||||
for i, ns := range container.Namespaces {
|
||||
f, err := os.OpenFile(filepath.Join("/proc/", strconv.Itoa(pid), "ns", namespaceFileMap[ns]), os.O_RDONLY, 0)
|
||||
|
|
|
@ -7,18 +7,17 @@ import (
|
|||
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||
"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
|
||||
"github.com/dotcloud/docker/pkg/libcontainer/network"
|
||||
"github.com/dotcloud/docker/pkg/libcontainer/utils"
|
||||
"github.com/dotcloud/docker/pkg/system"
|
||||
"github.com/dotcloud/docker/pkg/user"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
|
||||
// and other options required for the new container.
|
||||
func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
|
||||
rootfs, err := resolveRootfs(uncleanRootfs)
|
||||
rootfs, err := utils.ResolveRootfs(uncleanRootfs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -34,7 +33,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
|
|||
if console != "" {
|
||||
// close pipes so that we can replace it with the pty
|
||||
closeStdPipes()
|
||||
slave, err := openTerminal(console, syscall.O_RDWR)
|
||||
slave, err := system.OpenTerminal(console, syscall.O_RDWR)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open terminal %s", err)
|
||||
}
|
||||
|
@ -50,6 +49,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
|
|||
return fmt.Errorf("setctty %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := system.ParentDeathSignal(); err != nil {
|
||||
return fmt.Errorf("parent deth signal %s", err)
|
||||
}
|
||||
|
@ -73,18 +73,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
|
|||
return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
|
||||
}
|
||||
}
|
||||
return execArgs(args, container.Env)
|
||||
}
|
||||
|
||||
func execArgs(args []string, env []string) error {
|
||||
name, err := exec.LookPath(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := system.Exec(name, args[0:], env); err != nil {
|
||||
return fmt.Errorf("exec %s", err)
|
||||
}
|
||||
panic("unreachable")
|
||||
return system.Execv(args[0], args[0:], container.Env)
|
||||
}
|
||||
|
||||
func closeStdPipes() {
|
||||
|
@ -93,18 +82,19 @@ func closeStdPipes() {
|
|||
os.Stderr.Close()
|
||||
}
|
||||
|
||||
// resolveRootfs ensures that the current working directory is
|
||||
// not a symlink and returns the absolute path to the rootfs
|
||||
func resolveRootfs(uncleanRootfs string) (string, error) {
|
||||
rootfs, err := filepath.Abs(uncleanRootfs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.EvalSymlinks(rootfs)
|
||||
}
|
||||
|
||||
func setupUser(container *libcontainer.Container) error {
|
||||
if container.User != "" && container.User != "root" {
|
||||
switch container.User {
|
||||
case "root", "":
|
||||
if err := system.Setgroups(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := system.Setresgid(0, 0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := system.Setresuid(0, 0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
uid, gid, suppGids, err := user.GetUserGroupSupplementary(container.User, syscall.Getuid(), syscall.Getgid())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -118,16 +108,6 @@ func setupUser(container *libcontainer.Container) error {
|
|||
if err := system.Setuid(uid); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := system.Setgroups(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := system.Setresgid(0, 0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := system.Setresuid(0, 0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -147,16 +127,6 @@ func dupSlave(slave *os.File) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// openTerminal is a clone of os.OpenFile without the O_CLOEXEC
|
||||
// used to open the pty slave inside the container namespace
|
||||
func openTerminal(name string, flag int) (*os.File, error) {
|
||||
r, e := syscall.Open(name, flag, 0)
|
||||
if e != nil {
|
||||
return nil, &os.PathError{"open", name, e}
|
||||
}
|
||||
return os.NewFile(uintptr(r), name), nil
|
||||
}
|
||||
|
||||
// setupVethNetwork uses the Network config if it is not nil to initialize
|
||||
// the new veth interface inside the container for use by changing the name to eth0
|
||||
// setting the MTU and IP address along with the default gateway
|
||||
|
|
|
@ -2,9 +2,10 @@ package nsinit
|
|||
|
||||
import (
|
||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||
"log"
|
||||
)
|
||||
|
||||
// NsInit is an interface with the public facing methods to provide high level
|
||||
// exec operations on a container
|
||||
type NsInit interface {
|
||||
Exec(container *libcontainer.Container, term Terminal, args []string) (int, error)
|
||||
ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error)
|
||||
|
@ -13,17 +14,13 @@ type NsInit interface {
|
|||
|
||||
type linuxNs struct {
|
||||
root string
|
||||
logFile string
|
||||
logger *log.Logger
|
||||
commandFactory CommandFactory
|
||||
stateWriter StateWriter
|
||||
}
|
||||
|
||||
func NewNsInit(logger *log.Logger, logFile string, command CommandFactory, state StateWriter) NsInit {
|
||||
func NewNsInit(command CommandFactory, state StateWriter) NsInit {
|
||||
return &linuxNs{
|
||||
logger: logger,
|
||||
commandFactory: command,
|
||||
stateWriter: state,
|
||||
logFile: logFile,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"flag"
|
||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -16,7 +15,6 @@ import (
|
|||
var (
|
||||
console string
|
||||
pipeFd int
|
||||
logFile string
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -26,7 +24,6 @@ var (
|
|||
|
||||
func registerFlags() {
|
||||
flag.StringVar(&console, "console", "", "console (pty slave) path")
|
||||
flag.StringVar(&logFile, "log", "none", "log options (none, stderr, or a file path)")
|
||||
flag.IntVar(&pipeFd, "pipe", 0, "sync pipe fd")
|
||||
|
||||
flag.Parse()
|
||||
|
@ -113,26 +110,5 @@ func readPid() (int, error) {
|
|||
}
|
||||
|
||||
func newNsInit() (nsinit.NsInit, error) {
|
||||
logger, err := setupLogging()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nsinit.NewNsInit(logger, logFile, &nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{}), nil
|
||||
}
|
||||
|
||||
func setupLogging() (logger *log.Logger, err error) {
|
||||
var writer io.Writer
|
||||
|
||||
switch logFile {
|
||||
case "stderr":
|
||||
writer = os.Stderr
|
||||
case "none", "":
|
||||
writer = ioutil.Discard
|
||||
default:
|
||||
if writer, err = os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
logger = log.New(writer, "", log.LstdFlags)
|
||||
return
|
||||
return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{}), nil
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"path/filepath"
|
||||
)
|
||||
|
||||
// StateWriter handles writing and deleting the pid file
|
||||
// on disk
|
||||
type StateWriter interface {
|
||||
WritePid(pid int) error
|
||||
DeletePid() error
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// GenerateRandomName returns a new name joined with a prefix. This size
|
||||
|
@ -15,3 +16,13 @@ func GenerateRandomName(prefix string, size int) (string, error) {
|
|||
}
|
||||
return prefix + hex.EncodeToString(id)[:size], nil
|
||||
}
|
||||
|
||||
// ResolveRootfs ensures that the current working directory is
|
||||
// not a symlink and returns the absolute path to the rootfs
|
||||
func ResolveRootfs(uncleanRootfs string) (string, error) {
|
||||
rootfs, err := filepath.Abs(uncleanRootfs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.EvalSymlinks(rootfs)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
|
@ -16,6 +17,14 @@ func Exec(cmd string, args []string, env []string) error {
|
|||
return syscall.Exec(cmd, args, env)
|
||||
}
|
||||
|
||||
func Execv(cmd string, args []string, env []string) error {
|
||||
name, err := exec.LookPath(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Exec(name, args, env)
|
||||
}
|
||||
|
||||
func Fork() (int, error) {
|
||||
syscall.ForkLock.Lock()
|
||||
pid, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
|
||||
|
|
|
@ -24,8 +24,35 @@ func Ptsname(f *os.File) (string, error) {
|
|||
return fmt.Sprintf("/dev/pts/%d", n), nil
|
||||
}
|
||||
|
||||
// CreateMasterAndConsole will open /dev/ptmx on the host and retreive the
|
||||
// pts name for use as the pty slave inside the container
|
||||
func CreateMasterAndConsole() (*os.File, string, error) {
|
||||
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
console, err := Ptsname(master)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err := Unlockpt(master); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return master, console, nil
|
||||
}
|
||||
|
||||
// OpenPtmx opens /dev/ptmx, i.e. the PTY master.
|
||||
func OpenPtmx() (*os.File, error) {
|
||||
// O_NOCTTY and O_CLOEXEC are not present in os package so we use the syscall's one for all.
|
||||
return os.OpenFile("/dev/ptmx", syscall.O_RDONLY|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
||||
}
|
||||
|
||||
// OpenTerminal is a clone of os.OpenFile without the O_CLOEXEC
|
||||
// used to open the pty slave inside the container namespace
|
||||
func OpenTerminal(name string, flag int) (*os.File, error) {
|
||||
r, e := syscall.Open(name, flag, 0)
|
||||
if e != nil {
|
||||
return nil, &os.PathError{"open", name, e}
|
||||
}
|
||||
return os.NewFile(uintptr(r), name), nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue