diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts index 52bfd666a0ed4..86b8593f3e8ac 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts @@ -508,6 +508,21 @@ export interface IApplicationListener extends IResource, ec2.IConnectable { * Don't call this directly. It is called by ApplicationTargetGroup. */ registerConnectable(connectable: ec2.IConnectable, portRange: ec2.Port): void; + + /** + * Perform the given action on incoming requests + * + * This allows full control of the default action of the load balancer, + * including Action chaining, fixed responses and redirect responses. See + * the `ListenerAction` class for all options. + * + * It's possible to add routing conditions to the Action added in this way. + * + * It is not possible to add a default action to an imported IApplicationListener. + * In order to add actions to an imported IApplicationListener a `priority` + * must be provided. + */ + addAction(id: string, props: AddApplicationActionProps): void; } /** @@ -627,6 +642,36 @@ abstract class ExternalApplicationListener extends Resource implements IApplicat // 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.'); } + + /** + * Perform the given action on incoming requests + * + * This allows full control of the default action of the load balancer, + * including Action chaining, fixed responses and redirect responses. See + * the `ListenerAction` class for all options. + * + * It's possible to add routing conditions to the Action added in this way. + * + * It is not possible to add a default action to an imported IApplicationListener. + * In order to add actions to an imported IApplicationListener a `priority` + * must be provided. + */ + public addAction(id: string, props: AddApplicationActionProps): void { + checkAddRuleProps(props); + + if (props.priority !== undefined) { + // New rule + // + // TargetGroup.registerListener is called inside ApplicationListenerRule. + new ApplicationListenerRule(this, id + 'Rule', { + listener: this, + priority: props.priority, + ...props, + }); + } else { + throw new Error('priority must be set for actions added to an imported listener'); + } + } } /** diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/listener.test.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/listener.test.ts index 5caf1f756dd80..4167e012d6c4b 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/listener.test.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/listener.test.ts @@ -707,6 +707,85 @@ describe('tests', () => { }); }); + test('Can add actions to an imported listener', () => { + // GIVEN + const stack = new cdk.Stack(); + const stack2 = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const lb = new elbv2.ApplicationLoadBalancer(stack, 'LoadBalancer', { + vpc, + }); + const listener = lb.addListener('Listener', { + port: 80, + }); + + // WHEN + listener.addAction('Default', { + action: elbv2.ListenerAction.fixedResponse(404, { + contentType: 'text/plain', + messageBody: 'Not Found', + }), + }); + + const importedListener = elbv2.ApplicationListener.fromApplicationListenerAttributes(stack2, 'listener', { + listenerArn: 'listener-arn', + defaultPort: 443, + securityGroup: ec2.SecurityGroup.fromSecurityGroupId(stack2, 'SG', 'security-group-id', { + allowAllOutbound: false, + }), + }); + importedListener.addAction('Hello', { + action: elbv2.ListenerAction.fixedResponse(503), + conditions: [elbv2.ListenerCondition.pathPatterns(['/hello'])], + priority: 10, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { + DefaultActions: [ + { + FixedResponseConfig: { + ContentType: 'text/plain', + MessageBody: 'Not Found', + StatusCode: '404', + }, + Type: 'fixed-response', + }, + ], + }); + + Template.fromStack(stack2).hasResourceProperties('AWS::ElasticLoadBalancingV2::ListenerRule', { + ListenerArn: 'listener-arn', + Priority: 10, + Actions: [ + { + FixedResponseConfig: { + StatusCode: '503', + }, + Type: 'fixed-response', + }, + ], + }); + }); + + test('actions added to an imported listener must have a priority', () => { + // GIVEN + const stack = new cdk.Stack(); + + const importedListener = elbv2.ApplicationListener.fromApplicationListenerAttributes(stack, 'listener', { + listenerArn: 'listener-arn', + defaultPort: 443, + securityGroup: ec2.SecurityGroup.fromSecurityGroupId(stack, 'SG', 'security-group-id', { + allowAllOutbound: false, + }), + }); + expect(() => { + importedListener.addAction('Hello', { + action: elbv2.ListenerAction.fixedResponse(503), + }); + }).toThrow(/priority must be set for actions added to an imported listener/); + }); + testDeprecated('Can add redirect responses', () => { // GIVEN const stack = new cdk.Stack();