Skip to content

feat(core): allow mounting existing or volume copy volumes #32832

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

cgatt
Copy link

@cgatt cgatt commented Jan 10, 2025

Issue # (if applicable)

Closes #32805
Closes #20601

Reason for this change

This change is to better support image bundling in atypical (e.g. CI, DinD) environments by allowing volumes to be mounted using the more portable "Volume Copy" method, or to mount an existing volume. This supports use cases such as caching dependencies in CI environments to reduce asset bundling times.

Description of changes

The primary change made was the addition of two new DockerVolume varients (VolumeCopyDockerVolume and ExistingDockerVolume), facilitated by the new DockerVolumeHelper private class. This replaces the old asset-staging private classes AssetBundlingBindMount and AssetBundlingVolumeCopy, reproducing all the behaviours in a more flexible manner.
The decision to move these utility functions to private/bundling.ts was to better encapsulate the docker related behaviours and abstract them away from the asset management.

This currently uses a discriminated union type to clearly show which properties are required for each volume type. This passes linting, but if it's an issue it can be implemented just as an extension to the DockerVolume interface. However, I'm unsure how to best provide a clear developer experience with this approach.

How It Works

The interfaces for DockerRunOptions.volumes and the related BundlingOptions.volumes have been changed to accept VolumeCopyDockerVolume and ExistingDockerVolume as well as the existing DockerVolume type. The BundlingDockerImage.run() function has been modified slightly to instantiate a DockerVolumeHelper, which accepts the docker run options and uses them to prepare the volumes passed in, creating the volume command strings that run then uses.

If any if the passed volumes are VOLUME_COPY volumes, a copyContainer is spun up with each VOLUME_COPY volume atttached anonymously. The input copy is then performed using the same approach from AssetBundlingVolumeCopy. As these volumes are mounted to the primary container via --volumes-from, them being anonymous doesnt matter and makes creation and deletion of the helper more atomic, reducing the possibility of orphaned volumes or containers. Error handling has been added in multiple places to further prevent this happening in the case of a build failure.

BundlingDockerImage.run() makes use of the volume commands from DockerVolumeHelper to complete its image run, then runs DockerVolumeHelper.cleanup() to copy any outputs from the volume copy volumes, and delete the container (using the -v option to remove attach anonymous volumes).

Describe any new or updated permissions being added

N/A - All changes are at bundling time only

Description of how you validated changes

Unit tests were added to cover the new expected volume behaviours, and existing tests updated to match any changes to argument order. Tests were made to cover the new helper class and new error handling cleanup behaviour. No integration tests were made or modified as this is a local/bundling change,

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@cgatt cgatt requested a review from a team as a code owner January 10, 2025 08:43
@github-actions github-actions bot added beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK bug This issue is a bug. effort/medium Medium work item – several days of effort p2 labels Jan 10, 2025
@aws-cdk-automation aws-cdk-automation requested a review from a team January 10, 2025 08:44
Copy link
Collaborator

@aws-cdk-automation aws-cdk-automation left a comment

Choose a reason for hiding this comment

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

The pull request linter fails with the following errors:

❌ Features must contain a change to a README file.
❌ Features must contain a change to an integration test file and the resulting snapshot.

If you believe this pull request should receive an exemption, please comment and provide a justification. A comment requesting an exemption should contain the text Exemption Request. Additionally, if clarification is needed, add Clarification Request to a comment.

✅ A exemption request has been requested. Please wait for a maintainer's review.

@cgatt
Copy link
Author

cgatt commented Jan 10, 2025

Exemption Request (Integration Test): Unsure what sort of integration test would be beneficial here, as it's a synth time change.

Clarification Request (Readme): Happy to add a writeup, but the current DockerImage volume and mounting behaviour has no docs, so I wasnt sure where it would be appropriate to put it.

@aws-cdk-automation aws-cdk-automation added pr/reviewer-clarification-requested The contributor has requested clarification on feedback, a failing build, or a failing PR Linter run pr-linter/exemption-requested The contributor has requested an exemption to the PR Linter feedback. labels Jan 10, 2025
@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv
  • Commit ID: 06dda4d
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@aws-cdk-automation aws-cdk-automation added the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Jan 13, 2025
@QuantumNeuralCoder QuantumNeuralCoder self-assigned this Feb 19, 2025
@QuantumNeuralCoder
Copy link
Contributor

