diff --git a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts index c7cd541703c57..c965021665b52 100644 --- a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts +++ b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts @@ -112,6 +112,10 @@ export class HostedZone extends Resource implements IHostedZone { * @see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ public static fromLookup(scope: Construct, id: string, query: HostedZoneProviderProps): IHostedZone { + if (!query.domainName) { + throw new Error('Cannot use undefined value for attribute `domainName`'); + } + const DEFAULT_HOSTED_ZONE: HostedZoneContextResponse = { Id: 'DUMMY', Name: query.domainName, diff --git a/packages/@aws-cdk/aws-route53/test/hosted-zone.test.ts b/packages/@aws-cdk/aws-route53/test/hosted-zone.test.ts index f34d777220418..b659e41446d75 100644 --- a/packages/@aws-cdk/aws-route53/test/hosted-zone.test.ts +++ b/packages/@aws-cdk/aws-route53/test/hosted-zone.test.ts @@ -176,4 +176,19 @@ describe('hosted zone', () => { hz.zoneName; }).toThrow('Cannot reference `zoneName` when using `HostedZone.fromHostedZoneId()`. A construct consuming this hosted zone may be trying to reference its `zoneName`. If this is the case, use `fromHostedZoneAttributes()` or `fromLookup()` instead.'); }); + + test('fromLookup throws error when domainName is undefined', () => { + // GIVEN + let domainName!: string; + const stack = new cdk.Stack(undefined, 'TestStack', { + env: { account: '123456789012', region: 'us-east-1' }, + }); + + // THEN + expect(() => { + HostedZone.fromLookup(stack, 'HostedZone', { + domainName, + }); + }).toThrow(/Cannot use undefined value for attribute `domainName`/); + }); }); diff --git a/packages/@aws-cdk/lambda-layer-awscli/layer/requirements.txt b/packages/@aws-cdk/lambda-layer-awscli/layer/requirements.txt index faa0808b05129..1aed1c536cca2 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/layer/requirements.txt +++ b/packages/@aws-cdk/lambda-layer-awscli/layer/requirements.txt @@ -1 +1 @@ -awscli==1.25.46 +awscli==1.25.51 diff --git a/packages/@aws-cdk/pipelines/lib/main/pipeline-base.ts b/packages/@aws-cdk/pipelines/lib/main/pipeline-base.ts index 34402b0b5cffa..0bb27cb78a7bc 100644 --- a/packages/@aws-cdk/pipelines/lib/main/pipeline-base.ts +++ b/packages/@aws-cdk/pipelines/lib/main/pipeline-base.ts @@ -2,6 +2,8 @@ import { Aspects, Stage } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { AddStageOpts as StageOptions, WaveOptions, Wave, IFileSetProducer, ShellStep, FileSet } from '../blueprint'; +const PIPELINE_SYMBOL = Symbol.for('@aws-cdk/pipelines.PipelineBase'); + /** * Properties for a `Pipeline` */ @@ -32,6 +34,15 @@ export interface PipelineBaseProps { * happens first). */ export abstract class PipelineBase extends Construct { + /** + * Return whether the given object extends {@link PipelineBase}. + * + * We do attribute detection since we can't reliably use 'instanceof'. + */ + public static isPipeline(x: any): x is PipelineBase { + return x !== null && typeof (x) === 'object' && PIPELINE_SYMBOL in x; + } + /** * The build step that produces the CDK Cloud Assembly */ @@ -54,6 +65,8 @@ export abstract class PipelineBase extends Construct { constructor(scope: Construct, id: string, props: PipelineBaseProps) { super(scope, id); + Object.defineProperty(this, PIPELINE_SYMBOL, { value: true }); + if (props.synth instanceof ShellStep && !props.synth.primaryOutput) { props.synth.primaryOutputDirectory('cdk.out'); } diff --git a/packages/@aws-cdk/pipelines/test/main/pipeline-base.test.ts b/packages/@aws-cdk/pipelines/test/main/pipeline-base.test.ts new file mode 100644 index 0000000000000..680f33f7dc602 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/main/pipeline-base.test.ts @@ -0,0 +1,29 @@ +import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { App } from '../../../core/lib'; +import { PipelineBase, IFileSetProducer, FileSet } from '../../lib'; +import { PIPELINE_ENV } from '../testhelpers'; + +describe('pipeline base', () => { + + test('PipelineBase.isPipeline indicates that a construct extends PipelineBase', () => { + const app = new App(); + const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV }); + const construct = new Construct(pipelineStack, 'Construct'); + const pipeline = new class extends PipelineBase { + constructor() { + super(pipelineStack, 'Pipeline', { + synth: new class implements IFileSetProducer { + public readonly primaryOutput = new FileSet('PrimaryOutput'); + }(), + }); + } + protected doBuildPipeline(): void { } + }(); + + expect(PipelineBase.isPipeline(pipelineStack)).toStrictEqual(false); + expect(PipelineBase.isPipeline(construct)).toStrictEqual(false); + expect(PipelineBase.isPipeline(pipeline)).toStrictEqual(true); + }); + +}); \ No newline at end of file