Browse Source

Merge pull request #8635 from ndeloof/project_from_labels

rebuild compose project from labels
Ulysses Souza 4 năm trước cách đây
mục cha
commit
6b4b2ea633

+ 2 - 0
pkg/api/labels.go

@@ -49,6 +49,8 @@ const (
 	SlugLabel = "com.docker.compose.slug"
 	// ImageDigestLabel stores digest of the container image used to run service
 	ImageDigestLabel = "com.docker.compose.image"
+	// DependenciesLabel stores service dependencies
+	DependenciesLabel = "com.docker.compose.depends_on"
 	// VersionLabel stores the compose tool version used to run application
 	VersionLabel = "com.docker.compose.version"
 )

+ 31 - 17
pkg/compose/create.go

@@ -226,27 +226,11 @@ func getImageName(service types.ServiceConfig, projectName string) string {
 func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, service types.ServiceConfig, number int, inherit *moby.Container,
 	autoRemove bool) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
 
-	hash, err := ServiceHash(service)
+	labels, err := s.prepareLabels(p, service, number)
 	if err != nil {
 		return nil, nil, nil, err
 	}
 
-	labels := map[string]string{}
-	for k, v := range service.Labels {
-		labels[k] = v
-	}
-
-	labels[api.ProjectLabel] = p.Name
-	labels[api.ServiceLabel] = service.Name
-	labels[api.VersionLabel] = api.ComposeVersion
-	if _, ok := service.Labels[api.OneoffLabel]; !ok {
-		labels[api.OneoffLabel] = "False"
-	}
-	labels[api.ConfigHashLabel] = hash
-	labels[api.WorkingDirLabel] = p.WorkingDir
-	labels[api.ConfigFilesLabel] = strings.Join(p.ComposeFiles, ",")
-	labels[api.ContainerNumberLabel] = strconv.Itoa(number)
-
 	var (
 		runCmd     strslice.StrSlice
 		entrypoint strslice.StrSlice
@@ -394,6 +378,36 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
 	return &containerConfig, &hostConfig, networkConfig, nil
 }
 