Hi
I am beginning to review this now. Need help with some answers from you. The changes around private/asset-staging and bundling are major refactors and breaking. is there an alternative way this can be implemented without breaking backward compatibility.

Please also add a README. Let me know if any questions. The descriptions in this issue are a good explanation.

Copy link
Contributor

@QuantumNeuralCoder QuantumNeuralCoder left a comment

Choose a reason for hiding this comment

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

Please add a readme. Please help ans the questions posed above.

@cgatt
Copy link
Author

cgatt commented Mar 4, 2025

Hi I am beginning to review this now. Need help with some answers from you. The changes around private/asset-staging and bundling are major refactors and breaking. is there an alternative way this can be implemented without breaking backward compatibility.

Please also add a README. Let me know if any questions. The descriptions in this issue are a good explanation.

Thanks for having a look. I'm happy to add docs and tests, just let me know where is appropriate as there are currently no docs around these features for me to modify. If making a new readme is appropriate, I'll do that.

Regarding breaking changes, the private/* classes are not exported or exposed and so are not part of the interface, meaning this isnt a breaking change. This could be implemented without the refactor, but I feel that that would be a less logical final layout - the volume mounting behaviour is coupled with the bundling behaviour, and so should be part of the private bundling utils, not part of asset staging. This refactor cleans up that dependency tree to reduce coupling - asset staging depends on bundling, while bundling no longer has any references to asset staging.

I would also appreciate feedback or comments on the typing. I've used Typescript union types as they passed linting and build checks, and give the best developer experience in Typescript, but can also rewrite this a number of other ways (a single large interface with validation checks to ensure the required sets of properties are passed, or separate arrays for volume copy and existing volume mounts, completely separate from the current "volumes" array. both of these are a worse dev experience, but will work if there are restrictions on union types.

Cheers,
Chris

@shikha372 shikha372 removed the pr/reviewer-clarification-requested The contributor has requested clarification on feedback, a failing build, or a failing PR Linter run label Mar 17, 2025
@aws-cdk-automation aws-cdk-automation removed the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Mar 17, 2025
@QuantumNeuralCoder
Copy link
Contributor

Apologies for the delay in response. I suspect that heterogenous typing will cause issues in languages like Java wrt backward compatibility but havent been able to confirm it. Please expect a response by EOW.

@cgatt
Copy link
Author

cgatt commented Apr 30, 2025

Apologies for the delay in response. I suspect that heterogenous typing will cause issues in languages like Java wrt backward compatibility but havent been able to confirm it. Please expect a response by EOW.

Any update on this?

@iliapolo iliapolo changed the title feat(DockerImage): allow mounting existing or volume copy volumes feat(core): allow mounting existing or volume copy volumes May 28, 2025
Copy link
Contributor

@iliapolo iliapolo left a comment

Choose a reason for hiding this comment

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

@cgatt apologies for the delayed response. Please see my comments.

Also - I think we need an integration test for this. Granted, a lot of the bundling code can be covered by unit tests, but I still think we have a integ test gap that I would like us to not exacerbate.

It should be pretty straight forward to add a few more tests similar to:

https://github.com/aws/aws-cdk/blob/main/packages/%40aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.bundling.docker-opts.ts

Thanks for all the work you're putting into this!

@@ -61,7 +60,7 @@ export interface BundlingOptions {
*
* @default - no additional volumes are mounted
*/
readonly volumes?: DockerVolume[];
readonly volumes?: (DockerVolume | VolumeCopyDockerVolume | ExistingDockerVolume)[];
Copy link
Contributor

Choose a reason for hiding this comment

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

As you already surmised, this is a problem because it provides a poor user experience for static languages, where this translates into an generic Object type.

Before we consider alternatives, I have some questions.

VolumeCopyDockerVolume

Why do we need this new type? Both DockerVolume and VolumeCopyDockerVolume offer the same functionality: expose path X on the host machine as path Y inside the container.

So from a properties standpoint, they both should have the same properties. Looking at the code you described in the linked issue:

