Repository: docker/compose-cli
Pull request #920 (2020-11-17 17:25:57)

Fix tabs vs spaces
Signed-off-by: Guillaume Tardif <guillaume.tardif@docker.com>

**What I did**

**Related issue**
https://github.com/docker/node-sdk/pull/53#discussion_r525230435

<!-- optional tests
You can add a / mention to run tests executed by default only on main branch : 
* `test-aci` to run ACI E2E tests
* `test-ecs` to run ECS E2E tests
* `test-windows` to run tests & E2E tests on windows
-->

**(not mandatory) A picture of a cute animal, if possible in relation with what you did**



Patch:
diff --git a/aci/convert/convert.go b/aci/convert/convert.go
index 8049e602..accd50ad 100644
--- a/aci/convert/convert.go
+++ b/aci/convert/convert.go
@@ -272,6 +272,7 @@ func (s serviceConfigAciHelper) getLivenessProbe() *containerinstance.ContainerP
 		}
 		if retries != nil && *retries > 0 {
 			probe.FailureThreshold = retries
+			probe.SuccessThreshold = retries
 		}
 		return &probe
 	}
@@ -364,8 +365,8 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe
 			if cc.LivenessProbe.PeriodSeconds != nil {
 				healthcheck.Interval = types.Duration(int64(*cc.LivenessProbe.PeriodSeconds) * int64(time.Second))
 			}
