Просмотр исходного кода

No need to filter services again in backend, filter is done by cli command. Added e2e test, labels one-off and slug

Signed-off-by: Guillaume Tardif <[email protected]>
Guillaume Tardif 5 лет назад
Родитель
Сommit
b289138ca9

+ 24 - 6
local/compose/create.go

@@ -44,6 +44,20 @@ func (s *composeService) Create(ctx context.Context, project *types.Project) err
 		return err
 	}
 
+	if err := s.ensureProjectNetworks(ctx, project); err != nil {
+		return err
+	}
+
+	if err := s.ensureProjectNetworks(ctx, project); err != nil {
+		return err
+	}
+
+	return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
+		return s.ensureService(c, project, service)
+	})
+}
+
+func (s *composeService) ensureProjectNetworks(ctx context.Context, project *types.Project) error {
 	for k, network := range project.Networks {
 		if !network.External.External && network.Name != "" {
 			network.Name = fmt.Sprintf("%s_%s", project.Name, k)
@@ -57,7 +71,10 @@ func (s *composeService) Create(ctx context.Context, project *types.Project) err
 			return err
 		}
 	}
+	return nil
+}
 
+func (s *composeService) ensureProjectVolumes(ctx context.Context, project *types.Project) error {
 	for k, volume := range project.Volumes {
 		if !volume.External.External && volume.Name != "" {
 			volume.Name = fmt.Sprintf("%s_%s", project.Name, k)
@@ -71,10 +88,7 @@ func (s *composeService) Create(ctx context.Context, project *types.Project) err
 			return err
 		}
 	}
-
-	return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
-		return s.ensureService(c, project, service)
-	})
+	return nil
 }
 
 func getContainerCreateOptions(p *types.Project, s types.ServiceConfig, number int, inherit *moby.Container) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
@@ -88,11 +102,15 @@ func getContainerCreateOptions(p *types.Project, s types.ServiceConfig, number i
 		labels[k] = v
 	}
 
-	// TODO: change oneoffLabel value for containers started with `docker compose run`
 	labels[projectLabel] = p.Name
 	labels[serviceLabel] = s.Name
 	labels[versionLabel] = ComposeVersion
-	labels[oneoffLabel] = "False"
+	if _, ok := s.Labels[oneoffLabel]; ok {
+		labels[oneoffLabel] = s.Labels[oneoffLabel]
+		labels[slugLabel] = s.Labels[slugLabel]
+	} else {
+		labels[oneoffLabel] = "False"
+	}
 	labels[configHashLabel] = hash
 	labels[workingDirLabel] = p.WorkingDir
 	labels[configFilesLabel] = strings.Join(p.ComposeFiles, ",")

+ 1 - 0
local/compose/labels.go

