diff --git a/cluster/config.go b/cluster/config.go index ce085dd1e3..94e8dc097f 100644 --- a/cluster/config.go +++ b/cluster/config.go @@ -159,6 +159,22 @@ func (c *ContainerConfig) AddAffinity(affinity string) error { return nil } +// RemoveAffinity from config +func (c *ContainerConfig) RemoveAffinity(affinity string) error { + affinities := []string{} + for _, a := range c.extractExprs("affinities") { + if a != affinity { + affinities = append(affinities, a) + } + } + labels, err := json.Marshal(affinities) + if err != nil { + return err + } + c.Labels[SwarmLabelNamespace+".affinities"] = string(labels) + return nil +} + // HaveNodeConstraint in config func (c *ContainerConfig) HaveNodeConstraint() bool { constraints := c.extractExprs("constraints") diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index c29dcf7f99..2b8907aa81 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -118,12 +118,12 @@ func (c *Cluster) generateUniqueID() string { func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (*cluster.Container, error) { container, err := c.createContainer(config, name, false, authConfig) - // fails with image not found, then try to reschedule with image-affinity + // fails with image not found, then try to reschedule with image affinity if err != nil { bImageNotFoundError, _ := regexp.MatchString(`image \S* not found`, err.Error()) if bImageNotFoundError && !config.HaveNodeConstraint() { // Check if the image exists in the cluster - // If exists, retry with a image-affinity + // If exists, retry with a image affinity if c.Image(config.Image) != nil { container, err = c.createContainer(config, name, true, authConfig) } @@ -145,12 +145,16 @@ func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string, swarmID := c.generateUniqueID() config.SetSwarmID(swarmID) - configTemp := config if withImageAffinity { - configTemp.AddAffinity("image==" + config.Image) + config.AddAffinity("image==" + config.Image) + } + + nodes, err := c.scheduler.SelectNodesForContainer(c.listNodes(), config) + + if withImageAffinity { + config.RemoveAffinity("image==" + config.Image) } - nodes, err := c.scheduler.SelectNodesForContainer(c.listNodes(), configTemp) if err != nil { c.scheduler.Unlock() return nil, err diff --git a/test/integration/api/run.bats b/test/integration/api/run.bats index 40cf7a082e..c540170c7f 100644 --- a/test/integration/api/run.bats +++ b/test/integration/api/run.bats @@ -101,7 +101,7 @@ function teardown() { [[ "${output}" == *"\"StopSignal\": \"SIGKILL\""* ]] } -@test "docker run - reschedule with soft-image-affinity" { +@test "docker run - reschedule with image affinity" { start_docker_with_busybox 1 start_docker 1 @@ -115,15 +115,19 @@ function teardown() { # 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 + # then will retry with 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"* ]] + + # check the image affinity wasn't saved + run docker_swarm inspect test_container + [[ "${output}" != *"image==busyboxabcde"* ]] } -@test "docker run - reschedule with soft-image-affinity and node constraint" { +@test "docker run - reschedule with image affinity and node constraint" { start_docker_with_busybox 1 start_docker 1