runtime.go
a27b4b8c
 package docker
 
 import (
 	"container/list"
 	"fmt"
2e69e172
 	"github.com/dotcloud/docker/utils"
7c57a4cf
 	"io"
a27b4b8c
 	"io/ioutil"
003622c8
 	"log"
a27b4b8c
 	"os"
3dcaf20d
 	"os/exec"
a27b4b8c
 	"path"
f8f9285c
 	"sort"
3dcaf20d
 	"strings"
a27b4b8c
 )
 
640efc2e
 type Capabilities struct {
 	MemoryLimit bool
 	SwapLimit   bool
 }
 
b8547f31
 type Runtime struct {
bd2f5129
 	root           string
 	repository     string
 	containers     *list.List
 	networkManager *NetworkManager
ef711962
 	graph          *Graph
 	repositories   *TagStore
2e69e172
 	idIndex        *utils.TruncIndex
640efc2e
 	capabilities   *Capabilities
2e69e172
 	kernelVersion  *utils.KernelVersionInfo
50144aeb
 	autoRestart    bool
1df5f409
 	volumes        *Graph
95dd6d31
 	srv            *Server
84d68007
 	Dns            []string
7c57a4cf
 }
 
 var sysInitPath string
 
 func init() {
2e69e172
 	sysInitPath = utils.SelfPath()
a27b4b8c
 }
 
b8547f31
 func (runtime *Runtime) List() []*Container {
f8f9285c
 	containers := new(History)
b8547f31
 	for e := runtime.containers.Front(); e != nil; e = e.Next() {
f8f9285c
 		containers.Add(e.Value.(*Container))
c7a944ca
 	}
f8f9285c
 	return *containers
a27b4b8c
 }
 
b8547f31
 func (runtime *Runtime) getContainerElement(id string) *list.Element {
 	for e := runtime.containers.Front(); e != nil; e = e.Next() {
a27b4b8c
 		container := e.Value.(*Container)
fd224ee5
 		if container.ID == id {
a27b4b8c
 			return e
 		}
 	}
 	return nil
 }
 
0b9a3c86
 func (runtime *Runtime) Get(name string) *Container {
 	id, err := runtime.idIndex.Get(name)
 	if err != nil {
 		return nil
 	}
b8547f31
 	e := runtime.getContainerElement(id)
a27b4b8c
 	if e == nil {
 		return nil
 	}
 	return e.Value.(*Container)
 }
 
b8547f31
 func (runtime *Runtime) Exists(id string) bool {
 	return runtime.Get(id) != nil
a27b4b8c
 }
 
b8547f31
 func (runtime *Runtime) containerRoot(id string) string {
 	return path.Join(runtime.repository, id)
7c57a4cf
 }
 
b8547f31
 func (runtime *Runtime) Load(id string) (*Container, error) {
 	container := &Container{root: runtime.containerRoot(id)}
7c57a4cf
 	if err := container.FromDisk(); err != nil {
 		return nil, err
 	}
fd224ee5
 	if container.ID != id {
 		return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
7c57a4cf
 	}
313d13ea
 	if container.State.Running {
 		container.State.Ghost = true
 	}
b8547f31
 	if err := runtime.Register(container); err != nil {
a27b4b8c
 		return nil, err
 	}
 	return container, nil
 }
 
fd224ee5
 // Register makes a container object usable by the runtime as <container.ID>
