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

Draft: adjust public API #31

Merged
merged 7 commits into from Apr 29, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 35 additions & 10 deletions README.md
Expand Up @@ -9,14 +9,15 @@ The adpater allows writing AWS CDK code as part of an AWS CDK Stack inside a Pul
For example, to construct an [AWS AppRunner `Service` resource](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-apprunner-alpha-readme.html) from within a Pulumi program, and export the resulting service's URL as as Pulumi Stack Output you write the following:

```ts
import * as pulumi from '@pulumi/pulumi';
import * as pulumicdk from '@pulumi/cdk';
import { Construct } from 'constructs';
import { Service, Source } from '@aws-cdk/aws-apprunner-alpha';
import { CfnOutput, Stack } from 'aws-cdk-lib';

class AppRunnerStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
class AppRunnerStack extends pulumicdk.Stack {
url: pulumi.Output<string>;

constructor(id: string, options?: pulumicdk.StackOptions) {
super(id, options);

const service = new Service(this, 'service', {
source: Source.fromEcrPublic({
Expand All @@ -25,12 +26,14 @@ class AppRunnerStack extends Stack {
}),
});

new CfnOutput(this, 'url', { value: service.serviceUrl });
this.url = this.asOutput(service.serviceUrl);

this.synth();
}
}

const stack = new pulumicdk.Stack('teststack', AppRunnerStack);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expected this to be new AppRunnerStack('teststack'), no?

export const url = stack.outputs['url'];
export const url = stack.url;
```

And then deploy with `pulumi update`:
Expand Down Expand Up @@ -86,21 +89,24 @@ Coming soon!

### `Stack`

