Skip to content

Commit

Permalink
fix(sqs): unable to import a FIFO queue when the queue ARN is a token (
Browse files Browse the repository at this point in the history
…aws#15976)

The fifo property of a SQS queue is determined based on the queue name. The suffix `.fifo` indicates that it is a fifo queue.
When the queue is imported from a token, the name is not known on synthesis time. Therefore, the name of the queue can't be used to initialize the queue as a FIFO queue.

In some constructs, CDK checks during synthesis if the given queue is a FIFIO queue. Because of that, an additional option is necessary to specify a FIFO queue. 

A new boolean property `fifo` is introduced which can be used to specify a FIFO queue that is imported from a token. The property is optional and is only necessary when a FIFO queue should be imported from a token.

Closes aws#12466.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jumic authored and hollanddd committed Aug 26, 2021
1 parent fa09960 commit c4da942
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 4 deletions.
9 changes: 9 additions & 0 deletions packages/@aws-cdk/aws-sqs/lib/queue-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,13 @@ export interface QueueAttributes {
* @default - None
*/
readonly keyArn?: string;

/**
* Whether this queue is an Amazon SQS FIFO queue. If false, this is a standard queue.
*
* In case of a FIFO queue which is imported from a token, this value has to be explicitly set to true.
*
* @default - if fifo is not specified, the property will be determined based on the queue name (not possible for FIFO queues imported from a token)
*/
readonly fifo?: boolean;
}
25 changes: 22 additions & 3 deletions packages/@aws-cdk/aws-sqs/lib/queue.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as kms from '@aws-cdk/aws-kms';
import { Duration, RemovalPolicy, Stack, Token } from '@aws-cdk/core';
import { Duration, RemovalPolicy, Stack, Token, ArnFormat } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { IQueue, QueueAttributes, QueueBase } from './queue-base';
import { CfnQueue } from './sqs.generated';
Expand Down Expand Up @@ -257,7 +257,7 @@ export class Queue extends QueueBase {
*/
public static fromQueueAttributes(scope: Construct, id: string, attrs: QueueAttributes): IQueue {
const stack = Stack.of(scope);
const parsedArn = stack.parseArn(attrs.queueArn);
const parsedArn = stack.splitArn(attrs.queueArn, ArnFormat.NO_RESOURCE_NAME);
const queueName = attrs.queueName || parsedArn.resource;
const queueUrl = attrs.queueUrl || `https://sqs.${parsedArn.region}.${stack.urlSuffix}/${parsedArn.account}/${queueName}`;

Expand All @@ -268,9 +268,28 @@ export class Queue extends QueueBase {
public readonly encryptionMasterKey = attrs.keyArn
? kms.Key.fromKeyArn(this, 'Key', attrs.keyArn)
: undefined;
public readonly fifo = queueName.endsWith('.fifo') ? true : false;
public readonly fifo: boolean = this.determineFifo();;

protected readonly autoCreatePolicy = false;

/**
* Determine fifo flag based on queueName and fifo attribute
*/
private determineFifo(): boolean {
if (Token.isUnresolved(this.queueArn)) {
return attrs.fifo || false;
} else {
if (typeof attrs.fifo !== 'undefined') {
if (attrs.fifo && !queueName.endsWith('.fifo')) {
throw new Error("FIFO queue names must end in '.fifo'");
}
if (!attrs.fifo && queueName.endsWith('.fifo')) {
throw new Error("Non-FIFO queue name may not end in '.fifo'");
}
}
return queueName.endsWith('.fifo') ? true : false;
}
}
}

return new Import(scope, id);
Expand Down
83 changes: 82 additions & 1 deletion packages/@aws-cdk/aws-sqs/test/test.sqs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, haveResource, ResourcePart } from '@aws-cdk/assert-internal';
import * as iam from '@aws-cdk/aws-iam';
import * as kms from '@aws-cdk/aws-kms';
import { CfnParameter, Duration, Stack, App } from '@aws-cdk/core';
import { CfnParameter, Duration, Stack, App, Token } from '@aws-cdk/core';
import { Test } from 'nodeunit';
import * as sqs from '../lib';

Expand Down Expand Up @@ -191,6 +191,87 @@ export = {
test.done();
},

'import queueArn from token, fifo and standard queues can be defined'(test: Test) {
// GIVEN
const stack = new Stack();

// WHEN
const stdQueue1 = sqs.Queue.fromQueueAttributes(stack, 'StdQueue1', {
queueArn: Token.asString({ Ref: 'ARN' }),
});
const stdQueue2 = sqs.Queue.fromQueueAttributes(stack, 'StdQueue2', {
queueArn: Token.asString({ Ref: 'ARN' }),
fifo: false,
});
const fifoQueue = sqs.Queue.fromQueueAttributes(stack, 'FifoQueue', {
queueArn: Token.asString({ Ref: 'ARN' }),
fifo: true,
});

// THEN
test.deepEqual(stdQueue1.fifo, false);
test.deepEqual(stdQueue2.fifo, false);
test.deepEqual(fifoQueue.fifo, true);
test.done();
},

'import queueArn from token, check attributes'(test: Test) {
// GIVEN
const stack = new Stack();

// WHEN
const stdQueue1 = sqs.Queue.fromQueueArn(stack, 'StdQueue', Token.asString({ Ref: 'ARN' }));

// THEN
test.deepEqual(stack.resolve(stdQueue1.queueArn), {
Ref: 'ARN',
});
test.deepEqual(stack.resolve(stdQueue1.queueName), {
'Fn::Select': [5, { 'Fn::Split': [':', { Ref: 'ARN' }] }],
});
test.deepEqual(stack.resolve(stdQueue1.queueUrl), {
'Fn::Join':
['',
['https://sqs.',
{ 'Fn::Select': [3, { 'Fn::Split': [':', { Ref: 'ARN' }] }] },
'.',
{ Ref: 'AWS::URLSuffix' },
'/',
{ 'Fn::Select': [4, { 'Fn::Split': [':', { Ref: 'ARN' }] }] },
'/',
{ 'Fn::Select': [5, { 'Fn::Split': [':', { Ref: 'ARN' }] }] }]],
});
test.deepEqual(stdQueue1.fifo, false);
test.done();
},

'fails if fifo flag is set for non fifo queue name'(test: Test) {
// GIVEN
const app = new App();
const stack = new Stack(app, 'my-stack');

// THEN
test.throws(() => sqs.Queue.fromQueueAttributes(stack, 'ImportedStdQueue', {
queueArn: 'arn:aws:sqs:us-west-2:123456789012:queue1',
fifo: true,
}), /FIFO queue names must end in '.fifo'/);
test.done();
},


'fails if fifo flag is false for fifo queue name'(test: Test) {
// GIVEN
const app = new App();
const stack = new Stack(app, 'my-stack');

// THEN
test.throws(() => sqs.Queue.fromQueueAttributes(stack, 'ImportedFifoQueue', {
queueArn: 'arn:aws:sqs:us-west-2:123456789012:queue1.fifo',
fifo: false,
}), /Non-FIFO queue name may not end in '.fifo'/);
test.done();
},

'importing works correctly for cross region queue'(test: Test) {
// GIVEN
const stack = new Stack(undefined, 'Stack', { env: { region: 'us-east-1' } });
Expand Down

0 comments on commit c4da942

Please sign in to comment.