From 8c7c0b248b9639b2a9eefaa098baaf94e35ef466 Mon Sep 17 00:00:00 2001 From: Xian Chaobo Date: Tue, 16 Jun 2015 07:52:00 +0800 Subject: [PATCH 1/4] reschedule with soft image affinity Signed-off-by: Xian Chaobo --- cluster/config.go | 15 +++++++++++++++ cluster/swarm/cluster.go | 23 +++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/cluster/config.go b/cluster/config.go index 69c547a907..2041cf440f 100644 --- a/cluster/config.go +++ b/cluster/config.go @@ -146,3 +146,18 @@ func (c *ContainerConfig) Affinities() []string { func (c *ContainerConfig) Constraints() []string { return c.extractExprs("constraints") } + +// AddSoftImageAffinity +func (c *ContainerConfig) AddSoftImageAffinity(image string) error { + affnities_json := c.Labels[SwarmLabelNamespace+".affinities"] + var affinities []string + json.Unmarshal([]byte(affnities_json), &affinities) + affinities = append(affinities, "image==~"+image) + + labels, err := json.Marshal(affinities) + if err != nil { + return err + } + c.Labels[SwarmLabelNamespace+".affinities"] = string(labels) + return nil +} diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 8f2c7d1f65..1acf60d445 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -89,6 +89,20 @@ func (c *Cluster) generateUniqueID() string { // CreateContainer aka schedule a brand new container into the cluster. func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string) (*cluster.Container, error) { + container, err := c.createContainer(config, name, false) + + // fails with image not found, then try to reschedule with soft-image-affinity + if err != nil && strings.HasSuffix(err.Error(), "not found") { + // Check if the image exists in the cluster + // If exists, retry with a soft-image-affinity + if image := c.Image(config.Image); image != nil { + container, err = c.createContainer(config, name, true) + } + } + return container, err +} + +func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string, with_soft_image_affinity bool) (*cluster.Container, error) { c.scheduler.Lock() defer c.scheduler.Unlock() @@ -100,13 +114,18 @@ func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string) // Associate a Swarm ID to the container we are creating. config.SetSwarmID(c.generateUniqueID()) - n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), config) + config_temp := config + if with_soft_image_affinity { + config_temp.AddSoftImageAffinity(config.Image) + } + + n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), config_temp) if err != nil { return nil, err } if nn, ok := c.engines[n.ID]; ok { - container, err := nn.Create(config, name, true) + container, err := nn.Create(config_temp, name, true) if err != nil { return nil, err } From 8cd667e99606a7ac23a953b6d13f61b9672039c3 Mon Sep 17 00:00:00 2001 From: Xian Chaobo Date: Tue, 16 Jun 2015 07:52:53 +0800 Subject: [PATCH 2/4] add unit test for AddSoftImageAffinity Signed-off-by: Xian Chaobo --- cluster/config_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cluster/config_test.go b/cluster/config_test.go index f4452d5fc4..faa0d7ef0f 100644 --- a/cluster/config_test.go +++ b/cluster/config_test.go @@ -81,3 +81,11 @@ func TestConsolidateResourceFields(t *testing.T) { } } + +func TestAddSoftImageAffinity(t *testing.T) { + config := BuildContainerConfig(dockerclient.ContainerConfig{}) + assert.Empty(t, config.Affinities()) + + config.AddSoftImageAffinity("testimage") + assert.Len(t, config.Affinities(), 1) +} From 05ba86d32932f926754b7a9e322c83fda1d696fb Mon Sep 17 00:00:00 2001 From: Xian Chaobo Date: Tue, 16 Jun 2015 08:46:17 +0800 Subject: [PATCH 3/4] golint and add integration test Signed-off-by: Xian Chaobo --- cluster/config.go | 6 +++--- cluster/swarm/cluster.go | 12 ++++++------ test/integration/api/run.bats | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/cluster/config.go b/cluster/config.go index 2041cf440f..00eea8bad7 100644 --- a/cluster/config.go +++ b/cluster/config.go @@ -147,11 +147,11 @@ func (c *ContainerConfig) Constraints() []string { return c.extractExprs("constraints") } -// AddSoftImageAffinity +// AddSoftImageAffinity to config func (c *ContainerConfig) AddSoftImageAffinity(image string) error { - affnities_json := c.Labels[SwarmLabelNamespace+".affinities"] + affnitiesJSON := c.Labels[SwarmLabelNamespace+".affinities"] var affinities []string - json.Unmarshal([]byte(affnities_json), &affinities) + json.Unmarshal([]byte(affnitiesJSON), &affinities) affinities = append(affinities, "image==~"+image) labels, err := json.Marshal(affinities) diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 1acf60d445..b756303e33 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -102,7 +102,7 @@ func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string) return container, err } -func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string, with_soft_image_affinity bool) (*cluster.Container, error) { +func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string, withSoftImageAffinity bool) (*cluster.Container, error) { c.scheduler.Lock() defer c.scheduler.Unlock() @@ -114,18 +114,18 @@ func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string, // Associate a Swarm ID to the container we are creating. config.SetSwarmID(c.generateUniqueID()) - config_temp := config - if with_soft_image_affinity { - config_temp.AddSoftImageAffinity(config.Image) + configTemp := config + if withSoftImageAffinity { + configTemp.AddSoftImageAffinity(config.Image) } - n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), config_temp) + n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), configTemp) if err != nil { return nil, err } if nn, ok := c.engines[n.ID]; ok { - container, err := nn.Create(config_temp, name, true) + container, err := nn.Create(config, name, true) if err != nil { return nil, err } diff --git a/test/integration/api/run.bats b/test/integration/api/run.bats index ddcebed6ad..25b1762bd4 100644 --- a/test/integration/api/run.bats +++ b/test/integration/api/run.bats @@ -62,3 +62,25 @@ function teardown() { # pid [[ "${output}" == *"\"PidMode\": \"host\""* ]] } + +@test "docker run - reschedule with soft-image-affinity" { + start_docker_with_busybox 1 + start_docker 1 + + docker -H ${HOSTS[0]} tag busybox:latest busyboxabcde:latest + swarm_manage + + # make sure busyboxabcde exists + run docker_swarm images + [ "$status" -eq 0 ] + [[ "${output}" == *"busyboxabcde"* ]] + + # try to create container on node-1, node-1 does not have busyboxabcde and will pull it + # but can not find busyboxabcde in dockerhub + # then will retry with soft-image-affinity + docker_swarm run -d --name test_container -e constraint:node==~node-1 busyboxabcde sleep 1000 + + # check container running on node-0 + run docker_swarm ps + [[ "${output}" == *"node-0/test_container"* ]] +} From 0037c31a958ebf65a7d590a44cedc0671924f16d Mon Sep 17 00:00:00 2001 From: Xian Chaobo Date: Tue, 7 Jul 2015 11:30:26 +0800 Subject: [PATCH 4/4] fix func name and use extracExprs Signed-off-by: Xian Chaobo --- cluster/config.go | 11 ++++------- cluster/config_test.go | 4 ++-- cluster/swarm/cluster.go | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/cluster/config.go b/cluster/config.go index 00eea8bad7..a46b5f5731 100644 --- a/cluster/config.go +++ b/cluster/config.go @@ -147,13 +147,10 @@ func (c *ContainerConfig) Constraints() []string { return c.extractExprs("constraints") } -// AddSoftImageAffinity to config -func (c *ContainerConfig) AddSoftImageAffinity(image string) error { - affnitiesJSON := c.Labels[SwarmLabelNamespace+".affinities"] - var affinities []string - json.Unmarshal([]byte(affnitiesJSON), &affinities) - affinities = append(affinities, "image==~"+image) - +// AddAffinity to config +func (c *ContainerConfig) AddAffinity(affinity string) error { + affinities := c.extractExprs("affinities") + affinities = append(affinities, affinity) labels, err := json.Marshal(affinities) if err != nil { return err diff --git a/cluster/config_test.go b/cluster/config_test.go index faa0d7ef0f..15e0a6fe4f 100644 --- a/cluster/config_test.go +++ b/cluster/config_test.go @@ -82,10 +82,10 @@ func TestConsolidateResourceFields(t *testing.T) { } -func TestAddSoftImageAffinity(t *testing.T) { +func TestAddAffinity(t *testing.T) { config := BuildContainerConfig(dockerclient.ContainerConfig{}) assert.Empty(t, config.Affinities()) - config.AddSoftImageAffinity("testimage") + config.AddAffinity("image==~testimage") assert.Len(t, config.Affinities(), 1) } diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index b756303e33..d7d960f2a5 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -116,7 +116,7 @@ func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string, configTemp := config if withSoftImageAffinity { - configTemp.AddSoftImageAffinity(config.Image) + configTemp.AddAffinity("image==~" + config.Image) } n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), configTemp)