When using lambda.Code.fromAsset and cdk.BundlingDockerImage.fromAsset together, synth fails to find anything in \asset-output
/asset-output directoryFROM python:3.7-slim
COPY . /asset-input
COPY . /asset-output
WORKDIR /asset-input
RUN apt-get update && apt-get -y install curl make automake gcc g++ subversion python3-dev
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3 -
ENV PATH "/root/.poetry/bin:/opt/venv/bin:${PATH}"
RUN poetry export -f requirements.txt -o requirements.txt
RUN pip3 install -r requirements.txt -t /asset-output
code: lambda.Code.fromAsset(PROJECT_DIR, {
bundling: {
image: cdk.BundlingDockerImage.fromAsset(PROJECT_DIR)
}
}),
tsc && cdk synth -o cdk.outDocker should find the compiled assets in /asset-output
Error: Bundling did not produce any output. Check that content is written to /asset-output.
If I use an implementation of ILocalBundling that is mostly copied from asset-staging.ts but calls both run and cp the synth works but I don't believe that should be necessary:
class LocalBundling implements ILocalBundling {
tryBundle(outputDir: string, options: BundlingOptions): boolean {
let user: string;
if (options.user) {
user = options.user;
} else {
// Default to current user
const userInfo = os.userInfo();
user =
userInfo.uid !== -1 // uid is -1 on Windows
? `${userInfo.uid}:${userInfo.gid}`
: "1000:1000";
}
// Always mount input and output dir
const volumes = [
{
hostPath: PROJECT_DIR, // this.sourcePath
containerPath: AssetStaging.BUNDLING_INPUT_DIR,
},
{
hostPath: outputDir, // bundleDir
containerPath: AssetStaging.BUNDLING_OUTPUT_DIR ?? outputDir,
},
...(options.volumes ?? []),
];
options.image.run({
command: options.command,
user,
volumes,
environment: options.environment,
workingDirectory:
options.workingDirectory ?? AssetStaging.BUNDLING_INPUT_DIR,
});
options.image.cp(AssetStaging.BUNDLING_OUTPUT_DIR ?? outputDir, outputDir);
return true;
}
}
This is :bug: Bug Report
I think that what you are looking for is BundlingDockerImage.cp() (introduced in https://github.com/aws/aws-cdk/pull/9728).
After building their own Docker images, users can more easily run the image or copy files out of the image to create their own assets without using the bundling mechanism.
/asset-input and /asset-output (with mounted volumes) works when running the container not when building it.
So what is the appropriate use of the default bundling behavior? I would assume the snippet in step 2 above would work without needing to implement ILocalBundling to use the cp method.
So what is the appropriate use of the default bundling behavior?
The default behavior is to run a command in a container where the source path is mounted at /asset-input and during the execution it should put content at /asset-output. You can see examples here:
Gotcha, so anything put in /asset-output by the Dockerfile will be cleared out and it is assumed that the command will be the one to transfer the required bundling files?
Gotcha, so anything put in
/asset-outputby the Dockerfile will be cleared out
anything put in /asset-output when the container runs will be used as the final CDK asset.
and it is assumed that the command will be the one to transfer the required bundling files?
yes, you can do whatever you want as long as you put content in /asset-output at some point, you have access to the original asset in /asset-input. A trivial example would be cp -R /asset-input/* /asset-output.
I also ran into a situation where I just wanted to use some content from the built image as the asset output. I think our APIs can probably offer a better experience for this.
docker cp will be much faster to extract files from the built image as oppose to running a command inside the image.@jogold what do you think?
You can already do this:
const assetPath = '/path/to/my/asset';
const image = cdk.BundlingDockerImage.fromAsset('/path/to/docker');
image.cp('/path/in/the/image', assetPath);
new lambda.Function(this, 'Fn', {
code: lambda.Code.fromAsset(assetPath),
runtime: lambda.Runtime.NODEJS_12_X,
handler: 'index.handler',
});
Is it this that you want to improve?
docker cp is of course faster but has different use cases.
My issue was that the Dockerfile put everything needed in /asset-output but when the container ran that folder was empty. I am now putting everything in a folder named /asset-stage and passing cp -r ../asset-stage ../asset-output to copy everything from stage to output.
What I would like to see improved is either better documentation around the behavior of the asset-output folder or just simply taking what's already in there instead of wiping it before bundling.
const image = BundlingDockerImage.fromAsset('/path/to/docker');
new lambda.Function(this, 'Fn', {
code: lambda.Code.fromAsset(image.fetch('/path/in/the/image')),
runtime: lambda.Runtime.NODEJS_12_X,
handler: 'index.handler',
});
I think the API for BundlingDockerImage can be improved:
const image = Docker.build('/path/to/docker');
const tmpdir = image.cp('/path/in/the/image');
// alternatively, users can specify the destination for "cp"
image.cp('/path/in/the/image', tmpdir);
And then, we can also add something like:
new lambda.Function(this, 'Fn', {
code: lambda.Code.fromDockerBuildAsset('/path/in/the/image'),
runtime: lambda.Runtime.NODEJS_12_X,
handler: 'index.handler',
});
@eladb
error JSII5016: Members cannot be named "build" as it conflicts with synthetic declarations in some languages.
Haha... so perhaps Docker.fromBuild()?
Haha... so perhaps
Docker.fromBuild()?
yes
And then, we can also add something like:
new lambda.Function(this, 'Fn', { code: lambda.Code.fromDockerBuildAsset('/path/in/the/image'), runtime: lambda.Runtime.NODEJS_12_X, handler: 'index.handler', });
shouldn't this be lambda.Code.fromDockerBuildAsset('/path/to/docker', buildOptions) and the asset is supposed to be located at /asset in the image?
/**
* Loads the function code from an asset created by a Docker build.
*
* The asset is expected to be located at `/asset` in the image.
*
* @param path The path to the directory containing the Docker file
* @param options Docker build options
*/
public static fromDockerBuildAsset(path: string, options: cdk.DockerBuildOptions = {}): AssetCode {
const assetPath = cdk.DockerImage.fromBuild(path, options).cp('/asset');
return new AssetCode(assetPath);
}