A Pulumi Component that represents an AWS CDK stack deployed with Pulumi.
A Construct that represents an AWS CDK stack deployed with Pulumi. In order to deploy a CDK stack with Pulumi, it must derive from this class. The `synth` method must be called after all CDK resources have been defined in order to deploy the stack (usually, this is done as the last line of the subclass's constructor).

#### `constructor`

Create and register an AWS CDK stack deployed with Pulumi.

```ts
constructor(name: string, stack: typeof cdk.Stack, options?: StackOptions)
constructor(name: string, options?: StackOptions)
```

Parameters:
* `name`: The _unique_ name of the resource.
* `stack`: The CDK Stack subclass to create.
* `options`: A bag of options that control this resource's behavior.

### `urn`

The URN of the underlying Pulumi component.

#### `outputs`

The collection of outputs from the AWS CDK Stack represented as Pulumi Outputs. Each `CfnOutput` defined in the AWS CDK Stack will populate a value in the outputs.
Expand All @@ -109,6 +115,25 @@ The collection of outputs from the AWS CDK Stack represented as Pulumi Outputs.
outputs: { [outputId: string]: pulumi.Output<any> }
```

#### `synth`

Finalize the stack and deploy its resources.

```ts
protected synth()
```

#### `asOutput`

Convert a CDK value to a Pulumi output.

Parameters:
* `v`: A CDK value.

```ts
asOutput<T>(v: T): pulumi.Output<T>
```

### `StackOptions`

Options specific to the Stack component.
Expand Down
18 changes: 11 additions & 7 deletions examples/alb/index.ts
Expand Up @@ -4,11 +4,13 @@ import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as pulumi from '@pulumi/pulumi';
import * as pulumicdk from '@pulumi/cdk';
import { Construct } from 'constructs';
import { CfnOutput, Stack } from 'aws-cdk-lib';
import { CfnOutput } from 'aws-cdk-lib';

class AlbStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
class AlbStack extends pulumicdk.Stack {
url: pulumi.Output<string>;

constructor(id: string) {
super(id);

const vpc = new ec2.Vpc(this, 'VPC');

Expand Down Expand Up @@ -38,9 +40,11 @@ class AlbStack extends Stack {
targetRequestsPerMinute: 60,
});

new CfnOutput(this, 'url', { value: lb.loadBalancerDnsName });
this.url = this.asOutput(lb.loadBalancerDnsName);

this.synth();
}
}

const stack = new pulumicdk.Stack('teststack', AlbStack);
export const url = stack.outputs['url'];
const stack = new AlbStack('teststack');
export const url = stack.url;
10 changes: 6 additions & 4 deletions examples/api-websocket-lambda-dynamodb/index.ts
Expand Up @@ -12,9 +12,9 @@ const config = {
account_id: '',
};

class ChatAppStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
class ChatAppStack extends pulumicdk.Stack {
constructor(id: string) {
super(id);

// initialise api
const name = id + '-api';
Expand Down Expand Up @@ -163,8 +163,10 @@ class ChatAppStack extends Stack {
deployment.node.addDependency(connectRoute);
deployment.node.addDependency(disconnectRoute);
deployment.node.addDependency(messageRoute);

this.synth();
}
}

const stack = new pulumicdk.Stack('chat-app', ChatAppStack);
const stack = new ChatAppStack('chat-app');
export default stack.outputs;
18 changes: 11 additions & 7 deletions examples/apprunner/index.ts
Expand Up @@ -3,11 +3,13 @@ import * as pulumi from '@pulumi/pulumi';
import * as pulumicdk from '@pulumi/cdk';
import { Construct } from 'constructs';
import { Service, Source } from '@aws-cdk/aws-apprunner-alpha';
import { CfnOutput, Stack } from 'aws-cdk-lib';
import { CfnOutput } from 'aws-cdk-lib';

class AppRunnerStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
class AppRunnerStack extends pulumicdk.Stack {
url: pulumi.Output<string>;

constructor(id: string) {
super(id);

const service = new Service(this, 'service', {
source: Source.fromEcrPublic({
Expand All @@ -16,9 +18,11 @@ class AppRunnerStack extends Stack {
}),
});

new CfnOutput(this, 'url', { value: service.serviceUrl });
this.url = this.asOutput(service.serviceUrl);

this.synth();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to at least discover that the stack was not synthesized and emit an error?

}
}

const stack = new pulumicdk.Stack('teststack', AppRunnerStack);
export const url = stack.outputs['url'];
const stack = new AppRunnerStack('teststack');
export const url = stack.url;
18 changes: 11 additions & 7 deletions examples/appsvc/index.ts
Expand Up @@ -5,7 +5,7 @@ import * as pulumi from '@pulumi/pulumi';
import * as pulumicdk from '@pulumi/cdk';
import { Construct } from 'constructs';
import * as aws from '@pulumi/aws';
import { CfnOutput, Stack } from 'aws-cdk-lib';
import { CfnOutput } from 'aws-cdk-lib';

const defaultVpc = pulumi.output(aws.ec2.getVpc({ default: true }));
const defaultVpcSubnets = defaultVpc.id.apply((id) => aws.ec2.getSubnetIds({ vpcId: id }));
Expand Down Expand Up @@ -48,9 +48,11 @@ const atg = new aws.lb.TargetGroup('app-tg', {
// policyArn: "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
// });

class ClusterStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
class ClusterStack extends pulumicdk.Stack {
serviceName: pulumi.Output<string>;

constructor(name: string) {
super(name);

const cluster = new ecs.CfnCluster(this, 'clusterstack');

Expand Down Expand Up @@ -115,9 +117,11 @@ class ClusterStack extends Stack {
});
service.addDependsOn(listener);

new CfnOutput(this, 'serviceName', { value: service.attrName });
this.synth();

this.serviceName = this.asOutput(service.attrName);
}
}

const stack = new pulumicdk.Stack('teststack', ClusterStack);
export const serviceName = stack.outputs['serviceName'];
const stack = new ClusterStack('teststack');
export const serviceName = stack.serviceName;
21 changes: 13 additions & 8 deletions examples/cron-lambda/index.ts
Expand Up @@ -2,14 +2,17 @@ import * as fs from 'fs';
import * as aws_events from 'aws-cdk-lib/aws-events';
import * as aws_events_targets from 'aws-cdk-lib/aws-events-targets';
import * as aws_lambda from 'aws-cdk-lib/aws-lambda';
import { CfnOutput, Duration, Stack } from 'aws-cdk-lib';
import { CfnOutput, Duration } from 'aws-cdk-lib';
import * as pulumi from '@pulumi/pulumi';
import * as pulumicdk from '@pulumi/cdk';
import { Construct } from 'constructs';
import { remapCloudControlResource } from './adapter';

class LambdaStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
class LambdaStack extends pulumicdk.Stack {
lambdaArn: pulumi.Output<string>;

constructor(id: string, options?: pulumicdk.StackOptions) {
super(id, { ...options, remapCloudControlResource });

// Use the AWS CDK Lambda Function API directly.
const lambdaFn = new aws_lambda.Function(this, 'lambda', {
Expand All @@ -28,10 +31,12 @@ class LambdaStack extends Stack {
// Use the AWS CDK to add a Rule target to trigger the Function.
rule.addTarget(new aws_events_targets.LambdaFunction(lambdaFn));

// Register a CDK Output for the Lambda functionArn so that it can be retreived from Pulumi.
new CfnOutput(this, 'lambdaArn', { value: lambdaFn.functionArn });
// Export the Lambda function's ARN as an output.
this.lambdaArn = this.asOutput(lambdaFn.functionArn);

this.synth();
}
}

const stack = new pulumicdk.Stack('teststack', LambdaStack, { remapCloudControlResource });
export const lambdaArn = stack.outputs.lambdaArn;
const stack = new LambdaStack('teststack');
export const lambdaArn = stack.lambdaArn;
10 changes: 6 additions & 4 deletions examples/ec2-instance/index.ts
Expand Up @@ -7,9 +7,9 @@ import * as pulumicdk from '@pulumi/cdk';
import { Asset } from 'aws-cdk-lib/aws-s3-assets';
import { Construct } from 'constructs';

export class Ec2CdkStack extends cdk.Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
export class Ec2CdkStack extends pulumicdk.Stack {
constructor(id: string) {
super(id);

// Create a Key Pair to be used with this EC2 Instance
// Temporarily disabled since `cdk-ec2-key-pair` is not yet CDK v2 compatible
Expand Down Expand Up @@ -83,10 +83,12 @@ export class Ec2CdkStack extends cdk.Stack {
new cdk.CfnOutput(this, 'ssh command', {
value: 'ssh -i cdk-key.pem -o IdentitiesOnly=yes ec2-user@' + ec2Instance.instancePublicIp,
});

this.synth();
}
}

const stack = new pulumicdk.Stack('teststack', Ec2CdkStack);
const stack = new Ec2CdkStack('teststack');
export const ipAddress = stack.outputs['IP Address'];
export const keyCommand = stack.outputs['Download Key Command'];
export const sshCommand = stack.outputs['sshCommand'];
8 changes: 5 additions & 3 deletions examples/s3-object-lambda/index.ts
@@ -1,5 +1,7 @@
import * as pulumicdk from '@pulumi/cdk';
import { S3ObjectLambdaStack } from './lib/s3-object-lambda-stack';

const s = new pulumicdk.Stack('stack', S3ObjectLambdaStack);
export default s.outputs;
const s = new S3ObjectLambdaStack('stack');
export const exampleBucketArn = s.exampleBucketArn;
export const objectLambdaArn = s.objectLambdaArn;
export const objectLambdaAccessPointArn = s.objectLambdaAccessPointArn;
export const objectLambdaAccessPointUrl = s.objectLambdaAccessPointUrl;
27 changes: 18 additions & 9 deletions examples/s3-object-lambda/lib/s3-object-lambda-stack.ts
@@ -1,3 +1,5 @@
import * as pulumi from '@pulumi/pulumi';
import * as pulumicdk from '@pulumi/cdk';
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
Expand All @@ -9,9 +11,14 @@ import { Construct } from 'constructs';
const S3_ACCESS_POINT_NAME = 'example-test-ap';
const OBJECT_LAMBDA_ACCESS_POINT_NAME = 's3-object-lambda-ap';

export class S3ObjectLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
export class S3ObjectLambdaStack extends pulumicdk.Stack {
exampleBucketArn: pulumi.Output<string>;
objectLambdaArn: pulumi.Output<string>;
objectLambdaAccessPointArn: pulumi.Output<string>;
objectLambdaAccessPointUrl: pulumi.Output<string>;

constructor(id: string) {
super(id);

const accessPoint = `arn:aws:s3:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:accesspoint/${S3_ACCESS_POINT_NAME}`;

Expand Down Expand Up @@ -94,11 +101,13 @@ export class S3ObjectLambdaStack extends cdk.Stack {
},
});

new cdk.CfnOutput(this, 'exampleBucketArn', { value: bucket.bucketArn });
new cdk.CfnOutput(this, 'objectLambdaArn', { value: retrieveTransformedObjectLambda.functionArn });
new cdk.CfnOutput(this, 'objectLambdaAccessPointArn', { value: objectLambdaAP.attrArn });
new cdk.CfnOutput(this, 'objectLambdaAccessPointUrl', {
value: `https://console.aws.amazon.com/s3/olap/${cdk.Aws.ACCOUNT_ID}/${OBJECT_LAMBDA_ACCESS_POINT_NAME}?region=${cdk.Aws.REGION}`,
});
this.exampleBucketArn = this.asOutput(bucket.bucketArn);
this.objectLambdaArn = this.asOutput(retrieveTransformedObjectLambda.functionArn);
this.objectLambdaAccessPointArn = this.asOutput(objectLambdaAP.attrArn);
this.objectLambdaAccessPointUrl = this.asOutput(
`https://console.aws.amazon.com/s3/olap/${cdk.Aws.ACCOUNT_ID}/${OBJECT_LAMBDA_ACCESS_POINT_NAME}?region=${cdk.Aws.REGION}`,
);

