mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-21 05:07:22 +00:00
Restore container links at runtime start
This commit is contained in:
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user