+func (s *composeService) prepareLabels(p *types.Project, service types.ServiceConfig, number int) (map[string]string, error) {
+	labels := map[string]string{}
+	for k, v := range service.Labels {
+		labels[k] = v
+	}
+
+	labels[api.ProjectLabel] = p.Name
+	labels[api.ServiceLabel] = service.Name
+	labels[api.VersionLabel] = api.ComposeVersion
+	if _, ok := service.Labels[api.OneoffLabel]; !ok {
+		labels[api.OneoffLabel] = "False"
+	}
+
+	hash, err := ServiceHash(service)
+	if err != nil {
+		return nil, err
+	}
+
+	labels[api.ConfigHashLabel] = hash
+	labels[api.WorkingDirLabel] = p.WorkingDir
+	labels[api.ConfigFilesLabel] = strings.Join(p.ComposeFiles, ",")
+	labels[api.ContainerNumberLabel] = strconv.Itoa(number)
+	var dependencies []string
+	for s := range service.DependsOn {
+		dependencies = append(dependencies, s)
+	}
+	labels[api.DependenciesLabel] = strings.Join(dependencies, ",")
+	return labels, nil
+}
+
 func getDefaultNetworkMode(project *types.Project, service types.ServiceConfig) string {
 	mode := "none"
 	if len(project.Networks) > 0 {

+ 21 - 31
pkg/compose/down.go

@@ -22,7 +22,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/compose-spec/compose-go/cli"
 	"github.com/compose-spec/compose-go/types"
 	moby "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
@@ -52,11 +51,7 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
 	}
 
 	if options.Project == nil {
-		project, err := s.projectFromContainerLabels(containers, projectName)
-		if err != nil {
-			return err
-		}
-		options.Project = project
+		options.Project = s.projectFromContainerLabels(containers.filter(isNotOneOff), projectName)
 	}
 
 	if len(containers) > 0 {
@@ -242,36 +237,31 @@ func (s *composeService) removeContainers(ctx context.Context, w progress.Writer
 	return eg.Wait()
 }
 
-func (s *composeService) projectFromContainerLabels(containers Containers, projectName string) (*types.Project, error) {
-	fakeProject := &types.Project{
+func (s *composeService) projectFromContainerLabels(containers Containers, projectName string) *types.Project {
+	project := &types.Project{
 		Name: projectName,
 	}
 	if len(containers) == 0 {
-		return fakeProject, nil
+		return project
 	}
-	options, err := loadProjectOptionsFromLabels(containers[0])
-	if err != nil {
-		return nil, err
+	set := map[string]moby.Container{}
+	for _, c := range containers {
+		set[c.Labels[api.ServiceLabel]] = c
 	}
-	if options.ConfigPaths[0] == "-" {
-		for _, container := range containers {
-			fakeProject.Services = append(fakeProject.Services, types.ServiceConfig{
-				Name: container.Labels[api.ServiceLabel],
-			})
+	for s, c := range set {
+		service := types.ServiceConfig{
+			Name:   s,
+			Image:  c.Image,
+			Labels: c.Labels,
 		}
-		return fakeProject, nil
-	}
-	project, err := cli.ProjectFromOptions(options)
-	if err != nil {
-		return nil, err
+		dependencies := c.Labels[api.DependenciesLabel]
+		if len(dependencies) > 0 {
+			service.DependsOn = types.DependsOnConfig{}
+			for _, d := range strings.Split(dependencies, ",") {
+				service.DependsOn[d] = types.ServiceDependency{}
+			}
+		}
+		project.Services = append(project.Services, service)
 	}
-
-	return project, nil
-}
-
-func loadProjectOptionsFromLabels(c moby.Container) (*cli.ProjectOptions, error) {
-	return cli.NewProjectOptions(strings.Split(c.Labels[api.ConfigFilesLabel], ","),
-		cli.WithOsEnv,
-		cli.WithWorkingDirectory(c.Labels[api.WorkingDirLabel]),
-		cli.WithName(c.Labels[api.ProjectLabel]))
+	return project
 }

+ 12 - 3
pkg/compose/down_test.go

@@ -38,7 +38,12 @@ func TestDown(t *testing.T) {
 	tested.apiClient = api
 
 	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
-		[]moby.Container{testContainer("service1", "123"), testContainer("service2", "456"), testContainer("service2", "789"), testContainer("service_orphan", "321")}, nil)
+		[]moby.Container{
+			testContainer("service1", "123", false),
+			testContainer("service2", "456", false),
+			testContainer("service2", "789", false),
+			testContainer("service_orphan", "321", true),
+		}, nil)
 
 	api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
 	api.EXPECT().ContainerStop(gomock.Any(), "456", nil).Return(nil)
@@ -64,7 +69,11 @@ func TestDownRemoveOrphans(t *testing.T) {
 	tested.apiClient = api
 
 	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
-		[]moby.Container{testContainer("service1", "123"), testContainer("service2", "789"), testContainer("service_orphan", "321")}, nil)
+		[]moby.Container{
+			testContainer("service1", "123", false),
+			testContainer("service2", "789", false),
+			testContainer("service_orphan", "321", true),
+		}, nil)
 
 	api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
 	api.EXPECT().ContainerStop(gomock.Any(), "789", nil).Return(nil)
@@ -90,7 +99,7 @@ func TestDownRemoveVolumes(t *testing.T) {
 	tested.apiClient = api
 
 	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
-		[]moby.Container{testContainer("service1", "123")}, nil)
+		[]moby.Container{testContainer("service1", "123", false)}, nil)
 
 	api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
 	api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true, RemoveVolumes: true}).Return(nil)

+ 10 - 6
pkg/compose/kill_test.go

@@ -46,7 +46,7 @@ func TestKillAll(t *testing.T) {
 
 	ctx := context.Background()
 	api.EXPECT().ContainerList(ctx, projectFilterListOpt()).Return(
-		[]moby.Container{testContainer("service1", "123"), testContainer("service1", "456"), testContainer("service2", "789")}, nil)
+		[]moby.Container{testContainer("service1", "123", false), testContainer("service1", "456", false), testContainer("service2", "789", false)}, nil)
 	api.EXPECT().ContainerKill(anyCancellableContext(), "123", "").Return(nil)
 	api.EXPECT().ContainerKill(anyCancellableContext(), "456", "").Return(nil)
 	api.EXPECT().ContainerKill(anyCancellableContext(), "789", "").Return(nil)
@@ -64,7 +64,7 @@ func TestKillSignal(t *testing.T) {
 	project := types.Project{Name: strings.ToLower(testProject), Services: []types.ServiceConfig{testService("service1")}}
 
 	ctx := context.Background()
-	api.EXPECT().ContainerList(ctx, projectFilterListOpt()).Return([]moby.Container{testContainer("service1", "123")}, nil)
+	api.EXPECT().ContainerList(ctx, projectFilterListOpt()).Return([]moby.Container{testContainer("service1", "123", false)}, nil)
 	api.EXPECT().ContainerKill(anyCancellableContext(), "123", "SIGTERM").Return(nil)
 
 	err := tested.kill(ctx, &project, compose.KillOptions{Signal: "SIGTERM"})
@@ -75,22 +75,26 @@ func testService(name string) types.ServiceConfig {
 	return types.ServiceConfig{Name: name}
 }
 
-func testContainer(service string, id string) moby.Container {
+func testContainer(service string, id string, oneOff bool) moby.Container {
 	return moby.Container{
 		ID:     id,
 		Names:  []string{id},
-		Labels: containerLabels(service),
+		Labels: containerLabels(service, oneOff),
 	}
 }
 
-func containerLabels(service string) map[string]string {
+func containerLabels(service string, oneOff bool) map[string]string {
 	workingdir, _ := filepath.Abs("testdata")
 	composefile := filepath.Join(workingdir, "compose.yaml")
-	return map[string]string{
+	labels := map[string]string{
 		compose.ServiceLabel:     service,
 		compose.ConfigFilesLabel: composefile,
 		compose.WorkingDirLabel:  workingdir,
 		compose.ProjectLabel:     strings.ToLower(testProject)}
+	if oneOff {
+		labels[compose.OneoffLabel] = "True"
+	}
+	return labels
 }
 
 func anyCancellableContext() gomock.Matcher {

+ 1 - 1
pkg/compose/ps_test.go

@@ -66,7 +66,7 @@ func containerDetails(service string, id string, status string, health string, e
 	container := moby.Container{
 		ID:     id,
 		Names:  []string{"/" + id},
-		Labels: containerLabels(service),
+		Labels: containerLabels(service, false),
 		State:  status,
 	}
 	inspect := moby.ContainerJSON{ContainerJSONBase: &moby.ContainerJSONBase{State: &moby.ContainerState{Status: status, Health: &moby.Health{Status: health}, ExitCode: exitCode}}}

+ 3 - 3
pkg/compose/stop_test.go

@@ -40,9 +40,9 @@ func TestStopTimeout(t *testing.T) {
 	ctx := context.Background()
 	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
 		[]moby.Container{
-			testContainer("service1", "123"),
-			testContainer("service1", "456"),
-			testContainer("service2", "789"),
+			testContainer("service1", "123", false),
+			testContainer("service1", "456", false),
+			testContainer("service2", "789", false),
 		}, nil)
 
 	timeout := time.Duration(2) * time.Second