Przeglądaj źródła

Merge pull request #583 from docker/compose_ls

Nicolas De loof 5 lat temu
rodzic
commit
a8c290fe98
10 zmienionych plików z 204 dodań i 45 usunięć
  1. 3 0
      aci/backend.go
  2. 1 0
      cli/cmd/compose/compose.go
  3. 58 0
      cli/cmd/compose/list.go
  4. 5 0
      client/compose.go
  5. 23 0
      compose/api.go
  6. 2 44
      ecs/list.go
  7. 3 0
      ecs/local/compose.go
  8. 70 0
      ecs/ps.go
  9. 36 0
      ecs/sdk.go
  10. 3 1
      example/backend.go

+ 3 - 0
aci/backend.go

@@ -447,6 +447,9 @@ func (cs *aciComposeService) Ps(ctx context.Context, project string) ([]compose.
 	}
 	return res, nil
 }
+func (cs *aciComposeService) List(ctx context.Context, project string) ([]compose.Stack, error) {
+	return nil, errdefs.ErrNotImplemented
+}
 
 func (cs *aciComposeService) Logs(ctx context.Context, project string, w io.Writer) error {
 	return errdefs.ErrNotImplemented

+ 1 - 0
cli/cmd/compose/compose.go

@@ -73,6 +73,7 @@ func Command() *cobra.Command {
 		upCommand(),
 		downCommand(),
 		psCommand(),
+		listCommand(),
 		logsCommand(),
 		convertCommand(),
 	)

+ 58 - 0
cli/cmd/compose/list.go

@@ -0,0 +1,58 @@
+/*
+   Copyright 2020 Docker, Inc.
+
+   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"
+	"fmt"
+	"io"
+	"os"
+
+	"github.com/spf13/cobra"
+
+	"github.com/docker/compose-cli/client"
+)
+
+func listCommand() *cobra.Command {
+	opts := composeOptions{}
+	lsCmd := &cobra.Command{
+		Use: "ls",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runList(cmd.Context(), opts)
+		},
+	}
+	lsCmd.Flags().StringVarP(&opts.Name, "project-name", "p", "", "Project name")
+	return lsCmd
+}
+
+func runList(ctx context.Context, opts composeOptions) error {
+	c, err := client.New(ctx)
+	if err != nil {
+		return err
+	}
+	stackList, err := c.ComposeService().List(ctx, opts.Name)
+	if err != nil {
+		return err
+	}
+
+	err = printSection(os.Stdout, func(w io.Writer) {
+		for _, stack := range stackList {
+			fmt.Fprintf(w, "%s\t%s\n", stack.Name, stack.Status)
+		}
+	}, "NAME", "STATUS")
+	return err
+}

+ 5 - 0
client/compose.go

@@ -49,6 +49,11 @@ func (c *composeService) Ps(context.Context, string) ([]compose.ServiceStatus, e
 	return nil, errdefs.ErrNotImplemented
 }
 
+// List executes the equivalent to a `docker stack ls`
+func (c *composeService) List(context.Context, string) ([]compose.Stack, error) {
+	return nil, errdefs.ErrNotImplemented
+}
+
 // Convert translate compose model into backend's native format
 func (c *composeService) Convert(context.Context, *types.Project) ([]byte, error) {
 	return nil, errdefs.ErrNotImplemented

+ 23 - 0
compose/api.go

@@ -33,6 +33,8 @@ type Service interface {
 	Logs(ctx context.Context, projectName string, w io.Writer) error
 	// Ps executes the equivalent to a `compose ps`
 	Ps(ctx context.Context, projectName string) ([]ServiceStatus, error)
+	// List executes the equivalent to a `docker stack ls`
+	List(ctx context.Context, projectName string) ([]Stack, error)
 	// Convert translate compose model into backend's native format
 	Convert(ctx context.Context, project *types.Project) ([]byte, error)
 }
@@ -54,3 +56,24 @@ type ServiceStatus struct {
 	Ports      []string
 	Publishers []PortPublisher
 }
+
+// State of a compose stack
+type State string
+
+const (
+	// STARTING indicates that stack is being deployed
+	STARTING State = "starting"
+	// RUNNING indicates that stack is deployed and services are running
+	RUNNING State = "running"
+	// UPDATING indicates that some stack resources are being recreated
+	UPDATING State = "updating"
+	// REMOVING indicates that stack is being deleted
+	REMOVING State = "removing"
+)
+
+// Stack holds the name and state of a compose application/stack
+type Stack struct {
+	ID     string
+	Name   string
+	Status State
+}

+ 2 - 44
ecs/list.go

@@ -18,53 +18,11 @@ package ecs
 
 import (
 	"context"
-	"fmt"
-	"strings"
 
 	"github.com/docker/compose-cli/compose"
 )
 
-func (b *ecsAPIService) Ps(ctx context.Context, project string) ([]compose.ServiceStatus, error) {
-	parameters, err := b.SDK.ListStackParameters(ctx, project)
-	if err != nil {
-		return nil, err
-	}
-	cluster := parameters[parameterClusterName]
+func (b *ecsAPIService) List(ctx context.Context, project string) ([]compose.Stack, error) {
+	return b.SDK.ListStacks(ctx, project)
 
-	resources, err := b.SDK.ListStackResources(ctx, project)
-	if err != nil {
-		return nil, err
-	}
-
-	servicesARN := []string{}
-	for _, r := range resources {
-		switch r.Type {
-		case "AWS::ECS::Service":
-			servicesARN = append(servicesARN, r.ARN)
-		case "AWS::ECS::Cluster":
-			cluster = r.ARN
-		}
-	}
-	if len(servicesARN) == 0 {
-		return nil, nil
-	}
-	status, err := b.SDK.DescribeServices(ctx, cluster, servicesARN)
-	if err != nil {
-		return nil, err
-	}
-
-	for i, state := range status {
-		ports := []string{}
-		for _, lb := range state.Publishers {
-			ports = append(ports, fmt.Sprintf(
-				"%s:%d->%d/%s",
-				lb.URL,
-				lb.PublishedPort,
-				lb.TargetPort,
-				strings.ToLower(lb.Protocol)))
-		}
-		state.Ports = ports
-		status[i] = state
-	}
-	return status, nil
 }

+ 3 - 0
ecs/local/compose.go

@@ -177,3 +177,6 @@ func (e ecsLocalSimulation) Logs(ctx context.Context, projectName string, w io.W
 func (e ecsLocalSimulation) Ps(ctx context.Context, projectName string) ([]compose.ServiceStatus, error) {
 	return nil, errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose ps")
 }
+func (e ecsLocalSimulation) List(ctx context.Context, projectName string) ([]compose.Stack, error) {
+	return nil, errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose ls")
+}

+ 70 - 0
ecs/ps.go

@@ -0,0 +1,70 @@
+/*
+   Copyright 2020 Docker, Inc.
+
+   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 ecs
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/docker/compose-cli/compose"
+)
+
+func (b *ecsAPIService) Ps(ctx context.Context, project string) ([]compose.ServiceStatus, error) {
+	parameters, err := b.SDK.ListStackParameters(ctx, project)
+	if err != nil {
+		return nil, err
+	}
+	cluster := parameters[parameterClusterName]
+
+	resources, err := b.SDK.ListStackResources(ctx, project)
+	if err != nil {
+		return nil, err
+	}
+
+	servicesARN := []string{}
+	for _, r := range resources {
+		switch r.Type {
+		case "AWS::ECS::Service":
+			servicesARN = append(servicesARN, r.ARN)
+		case "AWS::ECS::Cluster":
+			cluster = r.ARN
+		}
+	}
+	if len(servicesARN) == 0 {
+		return nil, nil
+	}
+	status, err := b.SDK.DescribeServices(ctx, cluster, servicesARN)
+	if err != nil {
+		return nil, err
+	}
+
+	for i, state := range status {
+		ports := []string{}
+		for _, lb := range state.Publishers {
+			ports = append(ports, fmt.Sprintf(
+				"%s:%d->%d/%s",
+				lb.URL,
+				lb.PublishedPort,
+				lb.TargetPort,
+				strings.ToLower(lb.Protocol)))
+		}
+		state.Ports = ports
+		status[i] = state
+	}
+	return status, nil
+}

+ 36 - 0
ecs/sdk.go

@@ -208,6 +208,12 @@ func (s sdk) CreateStack(ctx context.Context, name string, template *cf.Template
 		Capabilities: []*string{
 			aws.String(cloudformation.CapabilityCapabilityIam),
 		},
+		Tags: []*cloudformation.Tag{
+			{
+				Key:   aws.String(compose.ProjectTag),
+				Value: aws.String(name),
+			},
+		},
 	})
 	return err
 }
@@ -296,6 +302,36 @@ func (s sdk) GetStackID(ctx context.Context, name string) (string, error) {
 	return *stacks.Stacks[0].StackId, nil
 }
 
+func (s sdk) ListStacks(ctx context.Context, name string) ([]compose.Stack, error) {
+	cfStacks, err := s.CF.DescribeStacksWithContext(ctx, &cloudformation.DescribeStacksInput{})
+	if err != nil {
+		return nil, err
+	}
+	stacks := []compose.Stack{}
+	for _, stack := range cfStacks.Stacks {
+		for _, t := range stack.Tags {
+			if *t.Key == compose.ProjectTag {
+				status := compose.RUNNING
+				switch aws.StringValue(stack.StackStatus) {
+				case "CREATE_IN_PROGRESS":
+					status = compose.STARTING
+				case "DELETE_IN_PROGRESS":
+					status = compose.REMOVING
+				case "UPDATE_IN_PROGRESS":
+					status = compose.UPDATING
+				}
+				stacks = append(stacks, compose.Stack{
+					ID:     aws.StringValue(stack.StackId),
+					Name:   aws.StringValue(stack.StackName),
+					Status: status,
+				})
+				continue
+			}
+		}
+	}
+	return stacks, nil
+}
+
 func (s sdk) DescribeStackEvents(ctx context.Context, stackID string) ([]*cloudformation.StackEvent, error) {
 	// Fixme implement Paginator on Events and return as a chan(events)
 	events := []*cloudformation.StackEvent{}

+ 3 - 1
example/backend.go

@@ -139,7 +139,9 @@ func (cs *composeService) Down(ctx context.Context, project string) error {
 func (cs *composeService) Ps(ctx context.Context, project string) ([]compose.ServiceStatus, error) {
 	return nil, errdefs.ErrNotImplemented
 }
-
+func (cs *composeService) List(ctx context.Context, project string) ([]compose.Stack, error) {
+	return nil, errdefs.ErrNotImplemented
+}
 func (cs *composeService) Logs(ctx context.Context, project string, w io.Writer) error {
 	return errdefs.ErrNotImplemented
 }