2014-02-21 20:11:57 -05:00
|
|
|
package namespaces
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/dotcloud/docker/execdriver"
|
2014-02-22 04:21:26 -05:00
|
|
|
"github.com/dotcloud/docker/execdriver/lxc"
|
2014-02-21 20:11:57 -05:00
|
|
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
|
|
|
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
|
2014-02-22 04:28:59 -05:00
|
|
|
"io"
|
2014-02-21 20:11:57 -05:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"syscall"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
DriverName = "namespaces"
|
|
|
|
Version = "0.1"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrNotSupported = errors.New("not supported")
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
type driver struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewDriver() (*driver, error) {
|
|
|
|
return &driver{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
|
|
var (
|
2014-02-22 04:21:26 -05:00
|
|
|
term nsinit.Terminal
|
|
|
|
container = createContainer(c)
|
|
|
|
factory = &dockerCommandFactory{c}
|
|
|
|
stateWriter = &dockerStateWriter{
|
|
|
|
callback: startCallback,
|
|
|
|
c: c,
|
|
|
|
dsw: &nsinit.DefaultStateWriter{c.Rootfs},
|
2014-02-21 20:11:57 -05:00
|
|
|
}
|
2014-02-22 04:21:26 -05:00
|
|
|
)
|
|
|
|
if c.Tty {
|
|
|
|
term = &dockerTtyTerm{
|
|
|
|
pipes: pipes,
|
2014-02-21 20:11:57 -05:00
|
|
|
}
|
|
|
|
} else {
|
2014-02-22 04:21:26 -05:00
|
|
|
term = &dockerStdTerm{
|
|
|
|
pipes: pipes,
|
2014-02-21 20:11:57 -05:00
|
|
|
}
|
|
|
|
}
|
2014-02-22 04:21:26 -05:00
|
|
|
c.Terminal = term
|
|
|
|
if err := writeContainerFile(container, c.Rootfs); err != nil {
|
|
|
|
return -1, err
|
2014-02-21 20:11:57 -05:00
|
|
|
}
|
2014-02-22 04:21:26 -05:00
|
|
|
args := append([]string{c.Entrypoint}, c.Arguments...)
|
|
|
|
return nsinit.Exec(container, factory, stateWriter, term, "/nsinit.log", args)
|
2014-02-21 20:11:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Kill(p *execdriver.Command, sig int) error {
|
|
|
|
return p.Process.Kill()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Restore(c *execdriver.Command) error {
|
|
|
|
return ErrNotSupported
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Info(id string) execdriver.Info {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) Name() string {
|
|
|
|
return fmt.Sprintf("%s-%s", DriverName, Version)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
|
|
|
return nil, ErrNotSupported
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeContainerFile(container *libcontainer.Container, rootfs string) error {
|
|
|
|
data, err := json.Marshal(container)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return ioutil.WriteFile(filepath.Join(rootfs, "container.json"), data, 0755)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getEnv(key string, env []string) string {
|
|
|
|
for _, pair := range env {
|
|
|
|
parts := strings.Split(pair, "=")
|
|
|
|
if parts[0] == key {
|
|
|
|
return parts[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2014-02-22 04:21:26 -05:00
|
|
|
type dockerCommandFactory struct {
|
|
|
|
c *execdriver.Command
|
2014-02-21 20:11:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// createCommand 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
|
2014-02-22 04:21:26 -05:00
|
|
|
func (d *dockerCommandFactory) Create(container *libcontainer.Container,
|
|
|
|
console, logFile string, syncFd uintptr, args []string) *exec.Cmd {
|
|
|
|
c := d.c
|
2014-02-21 20:11:57 -05:00
|
|
|
aname, _ := exec.LookPath("nsinit")
|
|
|
|
c.Path = aname
|
|
|
|
c.Args = append([]string{
|
|
|
|
aname,
|
|
|
|
"-console", console,
|
2014-02-22 04:21:26 -05:00
|
|
|
"-pipe", fmt.Sprint(syncFd),
|
2014-02-21 20:11:57 -05:00
|
|
|
"-log", logFile,
|
|
|
|
"init",
|
|
|
|
}, args...)
|
|
|
|
c.SysProcAttr = &syscall.SysProcAttr{
|
|
|
|
Cloneflags: uintptr(nsinit.GetNamespaceFlags(container.Namespaces)),
|
|
|
|
}
|
|
|
|
c.Env = container.Env
|
|
|
|
c.Dir = c.Rootfs
|
2014-02-22 04:21:26 -05:00
|
|
|
|
|
|
|
return &c.Cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
type dockerStateWriter struct {
|
|
|
|
dsw nsinit.StateWriter
|
|
|
|
c *execdriver.Command
|
|
|
|
callback execdriver.StartCallback
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *dockerStateWriter) WritePid(pid int) error {
|
|
|
|
err := d.dsw.WritePid(pid)
|
|
|
|
if d.callback != nil {
|
|
|
|
d.callback(d.c)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *dockerStateWriter) DeletePid() error {
|
|
|
|
return d.dsw.DeletePid()
|
2014-02-21 20:11:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func createContainer(c *execdriver.Command) *libcontainer.Container {
|
|
|
|
container := getDefaultTemplate()
|
|
|
|
|
|
|
|
container.Hostname = getEnv("HOSTNAME", c.Env)
|
|
|
|
container.Tty = c.Tty
|
|
|
|
container.User = c.User
|
|
|
|
container.WorkingDir = c.WorkingDir
|
|
|
|
container.Env = c.Env
|
|
|
|
|
|
|
|
container.Env = append(container.Env, "container=docker")
|
|
|
|
|
|
|
|
if c.Network != nil {
|
|
|
|
container.Network = &libcontainer.Network{
|
|
|
|
Mtu: c.Network.Mtu,
|
|
|
|
Address: fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
|
|
|
|
Gateway: c.Network.Gateway,
|
2014-02-22 04:21:26 -05:00
|
|
|
Type: "veth",
|
|
|
|
Context: libcontainer.Context{
|
|
|
|
"prefix": "dock",
|
|
|
|
"bridge": c.Network.Bridge,
|
|
|
|
},
|
2014-02-21 20:11:57 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if c.Privileged {
|
|
|
|
container.Capabilities = nil
|
|
|
|
}
|
|
|
|
if c.Resources != nil {
|
|
|
|
container.Cgroups.CpuShares = c.Resources.CpuShares
|
|
|
|
container.Cgroups.Memory = c.Resources.Memory
|
|
|
|
container.Cgroups.MemorySwap = c.Resources.MemorySwap
|
|
|
|
}
|
|
|
|
return container
|
|
|
|
}
|
2014-02-22 04:21:26 -05:00
|
|
|
|
|
|
|
type dockerStdTerm struct {
|
|
|
|
lxc.StdConsole
|
|
|
|
pipes *execdriver.Pipes
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *dockerStdTerm) Attach(cmd *exec.Cmd) error {
|
|
|
|
return d.AttachPipes(cmd, d.pipes)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *dockerStdTerm) SetMaster(master *os.File) {
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
type dockerTtyTerm struct {
|
|
|
|
lxc.TtyConsole
|
|
|
|
pipes *execdriver.Pipes
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
|
2014-02-22 04:28:59 -05:00
|
|
|
go io.Copy(t.pipes.Stdout, t.MasterPty)
|
|
|
|
if t.pipes.Stdin != nil {
|
|
|
|
go io.Copy(t.MasterPty, t.pipes.Stdin)
|
|
|
|
}
|
|
|
|
return nil
|
2014-02-22 04:21:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *dockerTtyTerm) SetMaster(master *os.File) {
|
|
|
|
t.MasterPty = master
|
|
|
|
}
|