Преглед изворни кода

sort containers to optimize scale down

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof пре 1 година
родитељ
комит
de3da829ea
3 измењених фајлова са 92 додато и 0 уклоњено
  1. 18 0
      pkg/compose/convergence.go
  2. 2 0
      pkg/e2e/fixtures/scale/compose.yaml
  3. 72 0
      pkg/e2e/scale_test.go

+ 18 - 0
pkg/compose/convergence.go

@@ -127,6 +127,24 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
 	}
 
 	sort.Slice(containers, func(i, j int) bool {
+		// select obsolete containers first, so they get removed as we scale down
+		if obsolete, _ := mustRecreate(service, containers[i], recreate); obsolete {
+			// i is obsolete, so must be first in the list
+			return true
+		}
+		if obsolete, _ := mustRecreate(service, containers[j], recreate); obsolete {
+			// j is obsolete, so must be first in the list
+			return false
+		}
+
+		// For up-to-date containers, sort by container number to preserve low-values in container numbers
+		ni, erri := strconv.Atoi(containers[i].Labels[api.ContainerNumberLabel])
+		nj, errj := strconv.Atoi(containers[j].Labels[api.ContainerNumberLabel])
+		if erri == nil && errj == nil {
+			return ni < nj
+		}
+
+		// If we don't get a container number (?) just sort by creation date
 		return containers[i].Created < containers[j].Created
 	})
 	for i, container := range containers {

+ 2 - 0
pkg/e2e/fixtures/scale/compose.yaml

@@ -5,6 +5,8 @@ services:
       - db
   db:
     image: nginx:alpine
+    environment:
+      - MAYBE
   front:
     image: nginx:alpine
     deploy:

+ 72 - 0
pkg/e2e/scale_test.go

@@ -95,6 +95,78 @@ func TestScaleWithDepsCases(t *testing.T) {
 	checkServiceContainer(t, res.Combined(), "scale-deps-tests-db", NO_STATE_TO_CHECK, 1)
 }
 
+func TestScaleUpAndDownPreserveContainerNumber(t *testing.T) {
+	const projectName = "scale-up-down-test"
+
+	c := NewCLI(t, WithEnv(
+		"COMPOSE_PROJECT_NAME="+projectName))
+
+	reset := func() {
+		c.RunDockerComposeCmd(t, "down", "--rmi", "all")
+	}
+	t.Cleanup(reset)
+	res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=2", "db")
+	res.Assert(t, icmd.Success)
+
+	res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
+	res.Assert(t, icmd.Success)
+	assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1\n"+projectName+"-db-2")
+
+	t.Log("scale down removes replica #2")
+	res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=1", "db")
+	res.Assert(t, icmd.Success)
+
+	res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
+	res.Assert(t, icmd.Success)
+	assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1")
+
+	t.Log("scale up restores replica #2")
+	res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=2", "db")
+	res.Assert(t, icmd.Success)
+
+	res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
+	res.Assert(t, icmd.Success)
+	assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1\n"+projectName+"-db-2")
+}
+
+func TestScaleDownRemovesObsolete(t *testing.T) {
+	const projectName = "scale-down-obsolete-test"
+	c := NewCLI(t, WithEnv(
+		"COMPOSE_PROJECT_NAME="+projectName))
+
+	reset := func() {
+		c.RunDockerComposeCmd(t, "down", "--rmi", "all")
+	}
+	t.Cleanup(reset)
+	res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "db")
+	res.Assert(t, icmd.Success)
+
+	res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
+	res.Assert(t, icmd.Success)
+	assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1")
+
+	cmd := c.NewDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=2", "db")
+	res = icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
+		cmd.Env = append(cmd.Env, "MAYBE=value")
+	})
+	res.Assert(t, icmd.Success)
+
+	res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
+	res.Assert(t, icmd.Success)
+	assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1\n"+projectName+"-db-2")
+
+	t.Log("scale down removes obsolete replica #1")
+	cmd = c.NewDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=1", "db")
+	res = icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
+		cmd.Env = append(cmd.Env, "MAYBE=value")
+	})
+	res.Assert(t, icmd.Success)
+
+	res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
+	res.Assert(t, icmd.Success)
+	assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1")
+}
+
 func checkServiceContainer(t *testing.T, stdout, containerName, containerState string, count int) {
 	found := 0
 	lines := strings.Split(stdout, "\n")