const codeAsset = new AssetStaging(this, 'CodeAsset', {
  sourcePath: path.resolve('mule'),
  exclude: ['target/*', 'bin/*', '*.class', '*.jar'],
  bundling: {
    image: DockerImage.fromRegistry('public.ecr.aws/docker/library/alpine'),
    bundlingFileAccess: BundlingFileAccess.VOLUME_COPY,
    volumes: [{
      hostPath: '~/.m2',
      containerPath: '/root/.m2',
    }],
  },
});

It seems like we can just apply the bundlingFileAccess for the additional volume configuration as well. I'm not sure I see a use-case for having each individual volume define its own "exposure" mechanism.

ExistingDockerVolume

This one seems to be addressing a different issue/use-case - i'm not opposed to it, but I would prefer for it to be a separate PR.

As for the implementation, lets drop the union type and instead change DockerVolume to support existing volumes, as proposed by @SamStephens in the issue:

  • Change hostPath to be optional.
  • Add a volumeName optional property to indicate we want an existing volume.
  • Validate mutual exclusivity of hostPath and volumeName.

Its not the best experience because it creates a runtime validation instead of compile time, but is still preferable over union types.

switch (options.bundlingFileAccess) {
case BundlingFileAccess.VOLUME_COPY:
new AssetBundlingVolumeCopy(assetStagingOptions).run();
Copy link
Contributor

Choose a reason for hiding this comment

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

Actually I prefer these individual helper classes over the proposed DockerVolumeHelper - even at the expense of some duplication. Since volume commands differ so much between the two options, a common helper class makes it harder to reason through.


/**
* The path on the host machine to be used as the input for the volume.
* @default - Does not copy from the host machine
Copy link
Contributor

Choose a reason for hiding this comment

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

Then why would we define a VolumeCopy volume if we aren't copying anything?

* The path on the host machine where the output from the volume will be written.
* @default - Does not copy to the host machine
*/
readonly hostOutputPath?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this capability is really related to what this PR is trying to address. There is also no test for it.

In general I don't think we need this, it poses a slight shift in how we think about bundling. Currently, the bundling mechanism takes N inputs and produces 1 output - thats clear and easy to reason about. Allowing for M outputs makes it more complex and I don't see a reason for it.

If you have a clear use case in mind - please elaborate on it, preferably in a different issue/PR.

@@ -1569,15 +1569,11 @@ describe('staging with docker cp', () => {
expect(staging.isArchive).toEqual(true);
const dockerCalls: string[] = readDockerStubInputConcat(STUB_INPUT_CP_CONCAT_FILE).split(/\r?\n/);
expect(dockerCalls).toEqual(expect.arrayContaining([
expect.stringContaining('volume create assetInput'),
Copy link
Contributor

Choose a reason for hiding this comment

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

What happened to this command? How come we don't need docker volume create anymore?

@iliapolo iliapolo added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label May 29, 2025
@iliapolo
Copy link
Contributor

iliapolo commented May 29, 2025

@cgatt

Clarification Request (Readme): Happy to add a writeup, but the current DockerImage volume and mounting behaviour has no docs, so I wasnt sure where it would be appropriate to put it.

Yeah there's no good place for it right now...Lets see how this PR ends up and then decide whether to add a blurb somewhere or exempt it.

@aws-cdk-automation aws-cdk-automation added the pr/reviewer-clarification-requested The contributor has requested clarification on feedback, a failing build, or a failing PR Linter run label May 29, 2025
@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label May 29, 2025
@iliapolo iliapolo added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Jul 2, 2025
Copy link
Contributor

github-actions bot commented Jul 2, 2025

This PR has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.

@github-actions github-actions bot added the closing-soon This issue will automatically close in 4 days unless further comments are made. label Jul 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK bug This issue is a bug. closing-soon This issue will automatically close in 4 days unless further comments are made. effort/medium Medium work item – several days of effort p2 pr/reviewer-clarification-requested The contributor has requested clarification on feedback, a failing build, or a failing PR Linter run pr-linter/exemption-requested The contributor has requested an exemption to the PR Linter feedback. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

AssetStaging: DockerVolume uses Bind Mount with BundlingFileAccess set to VOLUME_COPY (cdk): Ability to create a Docker volume
5 participants