Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS ELB-ALB support #211

Merged
merged 12 commits into from
Mar 23, 2017
Merged

AWS ELB-ALB support #211

merged 12 commits into from
Mar 23, 2017

Conversation

vcastellm
Copy link
Contributor

@vcastellm vcastellm commented Feb 23, 2017

We want to be able to create a new service and assign it an Application Load Balancer.

Add 5 new params that allows to specify the Load Balancer details in order for the service to be created with an associated ELB/ALB.

Specify the Target Group ARN, Load Balancer Name, Container Name, Container Port and Role to allow the service to register new instances in the ELB/ALB.

  • --target-group-arn: The full Amazon Resource Name (ARN) of the Elastic Load Balancing target group associated with a service.
  • --load-balancer-name: The name of the classic load balancer.
  • --container-name: The name of the container (as it appears in a container definition) to associate with the load balancer.
  • --container-port: The port on the container to associate with the load balancer. This port must correspond to a containerPort in the service's task definition. Your container instances must allow ingress traffic on the hostPort of the port mapping.
  • --role: The name or full Amazon Resource Name (ARN) of the IAM role that allows Amazon ECS to make calls to your load balancer on your behalf. This parameter is required if you are using a load balancer with your service. If you specify the role parameter, you must also specify a load balancer object with the loadBalancers parameter.

If --target-group-arn or --load-balancer-name is not passed then the current behaviour is maintained and it doesn't try to associate an ELB with the service.

Note: If both target-goup-arn and load-balancer-name present, target-group-arn param will take precedence.

When creating a new ECS service, allow the user to specify an ELB target group ARN, container name and port that it will associate with the newly created ECS service.
}

s.loadBalancer = &ecs.LoadBalancer{
TargetGroupArn: aws.String(targetGroupArn),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also support Classic Load Balancer (ELB) as well? You will need to add LoadBalancerName as a parameter.
http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LoadBalancer.html

func (client *ecsClient) CreateService(serviceName, taskDefName string, loadBalancer *ecs.LoadBalancer, role string, deploymentConfig *ecs.DeploymentConfiguration) error {
var err error

if *loadBalancer.TargetGroupArn != "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend initializing the common inputs first, and if TargetGroupARN or LoadBalancerName is specified, add the fields that are needed for creating ELB/ALB.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to check if loadBalancer is nil before dereferencing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per loadContext method https://github.com/aws/amazon-ecs-cli/pull/211/files#diff-97b4106c54aad3d402f437209e9fc4e3R78 loadBalancer could have empty values but it'll never be nil.

func (client *ecsClient) CreateService(serviceName, taskDefName string, loadBalancer *ecs.LoadBalancer, role string, deploymentConfig *ecs.DeploymentConfiguration) error {
var err error

if *loadBalancer.TargetGroupArn != "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to check if loadBalancer is nil before dereferencing it.

@@ -136,3 +136,36 @@ func deploymentConfigFlags(specifyDefaults bool) []cli.Flag {
},
}
}

func loadBalancerFlags(specifyDefaults bool) []cli.Flag {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nrdlngr Can you review this text?

Copy link
Contributor Author

@vcastellm vcastellm Mar 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just FYI, the text is copy pasted from the param description in the AWS API docs. But please, review.

Victor Castell added 2 commits March 3, 2017 16:40
CreateService use a common CreateServiceInput object for both cases (ELB/no ELB) set it first, then set the ELB params if present.
New param --load-balancer-name allow to set a classic load balancer name for the service.

Note that in case of both target-goup-arn and load-balancer-name present, target-group-arn param will take precedence.
loadBalancerNameUsageString := "[Optional] The name of the load balancer."
roleUsageString := "[Optional] The name or full Amazon Resource Name (ARN) of the IAM role that allows Amazon ECS to make calls to your load balancer on your behalf. This parameter is required if you are using a load balancer with your service. If you specify the role parameter, you must also specify a target-group-arn, container-name and container-port."

if specifyDefaults {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When will this be false?

Also, I don't think we need to specify the defaults as none. "Optional" already indicates that it will be empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it should never be called with false. Removed

containerNameUsageString += " Defaults to none"
containerPortUsageString += " Defaults to none"
loadBalancerNameUsageString += " Defaults to none"
roleUsageString += " Defaults to ecsServiceRole"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it actually defaults to ecsServiceRole?

Also, to specify defaults use Value in the cli.StringFlag

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took license here and used the role name used in AWS docs http://docs.aws.amazon.com/AmazonECS/latest/developerguide/service_IAM_role.html on creating an IAM role for ECS services.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Victorcoder I'm seeing 2 issues here.

  1. You are specifying in the command help text that the role defaults to ecsServiceRole, but in your actual implementation of createService, you're not setting the default to ecsServiceRole
  2. By setting the default role, we are assuming everyone has created the role ecsServiceRole which is not always the case, we should be creating the role for them if the role does not exist.

I would leave out the defaults in the command help text for now, and add it back once we have the implementations. Hope that makes sense!

ContainerNameFlag = "container-name"
ContainerPortFlag = "container-port"
LoadBalancerNameFlag = "load-balancer-name"
RoleDefaultValue = "ecsServiceRole"
Copy link
Contributor

@yinshiua yinshiua Mar 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also remove the RoleDefaultValue? This is something we need to revisit

ContainerPort: containerPort,
}

if targetGroupArn != "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to educate the customers that loadBalancerName and targetGroupArn by throwing an error when both are specified instead of picking one for them.

Copy link
Contributor Author

@vcastellm vcastellm Mar 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I did in with the other params is let the AWS API validate the user input, the validation errors are quite meaninfull in most cases.

If we remove the targetGoupArn/loadBalancerName fallback here, we get a good server side message educating the user:

FATA[0000] InvalidParameterException: loadBalancerName and targetGroupArn cannot both be specified. You must specify either a load BalancerName or a targetGroupArn.

So removing the check here

if err != nil {
}

if *loadBalancer.TargetGroupArn != "" || *loadBalancer.LoadBalancerName != "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I understand that for the current use case, the loadBalancer will always be set and will never be nil, but this CreateServce method can be reused in the future, hence I would still recommend adding a nil pointer check here.

  • Can you change *loadBalancer.TargetGroupArn to use aws.StringValue (aws.StringValue(loadBalancer.TargetGroupArn) and aws.StringValue(loadBalancer.LoadBalancerName))

@@ -136,3 +136,35 @@ func deploymentConfigFlags(specifyDefaults bool) []cli.Flag {
},
}
}

