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

feat(lambda): expose all docker run options to container bundling of all lambda variants #23318

Merged
merged 8 commits into from Dec 15, 2022
Merged
15 changes: 15 additions & 0 deletions packages/@aws-cdk/aws-lambda-go/README.md
Expand Up @@ -183,6 +183,21 @@ new go.GoFunction(this, 'GoFunction', {
});
```

You can set additional Docker options to configure the build environment:

```ts
new go.GoFunction(this, 'GoFunction', {
entry: 'app/cmd/api',
bundling: {
network: 'host',
securityOpt: 'no-new-privileges',
user: 'user:group',
volumesFrom: ['777f7dc92da7'],
volumes: [{ hostPath: '/host-path', containerPath: '/container-path' }],
},
});
```

## Command hooks

It is possible to run additional commands by specifying the `commandHooks` prop:
Expand Down
23 changes: 22 additions & 1 deletion packages/@aws-cdk/aws-lambda-go/lib/bundling.ts
Expand Up @@ -78,6 +78,13 @@ export class Bundling implements cdk.BundlingOptions {
command: bundling.command,
environment: bundling.environment,
local: bundling.local,
entrypoint: bundling.entrypoint,
volumes: bundling.volumes,
volumesFrom: bundling.volumesFrom,
workingDirectory: bundling.workingDirectory,
user: bundling.user,
securityOpt: bundling.securityOpt,
network: bundling.network,
},
});
}
Expand All @@ -93,6 +100,13 @@ export class Bundling implements cdk.BundlingOptions {
public readonly command: string[];
public readonly environment?: { [key: string]: string };
public readonly local?: cdk.ILocalBundling;
public readonly entrypoint?: string[]
public readonly volumes?: cdk.DockerVolume[];
public readonly volumesFrom?: string[];
public readonly workingDirectory?: string;
public readonly user?: string;
public readonly securityOpt?: string;
public readonly network?: string;

private readonly relativeEntryPath: string;

Expand Down Expand Up @@ -131,8 +145,15 @@ export class Bundling implements cdk.BundlingOptions {
: cdk.DockerImage.fromRegistry('dummy'); // Do not build if we don't need to

const bundlingCommand = this.createBundlingCommand(cdk.AssetStaging.BUNDLING_INPUT_DIR, cdk.AssetStaging.BUNDLING_OUTPUT_DIR);
this.command = ['bash', '-c', bundlingCommand];
this.command = props.command ?? ['bash', '-c', bundlingCommand];
this.environment = environment;
this.entrypoint = props.entrypoint;
this.volumes = props.volumes;
this.volumesFrom = props.volumesFrom;
this.workingDirectory = props.workingDirectory;
this.user = props.user;
this.securityOpt = props.securityOpt;
this.network = props.network;

// Local bundling
if (!props.forcedDockerBundling) { // only if Docker is not forced
Expand Down
11 changes: 2 additions & 9 deletions packages/@aws-cdk/aws-lambda-go/lib/types.ts
@@ -1,16 +1,9 @@
import { AssetHashType, DockerImage } from '@aws-cdk/core';
import { AssetHashType, DockerImage, DockerRunOptions } from '@aws-cdk/core';

/**
* Bundling options
*/
export interface BundlingOptions {
/**
* Environment variables defined when go runs.
*
* @default - no environment variables are defined.
*/
readonly environment?: { [key: string]: string; };

export interface BundlingOptions extends DockerRunOptions {
/**
* Force bundling in a Docker container even if local bundling is
* possible.
Expand Down
126 changes: 126 additions & 0 deletions packages/@aws-cdk/aws-lambda-go/test/bundling.test.ts
Expand Up @@ -335,3 +335,129 @@ test('with command hooks', () => {
}),
});
});

test('Custom bundling entrypoint', () => {
Bundling.bundle({
entry,
moduleDir,
runtime: Runtime.GO_1_X,
architecture: Architecture.X86_64,
forcedDockerBundling: true,
entrypoint: ['/cool/entrypoint', '--cool-entrypoint-arg'],
});

expect(Code.fromAsset).toHaveBeenCalledWith('/project', {
assetHashType: AssetHashType.OUTPUT,
bundling: expect.objectContaining({
entrypoint: ['/cool/entrypoint', '--cool-entrypoint-arg'],
}),
});
});

test('Custom bundling volumes', () => {
Bundling.bundle({
entry,
moduleDir,
runtime: Runtime.GO_1_X,
architecture: Architecture.X86_64,
forcedDockerBundling: true,
volumes: [{ hostPath: '/host-path', containerPath: '/container-path' }],
});

expect(Code.fromAsset).toHaveBeenCalledWith('/project', {
assetHashType: AssetHashType.OUTPUT,
bundling: expect.objectContaining({
volumes: [{ hostPath: '/host-path', containerPath: '/container-path' }],
}),
});
});

test('Custom bundling volumesFrom', () => {
Bundling.bundle({
entry,
moduleDir,
runtime: Runtime.GO_1_X,
architecture: Architecture.X86_64,
forcedDockerBundling: true,
volumesFrom: ['777f7dc92da7'],
});

expect(Code.fromAsset).toHaveBeenCalledWith('/project', {
assetHashType: AssetHashType.OUTPUT,
bundling: expect.objectContaining({
volumesFrom: ['777f7dc92da7'],
}),
});
});

test('Custom bundling workingDirectory', () => {
Bundling.bundle({
entry,
moduleDir,
runtime: Runtime.GO_1_X,
architecture: Architecture.X86_64,
forcedDockerBundling: true,
workingDirectory: '/working-directory',
});

expect(Code.fromAsset).toHaveBeenCalledWith('/project', {
assetHashType: AssetHashType.OUTPUT,
bundling: expect.objectContaining({
workingDirectory: '/working-directory',
}),
});
});

test('Custom bundling user', () => {
Bundling.bundle({
entry,
moduleDir,
runtime: Runtime.GO_1_X,
architecture: Architecture.X86_64,
forcedDockerBundling: true,
user: 'user:group',
});

expect(Code.fromAsset).toHaveBeenCalledWith('/project', {
assetHashType: AssetHashType.OUTPUT,
bundling: expect.objectContaining({
user: 'user:group',
}),
});
});

test('Custom bundling securityOpt', () => {
Bundling.bundle({
entry,
moduleDir,
runtime: Runtime.GO_1_X,
architecture: Architecture.X86_64,
forcedDockerBundling: true,
securityOpt: 'no-new-privileges',
});

expect(Code.fromAsset).toHaveBeenCalledWith('/project', {
assetHashType: AssetHashType.OUTPUT,
bundling: expect.objectContaining({
securityOpt: 'no-new-privileges',
}),
});
});

test('Custom bundling network', () => {
Bundling.bundle({
entry,
moduleDir,
runtime: Runtime.GO_1_X,
architecture: Architecture.X86_64,
forcedDockerBundling: true,
network: 'host',
});

expect(Code.fromAsset).toHaveBeenCalledWith('/project', {
assetHashType: AssetHashType.OUTPUT,
bundling: expect.objectContaining({
network: 'host',
}),
});
});
14 changes: 14 additions & 0 deletions packages/@aws-cdk/aws-lambda-nodejs/README.md
Expand Up @@ -307,6 +307,20 @@ should also have `npm`, `yarn` or `pnpm` depending on the lock file you're using
Use the [default image provided by `@aws-cdk/aws-lambda-nodejs`](https://github.com/aws/aws-cdk/blob/main/packages/%40aws-cdk/aws-lambda-nodejs/lib/Dockerfile)
as a source of inspiration.

You can set additional Docker options to configure the build environment:

```ts
new nodejs.NodejsFunction(this, 'my-handler', {
bundling: {
network: 'host',
securityOpt: 'no-new-privileges',
user: 'user:group',
volumesFrom: ['777f7dc92da7'],
volumes: [{ hostPath: '/host-path', containerPath: '/container-path' }],
},
});
```

## Asset hash

By default the asset hash will be calculated based on the bundled output (`AssetHashType.OUTPUT`).
Expand Down
16 changes: 14 additions & 2 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts
Expand Up @@ -73,9 +73,15 @@ export class Bundling implements cdk.BundlingOptions {

// Core bundling options
public readonly image: cdk.DockerImage;
public readonly entrypoint?: string[]
public readonly command: string[];
public readonly volumes?: cdk.DockerVolume[];
public readonly volumesFrom?: string[];
public readonly environment?: { [key: string]: string };
public readonly workingDirectory: string;
public readonly user?: string;
public readonly securityOpt?: string;
public readonly network?: string;
public readonly local?: cdk.ILocalBundling;

private readonly projectRoot: string;
Expand Down Expand Up @@ -137,11 +143,17 @@ export class Bundling implements cdk.BundlingOptions {
tscRunner: 'tsc', // tsc is installed globally in the docker image
osPlatform: 'linux', // linux docker image
});
this.command = ['bash', '-c', bundlingCommand];
this.command = props.command ?? ['bash', '-c', bundlingCommand];
this.environment = props.environment;
// Bundling sets the working directory to cdk.AssetStaging.BUNDLING_INPUT_DIR
// and we want to force npx to use the globally installed esbuild.
this.workingDirectory = '/';
this.workingDirectory = props.workingDirectory ?? '/';
this.entrypoint = props.entrypoint;
this.volumes = props.volumes;
this.volumesFrom = props.volumesFrom;
this.user = props.user;
this.securityOpt = props.securityOpt;
this.network = props.network;

// Local bundling
if (!props.forceDockerBundling) { // only if Docker is not forced
Expand Down
11 changes: 2 additions & 9 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/types.ts
@@ -1,9 +1,9 @@
import { DockerImage } from '@aws-cdk/core';
import { DockerImage, DockerRunOptions } from '@aws-cdk/core';

/**
* Bundling options
*/
export interface BundlingOptions {
export interface BundlingOptions extends DockerRunOptions {
/**
* Whether to minify files when bundling.
*
Expand Down Expand Up @@ -161,13 +161,6 @@ export interface BundlingOptions {
*/
readonly charset?: Charset;

/**
* Environment variables defined when bundling runs.
*
* @default - no environment variables are defined.
*/
readonly environment?: { [key: string]: string; };

/**
* Replace global identifiers with constant expressions.
*
Expand Down