this.synth();
}
}
13 changes: 9 additions & 4 deletions src/cfn-resource-mappings.ts
Expand Up @@ -45,10 +45,15 @@ export function mapToCfnResource(
return new iam.Role(logicalId, morphed, options);
}
case 'AWS::Lambda::Function':
return new lambda.Function(logicalId, {
...props,
environment: rawProps.Environment === undefined ? undefined : { variables: rawProps.Environment.Variables },
}, options);
return new lambda.Function(
logicalId,
{
...props,
environment:
rawProps.Environment === undefined ? undefined : { variables: rawProps.Environment.Variables },
},
options,
);
case 'AWS::S3::AccessPoint':
return new s3.AccessPoint(
logicalId,
Expand Down
12 changes: 6 additions & 6 deletions src/output.ts
Expand Up @@ -22,7 +22,7 @@ import { OutputMap } from './output-map';
* @param o A Pulumi Output value which represents a string.
* @returns A CDK token representing a string value.
*/
export function asString<T>(o: pulumi.Output<T>): string {
export function asString(o: pulumi.Output<string>): string {
return Token.asString(OutputMap.instance().registerOutput(o));
}

Expand All @@ -32,16 +32,16 @@ export function asString<T>(o: pulumi.Output<T>): string {
* @param o A Pulumi Output value which represents a number.
* @returns A CDK token representing a number value.
*/
export function asNumber<T>(o: pulumi.Output<T>): number {
export function asNumber(o: pulumi.Output<number>): number {
return Token.asNumber(OutputMap.instance().registerOutput(o));
}

/**
* Convert a Pulumi Output to a list of CDK values.
* Convert a Pulumi Output to a list of CDK string values.
*
* @param o A Pulumi Output value which represents a list.
* @returns A CDK token representing a list of values.
* @param o A Pulumi Output value which represents a list of strings.
* @returns A CDK token representing a list of string values.
*/
export function asList<T>(o: pulumi.Output<T>): string[] {
export function asList(o: pulumi.Output<string[]>): string[] {
return Token.asList(OutputMap.instance().registerOutput(o));
}