func loadBalancerFlags() []cli.Flag {
targetGroupArnUsageString := "[Optional] The full Amazon Resource Name (ARN) of the Elastic Load Balancing target group associated with a service."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am working on reviewing the text with others, and will update you once I have the final version.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind changing this to:

"[Optional] Specifies the full Amazon Resource Name (ARN) of a previously configured Elastic Load Balancing target group to associate with your service."

Copy link

@nrdlngr nrdlngr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few doc suggestions to keep things consistent with our existing help text.

I modified a few of the help strings to indicate that they were required for both classic and application load balancers, and that the load balancer resources must exist before the command is run.

containerNameUsageString := "[Optional] Mandatory if target-group-arn is specified. The container name (as it appears in a container definition)."
containerPortUsageString := "[Optional] Mandatory if target-group-arn is specified. The container port to access from the load balancer."
loadBalancerNameUsageString := "[Optional] The name of the load balancer."
roleUsageString := fmt.Sprintf("[Optional] The name or full Amazon Resource Name (ARN) of the IAM role that allows Amazon ECS to make calls to your load balancer on your behalf. This parameter is required if you are using a load balancer with your service. If you specify the role parameter, you must also specify a target-group-arn, container-name and container-port.")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind changing this to:

"[Optional] Specifies the name or full Amazon Resource Name (ARN) of the IAM role that allows Amazon ECS to make calls to your load balancer or target group on your behalf. This parameter is required if you are using a load balancer or target group with your service. If you specify the role parameter, you must also specify a load balancer name or target group ARN, along with a container name and container port."

@@ -136,3 +136,35 @@ func deploymentConfigFlags(specifyDefaults bool) []cli.Flag {
},
}
}

