Restore container links at runtime start

This commit is contained in:
Michael Crosby
2013-09-25 11:48:54 -07:00
parent 7a3d4b1d3f
commit f80d778077
5 changed files with 139 additions and 24 deletions
+13 -13
View File
@@ -17,8 +17,8 @@ type Link struct {
BridgeInterface string
Alias string
FromEnvironment []string
ports []Port
isEnabled bool
Ports []Port
IsEnabled bool
}
type LinkRepository struct {
@@ -34,8 +34,8 @@ func (r *LinkRepository) NewLink(to, from *Container, bridgeInterface string, al
}
ports := make([]Port, len(from.Config.ExposedPorts))
var i int
for k := range from.Config.ExposedPorts {
ports[i] = k
for p := range from.Config.ExposedPorts {
ports[i] = p
i++
}
l := &Link{
@@ -46,7 +46,7 @@ func (r *LinkRepository) NewLink(to, from *Container, bridgeInterface string, al
FromIP: from.NetworkSettings.IPAddress,
ToIP: to.NetworkSettings.IPAddress,
FromEnvironment: from.Config.Env,
ports: ports,
Ports: ports,
}
if err := r.registerLink(l); err != nil {
return nil, err
@@ -66,7 +66,7 @@ func (l *Link) ToEnv() []string {
}
// Load exposed ports into the environment
for _, p := range l.ports {
for _, p := range l.Ports {
env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", l.Alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.FromIP, p.Port()))
}
@@ -92,18 +92,18 @@ func (l *Link) ToEnv() []string {
// Default port rules
func (l *Link) getDefaultPort() *Port {
var p Port
i := len(l.ports)
i := len(l.Ports)
if i == 0 {
return nil
} else if i > 1 {
sortPorts(l.ports, func(ip, jp Port) bool {
sortPorts(l.Ports, func(ip, jp Port) bool {
// If the two ports have the same number, tcp takes priority
// Sort in desc order
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
})
}
p = l.ports[0]
p = l.Ports[0]
return &p
}
@@ -111,7 +111,7 @@ func (l *Link) Enable() error {
if err := l.toggle("-I", false); err != nil {
return err
}
l.isEnabled = true
l.IsEnabled = true
return nil
}
@@ -120,11 +120,11 @@ func (l *Link) Disable() {
// exist in iptables
l.toggle("-D", true)
l.isEnabled = false
l.IsEnabled = false
}
func (l *Link) toggle(action string, ignoreErrors bool) error {
for _, p := range l.ports {
for _, p := range l.Ports {
if err := iptables.Raw(action, "FORWARD",
"-i", l.BridgeInterface, "-o", l.BridgeInterface,
"-p", p.Proto(),
@@ -148,7 +148,7 @@ func (l *Link) toggle(action string, ignoreErrors bool) error {
return nil
}
func NewLinkRepository(root string) (*LinkRepository, error) {
func NewLinkRepository() (*LinkRepository, error) {
r := &LinkRepository{make(map[string]*Link)}
return r, nil
}
+2 -2
View File
@@ -8,7 +8,7 @@ import (
)
func newTestLinkRepository(t *testing.T) *LinkRepository {
r, err := NewLinkRepository("")
r, err := NewLinkRepository()
if err != nil {
t.Fatal(err)
}
@@ -70,7 +70,7 @@ func TestLinkNew(t *testing.T) {
if link.BridgeInterface != "172.0.17.1" {
t.Fail()
}
for _, p := range link.ports {
for _, p := range link.Ports {
if p != Port("6379/tcp") {
t.Fail()
}
+26 -9
View File
@@ -87,10 +87,9 @@ func (runtime *Runtime) containerRoot(id string) string {
return path.Join(runtime.repository, id)
}
// Load reads the contents of a container from disk and registers
// it with Register.
// Load reads the contents of a container from disk
// This is typically done at startup.
func (runtime *Runtime) Load(id string) (*Container, error) {
func (runtime *Runtime) load(id string) (*Container, error) {
container := &Container{root: runtime.containerRoot(id)}
if err := container.FromDisk(); err != nil {
return nil, err
@@ -101,9 +100,6 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
if container.State.Running {
container.State.Ghost = true
}
if err := runtime.Register(container); err != nil {
return nil, err
}
return container, nil
}
@@ -152,7 +148,7 @@ func (runtime *Runtime) Register(container *Container) error {
utils.Debugf("Restarting")
container.State.Ghost = false
container.State.setStopped(0)
hostConfig := &HostConfig{}
hostConfig, _ := container.ReadHostConfig()
if err := container.Start(hostConfig); err != nil {
return err
}
@@ -227,9 +223,10 @@ func (runtime *Runtime) restore() error {
if err != nil {
return err
}
containers := []*Container{}
for i, v := range dir {
id := v.Name()
container, err := runtime.Load(id)
container, err := runtime.load(id)
if i%21 == 0 && os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
fmt.Printf("\b%c", wheel[i%4])
}
@@ -238,10 +235,30 @@ func (runtime *Runtime) restore() error {
continue
}
utils.Debugf("Loaded container %v", container.ID)
containers = append(containers, container)
}
sortContainers(containers, func(i, j *Container) bool {
ic, _ := i.ReadHostConfig()
jc, _ := j.ReadHostConfig()
if ic == nil || ic.Links == nil {
return true
}
if jc == nil || jc.Links == nil {
return false
}
return len(ic.Links) < len(jc.Links)
})
for _, container := range containers {
if err := runtime.Register(container); err != nil {
utils.Debugf("Failed to register container %s: %s", container.ID, err)
continue
}
}
if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
fmt.Printf("\bdone.\n")
}
return nil
}
@@ -482,7 +499,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
if err != nil {
return nil, err
}
links, err := NewLinkRepository("")
links, err := NewLinkRepository()
if err != nil {
return nil, err
}
+76
View File
@@ -498,3 +498,79 @@ func TestRestore(t *testing.T) {
}
container2.State.Running = false
}
func TestReloadContainerLinks(t *testing.T) {
runtime1 := mkRuntime(t)
defer nuke(runtime1)
// Create a container with one instance of docker
container1, _, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t)
defer runtime1.Destroy(container1)
// Create a second container meant to be killed
container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
defer runtime1.Destroy(container2)
// Start the container non blocking
hostConfig := &HostConfig{}
if err := container2.Start(hostConfig); err != nil {
t.Fatal(err)
}
h1 := &HostConfig{}
// Add a link to container 2
h1.Links = []string{utils.TruncateID(container2.ID) + ":first"}
if err := container1.Start(h1); err != nil {
t.Fatal(err)
}
if !container2.State.Running {
t.Fatalf("Container %v should appear as running but isn't", container2.ID)
}
if !container1.State.Running {
t.Fatalf("Container %s should appear as running bu isn't", container1.ID)
}
if len(runtime1.List()) != 2 {
t.Errorf("Expected 2 container, %v found", len(runtime1.List()))
}
if !container2.State.Running {
t.Fatalf("Container %v should appear as running but isn't", container2.ID)
}
// Here are are simulating a docker restart - that is, reloading all containers
// from scratch
runtime1.config.AutoRestart = true
runtime2, err := NewRuntimeFromDirectory(runtime1.config)
if err != nil {
t.Fatal(err)
}
defer nuke(runtime2)
if len(runtime2.List()) != 2 {
t.Errorf("Expected 2 container, %v found", len(runtime2.List()))
}
runningCount := 0
for _, c := range runtime2.List() {
if c.State.Running {
t.Logf("Running container found: %v (%v)", c.ID, c.Path)
runningCount++
}
}
if runningCount != 2 {
t.Fatalf("Expected 2 container alive, %d found", runningCount)
}
// Make sure container 2 ( the child of container 1 ) was registered and started first
// with the runtime
first := runtime2.containers.Front()
if first.Value.(*Container).ID != container2.ID {
t.Fatalf("Container 2 %s should be registered first in the runtime", container2.ID)
}
t.Logf("Number of links: %d", len(runtime2.links.links))
// Verify that the link is still registered in the runtime
links := runtime2.links.Get(container1)
if len(links) != 1 {
t.Fatalf("Expected 1 link but found %d", len(links))
}
}
+22
View File
@@ -59,3 +59,25 @@ func sortPorts(ports []Port, predicate func(i, j Port) bool) {
s := &portSorter{ports, predicate}
sort.Sort(s)
}
type containerSorter struct {
containers []*Container
by func(i, j *Container) bool
}
func (s *containerSorter) Len() int {
return len(s.containers)
}
func (s *containerSorter) Swap(i, j int) {
s.containers[i], s.containers[j] = s.containers[j], s.containers[i]
}
func (s *containerSorter) Less(i, j int) bool {
return s.by(s.containers[i], s.containers[j])
}
func sortContainers(containers []*Container, predicate func(i, j *Container) bool) {
s := &containerSorter{containers, predicate}
sort.Sort(s)
}