-			if cc.LivenessProbe.FailureThreshold != nil {
-				healthcheck.Retries = int(*cc.LivenessProbe.FailureThreshold)
+			if cc.LivenessProbe.SuccessThreshold != nil {
+				healthcheck.Retries = int(*cc.LivenessProbe.SuccessThreshold)
 			}
 			if cc.LivenessProbe.TimeoutSeconds != nil {
 				healthcheck.Timeout = types.Duration(int64(*cc.LivenessProbe.TimeoutSeconds) * int64(time.Second))
diff --git a/cli/cmd/run/run.go b/cli/cmd/run/run.go
index 8bbeef81..591719f5 100644
--- a/cli/cmd/run/run.go
+++ b/cli/cmd/run/run.go
@@ -62,7 +62,7 @@ func Command(contextType string) *cobra.Command {
 	cmd.Flags().BoolVar(&opts.Rm, "rm", false, "Automatically remove the container when it exits")
 	cmd.Flags().StringVar(&opts.HealthCmd, "health-cmd", "", "Command to run to check health")
 	cmd.Flags().DurationVar(&opts.HealthInterval, "health-interval", time.Duration(0), "Time between running the check (ms|s|m|h) (default 0s)")
-	cmd.Flags().IntVar(&opts.HealthRetries, "health-retries", 0, "Consecutive failures needed to report unhealthy")
+	cmd.Flags().IntVar(&opts.HealthRetries, "health-retries", 10, "Consecutive failures needed to report unhealthy")
 	cmd.Flags().DurationVar(&opts.HealthStartPeriod, "health-start-period", time.Duration(0), "Start period for the container to initialize before starting "+
 		"health-retries countdown (ms|s|m|h) (default 0s)")
 	cmd.Flags().DurationVar(&opts.HealthTimeout, "health-timeout", time.Duration(0), "Maximum time to allow one check to run (ms|s|m|h) (default 0s)")
diff --git a/cli/options/run/opts.go b/cli/options/run/opts.go
index bd6a8b2a..eaca7a2e 100644
--- a/cli/options/run/opts.go
+++ b/cli/options/run/opts.go
@@ -86,6 +86,13 @@ func (r *Opts) ToContainerConfig(image string) (containers.ContainerConfig, erro
 		envVars = append(envVars, vars...)
 	}
 
+	var healthCmd []string
+	var healthInterval types.Duration
+	if len(r.HealthCmd) > 0 {
+		healthCmd = strings.Split(r.HealthCmd, " ")
+		healthInterval = types.Duration(r.HealthInterval)
+	}
+
 	return containers.ContainerConfig{
 		ID:                     r.Name,
 		Image:                  image,
@@ -99,26 +106,14 @@ func (r *Opts) ToContainerConfig(image string) (containers.ContainerConfig, erro
 		RestartPolicyCondition: restartPolicy,
 		DomainName:             r.DomainName,
 		AutoRemove:             r.Rm,
-		Healthcheck:            r.toHealthcheck(),
+		Healthcheck: containers.Healthcheck{
+			Disable:  len(healthCmd) == 0,
+			Test:     healthCmd,
+			Interval: healthInterval,
+		},
 	}, nil
 }
 
-func (r *Opts) toHealthcheck() containers.Healthcheck {
-	var healthCmd []string
-
-	if len(r.HealthCmd) > 0 {
-		healthCmd = strings.Split(r.HealthCmd, " ")
-	}
-	return containers.Healthcheck{
-		Disable:     len(healthCmd) == 0,
-		Test:        healthCmd,
-		Interval:    types.Duration(r.HealthInterval),
-		StartPeriod: types.Duration(r.HealthStartPeriod),
-		Timeout:     types.Duration(r.HealthTimeout),
-		Retries:     r.HealthRetries,
-	}
-}
-
 var restartPolicyMap = map[string]string{
 	"":                                containers.RestartPolicyNone,
 	containers.RestartPolicyNone:      containers.RestartPolicyNone,
diff --git a/docs/aci-container-features.md b/docs/aci-container-features.md
index 43f41d5a..e40b8d4a 100644
--- a/docs/aci-container-features.md
+++ b/docs/aci-container-features.md
@@ -71,7 +71,7 @@ When running a container with `docker run`, by default the command line stays at
 
 ## Healthchecks
 
-A health check can be described using the flags prefixed by `--health-`. This is translated into `LivenessProbe` for ACI. If the health check fails then the container is considered unhealthy and terminated.
+A health check can be described using the flags prefixed by `--healthcheck`. This is translated into `LivenessProbe` for ACI. If the health check fails then the container is considered unhealthy and terminated.
 In order for the container to be restarted automatically, the container needs to be run with a restart policy (set by the `--restart` flag) other than `no`. Note that the default restart policy if one isn't set is `no`.
 
 In order to restart automatically, the container also need to have a restart policy set with `--restart` (`docker run` defaults to no restart policy)
diff --git a/docs/ecs-compose-examples.md b/docs/ecs-compose-examples.md
index d708208a..840f48b4 100644
--- a/docs/ecs-compose-examples.md
+++ b/docs/ecs-compose-examples.md
@@ -39,32 +39,6 @@ services:
           memory: 2048M
 ```
 
-###### IAM roles
-
-Assign an existing user role to a task:
-
-```yaml
-services:
-  test:
-    x-aws-policies:
-      - "arn:aws:iam::aws:policy/AmazonS3FullAccess"
-```
-
-###### IAM policies
-
-Assign an in-line IAM policy to a task:
-
-```yaml
-services:
-  test:
-    x-aws-role:
-        Version: '2012-10-17'
-        Statement:
-        - Effect: Allow
-          Action: sqs:*
-          Resource: arn:aws:sqs:us-east-1:12345678:myqueue
-```
-
 ###### Logging
 Pass options to awslogs driver
 ```yaml
@@ -111,7 +85,7 @@ services:
 
 
 
-##### Load Balancers
+#### Load Balancers
 
 When a service in the compose file exposes a port, a load balancer is being created and configured to distribute the traffic between all containers.
 
@@ -215,26 +189,6 @@ volumes:
         provisioned_throughput: 1024
 ```
 
-## Networks
-
-Networks are mapped to security groups.
-```yaml
-services:
-  test:
-    image: nginx
-networks:
-  default:
-```
-Using an external network/security group:
-```yaml
-services:
-  test:
-    image: nginx
-networks:
-  default:
-    external: true
-    name: sg-123abc
-```
 
 ## Secrets
 Secrets are stored in __AWS SecretsManager__ as strings and are mounted to containers  under `/run/secrets/`.
@@ -267,7 +221,6 @@ secrets:
     external: true
 ```
 
-
 ## Access private images
 When a service is configured with an image from a private repository on Docker Hub, make sure you have configured pull credentials correctly before deploying the Compose stack.
 
@@ -297,3 +250,25 @@ services:
 
 
 
+
+#### Networks
+
+Networks are mapped to security groups.
+```yaml
+services:
+  test:
+    image: nginx
+networks:
+  default:
+```
+Using an external network/security group:
+```yaml
+services:
+  test:
+    image: nginx
+networks:
+  default:
+    external: true
+    name: sg-123abc
+```
+
diff --git a/ecs/local/compose.go b/ecs/local/compose.go
index eb16303d..148f9f08 100644
--- a/ecs/local/compose.go
+++ b/ecs/local/compose.go
@@ -20,7 +20,6 @@ import (
 	"bufio"
 	"bytes"
 	"context"
-	"encoding/json"
 	"fmt"
 	"io"
 	"os"
@@ -58,7 +57,7 @@ func (e ecsLocalSimulation) Up(ctx context.Context, project *types.Project, deta
 		return fmt.Errorf("ECS simulation mode require Docker-compose 1.27, found %s", version)
 	}
 
-	converted, err := e.Convert(ctx, project, "json")
+	converted, err := e.Convert(ctx, project, "yaml")
 	if err != nil {
 		return err
 	}
@@ -138,15 +137,7 @@ func (e ecsLocalSimulation) Convert(ctx context.Context, project *types.Project,
 		"secrets":  project.Secrets,
 		"configs":  project.Configs,
 	}
-	switch format {
-	case "json":
-		return json.MarshalIndent(config, "", "  ")
-	case "yaml":
-		return yaml.Marshal(config)
-	default:
-		return nil, fmt.Errorf("unsupported format %q", format)
-	}
-
+	return yaml.Marshal(config)
 }
 
 func (e ecsLocalSimulation) Down(ctx context.Context, projectName string) error {
diff --git a/local/compose.go b/local/compose.go
index 14dc3685..5997134c 100644
--- a/local/compose.go
+++ b/local/compose.go
@@ -165,7 +165,7 @@ func toProgressEvent(jm jsonmessage.JSONMessage, w progress.Writer) {
 func (s *local) Down(ctx context.Context, projectName string) error {
 	list, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{
 		Filters: filters.NewArgs(
-			projectFilter(projectName),
+			filters.Arg("label", "com.docker.compose.project="+projectName),
 		),
 	})
 	if err != nil {
@@ -212,7 +212,7 @@ func (s *local) Down(ctx context.Context, projectName string) error {
 func (s *local) Logs(ctx context.Context, projectName string, w io.Writer) error {
 	list, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{
 		Filters: filters.NewArgs(
-			projectFilter(projectName),
+			filters.Arg("label", "com.docker.compose.project="+projectName),
 		),
 	})
 	if err != nil {
@@ -221,7 +221,7 @@ func (s *local) Logs(ctx context.Context, projectName string, w io.Writer) error
 	var wg sync.WaitGroup
 	consumer := formatter.NewLogConsumer(w)
 	for _, c := range list {
-		service := c.Labels[serviceLabel]
+		service := c.Labels["com.docker.compose.service"]
 		containerId := c.ID
 		go func() {
 			s.containerService.Logs(ctx,containerId, containers.LogsRequest{
@@ -239,7 +239,7 @@ func (s *local) Logs(ctx context.Context, projectName string, w io.Writer) error
 func (s *local) Ps(ctx context.Context, projectName string) ([]compose.ServiceStatus, error) {
 	list, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{
 		Filters: filters.NewArgs(
-			projectFilter(projectName),
+			filters.Arg("label", "com.docker.compose.project="+projectName),
 		),
 	})
 	if err != nil {
@@ -250,7 +250,7 @@ func (s *local) Ps(ctx context.Context, projectName string) ([]compose.ServiceSt
 		// TODO group by service
 		status = append(status, compose.ServiceStatus{
 			ID:         c.ID,
-			Name:       c.Labels[serviceLabel],
+			Name:       c.Labels["com.docker.compose.service"],
 			Replicas:   0,
 			Desired:    0,
 			Ports:      nil,
@@ -260,7 +260,6 @@ func (s *local) Ps(ctx context.Context, projectName string) ([]compose.ServiceSt
 	return status, nil
 }
 
-
 func (s *local) List(ctx context.Context, projectName string) ([]compose.Stack, error) {
 	_, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{All: true})
 	if err != nil {
@@ -288,10 +287,10 @@ func getContainerCreateOptions(p *types.Project, s types.ServiceConfig, number i
 		return nil, nil, nil, err
 	}
 	labels := map[string]string{
-		projectLabel:     p.Name,
-		serviceLabel:     s.Name,
-		configHashLabel: hash,
-		containerNumberLabel: strconv.Itoa(number),
+		"com.docker.compose.project":     p.Name,
+		"com.docker.compose.service":     s.Name,
+		"com.docker.compose.config-hash": hash,
+		"com.docker.compose.container-number": strconv.Itoa(number),
 	}
 
 	var (
diff --git a/local/convergence.go b/local/convergence.go
index cca0f966..55ca3049 100644
--- a/local/convergence.go
+++ b/local/convergence.go
@@ -34,8 +34,8 @@ import (
 func (s *local) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
 	actual, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{
 		Filters: filters.NewArgs(
-			filters.Arg("label", fmt.Sprintf("%s=%s", projectLabel, project.Name)),
-			filters.Arg("label", fmt.Sprintf("%s=%s", serviceLabel, service.Name)),
+			filters.Arg("label", "com.docker.compose.project="+project.Name),
+			filters.Arg("label", "com.docker.compose.service="+service.Name),
 		),
 	})
 	if err != nil {
@@ -80,7 +80,7 @@ func (s *local) ensureService(ctx context.Context, project *types.Project, servi
 	}
 	for _, container := range actual {
 		container := container
-		diverged := container.Labels[configHashLabel] != expected
+		diverged := container.Labels["com.docker.compose.config-hash"] != expected
 		if diverged {
 			eg.Go(func() error {
 				return s.recreateContainer(ctx, project, service, container)
@@ -103,7 +103,7 @@ func (s *local) ensureService(ctx context.Context, project *types.Project, servi
 func nextContainerNumber(containers []moby.Container) (int, error) {
 	max := 0
 	for _, c := range containers {
-		n, err := strconv.Atoi(c.Labels[containerNumberLabel])
+		n, err := strconv.Atoi(c.Labels["com.docker.compose.container-number"])
 		if err != nil {
 			return 0, err
 		}
@@ -164,7 +164,7 @@ func (s *local) recreateContainer(ctx context.Context, project *types.Project, s
 	if err != nil {
 		return err
 	}
-	number, err := strconv.Atoi(container.Labels[containerNumberLabel])
+	number, err := strconv.Atoi(container.Labels["com.docker.compose.container-number"])
 	if err != nil {
 		return err
 	}
diff --git a/local/labels.go b/local/labels.go
deleted file mode 100644
index 29966b4d..00000000
--- a/local/labels.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// +build local
-
-/*
-   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 local
-
-import (
-	"fmt"
-	"github.com/docker/docker/api/types/filters"
-)
-
-const (
-	projectLabel = "com.docker.compose.project"
-	serviceLabel = "com.docker.compose.service"
-	configHashLabel = "com.docker.compose.config-hash"
-	containerNumberLabel = "com.docker.compose.container-number"
-)
-
-func projectFilter(projectName string) filters.KeyValuePair {
-	return filters.Arg("label", fmt.Sprintf("%s=%s", projectLabel, projectName))
-}
\ No newline at end of file
diff --git a/protos/compose/v1/compose.proto b/protos/compose/v1/compose.proto
index cfebdf1b..216a905e 100644
--- a/protos/compose/v1/compose.proto
+++ b/protos/compose/v1/compose.proto
@@ -55,10 +55,10 @@ message ComposeStacksResponse {
 }
 
 message Stack {
-	string id = 1;
-	string name = 2;
-	string status = 3;
-	string reason = 4;
+  string id = 1;
+  string name = 2;
+  string status = 3;
+  string reason = 4;
 }
 
 message ComposeServicesRequest {
@@ -73,8 +73,8 @@ message ComposeServicesResponse {
 
 message Service {
   string id = 1;
-	string name = 2;
-	uint32 replicas = 3;
-	uint32 desired = 4;
-	repeated string Ports = 5;
+  string name = 2;
+  uint32 replicas = 3;
+  uint32 desired = 4;
+  repeated string Ports = 5;
 }


Test patch:
diff --git a/aci/convert/convert_test.go b/aci/convert/convert_test.go
index 5032e301..8c74a774 100644
--- a/aci/convert/convert_test.go
+++ b/aci/convert/convert_test.go
@@ -98,7 +98,7 @@ func TestContainerGroupToContainer(t *testing.T) {
 					}),
 				},
 				PeriodSeconds:       to.Int32Ptr(10),
-				FailureThreshold:    to.Int32Ptr(3),
+				SuccessThreshold:    to.Int32Ptr(3),
 				InitialDelaySeconds: to.Int32Ptr(2),
 				TimeoutSeconds:      to.Int32Ptr(1),
 			},
@@ -178,7 +178,7 @@ func TestHealthcheckTranslation(t *testing.T) {
 		assert.NilError(t, err)
 		assert.DeepEqual(t, (*group.Containers)[0].LivenessProbe.Exec.Command, to.StringSlicePtr(test))
 		assert.Equal(t, *(*group.Containers)[0].LivenessProbe.PeriodSeconds, int32(10))
-		assert.Assert(t, (*group.Containers)[0].LivenessProbe.SuccessThreshold == nil)
+		assert.Equal(t, *(*group.Containers)[0].LivenessProbe.SuccessThreshold, int32(42))
 		assert.Equal(t, *(*group.Containers)[0].LivenessProbe.FailureThreshold, int32(42))
 		assert.Equal(t, *(*group.Containers)[0].LivenessProbe.InitialDelaySeconds, int32(2))
 		assert.Equal(t, *(*group.Containers)[0].LivenessProbe.TimeoutSeconds, int32(3))
diff --git a/cli/cmd/run/testdata/run-help.golden b/cli/cmd/run/testdata/run-help.golden
index a9066ff3..496f6449 100644
--- a/cli/cmd/run/testdata/run-help.golden
+++ b/cli/cmd/run/testdata/run-help.golden
@@ -11,7 +11,7 @@ Flags:
       --env-file stringArray           Path to environment files to be translated as environment variables
       --health-cmd string              Command to run to check health
       --health-interval duration       Time between running the check (ms|s|m|h) (default 0s)
-      --health-retries int             Consecutive failures needed to report unhealthy
+      --health-retries int             Consecutive failures needed to report unhealthy (default 10)
       --health-start-period duration   Start period for the container to initialize before starting health-retries countdown (ms|s|m|h) (default 0s)
       --health-timeout duration        Maximum time to allow one check to run (ms|s|m|h) (default 0s)
   -l, --label stringArray              Set meta data on a container
diff --git a/cli/options/run/opts_test.go b/cli/options/run/opts_test.go
index 639a71f9..547d0968 100644
--- a/cli/options/run/opts_test.go
+++ b/cli/options/run/opts_test.go
@@ -20,9 +20,7 @@ import (
 	"errors"
 	"regexp"
 	"testing"
-	"time"
 
-	"github.com/compose-spec/compose-go/types"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/assert/cmp"
@@ -229,31 +227,3 @@ func TestValidateRestartPolicy(t *testing.T) {
 		assert.Equal(t, testCase.expected, result)
 	}
 }
-
-func TestToHealthcheck(t *testing.T) {
-	testOpt := Opts{
-		HealthCmd: "curl",
-	}
-
-	assert.DeepEqual(t, testOpt.toHealthcheck(), containers.Healthcheck{
-		Disable: false,
-		Test:    []string{"curl"},
-	})
-
-	testOpt = Opts{
-		HealthCmd:         "curl",
-		HealthRetries:     3,
-		HealthInterval:    5 * time.Second,
-		HealthTimeout:     2 * time.Second,
-		HealthStartPeriod: 10 * time.Second,
-	}
-
-	assert.DeepEqual(t, testOpt.toHealthcheck(), containers.Healthcheck{
-		Disable:     false,
-		Test:        []string{"curl"},
-		Retries:     3,
-		Interval:    types.Duration(5 * time.Second),
-		StartPeriod: types.Duration(10 * time.Second),
-		Timeout:     types.Duration(2 * time.Second),
-	})
-}