Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { IConstruct } from 'constructs';
import { CfnResource } from 'aws-cdk-lib/core';
import type { IBucketRef, CfnBucketPolicy } from 'aws-cdk-lib/aws-s3';
import { CfnDeliverySource } from 'aws-cdk-lib/aws-logs';

/**
* Finds the closest related resource in the construct tree.
Expand Down Expand Up @@ -88,3 +89,11 @@ export function tryFindBucketPolicyForBucket(bucket: IBucketRef): CfnBucketPolic
},
);
}

export function tryFindDeliverySourceForResource(source: IConstruct, sourceArn: string, logType: string): CfnDeliverySource | undefined {
return findClosestRelatedResource<IConstruct, CfnDeliverySource>(
source,
'AWS::Logs::DeliverySource',
(_, deliverySource) => deliverySource.resourceArn === sourceArn && deliverySource.logType === logType,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as logs from 'aws-cdk-lib/aws-logs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct, type IConstruct } from 'constructs';
import type { IDeliveryStreamRef } from 'aws-cdk-lib/aws-kinesisfirehose';
import { tryFindBucketPolicyForBucket } from '../../mixins/private/reflections';
import { tryFindBucketPolicyForBucket, tryFindDeliverySourceForResource } from '../../mixins/private/reflections';
import { ConstructSelector, Mixins } from '../../core';
import { BucketPolicyStatementsMixins } from '../../mixins/private/s3';
import * as xray from '../aws-xray/policy';
Expand Down Expand Up @@ -32,13 +32,13 @@ export interface ILogsDeliveryConfig {
*/
export interface ILogsDelivery {
/**
* Binds the destination to a delivery source and creates a delivery connection between the source and destination.
* Binds the log delivery to a source resource and creates a delivery connection between the source and destination.
* @param scope - The construct scope
* @param deliverySource - The delivery source reference
* @param logType - The type of logs that the delivery source will produce
* @param sourceResourceArn - The Arn of the source resource
* @returns The delivery reference
*/
bind(scope: IConstruct, deliverySource: logs.IDeliverySourceRef, sourceResourceArn: string): ILogsDeliveryConfig;
bind(scope: IConstruct, logType: string, sourceResourceArn: string): ILogsDeliveryConfig;
}

