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(elasticloadbalancingv2): add load balancer lookups #11089

Merged
merged 59 commits into from
Nov 9, 2020
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
0f2a454
feat: add load balancer lookups
misterjoshua Oct 23, 2020
1215ffb
fix: schema version major version bump
misterjoshua Oct 24, 2020
5efac1b
fix: yarn update-schema was not run
misterjoshua Oct 25, 2020
5c542f0
fix: artifacts from failed auto-refactor
misterjoshua Oct 25, 2020
e3c6e21
style: doc comment format
misterjoshua Oct 25, 2020
c0175d6
fix: accidental removal of vpc prop
misterjoshua Oct 25, 2020
fe9d5c0
refactor: change dummy security group variable name
misterjoshua Oct 25, 2020
a1a0c74
chore: add test SecurityGroup.fromLookup()
misterjoshua Oct 25, 2020
b372c87
chore: add tests for security group context provider
misterjoshua Oct 25, 2020
aef71d6
fix: imports not used
misterjoshua Oct 25, 2020
d2ab397
fix: does not identify all traffic egress
misterjoshua Oct 25, 2020
9c1ae33
chore: add loadAssemblyManifest to allowed breaking changes
misterjoshua Oct 25, 2020
cf4da2d
fix: artifacts from other failed auto-refactor
misterjoshua Oct 25, 2020
5e5ffef
fix: typos in doc comments
misterjoshua Oct 25, 2020
83a9dd9
fix: describeLoadBalancers missing NextMarker
misterjoshua Oct 28, 2020
f3b3d92
fix: security group should check for tokens
misterjoshua Oct 28, 2020
79a81ec
style: remove securityGroupId key
misterjoshua Oct 28, 2020
2bc7750
fix: dummy attributes should be obviously false
misterjoshua Oct 28, 2020
0cc3f6a
refactor: use dummyValue getValue input
misterjoshua Oct 28, 2020
1c71d21
fix: use === and \!== throughout
misterjoshua Oct 28, 2020
6109fe3
refactor: keep connections and listenerArn abstract
misterjoshua Oct 28, 2020
e0707af
fix: unnecessary assignment to undefined
misterjoshua Oct 28, 2020
0e5f494
fix: docstring references to ApplicationListener in base-listener
misterjoshua Oct 28, 2020
f7c50c5
style: spacing on multiline conditionals
misterjoshua Oct 28, 2020
da69e95
refactor: reuse existing Tag type
misterjoshua Oct 28, 2020
a2bdb63
refactor: simplify LB context provider
misterjoshua Oct 28, 2020
3e18ef4
refactor: getListenerDirectly -> getListenerByArn
misterjoshua Oct 28, 2020
c5ec92b
fix: listener matching should be deterministic
misterjoshua Oct 28, 2020
f1eadc6
fix: load balancer lookup should be deterministic
misterjoshua Oct 28, 2020
5c851d2
fix: load balancer lookup should error when missing filters
misterjoshua Oct 28, 2020
b5a10af
fix: listener query should error when missing filters
misterjoshua Oct 28, 2020
e3a935b
fix: unnecessary non-null assertion
misterjoshua Oct 28, 2020
030f754
refactor: simplify load balancer collection
misterjoshua Oct 28, 2020
fa1bcc6
refactor: use optional chaining for next page markers
misterjoshua Oct 28, 2020
9555850
fix: detect ipv6 egress
misterjoshua Oct 28, 2020
8cfbe3f
fix: does not handle undefined tag values
misterjoshua Oct 28, 2020
0a3a0a5
chore: add tests for paginated calls
misterjoshua Oct 28, 2020
5dfc521
fix: restore the vpc-like user tag input
misterjoshua Oct 28, 2020
3664f00
fix: stubELBv2 comment
misterjoshua Oct 28, 2020
c4e9973
docs: note the error from multiple matches
misterjoshua Oct 28, 2020
9a34689
Merge branch 'master' into elbv2-lookups
misterjoshua Oct 29, 2020
34f8703
Merge branch 'master' of github.com:aws/aws-cdk into elbv2-lookups
misterjoshua Oct 29, 2020
0acf72b
fix: allowed breaking change
misterjoshua Oct 29, 2020
e2a133a
fix: missing public modifier
misterjoshua Oct 29, 2020
cbfc36b
fix: unable to allow optional listener protocol
misterjoshua Oct 29, 2020
7447424
refactor: rename ImportedApplicationListenerBase -> ExternalApplicati…
misterjoshua Oct 29, 2020
85a2091
refactor: move listenerArn to ApplicationListenerLookupOptions
misterjoshua Oct 29, 2020
8928287
docs: improve description of allowAllOutbound
misterjoshua Oct 29, 2020
6a4e369
fix: public accessor missing from SecurityGroup.fromLookup
misterjoshua Oct 29, 2020
f4c332a
fix: remove out of date comment
misterjoshua Oct 29, 2020
8c2c734
Merge branch 'master' of github.com:aws/aws-cdk into elbv2-lookups
misterjoshua Oct 29, 2020
d23f909
Merge branch 'master' into elbv2-lookups
misterjoshua Nov 2, 2020
f4c84e5
Merge branch 'master' of github.com:aws/aws-cdk into elbv2-lookups
misterjoshua Nov 3, 2020
f0ecfcd
fix: missing public modifier
misterjoshua Nov 5, 2020
0044974
style: use ternary for if else
misterjoshua Nov 5, 2020
2f2a992
chore: relocate allowed breaking change per review
misterjoshua Nov 5, 2020
19dc764
Merge branch 'master' into elbv2-lookups
misterjoshua Nov 5, 2020
e0d3b9c
Merge branch 'master' into elbv2-lookups
misterjoshua Nov 6, 2020
488b6d8
Merge branch 'master' into elbv2-lookups
mergify[bot] Nov 9, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion allowed-breaking-changes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,12 @@ incompatible-argument:@aws-cdk/aws-ecs.Ec2TaskDefinition.addVolume
incompatible-argument:@aws-cdk/aws-ecs.FargateTaskDefinition.<initializer>
incompatible-argument:@aws-cdk/aws-ecs.FargateTaskDefinition.addVolume
incompatible-argument:@aws-cdk/aws-ecs.TaskDefinition.<initializer>
incompatible-argument:@aws-cdk/aws-ecs.TaskDefinition.addVolume
incompatible-argument:@aws-cdk/aws-ecs.TaskDefinition.addVolume