b8547f31
 func (runtime *Runtime) Register(container *Container) error {
fd224ee5
 	if container.runtime != nil || runtime.Exists(container.ID) {
7c57a4cf
 		return fmt.Errorf("Container is already loaded")
 	}
fd224ee5
 	if err := validateID(container.ID); err != nil {
7c57a4cf
 		return err
 	}
3dcaf20d
 
9f83b9df
 	// init the wait lock
 	container.waitLock = make(chan struct{})
 
 	// Even if not running, we init the lock (prevents races in start/stop/kill)
7c2b085d
 	container.State.initLock()
3dcaf20d
 
b8547f31
 	container.runtime = runtime
329f4449
 
7c57a4cf
 	// Attach to stdout and stderr
2e69e172
 	container.stderr = utils.NewWriteBroadcaster()
 	container.stdout = utils.NewWriteBroadcaster()
7c57a4cf
 	// Attach to stdin
 	if container.Config.OpenStdin {
 		container.stdin, container.stdinPipe = io.Pipe()
 	} else {
2e69e172
 		container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
7c57a4cf
 	}
 	// done
b8547f31
 	runtime.containers.PushBack(container)
fd224ee5
 	runtime.idIndex.Add(container.ID)
82848d41
 
50144aeb
 	// When we actually restart, Start() do the monitoring.
 	// However, when we simply 'reattach', we have to restart a monitor
 	nomonitor := false
 
 	// FIXME: if the container is supposed to be running but is not, auto restart it?
 	//        if so, then we need to restart monitor and init a new lock
 	// If the container is supposed to be running, make sure of it
 	if container.State.Running {
fd224ee5
 		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
86ada2fa
 		if err != nil {
50144aeb
 			return err
86ada2fa
 		}
 		if !strings.Contains(string(output), "RUNNING") {
fd224ee5
 			utils.Debugf("Container %s was supposed to be running be is not.", container.ID)
86ada2fa
 			if runtime.autoRestart {
 				utils.Debugf("Restarting")
 				container.State.Ghost = false
 				container.State.setStopped(0)
 				if err := container.Start(); err != nil {
 					return err
 				}
 				nomonitor = true
 			} else {
 				utils.Debugf("Marking as stopped")
 				container.State.setStopped(-127)
 				if err := container.ToDisk(); err != nil {
 					return err
50144aeb
 				}
 			}
 		}
 	}
 
82848d41
 	// If the container is not running or just has been flagged not running
 	// then close the wait lock chan (will be reset upon start)
 	if !container.State.Running {
 		close(container.waitLock)
50144aeb
 	} else if !nomonitor {
82848d41
 		container.allocateNetwork()
 		go container.monitor()
 	}
7c57a4cf
 	return nil
 }
 
2e69e172
 func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst string) error {
7c57a4cf
 	log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
 	if err != nil {
 		return err
 	}
0f7a4534
 	src.AddWriter(log)
7c57a4cf
 	return nil
 }
 
b8547f31
 func (runtime *Runtime) Destroy(container *Container) error {
42b1ea48
 	if container == nil {
 		return fmt.Errorf("The given container is <nil>")
 	}
 
fd224ee5
 	element := runtime.getContainerElement(container.ID)
a27b4b8c
 	if element == nil {
fd224ee5
 		return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
a27b4b8c
 	}
 
28fd289b
 	if err := container.Stop(3); err != nil {
a27b4b8c
 		return err
 	}
7c57a4cf
 	if mounted, err := container.Mounted(); err != nil {
 		return err
 	} else if mounted {
 		if err := container.Unmount(); err != nil {
fd224ee5
 			return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
fb40a788
 		}
fcc0af9f
 	}
7c57a4cf
 	// Deregister the container before removing its directory, to avoid race conditions
fd224ee5
 	runtime.idIndex.Delete(container.ID)
b8547f31
 	runtime.containers.Remove(element)
7c57a4cf
 	if err := os.RemoveAll(container.root); err != nil {
fd224ee5
 		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
a27b4b8c
 	}
 	return nil
 }
 
b8547f31
 func (runtime *Runtime) restore() error {
 	dir, err := ioutil.ReadDir(runtime.repository)
a27b4b8c
 	if err != nil {
 		return err
 	}
 	for _, v := range dir {
7c57a4cf
 		id := v.Name()
b8547f31
 		container, err := runtime.Load(id)
a27b4b8c
 		if err != nil {
2e69e172
 			utils.Debugf("Failed to load container %v: %v", id, err)
a27b4b8c
 			continue
 		}
fd224ee5
 		utils.Debugf("Loaded container %v", container.ID)
a27b4b8c
 	}
 	return nil
 }
 