/**
Expand Down Expand Up @@ -84,28 +84,29 @@ export class S3LogsDelivery implements ILogsDelivery {
}

/**
* Binds the S3 destination to a delivery source and creates a delivery connection between them.
* Binds S3 Bucket to a source resource for the purposes of log delivery and creates a delivery source, a delivery destination, and a connection between them.
*/
public bind(scope: IConstruct, deliverySource: logs.IDeliverySourceRef, _sourceResourceArn: string): ILogsDeliveryConfig {
const container = new Construct(scope, deliveryId('S3', scope, this.bucket));
public bind(scope: IConstruct, logType: string, sourceResourceArn: string): ILogsDeliveryConfig {
const container = new Construct(scope, deliveryId('S3', logType, scope, this.bucket));

const bucketPolicy = this.getOrCreateBucketPolicy(container);
this.grantLogsDelivery(bucketPolicy);

const deliverySource = getOrCreateDeliverySource(logType, scope, sourceResourceArn);
const deliverySourceRef = deliverySource.deliverySourceRef;

const deliveryDestination = new logs.CfnDeliveryDestination(container, 'Dest', {
destinationResourceArn: this.bucket.bucketRef.bucketArn,
name: deliveryDestName('s3', container),
name: deliveryDestName('s3', logType, container),
deliveryDestinationType: 'S3',
});

const delivery = new logs.CfnDelivery(container, 'Delivery', {
deliveryDestinationArn: deliveryDestination.attrArn,
deliverySourceName: deliverySource.deliverySourceRef.deliverySourceName,
deliverySourceName: deliverySourceRef.deliverySourceName,
});

delivery.node.addDependency(bucketPolicy);
deliveryDestination.node.addDependency(bucketPolicy);
deliverySource.node.addDependency(bucketPolicy);

delivery.node.addDependency(deliverySource);
delivery.node.addDependency(deliveryDestination);
Expand Down Expand Up @@ -197,18 +198,20 @@ export class FirehoseLogsDelivery implements ILogsDelivery {
}

/**
* Binds the Firehose destination to a delivery source and creates a delivery connection between them.
* Binds Firehose Delivery Stream to a source resource for the purposes of log delivery and creates a delivery source, a delivery destination, and a connection between them.
*/
public bind(scope: IConstruct, deliverySource: logs.IDeliverySourceRef, _sourceResourceArn: string): ILogsDeliveryConfig {
const container = new Construct(scope, deliveryId('Firehose', scope, this.deliveryStream));
public bind(scope: IConstruct, logType: string, sourceResourceArn: string): ILogsDeliveryConfig {
const container = new Construct(scope, deliveryId('Firehose', logType, scope, this.deliveryStream));

// Firehose uses a service-linked role to deliver logs
// This tag marks the destination stream as an allowed destination for the service-linked role
Tags.of(this.deliveryStream).add('LogDeliveryEnabled', 'true');

const deliverySource = getOrCreateDeliverySource(logType, scope, sourceResourceArn);

const deliveryDestination = new logs.CfnDeliveryDestination(container, 'Dest', {
destinationResourceArn: this.deliveryStream.deliveryStreamRef.deliveryStreamArn,
name: deliveryDestName('fh', container),
name: deliveryDestName('fh', logType, container),
deliveryDestinationType: 'FH',
});

Expand Down Expand Up @@ -243,30 +246,31 @@ export class LogGroupLogsDelivery implements ILogsDelivery {
}

/**
* Binds the log group destination to a delivery source and creates a delivery connection between them.
* Binds Log Group to a source resource for the purposes of log delivery and creates a delivery source, a delivery destination, and a connection between them.
*/
public bind(scope: IConstruct, deliverySource: logs.IDeliverySourceRef, _sourceResourceArn: string): ILogsDeliveryConfig {
const container = new Construct(scope, deliveryId('LogGroup', scope, this.logGroup));
public bind(scope: IConstruct, logType: string, sourceResourceArn: string): ILogsDeliveryConfig {
const container = new Construct(scope, deliveryId('LogGroup', logType, scope, this.logGroup));

const deliverySource = getOrCreateDeliverySource(logType, scope, sourceResourceArn);
const deliverySourceRef = deliverySource.deliverySourceRef;

const logGroupPolicy = this.getOrCreateLogsResourcePolicy(container);
this.grantLogsDelivery(logGroupPolicy);

const deliveryDestination = new logs.CfnDeliveryDestination(container, 'Dest', {
destinationResourceArn: this.logGroup.logGroupRef.logGroupArn,
name: deliveryDestName('cwl', container),
name: deliveryDestName('cwl', logType, container),
deliveryDestinationType: 'CWL',
});

const delivery = new logs.CfnDelivery(container, 'Delivery', {
deliveryDestinationArn: deliveryDestination.deliveryDestinationRef.deliveryDestinationArn,
deliverySourceName: deliverySource.deliverySourceRef.deliverySourceName,
deliverySourceName: deliverySourceRef.deliverySourceName,
});

delivery.node.addDependency(logGroupPolicy);
delivery.node.addDependency(deliverySource);
delivery.node.addDependency(deliveryDestination);

deliverySource.node.addDependency(logGroupPolicy);
deliveryDestination.node.addDependency(logGroupPolicy);

return {
Expand Down Expand Up @@ -327,16 +331,17 @@ export class XRayLogsDelivery implements ILogsDelivery {
constructor() {}

/**
* Binds the X-Ray destination to a delivery source and creates a delivery connection between them.
* Binds X-Ray Destination to a source resource for the purposes of log delivery and creates a delivery source, a delivery destination, and a connection between them.
*/
public bind(scope: IConstruct, deliverySource: logs.IDeliverySourceRef, sourceResourceArn: string): ILogsDeliveryConfig {
const container = new Construct(scope, deliveryId('XRay', scope, deliverySource));
public bind(scope: IConstruct, logType: string, sourceResourceArn: string): ILogsDeliveryConfig {
const deliverySource = getOrCreateDeliverySource(logType, scope, sourceResourceArn);
const container = new Construct(scope, deliveryId('XRay', logType, scope, deliverySource));

const xrayResourcePolicy = this.getOrCreateResourcePolicy(container);
this.grantLogsDelivery(xrayResourcePolicy, sourceResourceArn);

const deliveryDestination = new logs.CfnDeliveryDestination(container, 'Dest', {
name: deliveryDestName('xray', container),
name: deliveryDestName('xray', logType, container),
deliveryDestinationType: 'XRAY',
});

Expand All @@ -345,9 +350,7 @@ export class XRayLogsDelivery implements ILogsDelivery {
deliverySourceName: deliverySource.deliverySourceRef.deliverySourceName,
});

delivery.node.addDependency(xrayResourcePolicy);
deliveryDestination.node.addDependency(xrayResourcePolicy);
deliverySource.node.addDependency(xrayResourcePolicy);

delivery.node.addDependency(deliverySource);
delivery.node.addDependency(deliveryDestination);
Expand Down Expand Up @@ -403,11 +406,26 @@ export class XRayLogsDelivery implements ILogsDelivery {
}
}

function deliveryId(type: string, ...scopes: IConstruct[]) {
return `Cdk${type}Delivery${scopes.map(s => Names.uniqueId(s)).join('')}`;
function deliveryId(destType: string, logType: string, ...scopes: IConstruct[]) {
return `Cdk${destType}${logType.split('_').map(word => word.charAt(0) + word.slice(1).toLowerCase()).join('')}Delivery${scopes.map(s => Names.uniqueId(s)).join('')}`;
}

function deliveryDestName(type: string, scope: IConstruct) {
const prefix = `cdk-${type}-dest-`;
function deliveryDestName(destType: string, logType: string, scope: IConstruct) {
const prefix = `cdk-${destType}-${logType.split('_').map(word => word.toLowerCase()).join('-')}-dest-`;
return `${prefix}${Names.uniqueResourceName(scope, { maxLength: 60 - prefix.length })}`;
}

function getOrCreateDeliverySource(logType: string, resource: IConstruct, sourceArn: string) {
const sourceResource = tryFindDeliverySourceForResource(resource, sourceArn, logType);

if (!sourceResource) {
const prefix = `cdk-${logType.split('_').map(word => word.toLowerCase()).join('')}-source-`;
const newSource = new logs.CfnDeliverySource(resource, `CDKSource${logType}${Names.uniqueId(resource)}`, {
name: `${prefix}${Names.uniqueResourceName(resource, { maxLength: 60 - prefix.length })}`,
logType,
resourceArn: sourceArn,
});
return newSource;
}
return sourceResource;
}
22 changes: 3 additions & 19 deletions packages/@aws-cdk/mixins-preview/scripts/spec2logs/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { naming, util } from '@aws-cdk/spec2cdk';
import { CDK_CORE, CDK_INTERFACES, CONSTRUCTS } from '@aws-cdk/spec2cdk/lib/cdk/cdk';
import type { Method } from '@cdklabs/typewriter';
import { Module, ExternalModule, ClassType, Stability, Type, expr, stmt, ThingSymbol, $this, CallableProxy, NewExpression, $E } from '@cdklabs/typewriter';
import { CDK_AWS_LOGS, MIXINS_LOGS_DELIVERY } from './helpers';
import { MIXINS_LOGS_DELIVERY } from './helpers';
import type { ServiceSubmoduleProps, LocatedModule } from '@aws-cdk/spec2cdk/lib/cdk/service-submodule';
import { BaseServiceSubmodule, relativeImportPath } from '@aws-cdk/spec2cdk/lib/cdk/service-submodule';
import type { AddServiceProps, LibraryBuilderProps } from '@aws-cdk/spec2cdk/lib/cdk/library-builder';
Expand Down Expand Up @@ -72,7 +72,6 @@ export class LogsDeliveryBuilder extends LibraryBuilder<LogsDeliveryBuilderServi
CDK_CORE.import(module, 'cdk');
CDK_INTERFACES.import(module, 'interfaces');
CONSTRUCTS.import(module, 'constructs');
CDK_AWS_LOGS.import(module, 'logs');
MIXINS_CORE.import(module, 'core', { fromLocation: relativeImportPath(filePath, '../core') });
MIXINS_LOGS_DELIVERY.import(module, 'logsDelivery', { fromLocation: '../aws-logs/logs-delivery' });
submodule.constructLibModule.import(module, 'service');
Expand Down Expand Up @@ -304,7 +303,7 @@ class LogsMixin extends ClassType {
const delivery = init.addParameter({
name: 'logDelivery',
type: MIXINS_LOGS_DELIVERY.ILogsDelivery,
documentation: 'Object in charge of setting up the delivery destination and delivery connection',
documentation: 'Object in charge of setting up the delivery source, delivery destination, and delivery connection',
});

init.addBody(
Expand Down Expand Up @@ -358,28 +357,13 @@ class LogsMixin extends ClassType {
const sourceArn = expr.ident('sourceArn');
const arnBuilder = $E(expr.sym(this.resourceType.symbol!)).callMethod(`arnFor${this.resource.name}`, resource);

const prefix = `${this.resource.name}Source-`;
const newCfnDeliverySource = CDK_AWS_LOGS.CfnDeliverySource.newInstance(
resource,
expr.strConcat(expr.str('CdkSource'), CDK_CORE.uniqueId(resource)),
expr.object({
name: expr.strConcat(expr.str(prefix), CDK_CORE.uniqueResourceName(resource, expr.object({
maxLength: expr.binOp(expr.num(60 - (prefix.length + 1)), '-', $this.logType.prop('length')),
})), expr.str('-'), $this.logType),
resourceArn: sourceArn,
logType: $this.logType,
}),
);
const deliverySource = expr.ident('deliverySource');

method.addBody(
stmt
.if_(expr.not(CallableProxy.fromMethod(supports).invoke(resource)))
.then(stmt.block(stmt.ret(resource))),

stmt.constVar(sourceArn, arnBuilder),
stmt.constVar(deliverySource, newCfnDeliverySource),
$this.logDelivery.callMethod('bind', resource, deliverySource, sourceArn),
$this.logDelivery.callMethod('bind', resource, $this.logType, sourceArn),

stmt.ret(resource),
);
Expand Down
5 changes: 0 additions & 5 deletions packages/@aws-cdk/mixins-preview/scripts/spec2logs/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,4 @@ class MixinsLogsDelivery extends ExternalModule {
public readonly S3LogsDeliveryPermissionsVersion = $T(Type.fromName(this, 'S3LogsDeliveryPermissionsVersion'));
}

class CdkAwsLogs extends ExternalModule {
public readonly CfnDeliverySource = Type.fromName(this, 'CfnDeliverySource');
}

export const MIXINS_LOGS_DELIVERY = new MixinsLogsDelivery('@aws-cdk/mixins-preview/services/aws-logs');
export const CDK_AWS_LOGS = new CdkAwsLogs('aws-cdk-lib/aws-logs');
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ exports[`Logs Delivery Mixin for a resource 1`] = `
import * as cdk from "aws-cdk-lib/core";
import * as interfaces from "aws-cdk-lib/interfaces";
import * as constructs from "constructs";
import * as logs from "aws-cdk-lib/aws-logs";
import * as core from "../../core";
import * as logsDelivery from "../aws-logs/logs-delivery";
import * as service from "aws-cdk-lib/aws-some";
Expand Down Expand Up @@ -105,7 +104,7 @@ export class CfnThingLogsMixin extends core.Mixin implements core.IMixin {
* Create a mixin to enable vended logs for \`AWS::Some::Resource\`.
*
* @param logType Type of logs that are getting vended
* @param logDelivery Object in charge of setting up the delivery destination and delivery connection
* @param logDelivery Object in charge of setting up the delivery source, delivery destination, and delivery connection
*/
public constructor(logType: string, logDelivery: logsDelivery.ILogsDelivery) {
super();
Expand All @@ -128,14 +127,7 @@ export class CfnThingLogsMixin extends core.Mixin implements core.IMixin {
return resource;
}
const sourceArn = service.CfnThing.arnForThing(resource);
const deliverySource = new logs.CfnDeliverySource(resource, "CdkSource" + cdk.Names.uniqueId(resource), {
name: "ThingSource-" + cdk.Names.uniqueResourceName(resource, {
maxLength: (47 - this.logType.length)
}) + "-" + this.logType,
resourceArn: sourceArn,
logType: this.logType
});
this.logDelivery.bind(resource, deliverySource, sourceArn);
this.logDelivery.bind(resource, this.logType, sourceArn);
return resource;
}
}"
Expand Down
Loading
Loading