@@ -25,6 +25,7 @@ import (
 const (
 	containerNumberLabel = "com.docker.compose.container-number"
 	oneoffLabel          = "com.docker.compose.oneoff"
+	slugLabel            = "com.docker.compose.slug"
 	projectLabel         = "com.docker.compose.project"
 	volumeLabel          = "com.docker.compose.volume"
 	workingDirLabel      = "com.docker.compose.project.working_dir"

+ 16 - 114
local/compose/run.go

@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"io"
 	"os"
-	"sort"
 
 	"github.com/compose-spec/compose-go/types"
 	"github.com/docker/compose-cli/api/compose"
@@ -37,12 +36,11 @@ func (s *composeService) CreateOneOffContainer(ctx context.Context, project *typ
 		return "", err
 	}
 
-	err = s.ensureRequiredNetworks(ctx, project, service)
-	if err != nil {
+	if err := s.ensureProjectNetworks(ctx, project); err != nil {
 		return "", err
 	}
-	err = s.ensureRequiredVolumes(ctx, project, service)
-	if err != nil {
+
+	if err := s.ensureProjectVolumes(ctx, project); err != nil {
 		return "", err
 	}
 	// ensure required services are up and running before creating the oneoff container
@@ -128,123 +126,27 @@ func updateOneOffServiceConfig(service *types.ServiceConfig, projectName string,
 	service.Scale = 1
 	service.ContainerName = fmt.Sprintf("%s_%s_run_%s", projectName, service.Name, moby.TruncateID(slug))
 	service.Labels = types.Labels{
-		"com.docker.compose.slug":   slug,
-		"com.docker.compose.oneoff": "True",
+		slugLabel:   slug,
+		oneoffLabel: "True",
 	}
 	service.Tty = true
 	service.StdinOpen = true
 }
 
 func (s *composeService) ensureRequiredServices(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
-	requiredServices := getDependencyNames(project, service, func() []string {
-		return service.GetDependencies()
-	})
-	if len(requiredServices) > 0 {
-		// dependencies here
-		services, err := project.GetServices(requiredServices)
-		if err != nil {
-			return err
-		}
-		project.Services = services
-		err = s.ensureImagesExists(ctx, project)
-		if err != nil {
-			return err
-		}
-
-		err = InDependencyOrder(ctx, project, func(c context.Context, svc types.ServiceConfig) error {
-			return s.ensureService(c, project, svc)
-		})
-		if err != nil {
-			return err
-		}
-		return s.Start(ctx, project, nil)
-	}
-	return nil
-}
-
-func (s *composeService) ensureRequiredNetworks(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
-	networks := getDependentNetworkNames(project, service)
-	for k, network := range project.Networks {
-		if !contains(networks, network.Name) {
-			continue
-		}
-		if !network.External.External && network.Name != "" {
-			network.Name = fmt.Sprintf("%s_%s", project.Name, k)
-			project.Networks[k] = network
-		}
-		network.Labels = network.Labels.Add(networkLabel, k)
-		network.Labels = network.Labels.Add(projectLabel, project.Name)
-		network.Labels = network.Labels.Add(versionLabel, ComposeVersion)
-
-		err := s.ensureNetwork(ctx, network)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (s *composeService) ensureRequiredVolumes(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
-	volumes := getDependentVolumeNames(project, service)
-
-	for k, volume := range project.Volumes {
-		if !contains(volumes, volume.Name) {
-			continue
-		}
-		if !volume.External.External && volume.Name != "" {
-			volume.Name = fmt.Sprintf("%s_%s", project.Name, k)
-			project.Volumes[k] = volume
-		}
-		volume.Labels = volume.Labels.Add(volumeLabel, k)
-		volume.Labels = volume.Labels.Add(projectLabel, project.Name)
-		volume.Labels = volume.Labels.Add(versionLabel, ComposeVersion)
-		err := s.ensureVolume(ctx, volume)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-type filterDependency func() []string
-
-func getDependencyNames(project *types.Project, service types.ServiceConfig, f filterDependency) []string {
-	names := f()
-	serviceNames := service.GetDependencies()
-	if len(serviceNames) == 0 {
-		return names
-	}
-	if len(serviceNames) > 0 {
-		services, _ := project.GetServices(serviceNames)
-		for _, s := range services {
-			svc := getDependencyNames(project, s, f)
-			names = append(names, svc...)
-		}
+	err := s.ensureImagesExists(ctx, project)
+	if err != nil {
+		return err
 	}
-	sort.Strings(names)
-	return unique(names)
-}
 
-func getDependentNetworkNames(project *types.Project, service types.ServiceConfig) []string {
-	return getDependencyNames(project, service, func() []string {
-		names := []string{}
-		for n := range service.Networks {
-			if contains(project.NetworkNames(), n) {
-				names = append(names, n)
-			}
-		}
-		return names
-	})
-}
-
-func getDependentVolumeNames(project *types.Project, service types.ServiceConfig) []string {
-	return getDependencyNames(project, service, func() []string {
-		names := []string{}
-		for _, v := range service.Volumes {
-			if contains(project.VolumeNames(), v.Source) {
-				names = append(names, v.Source)
-			}
+	err = InDependencyOrder(ctx, project, func(c context.Context, svc types.ServiceConfig) error {
+		if svc.Name != service.Name { // only start dependencies, not service to run one-off
+			return s.ensureService(c, project, svc)
 		}
-		return names
+		return nil
 	})
+	if err != nil {
+		return err
+	}
+	return s.Start(ctx, project, nil)
 }

+ 0 - 11
local/compose/util.go

@@ -38,14 +38,3 @@ func contains(slice []string, item string) bool {
 	}
 	return false
 }
-
-func unique(s []string) []string {
-	items := []string{}
-	for _, item := range s {
-		if contains(items, item) {
-			continue
-		}
-		items = append(items, item)
-	}
-	return items
-}

+ 51 - 0
tests/compose-e2e/compose_test.go

@@ -103,6 +103,57 @@ func TestLocalComposeUp(t *testing.T) {
 	})
 }
 
+func TestLocalComposeRun(t *testing.T) {
+	c := NewParallelE2eCLI(t, binDir)
+
+	t.Run("compose run", func(t *testing.T) {
+		res := c.RunDockerCmd("compose", "run", "-f", "./fixtures/run-test/docker-compose.yml", "back")
+		res.Assert(t, icmd.Expected{Out: "Hello there!!"})
+	})
+
+	t.Run("check run container exited", func(t *testing.T) {
+		res := c.RunDockerCmd("ps", "--all")
+		lines := Lines(res.Stdout())
+		var runContainerID string
+		for _, line := range lines {
+			fields := strings.Fields(line)
+			containerID := fields[len(fields)-1]
+			assert.Assert(t, !strings.HasPrefix(containerID, "run-test_front"))
+			if strings.HasPrefix(containerID, "run-test_back") {
+				//only the one-off container for back service
+				assert.Assert(t, strings.HasPrefix(containerID, "run-test_back_run_"), containerID)
+				runContainerID = containerID
+				assert.Assert(t, strings.Contains(line, "Exited"), line)
+			}
+			if strings.HasPrefix(containerID, "run-test_db_1") {
+				assert.Assert(t, strings.Contains(line, "Up"), line)
+			}
+		}
+		assert.Assert(t, runContainerID != "")
+		res = c.RunDockerCmd("inspect", runContainerID)
+		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.container-number": "1"`})
+		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": "run-test"`})
+		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.oneoff": "True",`})
+		res.Assert(t, icmd.Expected{Out: `"com.docker.compose.slug": "`})
+	})
+
+	t.Run("compose run --rm", func(t *testing.T) {
+		res := c.RunDockerCmd("compose", "run", "-f", "./fixtures/run-test/docker-compose.yml", "--rm", "back")
+		res.Assert(t, icmd.Expected{Out: "Hello there!!"})
+	})
+
+	t.Run("check run container removed", func(t *testing.T) {
+		res := c.RunDockerCmd("ps", "--all")
+		assert.Assert(t, strings.Contains(res.Stdout(), "run-test_back"), res.Stdout())
+	})
+
+	t.Run("down", func(t *testing.T) {
+		_ = c.RunDockerCmd("compose", "down", "-f", "./fixtures/run-test/docker-compose.yml")
+		res := c.RunDockerCmd("ps", "--all")
+		assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout())
+	})
+}
+
 func TestLocalComposeBuild(t *testing.T) {
 	c := NewParallelE2eCLI(t, binDir)
 

+ 24 - 0
tests/compose-e2e/fixtures/run-test/docker-compose.yml

@@ -0,0 +1,24 @@
+version: '3.8'
+services:
+  back:
+    image: alpine
+    command: echo "Hello there!!"
+    depends_on:
+      - db
+    networks:
+      - backnet
+  db:
+    image: nginx
+    networks:
+      - backnet
+    volumes:
+      - data:/test
+  front:
+    image: nginx
+    networks:
+      - frontnet
+networks:
+  frontnet: {}
+  backnet: {}
+volumes:
+  data: {}