9042535f
 func (runtime *Runtime) UpdateCapabilities(quiet bool) {
2e69e172
 	if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil {
9042535f
 		if !quiet {
 			log.Printf("WARNING: %s\n", err)
 		}
 	} else {
 		_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes"))
 		_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
 		runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
 		if !runtime.capabilities.MemoryLimit && !quiet {
 			log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
 		}
 
 		_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
 		runtime.capabilities.SwapLimit = err == nil
 		if !runtime.capabilities.SwapLimit && !quiet {
 			log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
 		}
 	}
 }
 
0b9a3c86
 // FIXME: harmonize with NewGraph()
84d68007
 func NewRuntime(autoRestart bool, dns []string) (*Runtime, error) {
50144aeb
 	runtime, err := NewRuntimeFromDirectory("/var/lib/docker", autoRestart)
003622c8
 	if err != nil {
 		return nil, err
 	}
84d68007
 	runtime.Dns = dns
003622c8
 
f3bab52d
 	if k, err := utils.GetKernelVersion(); err != nil {
3514e47e
 		log.Printf("WARNING: %s\n", err)
 	} else {
 		runtime.kernelVersion = k
2e69e172
 		if utils.CompareKernelVersion(k, &utils.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
3514e47e
 			log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
 		}
003622c8
 	}
9042535f
 	runtime.UpdateCapabilities(false)
003622c8
 	return runtime, nil
a27b4b8c
 }
 
50144aeb
 func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
a6da7f13
 	runtimeRepo := path.Join(root, "containers")
06553a75
 
a6da7f13
 	if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
06553a75
 		return nil, err
 	}
 
ef711962
 	g, err := NewGraph(path.Join(root, "graph"))
2ebf3464
 	if err != nil {
 		return nil, err
 	}
1df5f409
 	volumes, err := NewGraph(path.Join(root, "volumes"))
 	if err != nil {
 		return nil, err
 	}
ef711962
 	repositories, err := NewTagStore(path.Join(root, "repositories"), g)
44faa07b
 	if err != nil {
 		return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
 	}
1b370f9d
 	if NetworkBridgeIface == "" {
 		NetworkBridgeIface = DefaultNetworkBridge
 	}
f39af7e0
 	netManager, err := newNetworkManager(NetworkBridgeIface)
c08f5b2b
 	if err != nil {
 		return nil, err
 	}
b8547f31
 	runtime := &Runtime{
bd2f5129
 		root:           root,
a6da7f13
 		repository:     runtimeRepo,
bd2f5129
 		containers:     list.New(),
 		networkManager: netManager,
44faa07b
 		graph:          g,
 		repositories:   repositories,
2e69e172
 		idIndex:        utils.NewTruncIndex(),
640efc2e
 		capabilities:   &Capabilities{},
50144aeb
 		autoRestart:    autoRestart,
1df5f409
 		volumes:        volumes,
a27b4b8c
 	}
 
b8547f31
 	if err := runtime.restore(); err != nil {
a27b4b8c
 		return nil, err
 	}
b8547f31
 	return runtime, nil
a27b4b8c
 }
f8f9285c
 
 type History []*Container
 
 func (history *History) Len() int {
 	return len(*history)
 }
 
 func (history *History) Less(i, j int) bool {
 	containers := *history
 	return containers[j].When().Before(containers[i].When())
 }
 
 func (history *History) Swap(i, j int) {
 	containers := *history
 	tmp := containers[i]
 	containers[i] = containers[j]
 	containers[j] = tmp
 }
 
 func (history *History) Add(container *Container) {
 	*history = append(*history, container)
 	sort.Sort(history)
 }