فهرست منبع

Fix links resolution

Signed-off-by: Ulysses Souza <[email protected]>
Ulysses Souza 3 سال پیش
والد
کامیت
b725c56c42
2فایلهای تغییر یافته به همراه190 افزوده شده و 1 حذف شده
  1. 59 1
      pkg/compose/convergence.go
  2. 131 0
      pkg/compose/convergence_test.go

+ 59 - 1
pkg/compose/convergence.go

@@ -448,7 +448,7 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
 			Networks: inspectedContainer.NetworkSettings.Networks,
 		},
 	}
-	links := append(service.Links, service.ExternalLinks...)
+	links := s.getLinks(ctx, project.Name, service, number)
 	for _, netName := range service.NetworksByPriority() {
 		netwrk := project.Networks[netName]
 		cfg := service.Networks[netName]
@@ -476,6 +476,64 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
 	return created, err
 }
 
+// getLinks mimics V1 compose/service.py::Service::_get_links()
+func (s composeService) getLinks(ctx context.Context, projectName string, service types.ServiceConfig, number int) []string {
+	var links []string
+	format := func(k, v string) string {
+		return fmt.Sprintf("%s:%s", k, v)
+	}
+	getServiceContainers := func(serviceName string) (Containers, error) {
+		return s.getContainers(ctx, projectName, oneOffExclude, true, serviceName)
+	}
+
+	for _, rawLink := range service.Links {
+		linkSplit := strings.Split(rawLink, ":")
+		linkServiceName := linkSplit[0]
+		l := linkServiceName
+		if len(linkSplit) == 2 {
+			l = linkSplit[1] // linkName if informed like in: "serviceName:linkName"
+		}
+		cnts, err := getServiceContainers(linkServiceName)
+		if err != nil {
+			return nil
+		}
+		for _, c := range cnts {
+			containerName := getCanonicalContainerName(c)
+			links = append(links,
+				format(containerName, l),
+				format(containerName, strings.Join([]string{linkServiceName, strconv.Itoa(number)}, Separator)),
+				format(containerName, strings.Join([]string{projectName, linkServiceName, strconv.Itoa(number)}, Separator)),
+			)
+		}
+	}
+
+	if service.Labels[api.OneoffLabel] == "True" {
+		cnts, err := getServiceContainers(service.Name)
+		if err != nil {
+			return nil
+		}
+		for _, c := range cnts {
+			containerName := getCanonicalContainerName(c)
+			links = append(links,
+				format(containerName, service.Name),
+				format(containerName, strings.TrimPrefix(containerName, projectName+Separator)),
+				format(containerName, containerName),
+			)
+		}
+	}
+
+	for _, rawExtLink := range service.ExternalLinks {
+		extLinkSplit := strings.Split(rawExtLink, ":")
+		externalLink := extLinkSplit[0]
+		linkName := externalLink
+		if len(extLinkSplit) == 2 {
+			linkName = extLinkSplit[1]
+		}
+		links = append(links, format(externalLink, linkName))
+	}
+	return links
+}
+
 func shortIDAliasExists(containerID string, aliases ...string) bool {
 	for _, alias := range aliases {
 		if alias == containerID[:12] {

+ 131 - 0
pkg/compose/convergence_test.go

@@ -17,10 +17,16 @@
 package compose
 
 import (
+	"context"
 	"fmt"
 	"testing"
 
 	"github.com/compose-spec/compose-go/types"
+	"github.com/docker/compose/v2/pkg/api"
+	"github.com/docker/compose/v2/pkg/mocks"
+	moby "github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	"github.com/golang/mock/gomock"
 	"gotest.tools/assert"
 )
 
@@ -46,3 +52,128 @@ func TestContainerName(t *testing.T) {
 	_, err = getScale(s)
 	assert.Error(t, err, fmt.Sprintf(doubledContainerNameWarning, s.Name, s.ContainerName))
 }
+
+func TestServiceLinks(t *testing.T) {
+	const dbContainerName = "/" + testProject + "-db-1"
+	const webContainerName = "/" + testProject + "-web-1"
+	s := types.ServiceConfig{
+		Name:  "web",
+		Scale: 1,
+	}
+
+	containerListOptions := moby.ContainerListOptions{
+		Filters: filters.NewArgs(
+			projectFilter(testProject),
+			serviceFilter("db"),
+			oneOffFilter(false),
+		),
+		All: true,
+	}
+
+	t.Run("service links default", func(t *testing.T) {
+		mockCtrl := gomock.NewController(t)
+		defer mockCtrl.Finish()
+		apiClient := mocks.NewMockAPIClient(mockCtrl)
+		tested.apiClient = apiClient
+
+		s.Links = []string{"db"}
+
+		c := testContainer("db", dbContainerName, false)
+		apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
+
+		links := tested.getLinks(context.Background(), testProject, s, 1)
+
+		assert.Equal(t, len(links), 3)
+		assert.Equal(t, links[0], "testProject-db-1:db")
+		assert.Equal(t, links[1], "testProject-db-1:db-1")
+		assert.Equal(t, links[2], "testProject-db-1:testProject-db-1")
+	})
+
+	t.Run("service links", func(t *testing.T) {
+		mockCtrl := gomock.NewController(t)
+		defer mockCtrl.Finish()
+		apiClient := mocks.NewMockAPIClient(mockCtrl)
+		tested.apiClient = apiClient
+
+		s.Links = []string{"db:db"}
+
+		c := testContainer("db", dbContainerName, false)
+
+		apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
+		links := tested.getLinks(context.Background(), testProject, s, 1)
+
+		assert.Equal(t, len(links), 3)
+		assert.Equal(t, links[0], "testProject-db-1:db")
+		assert.Equal(t, links[1], "testProject-db-1:db-1")
+		assert.Equal(t, links[2], "testProject-db-1:testProject-db-1")
+	})
+
+	t.Run("service links name", func(t *testing.T) {
+		mockCtrl := gomock.NewController(t)
+		defer mockCtrl.Finish()
+		apiClient := mocks.NewMockAPIClient(mockCtrl)
+		tested.apiClient = apiClient
+
+		s.Links = []string{"db:dbname"}
+
+		c := testContainer("db", dbContainerName, false)
+		apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
+
+		links := tested.getLinks(context.Background(), testProject, s, 1)
+
+		assert.Equal(t, len(links), 3)
+		assert.Equal(t, links[0], "testProject-db-1:dbname")
+		assert.Equal(t, links[1], "testProject-db-1:db-1")
+		assert.Equal(t, links[2], "testProject-db-1:testProject-db-1")
+	})
+
+	t.Run("service links external links", func(t *testing.T) {
+		mockCtrl := gomock.NewController(t)
+		defer mockCtrl.Finish()
+		apiClient := mocks.NewMockAPIClient(mockCtrl)
+		tested.apiClient = apiClient
+
+		s.Links = []string{"db:dbname"}
+		s.ExternalLinks = []string{"db1:db2"}
+
+		c := testContainer("db", dbContainerName, false)
+		apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
+
+		links := tested.getLinks(context.Background(), testProject, s, 1)
+		assert.Equal(t, len(links), 4)
+		assert.Equal(t, links[0], "testProject-db-1:dbname")
+		assert.Equal(t, links[1], "testProject-db-1:db-1")
+		assert.Equal(t, links[2], "testProject-db-1:testProject-db-1")
+
+		// ExternalLink
+		assert.Equal(t, links[3], "db1:db2")
+	})
+
+	t.Run("service links itself oneoff", func(t *testing.T) {
+		mockCtrl := gomock.NewController(t)
+		defer mockCtrl.Finish()
+		apiClient := mocks.NewMockAPIClient(mockCtrl)
+		tested.apiClient = apiClient
+
+		s.Links = []string{}
+		s.ExternalLinks = []string{}
+		s.Labels = s.Labels.Add(api.OneoffLabel, "True")
+
+		c := testContainer("web", webContainerName, true)
+		containerListOptionsOneOff := moby.ContainerListOptions{
+			Filters: filters.NewArgs(
+				projectFilter(testProject),
+				serviceFilter("web"),
+				oneOffFilter(false),
+			),
+			All: true,
+		}
+		apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]moby.Container{c}, nil)
+
+		links := tested.getLinks(context.Background(), testProject, s, 1)
+		assert.Equal(t, len(links), 3)
+		assert.Equal(t, links[0], "testProject-web-1:web")
+		assert.Equal(t, links[1], "testProject-web-1:web-1")
+		assert.Equal(t, links[2], "testProject-web-1:testProject-web-1")
+	})
+}