This commit is contained in:
Solomon Hykes 2013-02-13 14:59:24 -08:00
commit 2d5a1abf79
6 changed files with 117 additions and 55 deletions

View File

@ -16,6 +16,12 @@ import (
"time" "time"
) )
var sysInitPath string
func init() {
sysInitPath = SelfPath()
}
type Container struct { type Container struct {
Id string Id string
Root string Root string
@ -29,6 +35,7 @@ type Container struct {
Filesystem *Filesystem Filesystem *Filesystem
State *State State *State
SysInitPath string
lxcConfigPath string lxcConfigPath string
cmd *exec.Cmd cmd *exec.Cmd
stdout *writeBroadcaster stdout *writeBroadcaster
@ -58,6 +65,7 @@ func createContainer(id string, root string, command string, args []string, laye
Filesystem: newFilesystem(path.Join(root, "rootfs"), path.Join(root, "rw"), layers), Filesystem: newFilesystem(path.Join(root, "rootfs"), path.Join(root, "rw"), layers),
State: newState(), State: newState(),
SysInitPath: sysInitPath,
lxcConfigPath: path.Join(root, "config.lxc"), lxcConfigPath: path.Join(root, "config.lxc"),
stdout: newWriteBroadcaster(), stdout: newWriteBroadcaster(),
stderr: newWriteBroadcaster(), stderr: newWriteBroadcaster(),
@ -261,6 +269,7 @@ func (container *Container) Start() error {
"-n", container.Id, "-n", container.Id,
"-f", container.lxcConfigPath, "-f", container.lxcConfigPath,
"--", "--",
"/sbin/init",
container.Path, container.Path,
} }
params = append(params, container.Args...) params = append(params, container.Args...)

View File

@ -6,6 +6,13 @@ import (
"testing" "testing"
) )
// Hack to run sys init during unit testing
func init() {
if SelfPath() == "/sbin/init" {
SysInit()
}
}
func newTestDocker() (*Docker, error) { func newTestDocker() (*Docker, error) {
root, err := ioutil.TempDir("", "docker-test") root, err := ioutil.TempDir("", "docker-test")
if err != nil { if err != nil {

View File

@ -1,26 +1,26 @@
package main package main
import ( import (
"github.com/dotcloud/docker"
"github.com/dotcloud/docker/rcli"
"github.com/dotcloud/docker/image"
"github.com/dotcloud/docker/future"
"bufio" "bufio"
"bytes"
"encoding/json"
"errors" "errors"
"log"
"io"
"flag" "flag"
"fmt" "fmt"
"strings" "github.com/dotcloud/docker"
"text/tabwriter" "github.com/dotcloud/docker/future"
"os" "github.com/dotcloud/docker/image"
"time" "github.com/dotcloud/docker/rcli"
"io"
"log"
"net/http" "net/http"
"encoding/json"
"bytes"
"sync"
"net/url" "net/url"
"os"
"path" "path"
"strings"
"sync"
"text/tabwriter"
"time"
) )
const VERSION = "0.0.1" const VERSION = "0.0.1"
@ -176,7 +176,6 @@ func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...strin
return errors.New("No such container: " + name) return errors.New("No such container: " + name)
} }
func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error { func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory") cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory")
if err := cmd.Parse(args); err != nil { if err := cmd.Parse(args); err != nil {
@ -268,7 +267,7 @@ func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string)
return errors.New("No such container: " + name) return errors.New("No such container: " + name)
} }
if err := srv.containers.Destroy(container); err != nil { if err := srv.containers.Destroy(container); err != nil {
fmt.Fprintln(stdout, "Error destroying container " + name + ": " + err.Error()) fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error())
} }
} }
return nil return nil
@ -286,7 +285,7 @@ func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string
return errors.New("No such container: " + name) return errors.New("No such container: " + name)
} }
if err := container.Kill(); err != nil { if err := container.Kill(); err != nil {
fmt.Fprintln(stdout, "Error killing container " + name + ": " + err.Error()) fmt.Fprintln(stdout, "Error killing container "+name+": "+err.Error())
} }
} }
return nil return nil
@ -373,7 +372,7 @@ func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...stri
nameFilter = cmd.Arg(0) nameFilter = cmd.Arg(0)
} }
w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0) w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
if (!*quiet) { if !*quiet {
fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n") fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n")
} }
for _, name := range srv.images.Names() { for _, name := range srv.images.Names() {
@ -407,7 +406,7 @@ func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...stri
} }
} }
} }
if (!*quiet) { if !*quiet {
w.Flush() w.Flush()
} }
return nil return nil
@ -424,7 +423,7 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
return nil return nil
} }
w := tabwriter.NewWriter(stdout, 12, 1, 3, ' ', 0) w := tabwriter.NewWriter(stdout, 12, 1, 3, ' ', 0)
if (!*quiet) { if !*quiet {
fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n") fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n")
} }
for _, container := range srv.containers.List() { for _, container := range srv.containers.List() {
@ -437,7 +436,7 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
if !*fl_full { if !*fl_full {
command = docker.Trunc(command, 20) command = docker.Trunc(command, 20)
} }
for idx, field := range[]string { for idx, field := range []string{
/* ID */ container.Id, /* ID */ container.Id,
/* IMAGE */ container.GetUserData("image"), /* IMAGE */ container.GetUserData("image"),
/* COMMAND */ command, /* COMMAND */ command,
@ -456,7 +455,7 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
stdout.Write([]byte(container.Id + "\n")) stdout.Write([]byte(container.Id + "\n"))
} }
} }
if (!*quiet) { if !*quiet {
w.Flush() w.Flush()
} }
return nil return nil
@ -475,7 +474,6 @@ func (srv *Server) CmdLayers(stdin io.ReadCloser, stdout io.Writer, args ...stri
return nil return nil
} }
func (srv *Server) CmdCp(stdin io.ReadCloser, stdout io.Writer, args ...string) error { func (srv *Server) CmdCp(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, cmd := rcli.Subcmd(stdout,
"cp", "[OPTIONS] IMAGE NAME", "cp", "[OPTIONS] IMAGE NAME",
@ -521,7 +519,6 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
return errors.New("No such container: " + containerName) return errors.New("No such container: " + containerName)
} }
func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error { func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, cmd := rcli.Subcmd(stdout,
"tar", "CONTAINER", "tar", "CONTAINER",
@ -592,7 +589,6 @@ func (srv *Server) CmdReset(stdin io.ReadCloser, stdout io.Writer, args ...strin
return nil return nil
} }
func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error { func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container") cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container")
if err := cmd.Parse(args); err != nil { if err := cmd.Parse(args); err != nil {
@ -615,7 +611,6 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
return errors.New("No such container: " + cmd.Arg(0)) return errors.New("No such container: " + cmd.Arg(0))
} }
func (srv *Server) CreateContainer(img *image.Image, tty bool, openStdin bool, comment string, cmd string, args ...string) (*docker.Container, error) { func (srv *Server) CreateContainer(img *image.Image, tty bool, openStdin bool, comment string, cmd string, args ...string) (*docker.Container, error) {
id := future.RandomId()[:8] id := future.RandomId()[:8]
container, err := srv.containers.Create(id, cmd, args, img.Layers, container, err := srv.containers.Create(id, cmd, args, img.Layers,
@ -658,7 +653,7 @@ func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...stri
return err return err
} }
wg.Add(1) wg.Add(1)
go func() { io.Copy(c_stdin, stdin); wg.Add(-1); }() go func() { io.Copy(c_stdin, stdin); wg.Add(-1) }()
} }
if *fl_o { if *fl_o {
c_stdout, err := container.StdoutPipe() c_stdout, err := container.StdoutPipe()
@ -666,7 +661,7 @@ func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...stri
return err return err
} }
wg.Add(1) wg.Add(1)
go func() { io.Copy(stdout, c_stdout); wg.Add(-1); }() go func() { io.Copy(stdout, c_stdout); wg.Add(-1) }()
} }
if *fl_e { if *fl_e {
c_stderr, err := container.StderrPipe() c_stderr, err := container.StderrPipe()
@ -674,7 +669,7 @@ func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...stri
return err return err
} }
wg.Add(1) wg.Add(1)
go func() { io.Copy(stdout, c_stderr); wg.Add(-1); }() go func() { io.Copy(stdout, c_stderr); wg.Add(-1) }()
} }
wg.Wait() wg.Wait()
return nil return nil
@ -690,7 +685,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
return nil return nil
} }
name := cmd.Arg(0) name := cmd.Arg(0)
var cmdline[]string var cmdline []string
if len(cmd.Args()) >= 2 { if len(cmd.Args()) >= 2 {
cmdline = cmd.Args()[1:] cmdline = cmd.Args()[1:]
} }
@ -723,7 +718,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
if *fl_attach { if *fl_attach {
future.Go(func() error { future.Go(func() error {
log.Printf("CmdRun(): start receiving stdin\n") log.Printf("CmdRun(): start receiving stdin\n")
_, err := io.Copy(cmd_stdin, stdin); _, err := io.Copy(cmd_stdin, stdin)
log.Printf("CmdRun(): done receiving stdin\n") log.Printf("CmdRun(): done receiving stdin\n")
cmd_stdin.Close() cmd_stdin.Close()
return err return err
@ -744,11 +739,11 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
return err return err
} }
sending_stdout := future.Go(func() error { sending_stdout := future.Go(func() error {
_, err := io.Copy(stdout, cmd_stdout); _, err := io.Copy(stdout, cmd_stdout)
return err return err
}) })
sending_stderr := future.Go(func() error { sending_stderr := future.Go(func() error {
_, err := io.Copy(stdout, cmd_stderr); _, err := io.Copy(stdout, cmd_stderr)
return err return err
}) })
err_sending_stdout := <-sending_stdout err_sending_stdout := <-sending_stdout
@ -770,6 +765,11 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
} }
func main() { func main() {
if docker.SelfPath() == "/sbin/init" {
// Running in init mode
docker.SysInit()
return
}
future.Seed() future.Seed()
flag.Parse() flag.Parse()
d, err := New() d, err := New()
@ -845,9 +845,7 @@ func (srv *Server) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...string)
return nil return nil
} }
type Server struct { type Server struct {
containers *docker.Docker containers *docker.Docker
images *image.Store images *image.Store
} }

View File

@ -22,7 +22,8 @@ lxc.utsname = {{.Id}}
#lxc.network.ipv4 = {ip_address}/{ip_prefix_len} #lxc.network.ipv4 = {ip_address}/{ip_prefix_len}
# root filesystem # root filesystem
lxc.rootfs = {{.Filesystem.RootFS}} {{$ROOTFS := .Filesystem.RootFS}}
lxc.rootfs = {{$ROOTFS}}
# use a dedicated pts for the container (and limit the number of pseudo terminal # use a dedicated pts for the container (and limit the number of pseudo terminal
# available) # available)
@ -66,15 +67,18 @@ lxc.cgroup.devices.allow = c 10:200 rwm
# standard mount point # standard mount point
lxc.mount.entry = proc {{.Filesystem.RootFS}}/proc proc nosuid,nodev,noexec 0 0 lxc.mount.entry = proc {{$ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
lxc.mount.entry = sysfs {{.Filesystem.RootFS}}/sys sysfs nosuid,nodev,noexec 0 0 lxc.mount.entry = sysfs {{$ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
lxc.mount.entry = devpts {{.Filesystem.RootFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0 lxc.mount.entry = devpts {{$ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
#lxc.mount.entry = varrun {{.Filesystem.RootFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0 #lxc.mount.entry = varrun {{$ROOTFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0
#lxc.mount.entry = varlock {{.Filesystem.RootFS}}/var/lock tmpfs size=1024k,nosuid,nodev,noexec 0 0 #lxc.mount.entry = varlock {{$ROOTFS}}/var/lock tmpfs size=1024k,nosuid,nodev,noexec 0 0
#lxc.mount.entry = shm {{.Filesystem.RootFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0 #lxc.mount.entry = shm {{$ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
# Inject docker-init
lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/sbin/init none bind,ro 0 0
# In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container # In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
lxc.mount.entry = /etc/resolv.conf {{.Filesystem.RootFS}}/etc/resolv.conf none bind,ro 0 0 lxc.mount.entry = /etc/resolv.conf {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
# drop linux capabilities (apply mainly to the user root in the container) # drop linux capabilities (apply mainly to the user root in the container)

29
sysinit.go Normal file
View File

@ -0,0 +1,29 @@
package docker
import (
"fmt"
"log"
"os"
"os/exec"
"syscall"
)
// Sys Init code
// This code is run INSIDE the container and is responsible for setting
// up the environment before running the actual process
func SysInit() {
if len(os.Args) <= 1 {
fmt.Println("You should not invoke docker-init manually")
os.Exit(1)
}
path, err := exec.LookPath(os.Args[1])
if err != nil {
log.Printf("Unable to locate %v", os.Args[1])
os.Exit(127)
}
if err := syscall.Exec(path, os.Args[1:], os.Environ()); err != nil {
panic(err)
}
}

View File

@ -4,7 +4,9 @@ import (
"bytes" "bytes"
"container/list" "container/list"
"io" "io"
"os"
"os/exec" "os/exec"
"path/filepath"
"sync" "sync"
) )
@ -34,6 +36,19 @@ func Tar(path string) (io.Reader, error) {
return output, nil return output, nil
} }
// Figure out the absolute path of our own binary
func SelfPath() string {
path, err := exec.LookPath(os.Args[0])
if err != nil {
panic(err)
}
path, err = filepath.Abs(path)
if err != nil {
panic(err)
}
return path
}
type nopWriteCloser struct { type nopWriteCloser struct {
io.Writer io.Writer
} }