Skip to content

Add support for copying existing files via WithContainerFiles API #8908

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 18 commits into
base: main
Choose a base branch
from

Conversation

danegsta
Copy link
Member

@danegsta danegsta commented Apr 21, 2025

Description

Adds the ability to specify an existing file via WithContainerFiles instead of having to specify the contents of a file. Uses the same permission mechanics and slots into the existing ability to create arbitrary files and folders. Specifically, a ContainerFile type passed to WithContainerFiles can take either a Contents property or SourcePath property (an exception will be thrown if both are set).

Fixes #5498

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?

@github-actions github-actions bot added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Apr 21, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds support for copying existing files via the WithContainerFiles API, replacing the previous bind mount mechanism for realm imports in Keycloak.

  • Updated tests to validate the new ContainerFileSystemCallbackAnnotation functionality via asynchronous execution.
  • Modified the Container model to enforce that file entries use either SourcePath or Contents but not both.
  • Updated the KeycloakResourceBuilderExtensions to utilize the WithContainerFiles API using new C# 12 collection patterns.

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
tests/Aspire.Hosting.Keycloak.Tests/KeycloakPublicApiTests.cs Updated test methods to async, altered assertions to verify ContainerFileSystemCallbackAnnotation usage.
src/Aspire.Hosting/Dcp/Model/Container.cs Added Source assignment from SourcePath and mutual exclusion check between Contents and SourcePath.
src/Aspire.Hosting/ApplicationModel/ContainerFileSystemCallbackAnnotation.cs Updated documentation for ContainerFile to clarify mutual exclusion semantics between Contents and SourcePath.
src/Aspire.Hosting/Keycloak/KeycloakResourceBuilderExtensions.cs Switched from WithBindMount to WithContainerFiles using new collection initialization syntax.
Comments suppressed due to low confidence (1)

tests/Aspire.Hosting.Keycloak.Tests/KeycloakPublicApiTests.cs:224

  • The variable 'file' is used in the assertion but is not defined anywhere in the test. Consider defining it (e.g., var file = Path.GetFileName(filePath);) before using it in assertions.
Assert.Equal(file, realmFile.Name);

Copy link
Member

@karolz-ms karolz-ms left a comment

Choose a reason for hiding this comment

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

No bind mount 😍

/// </summary>
public string? Contents { get; set; }

/// <summary>
/// The path to a file on the host system to copy into the container. This path must be absolute and point to a file on the host system.
Copy link
Member

Choose a reason for hiding this comment

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

Why does the path need to be absolute? We can use relative paths elsewhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

We don’t explicitly check for an absolute path; it’s more of a SHOULD requirement. We can’t necessarily guarantee the orchestrator and container runtime will have the correct working directory in all scenarios, so absolute paths guarantees consistent behavior. We probably should call out the same for the bind mount annotation.

@eerhardt
Copy link
Member

Are there any direct tests that can be added for this functionality? If we ever deleted Keycloak (unlikely) we wouldn't have any coverage.

@danegsta danegsta requested a review from mitchdenny as a code owner April 24, 2025 22:43
@danegsta
Copy link
Member Author

danegsta commented May 8, 2025

This would mitigate some of the bind mount issues in #7078, but there's implications for the generated manifest in switching from bind mount to WithContainerFiles.

@danegsta
Copy link
Member Author

danegsta commented May 9, 2025

This could be supported in the compose publish targets via the configs feature. One undocumented part of the compose configs spec is that you can specify the content inline and they'll be copied in the same way we do in WithContainerFiles.

@davidfowl
Copy link
Member

davidfowl commented May 10, 2025

I say we move this to 9.4

@davidfowl davidfowl added this to the 9.4 milestone May 10, 2025
@davidfowl davidfowl added blocked NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) and removed blocked labels May 10, 2025
Comment on lines 19 to 27
configs:
- source: "cache__usr_local_share_redis.conf"
target: "/usr/local/share/redis.conf"
mode: 0644
- source: "cache__usr_local_share_folder_file.sh"
target: "/usr/local/share/folder/file.sh"
uid: 1000
gid: 1000
mode: 0700
Copy link
Member

Choose a reason for hiding this comment

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

This is VERY cool 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

I was definitely happy to find out compose configs can support basically the full set of configuration from WithContainerFiles. Makes having a consistent runtime and publish experience a lot easier.

Copy link
Member Author

Choose a reason for hiding this comment

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

Spoke too soon; there is one headache in that compose will ignore uid, gid, and mode when using source file mode (vs specifying inline content). They use a volume when a specific file is referenced, but cp when contents are provided.

One option would be to simply read text and convert every referenced file to the inline content form, but that wouldn't work if a user was expecting to use WithContainerFiles for a binary file.

@@ -123,6 +140,41 @@ private async Task WriteDockerComposeOutputAsync(DistributedApplicationModel mod
envFile.Save(envFilePath);
}

private static void HandleComposeFileConfig(ComposeFile composeFile, Service composeService, ContainerFileSystemItem? item, int? uid, int? gid, UnixFileMode umask, string path)
Copy link
Member

@davidfowl davidfowl May 16, 2025

Choose a reason for hiding this comment

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

Do we need to copy files to the output folder?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've updated to copy source files into a subdirectory of the OutputPath named after the service they belong to (so <OutputPath>/<ServiceName>/<FileName>). I'm using a relative path to when generating the compose file to make it portable.

@davidfowl
Copy link
Member

All good?

@danegsta
Copy link
Member Author

All good?

@davidfowl yeah, there’s one annoyance with compose ignoring permissions and ownership settings when giving a source file (vs inline contents), but otherwise everything is working as expected. We can’t fix that permissions behavior (it’s a compose bug that others have by, but we may be able to workaround it. I’m inclined to tackle that in a separate PR though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Realm import fails for the Keycloak Integration when using WSL2
4 participants