From 799b799f30cfd5c630839303600d3988fce3df4b Mon Sep 17 00:00:00 2001 From: aiordache Date: Fri, 4 Sep 2020 18:11:02 +0200 Subject: [PATCH 1/6] Implement `compose ls` for ACI Signed-off-by: aiordache --- aci/backend.go | 71 +++++++++++++++++++++++++++++------------- aci/convert/convert.go | 11 ++++++- api/compose/api.go | 17 +++++----- ecs/sdk.go | 2 +- 4 files changed, 69 insertions(+), 32 deletions(-) diff --git a/aci/backend.go b/aci/backend.go index 02b578bf..2df3be63 100644 --- a/aci/backend.go +++ b/aci/backend.go @@ -134,32 +134,14 @@ type aciContainerService struct { } func (cs *aciContainerService) List(ctx context.Context, all bool) ([]containers.Container, error) { - groupsClient, err := login.NewContainerGroupsClient(cs.ctx.SubscriptionID) - if err != nil { - return nil, err - } - var containerGroups []containerinstance.ContainerGroup - result, err := groupsClient.ListByResourceGroup(ctx, cs.ctx.ResourceGroup) + containerGroups, err := getContainerGroups(ctx, cs.ctx.SubscriptionID, cs.ctx.ResourceGroup) if err != nil { return []containers.Container{}, err } - - for result.NotDone() { - containerGroups = append(containerGroups, result.Values()...) - if err := result.NextWithContext(ctx); err != nil { - return []containers.Container{}, err - } - } - var res []containers.Container - for _, containerGroup := range containerGroups { - group, err := groupsClient.Get(ctx, cs.ctx.ResourceGroup, *containerGroup.Name) - if err != nil { - return []containers.Container{}, err - } - + for _, group := range containerGroups { if group.Containers == nil || len(*group.Containers) < 1 { - return []containers.Container{}, fmt.Errorf("no containers found in ACI container group %s", *containerGroup.Name) + return []containers.Container{}, fmt.Errorf("no containers found in ACI container group %s", *group.Name) } for _, container := range *group.Containers { @@ -173,6 +155,26 @@ func (cs *aciContainerService) List(ctx context.Context, all bool) ([]containers return res, nil } +func getContainerGroups(ctx context.Context, subscriptionID string, resourceGroup string) ([]containerinstance.ContainerGroup, error) { + groupsClient, err := login.NewContainerGroupsClient(subscriptionID) + if err != nil { + return nil, err + } + var containerGroups []containerinstance.ContainerGroup + result, err := groupsClient.ListByResourceGroup(ctx, resourceGroup) + if err != nil { + return []containerinstance.ContainerGroup{}, err + } + + for result.NotDone() { + containerGroups = append(containerGroups, result.Values()...) + if err := result.NextWithContext(ctx); err != nil { + return []containerinstance.ContainerGroup{}, err + } + } + return containerGroups, nil +} + func getContainerID(group containerinstance.ContainerGroup, container containerinstance.Container) string { containerID := *group.Name + composeContainerSeparator + *container.Name if _, ok := group.Tags[singleContainerTag]; ok { @@ -446,8 +448,33 @@ 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 + containerGroups, err := getContainerGroups(ctx, cs.ctx.SubscriptionID, cs.ctx.ResourceGroup) + if err != nil { + return []compose.Stack{}, err + } + + stacks := []compose.Stack{} + for _, group := range containerGroups { + if _, found := group.Tags[composeContainerTag]; !found { + continue + } + state := compose.RUNNING + for _, container := range *group.ContainerGroupProperties.Containers { + containerState := convert.GetStatus(container, group) + if containerState != compose.RUNNING { + state = containerState + break + } + } + stacks = append(stacks, compose.Stack{ + ID: *group.ID, + Name: *group.Name, + Status: state, + }) + } + return stacks, nil } func (cs *aciComposeService) Logs(ctx context.Context, project string, w io.Writer) error { diff --git a/aci/convert/convert.go b/aci/convert/convert.go index 3df1ee05..f8da647f 100644 --- a/aci/convert/convert.go +++ b/aci/convert/convert.go @@ -451,7 +451,16 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe // GetStatus returns status for the specified container func GetStatus(container containerinstance.Container, group containerinstance.ContainerGroup) string { - status := "Unknown" + status := compose.UNKNOWN + if group.ContainerGroupProperties != nil && group.ContainerGroupProperties.ProvisioningState != nil { + status = *group.ContainerGroupProperties.ProvisioningState + switch status { + case "Succeeded": + status = compose.RUNNING + case "Failed": + status = compose.FAILED + } + } if group.InstanceView != nil && group.InstanceView.State != nil { status = "Node " + *group.InstanceView.State } diff --git a/api/compose/api.go b/api/compose/api.go index 666290e7..98ed050e 100644 --- a/api/compose/api.go +++ b/api/compose/api.go @@ -57,23 +57,24 @@ type ServiceStatus struct { Publishers []PortPublisher } -// State of a compose stack -type State string - const ( // STARTING indicates that stack is being deployed - STARTING State = "starting" + STARTING string = "Starting" // RUNNING indicates that stack is deployed and services are running - RUNNING State = "running" + RUNNING string = "Running" // UPDATING indicates that some stack resources are being recreated - UPDATING State = "updating" + UPDATING string = "Updating" // REMOVING indicates that stack is being deleted - REMOVING State = "removing" + REMOVING string = "Removing" + // UNKNOWN indicates unknown stack state + UNKNOWN string = "Unknown" + // FAILED indicates that stack deployment failed + FAILED string = "Failed" ) // Stack holds the name and state of a compose application/stack type Stack struct { ID string Name string - Status State + Status string } diff --git a/ecs/sdk.go b/ecs/sdk.go index 426081c5..0833f97f 100644 --- a/ecs/sdk.go +++ b/ecs/sdk.go @@ -325,7 +325,7 @@ func (s sdk) ListStacks(ctx context.Context, name string) ([]compose.Stack, erro Name: aws.StringValue(stack.StackName), Status: status, }) - continue + break } } } From 50c99ed7f6e4c146dbd1e440bd8e97a10a9d2718 Mon Sep 17 00:00:00 2001 From: aiordache Date: Mon, 7 Sep 2020 11:15:39 +0200 Subject: [PATCH 2/6] filter status result by project name Signed-off-by: aiordache --- aci/backend.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aci/backend.go b/aci/backend.go index 2df3be63..37ec9f0d 100644 --- a/aci/backend.go +++ b/aci/backend.go @@ -460,6 +460,9 @@ func (cs *aciComposeService) List(ctx context.Context, project string) ([]compos if _, found := group.Tags[composeContainerTag]; !found { continue } + if project != "" && *group.Name != project { + continue + } state := compose.RUNNING for _, container := range *group.ContainerGroupProperties.Containers { containerState := convert.GetStatus(container, group) From 19a707495fac47d15194fa3b24a99818fc7349b3 Mon Sep 17 00:00:00 2001 From: aiordache Date: Mon, 7 Sep 2020 11:23:55 +0200 Subject: [PATCH 3/6] add test for compose ls Signed-off-by: aiordache --- tests/ecs-e2e/e2e-ecs_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/ecs-e2e/e2e-ecs_test.go b/tests/ecs-e2e/e2e-ecs_test.go index 95f5ab62..982072d0 100644 --- a/tests/ecs-e2e/e2e-ecs_test.go +++ b/tests/ecs-e2e/e2e-ecs_test.go @@ -98,6 +98,17 @@ func TestCompose(t *testing.T) { url = "http://" + strings.Replace(fields[3], "->80/http", "", 1) }) + t.Run("compose ls", func(t *testing.T) { + res := c.RunDockerCmd("compose", "ls", "--project-name", stack) + lines := strings.Split(res.Stdout(), "\n") + + assert.Equal(t, 2, len(lines)) + fields := strings.Fields(lines[1]) + assert.Equal(t, 2, len(fields)) + assert.Check(t, strings.Contains(fields[0], stack)) + assert.Equal(t, "Running", fields[1]) + }) + t.Run("nginx GET", func(t *testing.T) { checkUp := func(t poll.LogT) poll.Result { r, err := http.Get(url) From b155fe0f2e7df47a9589fe839cce40d373327683 Mon Sep 17 00:00:00 2001 From: aiordache Date: Mon, 7 Sep 2020 14:26:10 +0200 Subject: [PATCH 4/6] retrieve status with get group by name Signed-off-by: aiordache --- aci/backend.go | 10 +++++++++- aci/convert/convert.go | 9 --------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/aci/backend.go b/aci/backend.go index 37ec9f0d..ceabd7cf 100644 --- a/aci/backend.go +++ b/aci/backend.go @@ -172,7 +172,15 @@ func getContainerGroups(ctx context.Context, subscriptionID string, resourceGrou return []containerinstance.ContainerGroup{}, err } } - return containerGroups, nil + var groups []containerinstance.ContainerGroup + for _, group := range containerGroups { + group, err := groupsClient.Get(ctx, resourceGroup, *group.Name) + if err != nil { + return []containerinstance.ContainerGroup{}, err + } + groups = append(groups, group) + } + return groups, nil } func getContainerID(group containerinstance.ContainerGroup, container containerinstance.Container) string { diff --git a/aci/convert/convert.go b/aci/convert/convert.go index f8da647f..eb505b4c 100644 --- a/aci/convert/convert.go +++ b/aci/convert/convert.go @@ -452,15 +452,6 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe // GetStatus returns status for the specified container func GetStatus(container containerinstance.Container, group containerinstance.ContainerGroup) string { status := compose.UNKNOWN - if group.ContainerGroupProperties != nil && group.ContainerGroupProperties.ProvisioningState != nil { - status = *group.ContainerGroupProperties.ProvisioningState - switch status { - case "Succeeded": - status = compose.RUNNING - case "Failed": - status = compose.FAILED - } - } if group.InstanceView != nil && group.InstanceView.State != nil { status = "Node " + *group.InstanceView.State } From 3ed90ea467913f28ba569b5a42202c7db1a71631 Mon Sep 17 00:00:00 2001 From: aiordache Date: Mon, 7 Sep 2020 14:49:15 +0200 Subject: [PATCH 5/6] replace check with Equal in e2e test Signed-off-by: aiordache --- tests/ecs-e2e/e2e-ecs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ecs-e2e/e2e-ecs_test.go b/tests/ecs-e2e/e2e-ecs_test.go index 982072d0..5d61196f 100644 --- a/tests/ecs-e2e/e2e-ecs_test.go +++ b/tests/ecs-e2e/e2e-ecs_test.go @@ -105,7 +105,7 @@ func TestCompose(t *testing.T) { assert.Equal(t, 2, len(lines)) fields := strings.Fields(lines[1]) assert.Equal(t, 2, len(fields)) - assert.Check(t, strings.Contains(fields[0], stack)) + assert.Equal(t, fields[0], stack) assert.Equal(t, "Running", fields[1]) }) From 66c92595e5d21edae8f3ffc2e6f52316cecc40c6 Mon Sep 17 00:00:00 2001 From: aiordache Date: Mon, 7 Sep 2020 14:59:02 +0200 Subject: [PATCH 6/6] list stacks by project name for ecs Signed-off-by: aiordache --- ecs/sdk.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ecs/sdk.go b/ecs/sdk.go index 0833f97f..e11ac709 100644 --- a/ecs/sdk.go +++ b/ecs/sdk.go @@ -303,7 +303,11 @@ func (s sdk) GetStackID(ctx context.Context, name string) (string, error) { } func (s sdk) ListStacks(ctx context.Context, name string) ([]compose.Stack, error) { - cfStacks, err := s.CF.DescribeStacksWithContext(ctx, &cloudformation.DescribeStacksInput{}) + params := cloudformation.DescribeStacksInput{} + if name != "" { + params.StackName = &name + } + cfStacks, err := s.CF.DescribeStacksWithContext(ctx, ¶ms) if err != nil { return nil, err }