Merge pull request #583 from docker/compose_ls

This commit is contained in:
Nicolas De loof 2020-09-04 14:13:27 +02:00 committed by GitHub
commit a8c290fe98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 204 additions and 45 deletions

View File

@ -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

View File

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

58
cli/cmd/compose/list.go Normal file
View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
ecs/ps.go Normal file
View File

@ -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
}

View File

@ -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{}

View File

@ -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
}