Browse Source

Store external cluster as metadata

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 5 years ago
parent
commit
4760ae334d
4 changed files with 47 additions and 6 deletions
  1. 5 3
      ecs/awsResources.go
  2. 2 2
      ecs/cloudformation.go
  3. 12 0
      ecs/cloudformation_test.go
  4. 28 1
      ecs/sdk.go

+ 5 - 3
ecs/awsResources.go

@@ -64,10 +64,10 @@ func (r *awsResources) allSecurityGroups() []string {
 }
 
 // parse look into compose project for configured resource to use, and check they are valid
-func (b *ecsAPIService) parse(ctx context.Context, project *types.Project) (awsResources, error) {
+func (b *ecsAPIService) parse(ctx context.Context, project *types.Project, template *cloudformation.Template) (awsResources, error) {
 	r := awsResources{}
 	var err error
-	r.cluster, err = b.parseClusterExtension(ctx, project)
+	r.cluster, err = b.parseClusterExtension(ctx, project, template)
 	if err != nil {
 		return r, err
 	}
@@ -90,7 +90,7 @@ func (b *ecsAPIService) parse(ctx context.Context, project *types.Project) (awsR
 	return r, nil
 }
 
-func (b *ecsAPIService) parseClusterExtension(ctx context.Context, project *types.Project) (string, error) {
+func (b *ecsAPIService) parseClusterExtension(ctx context.Context, project *types.Project, template *cloudformation.Template) (string, error) {
 	if x, ok := project.Extensions[extensionCluster]; ok {
 		cluster := x.(string)
 		ok, err := b.aws.ClusterExists(ctx, cluster)
@@ -100,6 +100,8 @@ func (b *ecsAPIService) parseClusterExtension(ctx context.Context, project *type
 		if !ok {
 			return "", errors.Wrapf(errdefs.ErrNotFound, "cluster %q does not exist", cluster)
 		}
+
+		template.Metadata["Cluster"] = cluster
 		return cluster, nil
 	}
 	return "", nil

+ 2 - 2
ecs/cloudformation.go

@@ -52,12 +52,12 @@ func (b *ecsAPIService) convert(ctx context.Context, project *types.Project) (*c
 		return nil, err
 	}
 
-	resources, err := b.parse(ctx, project)
+	template := cloudformation.NewTemplate()
+	resources, err := b.parse(ctx, project, template)
 	if err != nil {
 		return nil, err
 	}
 
-	template := cloudformation.NewTemplate()
 	err = b.ensureResources(&resources, project, template)
 	if err != nil {
 		return nil, err

+ 12 - 0
ecs/cloudformation_test.go

@@ -492,6 +492,18 @@ services:
 	}
 }
 
+func TestTemplateMetadata(t *testing.T) {
+	template := convertYaml(t, `
+x-aws-cluster: "arn:aws:ecs:region:account:cluster/name"
+services:
+  test:
+    image: nginx
+`, useDefaultVPC, func(m *MockAPIMockRecorder) {
+		m.ClusterExists(gomock.Any(), "arn:aws:ecs:region:account:cluster/name").Return(true, nil)
+	})
+	assert.Equal(t, template.Metadata["Cluster"], "arn:aws:ecs:region:account:cluster/name")
+}
+
 func convertYaml(t *testing.T, yaml string, fn ...func(m *MockAPIMockRecorder)) *cloudformation.Template {
 	project := loadConfig(t, yaml)
 	ctrl := gomock.NewController(t)

+ 28 - 1
ecs/sdk.go

@@ -25,6 +25,7 @@ import (
 
 	"github.com/docker/compose-cli/api/compose"
 	"github.com/docker/compose-cli/api/secrets"
+	"github.com/docker/compose-cli/errdefs"
 	"github.com/docker/compose-cli/internal"
 
 	"github.com/aws/aws-sdk-go/aws"
@@ -51,6 +52,7 @@ import (
 	"github.com/aws/aws-sdk-go/service/ssm"
 	"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
 	"github.com/hashicorp/go-multierror"
+	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
 
@@ -332,6 +334,7 @@ func (s sdk) ListStacks(ctx context.Context, name string) ([]compose.Stack, erro
 }
 
 func (s sdk) GetStackClusterID(ctx context.Context, stack string) (string, error) {
+	// Note: could use DescribeStackResource but we only can detect `does not exist` case by matching string error message
 	resources, err := s.CF.ListStackResourcesWithContext(ctx, &cloudformation.ListStackResourcesInput{
 		StackName: aws.String(stack),
 	})
@@ -343,7 +346,28 @@ func (s sdk) GetStackClusterID(ctx context.Context, stack string) (string, error
 			return aws.StringValue(r.PhysicalResourceId), nil
 		}
 	}
-	return "", nil
+	// stack is using user-provided cluster
+	res, err := s.CF.GetTemplateSummaryWithContext(ctx, &cloudformation.GetTemplateSummaryInput{
+		StackName: aws.String(stack),
+	})
+	if err != nil {
+		return "", err
+	}
+	c := aws.StringValue(res.Metadata)
+	var m templateMetadata
+	err = json.Unmarshal([]byte(c), &m)
+	if err != nil {
+		return "", err
+	}
+	if m.Cluster == "" {
+		return "", errors.Wrap(errdefs.ErrNotFound, "CloudFormation is missing cluster metadata")
+	}
+
+	return m.Cluster, nil
+}
+
+type templateMetadata struct {
+	Cluster string `json:",omitempty"`
 }
 
 func (s sdk) GetServiceTaskDefinition(ctx context.Context, cluster string, serviceArns []string) (map[string]string, error) {
@@ -645,6 +669,9 @@ func (s sdk) DescribeService(ctx context.Context, cluster string, arn string) (c
 		return compose.ServiceStatus{}, err
 	}
 
+	for _, f := range services.Failures {
+		return compose.ServiceStatus{}, errors.Wrapf(errdefs.ErrNotFound, "can't get service status %s: %s", aws.StringValue(f.Detail), aws.StringValue(f.Reason))
+	}
 	service := services.Services[0]
 	var name string
 	for _, t := range service.Tags {