func loadBalancerFlags() []cli.Flag {
targetGroupArnUsageString := "[Optional] The full Amazon Resource Name (ARN) of the Elastic Load Balancing target group associated with a service."
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind changing this to:

"[Optional] Specifies the full Amazon Resource Name (ARN) of a previously configured Elastic Load Balancing target group to associate with your service."


func loadBalancerFlags() []cli.Flag {
targetGroupArnUsageString := "[Optional] The full Amazon Resource Name (ARN) of the Elastic Load Balancing target group associated with a service."
containerNameUsageString := "[Optional] Mandatory if target-group-arn is specified. The container name (as it appears in a container definition)."
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind changing this to:

"[Optional] Specifies the container name (as it appears in a container definition). This parameter is required if a load balancer or target group is specified."

func loadBalancerFlags() []cli.Flag {
targetGroupArnUsageString := "[Optional] The full Amazon Resource Name (ARN) of the Elastic Load Balancing target group associated with a service."
containerNameUsageString := "[Optional] Mandatory if target-group-arn is specified. The container name (as it appears in a container definition)."
containerPortUsageString := "[Optional] Mandatory if target-group-arn is specified. The container port to access from the load balancer."
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind changing this to:

"[Optional] Specifies the port on the container to associate with the load balancer. This port must correspond to a containerPort in the service's task definition. This parameter is required if a load balancer or target group is specified."

targetGroupArnUsageString := "[Optional] The full Amazon Resource Name (ARN) of the Elastic Load Balancing target group associated with a service."
containerNameUsageString := "[Optional] Mandatory if target-group-arn is specified. The container name (as it appears in a container definition)."
containerPortUsageString := "[Optional] Mandatory if target-group-arn is specified. The container port to access from the load balancer."
loadBalancerNameUsageString := "[Optional] The name of the load balancer."
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind changing this to:

"[Optional] Specifies the name of a previously configured Elastic Load Balancing load balancer to associate with your service."

@yinshiua
Copy link
Contributor

@Victorcoder Can you please make text changes base on @nrdlngr's suggestions? Thank you!

@vcastellm
Copy link
Contributor Author

Changes applied @nrdlngr

containerNameUsageString := "[Optional] Specifies the container name (as it appears in a container definition). This parameter is required if a load balancer or target group is specified."
containerPortUsageString := "[Optional] Specifies the port on the container to associate with the load balancer. This port must correspond to a containerPort in the service's task definition. This parameter is required if a load balancer or target group is specified."
loadBalancerNameUsageString := "[Optional] Specifies the name of a previously configured Elastic Load Balancing load balancer to associate with your service."
roleUsageString := fmt.Sprintf("[Optional] Specifies the name or full Amazon Resource Name (ARN) of the IAM role that allows Amazon ECS to make calls to your load balancer or target group on your behalf. This parameter is required if you are using a load balancer or target group with your service. If you specify the role parameter, you must also specify a load balancer name or target group ARN, along with a container name and container port.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating the text. Just one minor comment

printf is not longer needed here.

@yinshiua
Copy link
Contributor

@Victorcoder Are you fine with your commit being merged in under the Apache 2.0 license?
Thanks!

@vcastellm
Copy link
Contributor Author

@yinshiua Thanks to you, I'm ok, go ahead.

@yinshiua
Copy link
Contributor

Thanks for your contribution.

I am giving a pass on this PR, but will have minor followup changes on bug fixing.

@yinshiua yinshiua merged commit df62308 into aws:master Mar 23, 2017
@markriggins
Copy link
Contributor

Shouldn't this be able to read from a JSON or YAML file, so the service can be created, dropped, recreated without having to reproduce command line options?

What about other service parameters such as maximumPercent and minimumHealthyPercent?

@vcastellm
Copy link
Contributor Author

@markriggins That could be a good improvement but those parameters wouldn't be compose compatible so it will force to have another file to create the service.

In any case, that would be the goal of another PR.

@markriggins
Copy link
Contributor

markriggins commented Mar 24, 2017 via email

@serragnoli
Copy link

Would it be possible for someone to give an example on how to use this new feature please?

@yinshiua
Copy link
Contributor

@markriggins This would be a good enhancement to read all these fields from yaml or JSON. (ex.ecs-cli compose --f docker-compose.yml service up --f ecs-service-config.yml)

@serragnoli An example for ALB would be:

ecs-cli compose service up --target-group-arn arn:aws:elasticloadbalancing:us-west-2:<**aws-account-id**>:targetgroup/ecs-cli/090481523c5c99ea
--container-name wordpress --container-port 80 --role ecsServiceRole

@markriggins
Copy link
Contributor

markriggins commented Apr 18, 2017 via email

@vchan2002
Copy link

@yinshiua Thank you for the example....

Just to clarify, when using the --target-group-arn setting, I would assume that the target group is already existing as a part of an ALB, and thus there's no need to specify the ALB, right?

Thus, when using the --load-balancer-name setting instead, it would assume that it is using a classic load balancer, not an ALB, since, as I'd imagine, just putting an ALB would be insufficient in stating where it needs to go?

I'll still trying to navigate my way thru in terms of understanding ECS, in terms of tasks, services, etc... so any help would be greatly appreciated!

@yinshiua
Copy link
Contributor

@vchan2002
Yes, current feature only allows you to use an existing ALB or ELB.
To specify an ALB, you use --target-group-arn
To specify an ELB, you use --load-balancer-name
Once the service is created with an ALB or ELB, the name/arn is immutable to the service. You will not be able to switch to use a different ALB or ELB.

The following documentations may be helpful:
http://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-load-balancing.html
http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LoadBalancer.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants