add scale command
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
This commit is contained in:
parent
19bbb12fac
commit
1a98a70b8a
@ -476,6 +476,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
|||||||
createCommand(&opts, dockerCli, backend),
|
createCommand(&opts, dockerCli, backend),
|
||||||
copyCommand(&opts, dockerCli, backend),
|
copyCommand(&opts, dockerCli, backend),
|
||||||
waitCommand(&opts, dockerCli, backend),
|
waitCommand(&opts, dockerCli, backend),
|
||||||
|
scaleCommand(&opts, dockerCli, backend),
|
||||||
alphaCommand(&opts, dockerCli, backend),
|
alphaCommand(&opts, dockerCli, backend),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
108
cmd/compose/scale.go
Normal file
108
cmd/compose/scale.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 Docker Compose CLI authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type scaleOptions struct {
|
||||||
|
*ProjectOptions
|
||||||
|
noDeps bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func scaleCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||||
|
opts := scaleOptions{
|
||||||
|
ProjectOptions: p,
|
||||||
|
}
|
||||||
|
scaleCmd := &cobra.Command{
|
||||||
|
Use: "scale [SERVICE=REPLICAS...]",
|
||||||
|
Short: "Scale services ",
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||||
|
serviceTuples, err := parseServicesReplicasArgs(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return runScale(ctx, dockerCli, backend, opts, serviceTuples)
|
||||||
|
}),
|
||||||
|
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||||
|
}
|
||||||
|
flags := scaleCmd.Flags()
|
||||||
|
flags.BoolVar(&opts.noDeps, "no-deps", false, "Don't start linked services.")
|
||||||
|
|
||||||
|
return scaleCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runScale(ctx context.Context, dockerCli command.Cli, backend api.Service, opts scaleOptions, serviceReplicaTuples map[string]int) error {
|
||||||
|
services := maps.Keys(serviceReplicaTuples)
|
||||||
|
project, err := opts.ToProject(dockerCli, services)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.noDeps {
|
||||||
|
if err := project.ForServices(services, types.IgnoreDependencies); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range serviceReplicaTuples {
|
||||||
|
for i, service := range project.Services {
|
||||||
|
if service.Name != key {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if service.Deploy == nil {
|
||||||
|
service.Deploy = &types.DeployConfig{}
|
||||||
|
}
|
||||||
|
scale := uint64(value)
|
||||||
|
service.Deploy.Replicas = &scale
|
||||||
|
project.Services[i] = service
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return backend.Scale(ctx, project, api.ScaleOptions{Services: services})
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseServicesReplicasArgs(args []string) (map[string]int, error) {
|
||||||
|
serviceReplicaTuples := map[string]int{}
|
||||||
|
for _, arg := range args {
|
||||||
|
key, val, ok := strings.Cut(arg, "=")
|
||||||
|
if !ok || key == "" || val == "" {
|
||||||
|
return nil, errors.Errorf("Invalide scale specifier %q.", arg)
|
||||||
|
}
|
||||||
|
intValue, err := strconv.Atoi(val)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("Invalide scale specifier, can't parse replicate value to int %q.", arg)
|
||||||
|
}
|
||||||
|
serviceReplicaTuples[key] = intValue
|
||||||
|
}
|
||||||
|
return serviceReplicaTuples, nil
|
||||||
|
}
|
@ -26,6 +26,7 @@ Define and run multi-container applications with Docker.
|
|||||||
| [`restart`](compose_restart.md) | Restart service containers |
|
| [`restart`](compose_restart.md) | Restart service containers |
|
||||||
| [`rm`](compose_rm.md) | Removes stopped service containers |
|
| [`rm`](compose_rm.md) | Removes stopped service containers |
|
||||||
| [`run`](compose_run.md) | Run a one-off command on a service. |
|
| [`run`](compose_run.md) | Run a one-off command on a service. |
|
||||||
|
| [`scale`](compose_scale.md) | Scale services |
|
||||||
| [`start`](compose_start.md) | Start services |
|
| [`start`](compose_start.md) | Start services |
|
||||||
| [`stop`](compose_stop.md) | Stop services |
|
| [`stop`](compose_stop.md) | Stop services |
|
||||||
| [`top`](compose_top.md) | Display the running processes |
|
| [`top`](compose_top.md) | Display the running processes |
|
||||||
|
15
docs/reference/compose_alpha_scale.md
Normal file
15
docs/reference/compose_alpha_scale.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# docker compose alpha scale
|
||||||
|
|
||||||
|
<!---MARKER_GEN_START-->
|
||||||
|
Scale services
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
|:------------|:-----|:--------|:--------------------------------|
|
||||||
|
| `--dry-run` | | | Execute command in dry run mode |
|
||||||
|
| `--no-deps` | | | Don't start linked services. |
|
||||||
|
|
||||||
|
|
||||||
|
<!---MARKER_GEN_END-->
|
||||||
|
|
15
docs/reference/compose_scale.md
Normal file
15
docs/reference/compose_scale.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# docker compose scale
|
||||||
|
|
||||||
|
<!---MARKER_GEN_START-->
|
||||||
|
Scale services
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
|:------------|:-----|:--------|:--------------------------------|
|
||||||
|
| `--dry-run` | | | Execute command in dry run mode |
|
||||||
|
| `--no-deps` | | | Don't start linked services. |
|
||||||
|
|
||||||
|
|
||||||
|
<!---MARKER_GEN_END-->
|
||||||
|
|
@ -165,6 +165,7 @@ cname:
|
|||||||
- docker compose restart
|
- docker compose restart
|
||||||
- docker compose rm
|
- docker compose rm
|
||||||
- docker compose run
|
- docker compose run
|
||||||
|
- docker compose scale
|
||||||
- docker compose start
|
- docker compose start
|
||||||
- docker compose stop
|
- docker compose stop
|
||||||
- docker compose top
|
- docker compose top
|
||||||
@ -192,6 +193,7 @@ clink:
|
|||||||
- docker_compose_restart.yaml
|
- docker_compose_restart.yaml
|
||||||
- docker_compose_rm.yaml
|
- docker_compose_rm.yaml
|
||||||
- docker_compose_run.yaml
|
- docker_compose_run.yaml
|
||||||
|
- docker_compose_scale.yaml
|
||||||
- docker_compose_start.yaml
|
- docker_compose_start.yaml
|
||||||
- docker_compose_stop.yaml
|
- docker_compose_stop.yaml
|
||||||
- docker_compose_top.yaml
|
- docker_compose_top.yaml
|
||||||
|
35
docs/reference/docker_compose_alpha_scale.yaml
Normal file
35
docs/reference/docker_compose_alpha_scale.yaml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
command: docker compose alpha scale
|
||||||
|
short: Scale services
|
||||||
|
long: Scale services
|
||||||
|
usage: docker compose alpha scale [SERVICE=REPLICAS...]
|
||||||
|
pname: docker compose alpha
|
||||||
|
plink: docker_compose_alpha.yaml
|
||||||
|
options:
|
||||||
|
- option: no-deps
|
||||||
|
value_type: bool
|
||||||
|
default_value: "false"
|
||||||
|
description: Don't start linked services.
|
||||||
|
deprecated: false
|
||||||
|
hidden: false
|
||||||
|
experimental: false
|
||||||
|
experimentalcli: false
|
||||||
|
kubernetes: false
|
||||||
|
swarm: false
|
||||||
|
inherited_options:
|
||||||
|
- option: dry-run
|
||||||
|
value_type: bool
|
||||||
|
default_value: "false"
|
||||||
|
description: Execute command in dry run mode
|
||||||
|
deprecated: false
|
||||||
|
hidden: false
|
||||||
|
experimental: false
|
||||||
|
experimentalcli: false
|
||||||
|
kubernetes: false
|
||||||
|
swarm: false
|
||||||
|
deprecated: false
|
||||||
|
hidden: false
|
||||||
|
experimental: false
|
||||||
|
experimentalcli: true
|
||||||
|
kubernetes: false
|
||||||
|
swarm: false
|
||||||
|
|
35
docs/reference/docker_compose_scale.yaml
Normal file
35
docs/reference/docker_compose_scale.yaml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
command: docker compose scale
|
||||||
|
short: Scale services
|
||||||
|
long: Scale services
|
||||||
|
usage: docker compose scale [SERVICE=REPLICAS...]
|
||||||
|
pname: docker compose
|
||||||
|
plink: docker_compose.yaml
|
||||||
|
options:
|
||||||
|
- option: no-deps
|
||||||
|
value_type: bool
|
||||||
|
default_value: "false"
|
||||||
|
description: Don't start linked services.
|
||||||
|
deprecated: false
|
||||||
|
hidden: false
|
||||||
|
experimental: false
|
||||||
|
experimentalcli: false
|
||||||
|
kubernetes: false
|
||||||
|
swarm: false
|
||||||
|
inherited_options:
|
||||||
|
- option: dry-run
|
||||||
|
value_type: bool
|
||||||
|
default_value: "false"
|
||||||
|
description: Execute command in dry run mode
|
||||||
|
deprecated: false
|
||||||
|
hidden: false
|
||||||
|
experimental: false
|
||||||
|
experimentalcli: false
|
||||||
|
kubernetes: false
|
||||||
|
swarm: false
|
||||||
|
deprecated: false
|
||||||
|
hidden: false
|
||||||
|
experimental: false
|
||||||
|
experimentalcli: false
|
||||||
|
kubernetes: false
|
||||||
|
swarm: false
|
||||||
|
|
3
go.mod
3
go.mod
@ -51,6 +51,8 @@ require (
|
|||||||
gotest.tools/v3 v3.5.0
|
gotest.tools/v3 v3.5.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
@ -155,7 +157,6 @@ require (
|
|||||||
go.opentelemetry.io/otel/metric v0.37.0 // indirect
|
go.opentelemetry.io/otel/metric v0.37.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
||||||
golang.org/x/crypto v0.11.0 // indirect
|
golang.org/x/crypto v0.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect
|
|
||||||
golang.org/x/mod v0.11.0 // indirect
|
golang.org/x/mod v0.11.0 // indirect
|
||||||
golang.org/x/net v0.12.0 // indirect
|
golang.org/x/net v0.12.0 // indirect
|
||||||
golang.org/x/oauth2 v0.10.0 // indirect
|
golang.org/x/oauth2 v0.10.0 // indirect
|
||||||
|
@ -88,6 +88,12 @@ type Service interface {
|
|||||||
Viz(ctx context.Context, project *types.Project, options VizOptions) (string, error)
|
Viz(ctx context.Context, project *types.Project, options VizOptions) (string, error)
|
||||||
// Wait blocks until at least one of the services' container exits
|
// Wait blocks until at least one of the services' container exits
|
||||||
Wait(ctx context.Context, projectName string, options WaitOptions) (int64, error)
|
Wait(ctx context.Context, projectName string, options WaitOptions) (int64, error)
|
||||||
|
// Scale manages numbers of container instances running per service
|
||||||
|
Scale(ctx context.Context, project *types.Project, options ScaleOptions) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScaleOptions struct {
|
||||||
|
Services []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type WaitOptions struct {
|
type WaitOptions struct {
|
||||||
|
@ -56,6 +56,7 @@ type ServiceProxy struct {
|
|||||||
VizFn func(ctx context.Context, project *types.Project, options VizOptions) (string, error)
|
VizFn func(ctx context.Context, project *types.Project, options VizOptions) (string, error)
|
||||||
WaitFn func(ctx context.Context, projectName string, options WaitOptions) (int64, error)
|
WaitFn func(ctx context.Context, projectName string, options WaitOptions) (int64, error)
|
||||||
PublishFn func(ctx context.Context, project *types.Project, repository string, options PublishOptions) error
|
PublishFn func(ctx context.Context, project *types.Project, repository string, options PublishOptions) error
|
||||||
|
ScaleFn func(ctx context.Context, project *types.Project, options ScaleOptions) error
|
||||||
interceptors []Interceptor
|
interceptors []Interceptor
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +100,7 @@ func (s *ServiceProxy) WithService(service Service) *ServiceProxy {
|
|||||||
s.DryRunModeFn = service.DryRunMode
|
s.DryRunModeFn = service.DryRunMode
|
||||||
s.VizFn = service.Viz
|
s.VizFn = service.Viz
|
||||||
s.WaitFn = service.Wait
|
s.WaitFn = service.Wait
|
||||||
|
s.ScaleFn = service.Scale
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,6 +351,13 @@ func (s *ServiceProxy) Wait(ctx context.Context, projectName string, options Wai
|
|||||||
return s.WaitFn(ctx, projectName, options)
|
return s.WaitFn(ctx, projectName, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ServiceProxy) Scale(ctx context.Context, project *types.Project, options ScaleOptions) error {
|
||||||
|
if s.ScaleFn == nil {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
return s.ScaleFn(ctx, project, options)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ServiceProxy) MaxConcurrency(i int) {
|
func (s *ServiceProxy) MaxConcurrency(i int) {
|
||||||
s.MaxConcurrencyFn(i)
|
s.MaxConcurrencyFn(i)
|
||||||
}
|
}
|
||||||
|
36
pkg/compose/scale.go
Normal file
36
pkg/compose/scale.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 Docker Compose CLI authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/docker/compose/v2/internal/tracing"
|
||||||
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *composeService) Scale(ctx context.Context, project *types.Project, options api.ScaleOptions) error {
|
||||||
|
return progress.Run(ctx, tracing.SpanWrapFunc("project/scale", tracing.ProjectOptions(project), func(ctx context.Context) error {
|
||||||
|
err := s.create(ctx, project, api.CreateOptions{Services: options.Services})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.start(ctx, project.Name, api.StartOptions{Project: project, Services: options.Services}, nil)
|
||||||
|
|
||||||
|
}), s.stdinfo())
|
||||||
|
}
|
15
pkg/e2e/fixtures/scale/compose.yaml
Normal file
15
pkg/e2e/fixtures/scale/compose.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
back:
|
||||||
|
image: nginx:alpine
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
db:
|
||||||
|
image: nginx:alpine
|
||||||
|
front:
|
||||||
|
image: nginx:alpine
|
||||||
|
deploy:
|
||||||
|
replicas: 2
|
||||||
|
dbadmin:
|
||||||
|
image: nginx:alpine
|
||||||
|
deploy:
|
||||||
|
replicas: 0
|
110
pkg/e2e/scale_test.go
Normal file
110
pkg/e2e/scale_test.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 Docker Compose CLI authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
testify "github.com/stretchr/testify/assert"
|
||||||
|
"gotest.tools/v3/assert"
|
||||||
|
"gotest.tools/v3/icmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
const NO_STATE_TO_CHECK = ""
|
||||||
|
|
||||||
|
func TestScaleBasicCases(t *testing.T) {
|
||||||
|
c := NewCLI(t, WithEnv(
|
||||||
|
"COMPOSE_PROJECT_NAME=scale-basic-tests"))
|
||||||
|
|
||||||
|
reset := func() {
|
||||||
|
c.RunDockerComposeCmd(t, "down", "--rmi", "all")
|
||||||
|
}
|
||||||
|
t.Cleanup(reset)
|
||||||
|
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d")
|
||||||
|
res.Assert(t, icmd.Success)
|
||||||
|
|
||||||
|
t.Log("scale up one service")
|
||||||
|
res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "scale", "dbadmin=2")
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-basic-tests-dbadmin", "Started", 2)
|
||||||
|
|
||||||
|
t.Log("scale up 2 services")
|
||||||
|
res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "scale", "front=3", "back=2")
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-basic-tests-front", "Running", 2)
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-basic-tests-front", "Started", 1)
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-basic-tests-back", "Running", 1)
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-basic-tests-back", "Started", 1)
|
||||||
|
|
||||||
|
t.Log("scale down one service")
|
||||||
|
res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "scale", "dbadmin=1")
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-basic-tests-dbadmin", "Running", 1)
|
||||||
|
|
||||||
|
t.Log("scale to 0 a service")
|
||||||
|
res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "scale", "dbadmin=0")
|
||||||
|
assert.Check(t, res.Stdout() == "", res.Stdout())
|
||||||
|
|
||||||
|
t.Log("scale down 2 services")
|
||||||
|
res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "scale", "front=2", "back=1")
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-basic-tests-front", "Running", 2)
|
||||||
|
assert.Check(t, !strings.Contains(res.Combined(), "Container scale-basic-tests-front-3 Running"), res.Combined())
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-basic-tests-back", "Running", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScaleWithDepsCases(t *testing.T) {
|
||||||
|
c := NewCLI(t, WithEnv(
|
||||||
|
"COMPOSE_PROJECT_NAME=scale-deps-tests"))
|
||||||
|
|
||||||
|
reset := func() {
|
||||||
|
c.RunDockerComposeCmd(t, "down", "--rmi", "all")
|
||||||
|
}
|
||||||
|
t.Cleanup(reset)
|
||||||
|
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=2")
|
||||||
|
res.Assert(t, icmd.Success)
|
||||||
|
|
||||||
|
res = c.RunDockerComposeCmd(t, "ps")
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-deps-tests-db", NO_STATE_TO_CHECK, 2)
|
||||||
|
|
||||||
|
t.Log("scale up 1 service with --no-deps")
|
||||||
|
_ = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "scale", "--no-deps", "back=2")
|
||||||
|
res = c.RunDockerComposeCmd(t, "ps")
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-deps-tests-back", NO_STATE_TO_CHECK, 2)
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-deps-tests-db", NO_STATE_TO_CHECK, 2)
|
||||||
|
|
||||||
|
t.Log("scale up 1 service without --no-deps")
|
||||||
|
_ = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "scale", "back=2")
|
||||||
|
res = c.RunDockerComposeCmd(t, "ps")
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-deps-tests-back", NO_STATE_TO_CHECK, 2)
|
||||||
|
checkServiceContainer(t, res.Combined(), "scale-deps-tests-db", NO_STATE_TO_CHECK, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkServiceContainer(t *testing.T, stdout, containerName, containerState string, count int) {
|
||||||
|
found := 0
|
||||||
|
lines := strings.Split(stdout, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, containerName) && strings.Contains(line, containerState) {
|
||||||
|
found++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found == count {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errMessage := fmt.Sprintf("expected %d but found %d instance(s) of container %s in stoud", count, found, containerName)
|
||||||
|
if containerState != "" {
|
||||||
|
errMessage += fmt.Sprintf(" with expected state %s", containerState)
|
||||||
|
}
|
||||||
|
testify.Fail(t, errMessage, stdout)
|
||||||
|
}
|
@ -351,6 +351,20 @@ func (mr *MockServiceMockRecorder) RunOneOffContainer(ctx, project, opts interfa
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunOneOffContainer", reflect.TypeOf((*MockService)(nil).RunOneOffContainer), ctx, project, opts)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunOneOffContainer", reflect.TypeOf((*MockService)(nil).RunOneOffContainer), ctx, project, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scale mocks base method.
|
||||||
|
func (m *MockService) Scale(ctx context.Context, project *types.Project, options api.ScaleOptions) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Scale", ctx, project, options)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale indicates an expected call of Scale.
|
||||||
|
func (mr *MockServiceMockRecorder) Scale(ctx, project, options interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scale", reflect.TypeOf((*MockService)(nil).Scale), ctx, project, options)
|
||||||
|
}
|
||||||
|
|
||||||
// Start mocks base method.
|
// Start mocks base method.
|
||||||
func (m *MockService) Start(ctx context.Context, projectName string, options api.StartOptions) error {
|
func (m *MockService) Start(ctx context.Context, projectName string, options api.StartOptions) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user