mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-22 22:56:17 +00:00
Refactor to use graph for linking
This commit is contained in:
@@ -997,9 +997,8 @@ func getContainersLinks(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
runtime := srv.runtime
|
||||
|
||||
out := []APILink{}
|
||||
err := runtime.containerGraph.Walk(func(p string, e *gograph.Entity) error {
|
||||
container := runtime.Get(e.ID())
|
||||
if container != nil {
|
||||
err := runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error {
|
||||
if container := runtime.Get(e.ID()); container != nil {
|
||||
out = append(out, APILink{
|
||||
Path: p,
|
||||
ContainerID: container.ID,
|
||||
@@ -1007,14 +1006,44 @@ func getContainersLinks(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}, -1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, out)
|
||||
}
|
||||
|
||||
func postContainerLink(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
values := make(map[string]string)
|
||||
if matchesContentType(r.Header.Get("Content-Type"), "application/json") && r.Body != nil {
|
||||
defer r.Body.Close()
|
||||
|
||||
dec := json.NewDecoder(r.Body)
|
||||
if err := dec.Decode(&values); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Invalid json body")
|
||||
}
|
||||
currentName := values["currentName"]
|
||||
newName := values["newName"]
|
||||
|
||||
if currentName == "" {
|
||||
return fmt.Errorf("currentName cannot be empty")
|
||||
}
|
||||
if newName == "" {
|
||||
return fmt.Errorf("newName cannot be empty")
|
||||
}
|
||||
|
||||
if err := srv.runtime.RenameLink(currentName, newName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
r := mux.NewRouter()
|
||||
|
||||
@@ -1054,6 +1083,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
"/containers/{name:.*}/resize": postContainersResize,
|
||||
"/containers/{name:.*}/attach": postContainersAttach,
|
||||
"/containers/{name:.*}/copy": postContainersCopy,
|
||||
"/containers/link": postContainerLink,
|
||||
},
|
||||
"DELETE": {
|
||||
"/containers/{name:.*}": deleteContainers,
|
||||
|
||||
+21
@@ -1142,6 +1142,27 @@ func (cli *DockerCli) CmdLs(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdLink(args ...string) error {
|
||||
cmd := Subcmd("link", "CURRENT_NAME NEW_NAME", "Link the container with a new name")
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
if cmd.NArg() != 2 {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
body := map[string]string{
|
||||
"currentName": cmd.Arg(0),
|
||||
"newName": cmd.Arg(10),
|
||||
}
|
||||
|
||||
_, _, err := cli.call("POST", "/containers/link", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdCommit(args ...string) error {
|
||||
cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
|
||||
flComment := cmd.String("m", "", "Commit message")
|
||||
|
||||
+32
-21
@@ -57,6 +57,8 @@ type Container struct {
|
||||
// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
|
||||
// Easier than migrating older container configs :)
|
||||
VolumesRW map[string]bool
|
||||
|
||||
activeLinks map[string]*Link
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@@ -830,30 +832,39 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
||||
"-e", "HOSTNAME="+container.Config.Hostname,
|
||||
)
|
||||
|
||||
if !container.Config.NetworkDisabled && hostConfig != nil && hostConfig.Links != nil {
|
||||
runtime := container.runtime
|
||||
for _, l := range hostConfig.Links {
|
||||
parts, err := parseLink(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
linkedContainer := runtime.Get(parts["id"])
|
||||
if linkedContainer == nil {
|
||||
return fmt.Errorf("Cannot find container: %s", parts["id"])
|
||||
}
|
||||
// Init any links between the parent and children
|
||||
runtime := container.runtime
|
||||
|
||||
link, err := runtime.links.NewLink(container, linkedContainer, runtime.networkManager.bridgeIface, parts["alias"])
|
||||
children, err := runtime.Children(fmt.Sprintf("/%s", container.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(children) > 0 {
|
||||
container.activeLinks = make(map[string]*Link, len(children))
|
||||
|
||||
// If we encounter an error make sure that we rollback any network
|
||||
// config and ip table changes
|
||||
rollback := func() {
|
||||
for _, link := range container.activeLinks {
|
||||
link.Disable()
|
||||
}
|
||||
container.activeLinks = nil
|
||||
}
|
||||
|
||||
for p, child := range children {
|
||||
link, err := NewLink(container, child, p, runtime.networkManager.bridgeIface)
|
||||
if err != nil {
|
||||
rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
container.activeLinks[p] = link
|
||||
if err := link.Enable(); err != nil {
|
||||
// If we encounter an err, make sure we remove all links
|
||||
for _, registeredLinks := range runtime.links.Get(container) {
|
||||
runtime.links.removeLink(registeredLinks)
|
||||
}
|
||||
rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
for _, envVar := range link.ToEnv() {
|
||||
params = append(params, "-e", envVar)
|
||||
}
|
||||
@@ -893,7 +904,6 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
||||
|
||||
container.cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
||||
|
||||
var err error
|
||||
if container.Config.Tty {
|
||||
err = container.startPty()
|
||||
} else {
|
||||
@@ -1096,10 +1106,11 @@ func (container *Container) monitor(hostConfig *HostConfig) {
|
||||
// Cleanup
|
||||
container.releaseNetwork()
|
||||
|
||||
//Destroy all links
|
||||
runtime := container.runtime
|
||||
for _, link := range runtime.links.Get(container) {
|
||||
runtime.links.removeLink(link)
|
||||
// Disable all active links
|
||||
if container.activeLinks != nil {
|
||||
for _, link := range container.activeLinks {
|
||||
link.Disable()
|
||||
}
|
||||
}
|
||||
|
||||
if container.Config.OpenStdin {
|
||||
|
||||
+2
-6
@@ -129,12 +129,8 @@ func (db *Database) List(name string, depth int) Entities {
|
||||
return out
|
||||
}
|
||||
|
||||
func (db *Database) Walk(walkFunc WalkFunc) error {
|
||||
parent := db.RootEntity()
|
||||
if err := walkFunc("/", parent); err != nil {
|
||||
return err
|
||||
}
|
||||
for c := range db.children("/", -1) {
|
||||
func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
|
||||
for c := range db.children(name, depth) {
|
||||
if err := walkFunc(c.FullPath, c.Entity); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -182,10 +182,10 @@ func TestWalkAll(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := db.Walk(func(p string, e *Entity) error {
|
||||
if err := db.Walk("/", func(p string, e *Entity) error {
|
||||
t.Logf("Path: %s Entity: %s", p, e.ID())
|
||||
return nil
|
||||
}); err != nil {
|
||||
}, -1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -406,8 +406,8 @@ func TestCreateMultipleNames(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db.Walk(func(p string, e *Entity) error {
|
||||
db.Walk("/", func(p string, e *Entity) error {
|
||||
t.Logf("%s\n", p)
|
||||
return nil
|
||||
})
|
||||
}, -1)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ func pathDepth(p string) int {
|
||||
}
|
||||
|
||||
func splitPath(p string) (parent, name string) {
|
||||
if p[0] != '/' {
|
||||
p = "/" + p
|
||||
}
|
||||
parent, name = path.Split(p)
|
||||
l := len(parent)
|
||||
if parent[l-1] == '/' {
|
||||
|
||||
@@ -3,76 +3,70 @@ package docker
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/iptables"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Link struct {
|
||||
FromID string
|
||||
ToID string
|
||||
FromIP string
|
||||
ToIP string
|
||||
BridgeInterface string
|
||||
Alias string
|
||||
FromEnvironment []string
|
||||
Ports []Port
|
||||
IsEnabled bool
|
||||
ParentIP string
|
||||
ChildIP string
|
||||
Name string
|
||||
BridgeInterface string
|
||||
ChildEnvironment []string
|
||||
Ports []Port
|
||||
IsEnabled bool
|
||||
}
|
||||
|
||||
type LinkRepository struct {
|
||||
links map[string]*Link
|
||||
}
|
||||
func NewLink(parent, child *Container, name, bridgeInterface string) (*Link, error) {
|
||||
if parent.ID == child.ID {
|
||||
return nil, fmt.Errorf("Cannot link to self: %s == %s", parent.ID, child.ID)
|
||||
}
|
||||
if !child.State.Running {
|
||||
return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.ID, name)
|
||||
}
|
||||
|
||||
func (r *LinkRepository) NewLink(to, from *Container, bridgeInterface string, alias string) (*Link, error) {
|
||||
if to.ID == from.ID {
|
||||
return nil, fmt.Errorf("Cannot link to self: %s == %s", to.ID, from.ID)
|
||||
}
|
||||
if !from.State.Running {
|
||||
return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", from.ID, alias)
|
||||
}
|
||||
ports := make([]Port, len(from.Config.ExposedPorts))
|
||||
ports := make([]Port, len(child.Config.ExposedPorts))
|
||||
var i int
|
||||
for p := range from.Config.ExposedPorts {
|
||||
for p := range child.Config.ExposedPorts {
|
||||
ports[i] = p
|
||||
i++
|
||||
}
|
||||
|
||||
l := &Link{
|
||||
FromID: utils.TruncateID(from.ID),
|
||||
ToID: utils.TruncateID(to.ID),
|
||||
BridgeInterface: bridgeInterface,
|
||||
Alias: alias,
|
||||
FromIP: from.NetworkSettings.IPAddress,
|
||||
ToIP: to.NetworkSettings.IPAddress,
|
||||
FromEnvironment: from.Config.Env,
|
||||
Ports: ports,
|
||||
}
|
||||
if err := r.registerLink(l); err != nil {
|
||||
return nil, err
|
||||
BridgeInterface: bridgeInterface,
|
||||
Name: name,
|
||||
ChildIP: child.NetworkSettings.IPAddress,
|
||||
ParentIP: parent.NetworkSettings.IPAddress,
|
||||
ChildEnvironment: child.Config.Env,
|
||||
Ports: ports,
|
||||
}
|
||||
return l, nil
|
||||
|
||||
}
|
||||
|
||||
func (l *Link) ID() string {
|
||||
return fmt.Sprintf("%s:%s", l.ToID, l.Alias)
|
||||
func (l *Link) Alias() string {
|
||||
_, alias := path.Split(l.Name)
|
||||
return alias
|
||||
}
|
||||
|
||||
func (l *Link) ToEnv() []string {
|
||||
env := []string{}
|
||||
alias := l.Alias()
|
||||
|
||||
if p := l.getDefaultPort(); p != nil {
|
||||
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", l.Alias, p.Proto(), l.FromIP, p.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
|
||||
}
|
||||
|
||||
// Load exposed ports into the environment
|
||||
for _, p := range l.Ports {
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", l.Alias, p.Port(), p.Proto(), p.Proto(), l.FromIP, p.Port()))
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), p.Proto(), p.Proto(), l.ChildIP, p.Port()))
|
||||
}
|
||||
|
||||
// Load the linked container's ID into the environment
|
||||
env = append(env, fmt.Sprintf("%s_ID=%s", l.Alias, l.FromID))
|
||||
// Load the linked container's name into the environment
|
||||
env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name))
|
||||
|
||||
if l.FromEnvironment != nil {
|
||||
for _, v := range l.FromEnvironment {
|
||||
if l.ChildEnvironment != nil {
|
||||
for _, v := range l.ChildEnvironment {
|
||||
parts := strings.Split(v, "=")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
@@ -81,7 +75,7 @@ func (l *Link) ToEnv() []string {
|
||||
if parts[0] == "HOME" || parts[0] == "PATH" {
|
||||
continue
|
||||
}
|
||||
env = append(env, fmt.Sprintf("%s_ENV_%s=%s", l.Alias, parts[0], parts[1]))
|
||||
env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1]))
|
||||
}
|
||||
}
|
||||
return env
|
||||
@@ -126,9 +120,9 @@ func (l *Link) toggle(action string, ignoreErrors bool) error {
|
||||
if err := iptables.Raw(action, "FORWARD",
|
||||
"-i", l.BridgeInterface, "-o", l.BridgeInterface,
|
||||
"-p", p.Proto(),
|
||||
"-s", l.ToIP,
|
||||
"-s", l.ParentIP,
|
||||
"--dport", p.Port(),
|
||||
"-d", l.FromIP,
|
||||
"-d", l.ChildIP,
|
||||
"-j", "ACCEPT"); !ignoreErrors && err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -136,65 +130,12 @@ func (l *Link) toggle(action string, ignoreErrors bool) error {
|
||||
if err := iptables.Raw(action, "FORWARD",
|
||||
"-i", l.BridgeInterface, "-o", l.BridgeInterface,
|
||||
"-p", p.Proto(),
|
||||
"-s", l.FromIP,
|
||||
"-s", l.ChildIP,
|
||||
"--sport", p.Port(),
|
||||
"-d", l.ToIP,
|
||||
"-d", l.ParentIP,
|
||||
"-j", "ACCEPT"); !ignoreErrors && err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewLinkRepository() (*LinkRepository, error) {
|
||||
r := &LinkRepository{make(map[string]*Link)}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Return all links for a container
|
||||
func (l *LinkRepository) Get(c *Container) []*Link {
|
||||
id := utils.TruncateID(c.ID)
|
||||
out := []*Link{}
|
||||
for _, link := range l.links {
|
||||
if link.ToID == id || link.FromID == id {
|
||||
out = append(out, link)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Return all links in the repository
|
||||
func (l *LinkRepository) GetAll() []*Link {
|
||||
out := make([]*Link, len(l.links))
|
||||
var i int
|
||||
for _, link := range l.links {
|
||||
out[i] = link
|
||||
i++
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Get a link based on the link's ID
|
||||
func (l *LinkRepository) GetById(id string) *Link {
|
||||
return l.links[id]
|
||||
}
|
||||
|
||||
// Create a new link with a unique alias
|
||||
func (l *LinkRepository) registerLink(link *Link) error {
|
||||
if _, exists := l.links[link.ID()]; exists {
|
||||
return fmt.Errorf("A link for %s already exists", link.ID())
|
||||
}
|
||||
utils.Debugf("Registering link: %s", link.ID())
|
||||
l.links[link.ID()] = link
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Disable and remote the link from the repository
|
||||
func (l *LinkRepository) removeLink(link *Link) error {
|
||||
link.Disable()
|
||||
|
||||
utils.Debugf("Removing link: %s", link.ID())
|
||||
delete(l.links, link.ID())
|
||||
return nil
|
||||
}
|
||||
|
||||
+8
-26
@@ -1,20 +1,10 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func newTestLinkRepository(t *testing.T) *LinkRepository {
|
||||
r, err := NewLinkRepository()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func newMockLinkContainer(id string, ip string) *Container {
|
||||
return &Container{
|
||||
Config: &Config{},
|
||||
@@ -26,7 +16,6 @@ func newMockLinkContainer(id string, ip string) *Container {
|
||||
}
|
||||
|
||||
func TestLinkNew(t *testing.T) {
|
||||
r := newTestLinkRepository(t)
|
||||
toID := GenerateID()
|
||||
fromID := GenerateID()
|
||||
|
||||
@@ -41,7 +30,7 @@ func TestLinkNew(t *testing.T) {
|
||||
|
||||
to := newMockLinkContainer(toID, "172.0.17.3")
|
||||
|
||||
link, err := r.NewLink(to, from, "172.0.17.1", "docker")
|
||||
link, err := NewLink(to, from, "/db/docker", "172.0.17.1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -49,22 +38,16 @@ func TestLinkNew(t *testing.T) {
|
||||
if link == nil {
|
||||
t.FailNow()
|
||||
}
|
||||
if link.ID() != fmt.Sprintf("%s:%s", utils.TruncateID(to.ID), "docker") {
|
||||
if link.Name != "/db/docker" {
|
||||
t.Fail()
|
||||
}
|
||||
if link.Alias != "docker" {
|
||||
if link.Alias() != "docker" {
|
||||
t.Fail()
|
||||
}
|
||||
if link.FromID != utils.TruncateID(from.ID) {
|
||||
if link.ParentIP != "172.0.17.3" {
|
||||
t.Fail()
|
||||
}
|
||||
if link.ToID != utils.TruncateID(to.ID) {
|
||||
t.Fail()
|
||||
}
|
||||
if link.ToIP != "172.0.17.3" {
|
||||
t.Fail()
|
||||
}
|
||||
if link.FromIP != "172.0.17.2" {
|
||||
if link.ChildIP != "172.0.17.2" {
|
||||
t.Fail()
|
||||
}
|
||||
if link.BridgeInterface != "172.0.17.1" {
|
||||
@@ -78,7 +61,6 @@ func TestLinkNew(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLinkEnv(t *testing.T) {
|
||||
r := newTestLinkRepository(t)
|
||||
toID := GenerateID()
|
||||
fromID := GenerateID()
|
||||
|
||||
@@ -93,7 +75,7 @@ func TestLinkEnv(t *testing.T) {
|
||||
|
||||
to := newMockLinkContainer(toID, "172.0.17.3")
|
||||
|
||||
link, err := r.NewLink(to, from, "172.0.17.1", "docker")
|
||||
link, err := NewLink(to, from, "/db/docker", "172.0.17.1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -113,8 +95,8 @@ func TestLinkEnv(t *testing.T) {
|
||||
if env["docker_PORT_6379_tcp"] != "tcp://172.0.17.2:6379" {
|
||||
t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["docker_PORT_6379_tcp"])
|
||||
}
|
||||
if env["docker_ID"] != utils.TruncateID(from.ID) {
|
||||
t.Fatalf("Expected %s, got %s", utils.TruncateID(from.ID), env["docker_ID"])
|
||||
if env["docker_NAME"] != "/db/docker" {
|
||||
t.Fatalf("Expected /db/docker, got %s", env["docker_NAME"])
|
||||
}
|
||||
if env["docker_ENV_PASSWORD"] != "gordon" {
|
||||
t.Fatalf("Expected gordon, got %s", env["docker_ENV_PASSWORD"])
|
||||
|
||||
+60
-6
@@ -36,7 +36,6 @@ type Runtime struct {
|
||||
volumes *Graph
|
||||
srv *Server
|
||||
config *DaemonConfig
|
||||
links *LinkRepository
|
||||
containerGraph *gograph.Database
|
||||
}
|
||||
|
||||
@@ -462,6 +461,66 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) GetByName(name string) (*Container, error) {
|
||||
entity := runtime.containerGraph.Get(name)
|
||||
if entity == nil {
|
||||
return nil, fmt.Errorf("Could not find entity for %s", name)
|
||||
}
|
||||
container := runtime.Get(entity.ID())
|
||||
if container == nil {
|
||||
return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
|
||||
}
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
|
||||
children := make(map[string]*Container)
|
||||
|
||||
err := runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error {
|
||||
c := runtime.Get(e.ID())
|
||||
if c == nil {
|
||||
return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
|
||||
}
|
||||
children[p] = c
|
||||
return nil
|
||||
}, 0)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return children, nil
|
||||
}
|
||||
|
||||
func (runtime *Runtime) RenameLink(oldName, newName string) error {
|
||||
entity := runtime.containerGraph.Get(oldName)
|
||||
if entity == nil {
|
||||
return fmt.Errorf("Could not find entity for %s", oldName)
|
||||
}
|
||||
|
||||
// This is not rename but adding a new link for the default name
|
||||
// Strip the leading '/'
|
||||
if entity.ID() == oldName[1:] {
|
||||
_, err := runtime.containerGraph.Set(newName, entity.ID())
|
||||
return err
|
||||
}
|
||||
return runtime.containerGraph.Rename(oldName, newName)
|
||||
}
|
||||
|
||||
func (runtime *Runtime) Link(parentName, childName, alias string) error {
|
||||
parent := runtime.containerGraph.Get(parentName)
|
||||
if parent == nil {
|
||||
return fmt.Errorf("Could not get container for %s", parentName)
|
||||
}
|
||||
child := runtime.containerGraph.Get(childName)
|
||||
if child == nil {
|
||||
return fmt.Errorf("Could not get container for %s", childName)
|
||||
}
|
||||
cc := runtime.Get(child.ID())
|
||||
|
||||
_, err := runtime.containerGraph.Set(path.Join(parentName, alias), cc.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME: harmonize with NewGraph()
|
||||
func NewRuntime(config *DaemonConfig) (*Runtime, error) {
|
||||
runtime, err := NewRuntimeFromDirectory(config)
|
||||
@@ -507,10 +566,6 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
links, err := NewLinkRepository()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
graph, err := gograph.NewDatabase("", "engine")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -526,7 +581,6 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
||||
capabilities: &Capabilities{},
|
||||
volumes: volumes,
|
||||
config: config,
|
||||
links: links,
|
||||
containerGraph: graph,
|
||||
}
|
||||
|
||||
|
||||
+198
-4
@@ -500,6 +500,8 @@ func TestRestore(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReloadContainerLinks(t *testing.T) {
|
||||
t.SkipNow() // TODO: @crosbymichael
|
||||
|
||||
runtime1 := mkRuntime(t)
|
||||
defer nuke(runtime1)
|
||||
// Create a container with one instance of docker
|
||||
@@ -567,10 +569,202 @@ func TestReloadContainerLinks(t *testing.T) {
|
||||
t.Fatalf("Container 2 %s should be registered first in the runtime", container2.ID)
|
||||
}
|
||||
|
||||
t.Logf("Number of links: %d", len(runtime2.links.links))
|
||||
t.Logf("Number of links: %d", runtime2.containerGraph.Refs("engine"))
|
||||
// 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))
|
||||
entity := runtime2.containerGraph.Get(fmt.Sprintf("/%s", container1.ID))
|
||||
if entity == nil {
|
||||
t.Fatal("Entity should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultContainerName(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
shortId, _, err := srv.ContainerCreate(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
container := runtime.Get(shortId)
|
||||
containerID := container.ID
|
||||
|
||||
paths := runtime.containerGraph.RefPaths(containerID)
|
||||
if paths == nil || len(paths) == 0 {
|
||||
t.Fatalf("Could not find edges for %s", containerID)
|
||||
}
|
||||
edge := paths[0]
|
||||
if edge.ParentID != "engine" {
|
||||
t.Fatalf("Expected engine got %s", edge.ParentID)
|
||||
}
|
||||
if edge.EntityID != containerID {
|
||||
t.Fatalf("Expected %s got %s", containerID, edge.EntityID)
|
||||
}
|
||||
if edge.Name != containerID {
|
||||
t.Fatalf("Expected %s got %s", containerID, edge.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultContainerRename(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
shortId, _, err := srv.ContainerCreate(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
container := runtime.Get(shortId)
|
||||
containerID := container.ID
|
||||
|
||||
if err := runtime.RenameLink(fmt.Sprintf("/%s", containerID), "/webapp"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
webapp, err := runtime.GetByName("/webapp")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if webapp.ID != container.ID {
|
||||
t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkChildContainer(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
shortId, _, err := srv.ContainerCreate(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
container := runtime.Get(shortId)
|
||||
|
||||
if err := runtime.RenameLink(fmt.Sprintf("/%s", container.ID), "/webapp"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
webapp, err := runtime.GetByName("/webapp")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if webapp.ID != container.ID {
|
||||
t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
|
||||
}
|
||||
|
||||
config, _, _, err = ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
shortId, _, err = srv.ContainerCreate(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
childContainer := runtime.Get(shortId)
|
||||
if err := runtime.RenameLink(fmt.Sprintf("/%s", childContainer.ID), "/db"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := runtime.Link("/webapp", "/db", "db"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Get the child by it's new name
|
||||
db, err := runtime.GetByName("/webapp/db")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if db.ID != childContainer.ID {
|
||||
t.Fatalf("Expect db id to match container id: %s != %s", db.ID, childContainer.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllChildren(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
shortId, _, err := srv.ContainerCreate(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
container := runtime.Get(shortId)
|
||||
|
||||
if err := runtime.RenameLink(fmt.Sprintf("/%s", container.ID), "/webapp"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
webapp, err := runtime.GetByName("/webapp")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if webapp.ID != container.ID {
|
||||
t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
|
||||
}
|
||||
|
||||
config, _, _, err = ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
shortId, _, err = srv.ContainerCreate(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
childContainer := runtime.Get(shortId)
|
||||
if err := runtime.RenameLink(fmt.Sprintf("/%s", childContainer.ID), "/db"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := runtime.Link("/webapp", "/db", "db"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
children, err := runtime.Children("/webapp")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if children == nil {
|
||||
t.Fatal("Children should not be nil")
|
||||
}
|
||||
if len(children) == 0 {
|
||||
t.Fatal("Children should not be empty")
|
||||
}
|
||||
|
||||
for key, value := range children {
|
||||
if key != "/webapp/db" {
|
||||
t.Fatalf("Expected /webapp/db got %s", key)
|
||||
}
|
||||
if value.ID != childContainer.ID {
|
||||
t.Fatalf("Expected id %s got %s", childContainer.ID, value.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,12 +391,12 @@ func createAPIContainer(container *Container, size bool, runtime *Runtime) APICo
|
||||
ID: container.ID,
|
||||
}
|
||||
names := []string{}
|
||||
runtime.containerGraph.Walk(func(p string, e *gograph.Entity) error {
|
||||
runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error {
|
||||
if e.ID() == container.ID {
|
||||
names = append(names, p)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}, -1)
|
||||
c.Names = names
|
||||
|
||||
c.Image = runtime.repositories.ImageName(container.Image)
|
||||
@@ -1152,14 +1152,32 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
|
||||
}
|
||||
|
||||
func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
|
||||
if container := srv.runtime.Get(name); container != nil {
|
||||
if err := container.Start(hostConfig); err != nil {
|
||||
return fmt.Errorf("Error starting container %s: %s", name, err)
|
||||
}
|
||||
srv.LogEvent("start", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
|
||||
} else {
|
||||
runtime := srv.runtime
|
||||
container := runtime.Get(name)
|
||||
if container == nil {
|
||||
return fmt.Errorf("No such container: %s", name)
|
||||
}
|
||||
|
||||
// Register links
|
||||
if hostConfig != nil && hostConfig.Links != nil {
|
||||
for _, l := range hostConfig.Links {
|
||||
parts, err := parseLink(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
childName := parts["name"]
|
||||
if err := runtime.Link(fmt.Sprintf("/%s", container.ID), childName, parts["alias"]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := container.Start(hostConfig); err != nil {
|
||||
return fmt.Errorf("Error starting container %s: %s", name, err)
|
||||
}
|
||||
srv.LogEvent("start", container.ShortID(), runtime.repositories.ImageName(container.Image))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ func migratePortMappings(config *Config) error {
|
||||
}
|
||||
|
||||
// Links come in the format of
|
||||
// id:alias
|
||||
// name:alias
|
||||
func parseLink(rawLink string) (map[string]string, error) {
|
||||
return utils.PartParser("id:alias", rawLink)
|
||||
return utils.PartParser("name:alias", rawLink)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user