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

feat(elasticloadbalancing): classic load balancer supports ec2 instances #24353

Merged
merged 14 commits into from
Mar 17, 2023
Merged
17 changes: 17 additions & 0 deletions packages/@aws-cdk/aws-elasticloadbalancing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,20 @@ lb.addListener({
allowConnectionsFrom: [mySecurityGroup],
});
```

### Adding Ec2 Instance as a target for the load balancer

EC2 instaces can be added as the target for the load balancer via `addTarget()` method using `InstanceTarget` class by providing ec2 `instance` as a target.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
EC2 instaces can be added as the target for the load balancer via `addTarget()` method using `InstanceTarget` class by providing ec2 `instance` as a target.
You can add an EC2 instance to the load balancer by calling using `new InstanceTarget` as the argument to `addTarget()`:


```ts
const lb = new elb.LoadBalancer(this, 'LB', {
vpc,
});
// instance to add as the target for load balancer.
const instance = new Instance(stack, 'targetInstance', {
vpc: vpc,
instanceType: InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.MICRO),
machineImage: new AmazonLinuxImage(),
});
lb.addTarget(elb.InstanceTarget(instance))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
lb.addTarget(elb.InstanceTarget(instance))
lb.addTarget(elb.InstanceTarget(instance));

```
44 changes: 42 additions & 2 deletions packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
Connections, IConnectable, ISecurityGroup, IVpc, Peer, Port,
Connections, IConnectable, IInstance, Instance, ISecurityGroup, IVpc, Peer, Port,
SecurityGroup, SelectedSubnets, SubnetSelection, SubnetType,
} from '@aws-cdk/aws-ec2';
import { Duration, Lazy, Resource } from '@aws-cdk/core';
Expand Down Expand Up @@ -248,23 +248,25 @@ export class LoadBalancer extends Resource implements IConnectable {
private readonly elb: CfnLoadBalancer;
private readonly securityGroup: SecurityGroup;
private readonly listeners: CfnLoadBalancer.ListenersProperty[] = [];
private readonly instances: IInstance[] = [];
Copy link
Contributor

Choose a reason for hiding this comment

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

This holds on to IInstances, for the sole purpose of using them as IConnectables. This collection doesn't need to be as specific as it is. If we just need "anything that's IConnectable" we can just write that. By writing IInstance we are excluding other things that could be connectable.

But I'm now noticing that ILoadBalancerTarget extends IConnectable, so we already have a list of IConnectables... it's this.targets !


private readonly instancePorts: number[] = [];
private readonly targets: ILoadBalancerTarget[] = [];
private readonly instanceIds: string[] = [];

constructor(scope: Construct, id: string, props: LoadBalancerProps) {
super(scope, id);

this.securityGroup = new SecurityGroup(this, 'SecurityGroup', { vpc: props.vpc, allowAllOutbound: false });
this.connections = new Connections({ securityGroups: [this.securityGroup] });

// Depending on whether the ELB has public or internal IPs, pick the right backend subnets
const selectedSubnets: SelectedSubnets = loadBalancerSubnets(props);

this.elb = new CfnLoadBalancer(this, 'Resource', {
securityGroups: [this.securityGroup.securityGroupId],
subnets: selectedSubnets.subnetIds,
listeners: Lazy.any({ produce: () => this.listeners }),
instances: Lazy.list({ produce: () => this.instanceIds }, { omitEmpty: true }),
scheme: props.internetFacing ? 'internet-facing' : 'internal',
healthCheck: props.healthCheck && healthCheckToJSON(props.healthCheck),
crossZone: props.crossZone ?? true,
Expand Down Expand Up @@ -318,6 +320,8 @@ export class LoadBalancer extends Resource implements IConnectable {
// Keep track using array so user can get to them even if they were all supplied in the constructor
this.listenerPorts.push(port);

// Allow connection to all instances to new listener.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm seeing this line of code a couple of lines above this line.

     this.newInstancePort(instancePort);

It's named suspiciously like some of the code you just added. Could you have a look at that and see if that is maybe already doing what we need it to do?

this.instances.forEach(i => i.connections.allowFrom(this.connections, Port.tcp(Number(instancePort))));
return port;
}

Expand Down Expand Up @@ -398,6 +402,42 @@ export class LoadBalancer extends Resource implements IConnectable {
Port.tcp(instancePort),
`Port ${instancePort} LB to fleet`);
}

/**
* Allow connection to all listeners to new instance port.
*/
private allowInstanceConnection(instance: IConnectable) {
this.listeners.forEach(l => instance.connections.allowFrom(this.connections, Port.tcp(Number(l.instancePort))));
}

/**
* Add instance to the load balancer.
* @internal
*/
public _addInstance(instance: IInstance) {
this.instances.push(instance);
this.instanceIds.push(instance.instanceId);
this.allowInstanceConnection(instance);
}
}

/**
* An EC2 instance that is the target for load balancing
*
rix0rrr marked this conversation as resolved.
Show resolved Hide resolved
*/
export class InstanceTarget implements ILoadBalancerTarget {
readonly connections: Connections;
/**
* Create a new Instance target.
*
* @param instance Instance to register to.
*/
constructor(public readonly instance: Instance) {
this.connections = instance.connections;
}
public attachToClassicLB(loadBalancer: LoadBalancer): void {
rix0rrr marked this conversation as resolved.
Show resolved Hide resolved
loadBalancer._addInstance(this.instance);
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-elasticloadbalancing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"@aws-cdk/assertions": "0.0.0",
"@aws-cdk/cdk-build-tools": "0.0.0",
"@aws-cdk/integ-runner": "0.0.0",
"@aws-cdk/integ-tests": "0.0.0",
"@aws-cdk/cfn2ts": "0.0.0",
"@aws-cdk/pkglint": "0.0.0",
"@types/jest": "^27.5.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "29.0.0",
"files": {
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
"source": {
"path": "InstanceTargetTestDefaultTestDeployAssertAF607556.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
},
"Rules": {
"CheckBootstrapVersion": {
"Assertions": [
{
"Assert": {
"Fn::Not": [
{
"Fn::Contains": [
[
"1",
"2",
"3",
"4",
"5"
],
{
"Ref": "BootstrapVersion"
}
]
}
]
},
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "29.0.0",
"files": {
"11ca0111a871a53be970c5db0c5a24d4146213fd59f6d172b6fc1bc3de206cf9": {
"source": {
"path": "aws-cdk-elb-instance-target-integ.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "11ca0111a871a53be970c5db0c5a24d4146213fd59f6d172b6fc1bc3de206cf9.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Loading