generic URL/port/protocol retrieval for compose ps

Signed-off-by: aiordache <anca.iordache@docker.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
aiordache 2020-07-21 17:45:16 +02:00 committed by Nicolas De Loof
parent 2d931dab9d
commit 12215130b5
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
4 changed files with 91 additions and 28 deletions

View File

@ -91,7 +91,7 @@ func PsCommand(dockerCli command.Cli, options *cli.ProjectOptions) *cobra.Comman
}
printSection(os.Stdout, len(status), func(w io.Writer) {
for _, service := range status {
fmt.Fprintf(w, "%s\t%s\t%d/%d\t%s\n", service.ID, service.Name, service.Replicas, service.Desired, strings.Join(service.Ports, " "))
fmt.Fprintf(w, "%s\t%s\t%d/%d\t%s\n", service.ID, service.Name, service.Replicas, service.Desired, strings.Join(service.Ports, ", "))
}
}, "ID", "NAME", "REPLICAS", "PORTS")
return nil

View File

@ -21,7 +21,6 @@ func (b *Backend) Ps(ctx context.Context, options cli.ProjectOptions) ([]compose
if err != nil {
return nil, err
}
loadBalancer := parameters[ParameterLoadBalancerARN]
cluster := parameters[ParameterClusterName]
resources, err := b.api.ListStackResources(ctx, projectName)
@ -30,20 +29,14 @@ func (b *Backend) Ps(ctx context.Context, options cli.ProjectOptions) ([]compose
}
servicesARN := []string{}
targetGroups := []string{}
for _, r := range resources {
switch r.Type {
case "AWS::ECS::Service":
servicesARN = append(servicesARN, r.ARN)
case "AWS::ECS::Cluster":
cluster = r.ARN
case "AWS::ElasticLoadBalancingV2::LoadBalancer":
loadBalancer = r.ARN
case "AWS::ElasticLoadBalancingV2::TargetGroup":
targetGroups = append(targetGroups, r.LogicalID)
}
}
if len(servicesARN) == 0 {
return nil, nil
}
@ -52,18 +45,15 @@ func (b *Backend) Ps(ctx context.Context, options cli.ProjectOptions) ([]compose
return nil, err
}
url, err := b.api.GetLoadBalancerURL(ctx, loadBalancer)
if err != nil {
return nil, err
}
for i, state := range status {
ports := []string{}
for _, tg := range targetGroups {
groups := targetGroupLogicalName.FindStringSubmatch(tg)
if groups[0] == state.Name {
ports = append(ports, fmt.Sprintf("%s:%s->%s/%s", url, groups[2], groups[2], strings.ToLower(groups[1])))
}
for _, lb := range state.LoadBalancers {
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

View File

@ -449,6 +449,7 @@ func (s sdk) DescribeServices(ctx context.Context, cluster string, arns []string
if err != nil {
return nil, err
}
status := []compose.ServiceStatus{}
for _, service := range services.Services {
var name string
@ -460,17 +461,76 @@ func (s sdk) DescribeServices(ctx context.Context, cluster string, arns []string
if name == "" {
return nil, fmt.Errorf("service %s doesn't have a %s tag", *service.ServiceArn, compose.ServiceTag)
}
targetGroupArns := []string{}
for _, lb := range service.LoadBalancers {
targetGroupArns = append(targetGroupArns, *lb.TargetGroupArn)
}
// getURLwithPortMapping makes 2 queries
// one to get the target groups and another for load balancers
loadBalancers, err := s.getURLWithPortMapping(ctx, targetGroupArns)
if err != nil {
return nil, err
}
status = append(status, compose.ServiceStatus{
ID: *service.ServiceName,
Name: name,
Replicas: int(*service.RunningCount),
Desired: int(*service.DesiredCount),
ID: *service.ServiceName,
Name: name,
Replicas: int(*service.RunningCount),
Desired: int(*service.DesiredCount),
LoadBalancers: loadBalancers,
})
}
return status, nil
}
func (s sdk) getURLWithPortMapping(ctx context.Context, targetGroupArns []string) ([]compose.LoadBalancer, error) {
if len(targetGroupArns) == 0 {
return nil, nil
}
groups, err := s.ELB.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{
TargetGroupArns: aws.StringSlice(targetGroupArns),
})
if err != nil {
return nil, err
}
lbarns := []*string{}
for _, tg := range groups.TargetGroups {
lbarns = append(lbarns, tg.LoadBalancerArns...)
}
lbs, err := s.ELB.DescribeLoadBalancersWithContext(ctx, &elbv2.DescribeLoadBalancersInput{
LoadBalancerArns: lbarns,
})
if err != nil {
return nil, err
}
filterLB := func(arn *string, lbs []*elbv2.LoadBalancer) *elbv2.LoadBalancer {
for _, lb := range lbs {
if *lb.LoadBalancerArn == *arn {
return lb
}
}
return nil
}
loadBalancers := []compose.LoadBalancer{}
for _, tg := range groups.TargetGroups {
for _, lbarn := range tg.LoadBalancerArns {
lb := filterLB(lbarn, lbs.LoadBalancers)
if lb == nil {
continue
}
loadBalancers = append(loadBalancers, compose.LoadBalancer{
URL: *lb.DNSName,
TargetPort: int(*tg.Port),
PublishedPort: int(*tg.Port),
Protocol: *tg.Protocol,
})
}
}
return loadBalancers, nil
}
func (s sdk) ListTasks(ctx context.Context, cluster string, family string) ([]string, error) {
tasks, err := s.ECS.ListTasksWithContext(ctx, &ecs.ListTasksInput{
Cluster: aws.String(cluster),

View File

@ -9,12 +9,25 @@ type StackResource struct {
Status string
}
type PortMapping struct {
Source int
Target int
}
type LoadBalancer struct {
URL string
TargetPort int
PublishedPort int
Protocol string
}
type ServiceStatus struct {
ID string
Name string
Replicas int
Desired int
Ports []string
ID string
Name string
Replicas int
Desired int
Ports []string
LoadBalancers []LoadBalancer
}
const (