mirror of
https://github.com/docker/compose.git
synced 2025-07-25 22:54:54 +02:00
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 <guillaume.tardif@gmail.com>
This commit is contained in:
parent
370781e95e
commit
b289138ca9
@ -44,6 +44,20 @@ func (s *composeService) Create(ctx context.Context, project *types.Project) err
|
|||||||
return 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 {
|
for k, network := range project.Networks {
|
||||||
if !network.External.External && network.Name != "" {
|
if !network.External.External && network.Name != "" {
|
||||||
network.Name = fmt.Sprintf("%s_%s", project.Name, k)
|
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 err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *composeService) ensureProjectVolumes(ctx context.Context, project *types.Project) error {
|
||||||
for k, volume := range project.Volumes {
|
for k, volume := range project.Volumes {
|
||||||
if !volume.External.External && volume.Name != "" {
|
if !volume.External.External && volume.Name != "" {
|
||||||
volume.Name = fmt.Sprintf("%s_%s", project.Name, k)
|
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 err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
|
|
||||||
return s.ensureService(c, project, service)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContainerCreateOptions(p *types.Project, s types.ServiceConfig, number int, inherit *moby.Container) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
|
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
|
labels[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change oneoffLabel value for containers started with `docker compose run`
|
|
||||||
labels[projectLabel] = p.Name
|
labels[projectLabel] = p.Name
|
||||||
labels[serviceLabel] = s.Name
|
labels[serviceLabel] = s.Name
|
||||||
labels[versionLabel] = ComposeVersion
|
labels[versionLabel] = ComposeVersion
|
||||||
|
if _, ok := s.Labels[oneoffLabel]; ok {
|
||||||
|
labels[oneoffLabel] = s.Labels[oneoffLabel]
|
||||||
|
labels[slugLabel] = s.Labels[slugLabel]
|
||||||
|
} else {
|
||||||
labels[oneoffLabel] = "False"
|
labels[oneoffLabel] = "False"
|
||||||
|
}
|
||||||
labels[configHashLabel] = hash
|
labels[configHashLabel] = hash
|
||||||
labels[workingDirLabel] = p.WorkingDir
|
labels[workingDirLabel] = p.WorkingDir
|
||||||
labels[configFilesLabel] = strings.Join(p.ComposeFiles, ",")
|
labels[configFilesLabel] = strings.Join(p.ComposeFiles, ",")
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
containerNumberLabel = "com.docker.compose.container-number"
|
containerNumberLabel = "com.docker.compose.container-number"
|
||||||
oneoffLabel = "com.docker.compose.oneoff"
|
oneoffLabel = "com.docker.compose.oneoff"
|
||||||
|
slugLabel = "com.docker.compose.slug"
|
||||||
projectLabel = "com.docker.compose.project"
|
projectLabel = "com.docker.compose.project"
|
||||||
volumeLabel = "com.docker.compose.volume"
|
volumeLabel = "com.docker.compose.volume"
|
||||||
workingDirLabel = "com.docker.compose.project.working_dir"
|
workingDirLabel = "com.docker.compose.project.working_dir"
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/compose-cli/api/compose"
|
"github.com/docker/compose-cli/api/compose"
|
||||||
@ -37,12 +36,11 @@ func (s *composeService) CreateOneOffContainer(ctx context.Context, project *typ
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.ensureRequiredNetworks(ctx, project, service)
|
if err := s.ensureProjectNetworks(ctx, project); err != nil {
|
||||||
if err != nil {
|
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
err = s.ensureRequiredVolumes(ctx, project, service)
|
|
||||||
if err != nil {
|
if err := s.ensureProjectVolumes(ctx, project); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
// ensure required services are up and running before creating the oneoff container
|
// 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.Scale = 1
|
||||||
service.ContainerName = fmt.Sprintf("%s_%s_run_%s", projectName, service.Name, moby.TruncateID(slug))
|
service.ContainerName = fmt.Sprintf("%s_%s_run_%s", projectName, service.Name, moby.TruncateID(slug))
|
||||||
service.Labels = types.Labels{
|
service.Labels = types.Labels{
|
||||||
"com.docker.compose.slug": slug,
|
slugLabel: slug,
|
||||||
"com.docker.compose.oneoff": "True",
|
oneoffLabel: "True",
|
||||||
}
|
}
|
||||||
service.Tty = true
|
service.Tty = true
|
||||||
service.StdinOpen = true
|
service.StdinOpen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) ensureRequiredServices(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
|
func (s *composeService) ensureRequiredServices(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
|
||||||
requiredServices := getDependencyNames(project, service, func() []string {
|
err := s.ensureImagesExists(ctx, project)
|
||||||
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = InDependencyOrder(ctx, project, func(c context.Context, svc types.ServiceConfig) error {
|
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 s.ensureService(c, project, svc)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.Start(ctx, project, nil)
|
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...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return names
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -38,14 +38,3 @@ func contains(slice []string, item string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func unique(s []string) []string {
|
|
||||||
items := []string{}
|
|
||||||
for _, item := range s {
|
|
||||||
if contains(items, item) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
items = append(items, item)
|
|
||||||
}
|
|
||||||
return items
|
|
||||||
}
|
|
||||||
|
@ -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) {
|
func TestLocalComposeBuild(t *testing.T) {
|
||||||
c := NewParallelE2eCLI(t, binDir)
|
c := NewParallelE2eCLI(t, binDir)
|
||||||
|
|
||||||
|
24
tests/compose-e2e/fixtures/run-test/docker-compose.yml
Normal file
24
tests/compose-e2e/fixtures/run-test/docker-compose.yml
Normal file
@ -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: {}
|
Loading…
x
Reference in New Issue
Block a user