# The new ELBv2 context queries added these arms to the ContextQueryProperties
# union:
# | LoadBalancerContextQuery
# | LoadBalancerListenerContextQuery
# | SecurityGroupContextQuery
# These additions changed the signature of MissingContext
weakened:@aws-cdk/cloud-assembly-schema.MissingContext
misterjoshua marked this conversation as resolved.
Show resolved Hide resolved
26 changes: 25 additions & 1 deletion packages/@aws-cdk/aws-ec2/lib/security-group.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Annotations, IResource, Lazy, Names, Resource, ResourceProps, Stack, Token } from '@aws-cdk/core';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
import { Annotations, ContextProvider, IResource, Lazy, Names, Resource, ResourceProps, Stack, Token } from '@aws-cdk/core';
import * as cxapi from '@aws-cdk/cx-api';
import { Construct } from 'constructs';
import { Connections } from './connections';
import { CfnSecurityGroup, CfnSecurityGroupEgress, CfnSecurityGroupIngress } from './ec2.generated';
Expand Down Expand Up @@ -294,6 +296,28 @@ export interface SecurityGroupImportOptions {
* ```
*/
export class SecurityGroup extends SecurityGroupBase {
/**
* Look up a security group by id.
*/
public static fromLookup(scope: Construct, id: string, securityGroupId: string) {
if (Token.isUnresolved(securityGroupId)) {
throw new Error('All arguments to look up a security group must be concrete (no Tokens)');
}

const attributes: cxapi.SecurityGroupContextResponse = ContextProvider.getValue(scope, {
provider: cxschema.ContextProvider.SECURITY_GROUP_PROVIDER,
props: { securityGroupId },
dummyValue: {
securityGroupId: 'sg-12345',
allowAllOutbound: true,
} as cxapi.SecurityGroupContextResponse,
}).value;

return SecurityGroup.fromSecurityGroupId(scope, id, attributes.securityGroupId, {
allowAllOutbound: attributes.allowAllOutbound,
mutable: true,
});
}

/**
* Import an existing security group into this app.
Expand Down
19 changes: 18 additions & 1 deletion packages/@aws-cdk/aws-ec2/test/security-group.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, haveResource, not } from '@aws-cdk/assert';
import { Intrinsic, Lazy, Stack, Token } from '@aws-cdk/core';
import { App, Intrinsic, Lazy, Stack, Token } from '@aws-cdk/core';
import { nodeunitShim, Test } from 'nodeunit-shim';
import { Peer, Port, SecurityGroup, Vpc } from '../lib';

Expand Down Expand Up @@ -293,4 +293,21 @@ nodeunitShim({
test.done();
},
},

'can look up a security group'(test: Test) {
const app = new App();
const stack = new Stack(app, 'stack', {
env: {
account: '1234',
region: 'us-east-1',
},
});

const securityGroup = SecurityGroup.fromLookup(stack, 'stack', 'sg-1234');

test.equal(securityGroup.securityGroupId, 'sg-12345');
test.equal(securityGroup.allowAllOutbound, true);

test.done();
},
});
88 changes: 88 additions & 0 deletions packages/@aws-cdk/aws-elasticloadbalancingv2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,91 @@ case for ECS Services for example), take a resource dependency on
// has been associated with the LoadBalancer, before 'resource' is created.
resourced.addDependency(targetGroup.loadBalancerDependency());
```

## Looking up Load Balancers and Listeners

You may look up load balancers and load balancer listeners by using one of the
following lookup methods:

- `ApplicationLoadBalancer.fromlookup(options)` - Look up an application load
balancer.
- `ApplicationListener.fromLookup(options)` - Look up an application load
balancer listener.
- `NetworkLoadBalancer.fromLookup(options)` - Look up a network load balancer.
- `NetworkListener.fromLookup(options)` - Look up a network load balancer
listener.

### Load Balancer lookup options

You may look up a load balancer by ARN or by associated tags. When you look a
load balancer up by ARN, that load balancer will be returned unless CDK detects
that the load balancer is of the wrong type. When you look up a load balancer by
tags, CDK will return the load balancer matching all specified tags. If more
than one load balancer matches, CDK will throw an error requesting that you
provide more specific criteria.

**Look up a Application Load Balancer by ARN**
```ts
const loadBalancer = ApplicationLoadBalancer.fromLookup(stack, 'ALB', {
loadBalancerArn: YOUR_ALB_ARN,
});
```

**Look up an Application Load Balancer by tags**
```ts
const loadBalancer = ApplicationLoadBalancer.fromLookup(stack, 'ALB', {
loadBalancerTags: {
// Finds a load balancer matching all tags.
some: 'tag',
someother: 'tag',
},
});
```

## Load Balancer Listener lookup options

You may look up a load balancer listener by the following criteria:

- Associated load balancer ARN
- Associated load balancer tags
- Listener ARN
- Listener port
- Listener protocol

The lookup method will return the matching listener. If more than one listener
matches, CDK will throw an error requesting that you specify additional
criteria.

**Look up a Listener by associated Load Balancer, Port, and Protocol**

```ts
const listener = ApplicationListener.fromLookup(stack, 'ALBListener', {
loadBalancerArn: YOUR_ALB_ARN,
listenerProtocol: ApplicationProtocol.HTTPS,
listenerPort: 443,
});
```

**Look up a Listener by associated Load Balancer Tag, Port, and Protocol**

```ts
const listener = ApplicationListener.fromLookup(stack, 'ALBListener', {
loadBalancerTags: {
Cluster: 'MyClusterName',
},
listenerProtocol: ApplicationProtocol.HTTPS,
listenerPort: 443,
});
```

**Look up a Network Listener by associated Load Balancer Tag, Port, and Protocol**

```ts
const listener = NetworkListener.fromLookup(stack, 'ALBListener', {
loadBalancerTags: {
Cluster: 'MyClusterName',
},
listenerProtocol: Protocol.TCP,
listenerPort: 12345,
});
```
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as ec2 from '@aws-cdk/aws-ec2';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
import { Duration, IResource, Lazy, Resource, Token } from '@aws-cdk/core';
import * as cxapi from '@aws-cdk/cx-api';
import { Construct } from 'constructs';
import { BaseListener } from '../shared/base-listener';
import { BaseListener, BaseListenerLookupOptions } from '../shared/base-listener';
import { HealthCheck } from '../shared/base-target-group';
import { ApplicationProtocol, IpAddressType, SslPolicy } from '../shared/enums';
import { IListenerCertificate, ListenerCertificate } from '../shared/listener-certificate';
Expand Down Expand Up @@ -106,12 +108,53 @@ export interface ApplicationListenerProps extends BaseApplicationListenerProps {
readonly loadBalancer: IApplicationLoadBalancer;
}

/**
* Options for ApplicationListener lookup
*/
export interface ApplicationListenerLookupOptions extends BaseListenerLookupOptions {
/**
* ARN of the listener to look up
* @default - does not filter by listener arn
*/
readonly listenerArn?: string;

/**
* Filter listeners by listener protocol
* @default - does not filter by listener protocol
*/
readonly listenerProtocol?: ApplicationProtocol;
}

/**
* Define an ApplicationListener
*
* @resource AWS::ElasticLoadBalancingV2::Listener
*/
export class ApplicationListener extends BaseListener implements IApplicationListener {
/**
* Look up an ApplicationListener.
*/
public static fromLookup(scope: Construct, id: string, options: ApplicationListenerLookupOptions): IApplicationListener {
if (Token.isUnresolved(options.listenerArn)) {
throw new Error('All arguments to look up a load balancer listener must be concrete (no Tokens)');
}

let listenerProtocol: cxschema.LoadBalancerListenerProtocol | undefined;
switch (options.listenerProtocol) {
case ApplicationProtocol.HTTP: listenerProtocol = cxschema.LoadBalancerListenerProtocol.HTTP; break;
case ApplicationProtocol.HTTPS: listenerProtocol = cxschema.LoadBalancerListenerProtocol.HTTPS; break;
}

const props = BaseListener._queryContextProvider(scope, {
userOptions: options,
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
listenerArn: options.listenerArn,
listenerProtocol,
});

return new LookedUpApplicationListener(scope, id, props);
}

/**
* Import an existing listener
*/
Expand Down Expand Up @@ -517,36 +560,28 @@ export interface ApplicationListenerAttributes {
readonly securityGroupAllowsAllOutbound?: boolean;
}

class ImportedApplicationListener extends Resource implements IApplicationListener {
public readonly connections: ec2.Connections;
abstract class ExternalApplicationListener extends Resource implements IApplicationListener {
/**
* Connections object.
*/
public abstract readonly connections: ec2.Connections;

/**
* ARN of the listener
*/
public readonly listenerArn: string;
public abstract readonly listenerArn: string;

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

this.listenerArn = props.listenerArn;

const defaultPort = props.defaultPort !== undefined ? ec2.Port.tcp(props.defaultPort) : undefined;

let securityGroup: ec2.ISecurityGroup;
if (props.securityGroup) {
securityGroup = props.securityGroup;
} else if (props.securityGroupId) {
securityGroup = ec2.SecurityGroup.fromSecurityGroupId(scope, 'SecurityGroup', props.securityGroupId, {
allowAllOutbound: props.securityGroupAllowsAllOutbound,
});
} else {
throw new Error('Either `securityGroup` or `securityGroupId` must be specified to import an application listener.');
}

this.connections = new ec2.Connections({
securityGroups: [securityGroup],
defaultPort,
});
/**
* Register that a connectable that has been added to this load balancer.
*
* Don't call this directly. It is called by ApplicationTargetGroup.
*/
public registerConnectable(connectable: ec2.IConnectable, portRange: ec2.Port): void {
this.connections.allowTo(connectable, portRange, 'Load balancer to target');
}

/**
Expand Down Expand Up @@ -599,14 +634,55 @@ class ImportedApplicationListener extends Resource implements IApplicationListen
// eslint-disable-next-line max-len
throw new Error('Can only call addTargets() when using a constructed ApplicationListener; construct a new TargetGroup and use addTargetGroup.');
}
}

/**
* Register that a connectable that has been added to this load balancer.
*
* Don't call this directly. It is called by ApplicationTargetGroup.
*/
public registerConnectable(connectable: ec2.IConnectable, portRange: ec2.Port): void {
this.connections.allowTo(connectable, portRange, 'Load balancer to target');
/**
* An imported application listener.
*/
class ImportedApplicationListener extends ExternalApplicationListener {
public readonly listenerArn: string;
public readonly connections: ec2.Connections;

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

this.listenerArn = props.listenerArn;
const defaultPort = props.defaultPort !== undefined ? ec2.Port.tcp(props.defaultPort) : undefined;

let securityGroup: ec2.ISecurityGroup;
if (props.securityGroup) {
securityGroup = props.securityGroup;
} else if (props.securityGroupId) {
securityGroup = ec2.SecurityGroup.fromSecurityGroupId(scope, 'SecurityGroup', props.securityGroupId, {
allowAllOutbound: props.securityGroupAllowsAllOutbound,
});
} else {
throw new Error('Either `securityGroup` or `securityGroupId` must be specified to import an application listener.');
}

this.connections = new ec2.Connections({
securityGroups: [securityGroup],
defaultPort,
});
}
}

class LookedUpApplicationListener extends ExternalApplicationListener {
public readonly listenerArn: string;
public readonly connections: ec2.Connections;

constructor(scope: Construct, id: string, props: cxapi.LoadBalancerListenerContextResponse) {
super(scope, id);

this.listenerArn = props.listenerArn;
this.connections = new ec2.Connections({
defaultPort: ec2.Port.tcp(props.listenerPort),
});

for (const securityGroupId of props.securityGroupIds) {
const securityGroup = ec2.SecurityGroup.fromLookup(this, `SecurityGroup-${securityGroupId}`, securityGroupId);
this.connections.addSecurityGroup(securityGroup);
}
}
}

Expand Down
Loading