From 3a762cc7dd9a233172e51f3f2072da05561381bd Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 8 May 2025 17:02:26 -0700 Subject: [PATCH 1/7] Add method to emitting env placeholders - AsEnvPlaceHolder will add an envrionment variable place holder for docker service/file values. - Unknown parameters become env placeholders --- .../DockerComposeEnvironmentResource.cs | 7 ++- .../DockerComposeServiceExtensions.cs | 56 +++++++++++++++++++ .../DockerComposeServiceResourceExtensions.cs | 42 +------------- .../DockerComposePublisherTests.cs | 8 +++ ...eAppliesServiceCustomizations.verified.env | 3 + ...AppliesServiceCustomizations.verified.yaml | 1 + 6 files changed, 76 insertions(+), 41 deletions(-) create mode 100644 tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.env diff --git a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs index 1770145ec5c..9f323cb0923 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs @@ -39,7 +39,7 @@ public class DockerComposeEnvironmentResource : Resource, IComputeEnvironmentRes /// Gets the collection of environment variables captured from the Docker Compose environment. /// These will be populated into a top-level .env file adjacent to the Docker Compose file. /// - internal Dictionary CapturedEnvironmentVariables { get; } = []; + internal Dictionary CapturedEnvironmentVariables { get; } = []; /// The name of the Docker Compose environment. public DockerComposeEnvironmentResource(string name) : base(name) @@ -62,4 +62,9 @@ private Task PublishAsync(PublishingContext context) return dockerComposePublishingContext.WriteModelAsync(context.Model, this); } + + internal void AddEnvironmentVariable(string name, string? description = null, string? defaultValue = null) + { + CapturedEnvironmentVariables[name] = (description, defaultValue); + } } diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs index 0fbe0055a42..1520d62653f 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs @@ -48,4 +48,60 @@ public static IResourceBuilder PublishAsDockerComposeService(this IResourc return builder; } + + /// + /// Creates a placeholder for an environment variable in the Docker Compose file. + /// + /// + /// + /// + public static string AsEnvPlaceHolder(this IManifestExpressionProvider builder, DockerComposeServiceResource dockerComposeService) + { + + // Placeholder for resolving the actual parameter value + // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax + + // Treat secrets as environment variable placeholders as for now + // this doesn't handle generation of parameter values with defaults + var env = builder.ValueExpression.Replace("{", "") + .Replace("}", "") + .Replace(".", "_") + .Replace("-", "_") + .ToUpperInvariant(); + + dockerComposeService.Parent.AddEnvironmentVariable(env); + + return $"${{{env}}}"; + } + + /// + /// Creates a placeholder for an environment variable in the Docker Compose file. + /// + /// + /// + /// + public static string AsEnvPlaceHolder(this IResourceBuilder builder, DockerComposeServiceResource dockerComposeService) + { + return builder.Resource.AsEnvPlaceHolder(dockerComposeService); + } + + /// + /// Creates a placeholder for an environment variable in the Docker Compose file. + /// + /// + /// + /// + public static string AsEnvPlaceHolder(this ParameterResource parameter, DockerComposeServiceResource dockerComposeService) + { + // Placeholder for resolving the actual parameter value + // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax + + // Treat secrets as environment variable placeholders as for now + // this doesn't handle generation of parameter values with defaults + var env = parameter.Name.ToUpperInvariant().Replace("-", "_"); + + dockerComposeService.Parent.AddEnvironmentVariable(env, $"Parameter {parameter.Name}", parameter.Secret || parameter.Default is null ? null : parameter.Value); + + return $"${{{env}}}"; + } } diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs index d34c0aa32af..b44759c0432 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs @@ -32,7 +32,7 @@ internal static async Task ProcessValueAsync(this DockerComposeServiceRe if (value is ParameterResource param) { - return AllocateParameter(param, context); + return param.AsEnvPlaceHolder(resource); } if (value is ConnectionStringReference cs) @@ -82,7 +82,7 @@ internal static async Task ProcessValueAsync(this DockerComposeServiceRe // If we don't know how to process the value, we just return it as an external reference if (value is IManifestExpressionProvider r) { - return ResolveUnknownValue(r, resource); + return r.AsEnvPlaceHolder(resource); } return value; // todo: we need to never get here really... @@ -107,42 +107,4 @@ string GetHostValue(string? prefix = null, string? suffix = null) return $"{prefix}{mapping.Host}{suffix}"; } } - - private static string ResolveParameterValue(ParameterResource parameter, DockerComposeEnvironmentContext context) - { - // Placeholder for resolving the actual parameter value - // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax - - // Treat secrets as environment variable placeholders as for now - // this doesn't handle generation of parameter values with defaults - var env = parameter.Name.ToUpperInvariant().Replace("-", "_"); - - context.AddEnv(env, $"Parameter {parameter.Name}", - parameter.Secret || parameter.Default is null ? null : parameter.Value); - - return $"${{{env}}}"; - } - - private static string AllocateParameter(ParameterResource parameter, DockerComposeEnvironmentContext context) - { - return ResolveParameterValue(parameter, context); - } - - private static string ResolveUnknownValue(IManifestExpressionProvider parameter, DockerComposeServiceResource serviceResource) - { - // Placeholder for resolving the actual parameter value - // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax - - // Treat secrets as environment variable placeholders as for now - // this doesn't handle generation of parameter values with defaults - var env = parameter.ValueExpression.Replace("{", "") - .Replace("}", "") - .Replace(".", "_") - .Replace("-", "_") - .ToUpperInvariant(); - - serviceResource.EnvironmentVariables.Add(env, $"Unknown reference {parameter.ValueExpression}"); - - return $"${{{env}}}"; - } } diff --git a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs index 9b567708c98..65f4e9be1b8 100644 --- a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs +++ b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs @@ -137,6 +137,8 @@ public async Task DockerComposeAppliesServiceCustomizations() builder.Services.AddSingleton(); + var containerNameParam = builder.AddParameter("param1", "default-name", publishValueAsDefault: true); + builder.AddDockerComposeEnvironment("docker-compose") .WithProperties(e => e.DefaultNetworkName = "default-network") .ConfigureComposeFile(file => @@ -160,6 +162,8 @@ public async Task DockerComposeAppliesServiceCustomizations() // Set a restart policy composeService.Restart = "always"; + composeService.ContainerName = containerNameParam.AsEnvPlaceHolder(serviceResource); + // Add a custom network composeService.Networks.Add("custom-network"); }); @@ -167,11 +171,15 @@ public async Task DockerComposeAppliesServiceCustomizations() var app = builder.Build(); app.Run(); + // Assert var composePath = Path.Combine(tempDir.Path, "docker-compose.yaml"); Assert.True(File.Exists(composePath)); + var envPath = Path.Combine(tempDir.Path, ".env"); + Assert.True(File.Exists(envPath)); await Verify(File.ReadAllText(composePath), "yaml") + .AppendContentAsFile(File.ReadAllText(envPath), "env") .UseHelixAwareDirectory(); } diff --git a/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.env b/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.env new file mode 100644 index 00000000000..7fb8c368975 --- /dev/null +++ b/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.env @@ -0,0 +1,3 @@ +# Parameter param1 +PARAM1=default-name + diff --git a/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.yaml b/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.yaml index 7f24b6dddc7..bb5582b6944 100644 --- a/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.yaml +++ b/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.yaml @@ -2,6 +2,7 @@ services: service: image: "nginx:latest" + container_name: "${PARAM1}" environment: ORIGINAL_ENV: "value" CUSTOM_ENV: "custom-value" From 062db6e2e5adeb5abf721a89316503cdcd53c3d1 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 8 May 2025 22:06:39 -0700 Subject: [PATCH 2/7] PR feedback --- .../DockerComposeServiceExtensions.cs | 25 +++++++++---------- .../DockerComposeServiceResourceExtensions.cs | 4 +-- .../DockerComposePublisherTests.cs | 4 +-- ...eAppliesServiceCustomizations.verified.env | 4 +-- ...AppliesServiceCustomizations.verified.yaml | 2 +- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs index 1520d62653f..e9aa2fa8ada 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs @@ -55,9 +55,8 @@ public static IResourceBuilder PublishAsDockerComposeService(this IResourc /// /// /// - public static string AsEnvPlaceHolder(this IManifestExpressionProvider builder, DockerComposeServiceResource dockerComposeService) + public static string AsEnvironmentPlaceHolder(this IManifestExpressionProvider builder, DockerComposeServiceResource dockerComposeService) { - // Placeholder for resolving the actual parameter value // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax @@ -75,23 +74,23 @@ public static string AsEnvPlaceHolder(this IManifestExpressionProvider builder, } /// - /// Creates a placeholder for an environment variable in the Docker Compose file. + /// Creates a placeholder for an environment variable in the Docker Compose file for the specified . /// - /// - /// - /// - public static string AsEnvPlaceHolder(this IResourceBuilder builder, DockerComposeServiceResource dockerComposeService) + /// The resource builder for the parameter resource. + /// The Docker Compose service resource to associate the environment variable with. + /// A string representing the environment variable placeholder in Docker Compose syntax. + public static string AsEnvironmentPlaceHolder(this IResourceBuilder builder, DockerComposeServiceResource dockerComposeService) { - return builder.Resource.AsEnvPlaceHolder(dockerComposeService); + return builder.Resource.AsEnvironmentPlaceHolder(dockerComposeService); } /// - /// Creates a placeholder for an environment variable in the Docker Compose file. + /// Creates a placeholder for an environment variable in the Docker Compose file for the specified . /// - /// - /// - /// - public static string AsEnvPlaceHolder(this ParameterResource parameter, DockerComposeServiceResource dockerComposeService) + /// The parameter resource for which to create the environment variable placeholder. + /// The Docker Compose service resource to associate the environment variable with. + /// A string representing the environment variable placeholder in Docker Compose syntax (e.g., ${ENV_VAR}). + public static string AsEnvironmentPlaceHolder(this ParameterResource parameter, DockerComposeServiceResource dockerComposeService) { // Placeholder for resolving the actual parameter value // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs index b44759c0432..12682c8258a 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs @@ -32,7 +32,7 @@ internal static async Task ProcessValueAsync(this DockerComposeServiceRe if (value is ParameterResource param) { - return param.AsEnvPlaceHolder(resource); + return param.AsEnvironmentPlaceHolder(resource); } if (value is ConnectionStringReference cs) @@ -82,7 +82,7 @@ internal static async Task ProcessValueAsync(this DockerComposeServiceRe // If we don't know how to process the value, we just return it as an external reference if (value is IManifestExpressionProvider r) { - return r.AsEnvPlaceHolder(resource); + return r.AsEnvironmentPlaceHolder(resource); } return value; // todo: we need to never get here really... diff --git a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs index 65f4e9be1b8..6fb1f942e2f 100644 --- a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs +++ b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs @@ -137,7 +137,7 @@ public async Task DockerComposeAppliesServiceCustomizations() builder.Services.AddSingleton(); - var containerNameParam = builder.AddParameter("param1", "default-name", publishValueAsDefault: true); + var containerNameParam = builder.AddParameter("param-1", "default-name", publishValueAsDefault: true); builder.AddDockerComposeEnvironment("docker-compose") .WithProperties(e => e.DefaultNetworkName = "default-network") @@ -162,7 +162,7 @@ public async Task DockerComposeAppliesServiceCustomizations() // Set a restart policy composeService.Restart = "always"; - composeService.ContainerName = containerNameParam.AsEnvPlaceHolder(serviceResource); + composeService.ContainerName = containerNameParam.AsEnvironmentPlaceHolder(serviceResource); // Add a custom network composeService.Networks.Add("custom-network"); diff --git a/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.env b/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.env index 7fb8c368975..63d7826c174 100644 --- a/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.env +++ b/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.env @@ -1,3 +1,3 @@ -# Parameter param1 -PARAM1=default-name +# Parameter param-1 +PARAM_1=default-name diff --git a/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.yaml b/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.yaml index bb5582b6944..4402c591051 100644 --- a/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.yaml +++ b/tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppliesServiceCustomizations.verified.yaml @@ -2,7 +2,7 @@ services: service: image: "nginx:latest" - container_name: "${PARAM1}" + container_name: "${PARAM_1}" environment: ORIGINAL_ENV: "value" CUSTOM_ENV: "custom-value" From e18846e33a70fdf46e44ebf3c486644a231c9f8e Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 8 May 2025 22:22:17 -0700 Subject: [PATCH 3/7] Enhance environment variable handling in Docker Compose resources - Updated CapturedEnvironmentVariables to include a source object. - Modified AddEnvironmentVariable method to accept a source parameter. - Adjusted AsEnvironmentPlaceHolder methods to utilize the new source parameter for better context. --- .../DockerComposeEnvironmentContext.cs | 5 ---- .../DockerComposeEnvironmentResource.cs | 6 ++--- .../DockerComposePublishingContext.cs | 2 +- .../DockerComposeServiceExtensions.cs | 27 ++++++++++++------- .../DockerComposeServiceResource.cs | 6 ++++- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentContext.cs b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentContext.cs index 53aa0379b9c..a90fb60739d 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentContext.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentContext.cs @@ -141,9 +141,4 @@ private async Task ProcessArgumentsAsync(DockerComposeServiceResource serviceRes } } } - - public void AddEnv(string name, string description, string? defaultValue = null) - { - environment.CapturedEnvironmentVariables[name] = (description, defaultValue); - } } \ No newline at end of file diff --git a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs index 9f323cb0923..25d6ac95a43 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs @@ -39,7 +39,7 @@ public class DockerComposeEnvironmentResource : Resource, IComputeEnvironmentRes /// Gets the collection of environment variables captured from the Docker Compose environment. /// These will be populated into a top-level .env file adjacent to the Docker Compose file. /// - internal Dictionary CapturedEnvironmentVariables { get; } = []; + internal Dictionary CapturedEnvironmentVariables { get; } = []; /// The name of the Docker Compose environment. public DockerComposeEnvironmentResource(string name) : base(name) @@ -63,8 +63,8 @@ private Task PublishAsync(PublishingContext context) return dockerComposePublishingContext.WriteModelAsync(context.Model, this); } - internal void AddEnvironmentVariable(string name, string? description = null, string? defaultValue = null) + internal void AddEnvironmentVariable(string name, string? description = null, string? defaultValue = null, object? source = null) { - CapturedEnvironmentVariables[name] = (description, defaultValue); + CapturedEnvironmentVariables[name] = (description, defaultValue, source); } } diff --git a/src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs b/src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs index 330dc2fe8cb..1846d8a6e11 100644 --- a/src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs +++ b/src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs @@ -116,7 +116,7 @@ private async Task WriteDockerComposeOutputAsync(DistributedApplicationModel mod foreach (var entry in environment.CapturedEnvironmentVariables ?? []) { - var (key, (description, defaultValue)) = entry; + var (key, (description, defaultValue, _)) = entry; envFile.AddIfMissing(key, defaultValue, description); } diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs index e9aa2fa8ada..059d20edef1 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs @@ -52,40 +52,42 @@ public static IResourceBuilder PublishAsDockerComposeService(this IResourc /// /// Creates a placeholder for an environment variable in the Docker Compose file. /// - /// - /// - /// - public static string AsEnvironmentPlaceHolder(this IManifestExpressionProvider builder, DockerComposeServiceResource dockerComposeService) + /// The manifest expression provider. + /// The Docker Compose service resource to associate the environment variable with. + /// A string representing the environment variable placeholder in Docker Compose syntax (e.g., ${ENV_VAR}). + public static string AsEnvironmentPlaceHolder(this IManifestExpressionProvider manifestExpressionProvider, DockerComposeServiceResource dockerComposeService) { // Placeholder for resolving the actual parameter value // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax // Treat secrets as environment variable placeholders as for now // this doesn't handle generation of parameter values with defaults - var env = builder.ValueExpression.Replace("{", "") + var env = manifestExpressionProvider.ValueExpression.Replace("{", "") .Replace("}", "") .Replace(".", "_") .Replace("-", "_") .ToUpperInvariant(); - dockerComposeService.Parent.AddEnvironmentVariable(env); + dockerComposeService.Parent.AddEnvironmentVariable( + env, + source: manifestExpressionProvider); return $"${{{env}}}"; } /// - /// Creates a placeholder for an environment variable in the Docker Compose file for the specified . + /// Creates a Docker Compose environment variable placeholder for the specified . /// /// The resource builder for the parameter resource. /// The Docker Compose service resource to associate the environment variable with. - /// A string representing the environment variable placeholder in Docker Compose syntax. + /// A string representing the environment variable placeholder in Docker Compose syntax (e.g., ${ENV_VAR}). public static string AsEnvironmentPlaceHolder(this IResourceBuilder builder, DockerComposeServiceResource dockerComposeService) { return builder.Resource.AsEnvironmentPlaceHolder(dockerComposeService); } /// - /// Creates a placeholder for an environment variable in the Docker Compose file for the specified . + /// Creates a Docker Compose environment variable placeholder for this . /// /// The parameter resource for which to create the environment variable placeholder. /// The Docker Compose service resource to associate the environment variable with. @@ -99,7 +101,12 @@ public static string AsEnvironmentPlaceHolder(this ParameterResource parameter, // this doesn't handle generation of parameter values with defaults var env = parameter.Name.ToUpperInvariant().Replace("-", "_"); - dockerComposeService.Parent.AddEnvironmentVariable(env, $"Parameter {parameter.Name}", parameter.Secret || parameter.Default is null ? null : parameter.Value); + dockerComposeService.Parent.AddEnvironmentVariable( + env, + description: $"Parameter {parameter.Name}", + defaultValue: parameter.Secret || parameter.Default is null ? null : parameter.Value, + source: parameter + ); return $"${{{env}}}"; } diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs index 0aa6de9e122..d8c7cf4657f 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs @@ -98,7 +98,11 @@ private bool TryGetContainerImageName(IResource resourceInstance, out string? co { var imageEnvName = $"{resourceInstance.Name.ToUpperInvariant().Replace("-", "_")}_IMAGE"; - composeEnvironmentResource.CapturedEnvironmentVariables.Add(imageEnvName, ($"Container image name for {resourceInstance.Name}", $"{resourceInstance.Name}:latest")); + composeEnvironmentResource.AddEnvironmentVariable( + imageEnvName, + description: $"Container image name for {resourceInstance.Name}", + defaultValue: $"{resourceInstance.Name}:latest" + ); containerImageName = $"${{{imageEnvName}}}"; return true; From 59d27aa691104a78b7dba9a9c2d1791342282e69 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 8 May 2025 22:29:28 -0700 Subject: [PATCH 4/7] Refactor AddEnvironmentVariable method to return environment variable placeholder and update usage in service extensions --- .../DockerComposeEnvironmentResource.cs | 4 +++- .../DockerComposeServiceExtensions.cs | 11 ++++------- .../DockerComposeServiceResource.cs | 10 ++++------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs index 25d6ac95a43..f4cd8f70caf 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs @@ -63,8 +63,10 @@ private Task PublishAsync(PublishingContext context) return dockerComposePublishingContext.WriteModelAsync(context.Model, this); } - internal void AddEnvironmentVariable(string name, string? description = null, string? defaultValue = null, object? source = null) + internal string AddEnvironmentVariable(string name, string? description = null, string? defaultValue = null, object? source = null) { CapturedEnvironmentVariables[name] = (description, defaultValue, source); + + return $"${{{name}}}"; } } diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs index 059d20edef1..55fbec8eeaa 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs @@ -68,11 +68,10 @@ public static string AsEnvironmentPlaceHolder(this IManifestExpressionProvider m .Replace("-", "_") .ToUpperInvariant(); - dockerComposeService.Parent.AddEnvironmentVariable( + return dockerComposeService.Parent.AddEnvironmentVariable( env, - source: manifestExpressionProvider); - - return $"${{{env}}}"; + source: manifestExpressionProvider + ); } /// @@ -101,13 +100,11 @@ public static string AsEnvironmentPlaceHolder(this ParameterResource parameter, // this doesn't handle generation of parameter values with defaults var env = parameter.Name.ToUpperInvariant().Replace("-", "_"); - dockerComposeService.Parent.AddEnvironmentVariable( + return dockerComposeService.Parent.AddEnvironmentVariable( env, description: $"Parameter {parameter.Name}", defaultValue: parameter.Secret || parameter.Default is null ? null : parameter.Value, source: parameter ); - - return $"${{{env}}}"; } } diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs index d8c7cf4657f..20f57fa50b4 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs @@ -98,13 +98,11 @@ private bool TryGetContainerImageName(IResource resourceInstance, out string? co { var imageEnvName = $"{resourceInstance.Name.ToUpperInvariant().Replace("-", "_")}_IMAGE"; - composeEnvironmentResource.AddEnvironmentVariable( - imageEnvName, - description: $"Container image name for {resourceInstance.Name}", - defaultValue: $"{resourceInstance.Name}:latest" + containerImageName = composeEnvironmentResource.AddEnvironmentVariable( + imageEnvName, + description: $"Container image name for {resourceInstance.Name}", + defaultValue: $"{resourceInstance.Name}:latest" ); - - containerImageName = $"${{{imageEnvName}}}"; return true; } From 33996d4fe3ed84b866472846ee9dc372e26ca458 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 8 May 2025 23:35:34 -0700 Subject: [PATCH 5/7] Add source parameter to AddEnvironmentVariable for container image reference --- src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs index 20f57fa50b4..095b5cfe4b4 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceResource.cs @@ -101,7 +101,8 @@ private bool TryGetContainerImageName(IResource resourceInstance, out string? co containerImageName = composeEnvironmentResource.AddEnvironmentVariable( imageEnvName, description: $"Container image name for {resourceInstance.Name}", - defaultValue: $"{resourceInstance.Name}:latest" + defaultValue: $"{resourceInstance.Name}:latest", + source: new ContainerImageReference(resourceInstance) ); return true; } From 6d3875a161b2209340e97c0c57c2b9b62f97d5a0 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 9 May 2025 06:58:45 -0700 Subject: [PATCH 6/7] Rename AsEnvironmentPlaceHolder methods to AsEnvironmentPlaceholder for consistency --- .../DockerComposeServiceExtensions.cs | 8 ++++---- .../DockerComposeServiceResourceExtensions.cs | 4 ++-- .../DockerComposePublisherTests.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs index 55fbec8eeaa..0e6cae1c8ca 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs @@ -55,7 +55,7 @@ public static IResourceBuilder PublishAsDockerComposeService(this IResourc /// The manifest expression provider. /// The Docker Compose service resource to associate the environment variable with. /// A string representing the environment variable placeholder in Docker Compose syntax (e.g., ${ENV_VAR}). - public static string AsEnvironmentPlaceHolder(this IManifestExpressionProvider manifestExpressionProvider, DockerComposeServiceResource dockerComposeService) + public static string AsEnvironmentPlaceholder(this IManifestExpressionProvider manifestExpressionProvider, DockerComposeServiceResource dockerComposeService) { // Placeholder for resolving the actual parameter value // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax @@ -80,9 +80,9 @@ public static string AsEnvironmentPlaceHolder(this IManifestExpressionProvider m /// The resource builder for the parameter resource. /// The Docker Compose service resource to associate the environment variable with. /// A string representing the environment variable placeholder in Docker Compose syntax (e.g., ${ENV_VAR}). - public static string AsEnvironmentPlaceHolder(this IResourceBuilder builder, DockerComposeServiceResource dockerComposeService) + public static string AsEnvironmentPlaceholder(this IResourceBuilder builder, DockerComposeServiceResource dockerComposeService) { - return builder.Resource.AsEnvironmentPlaceHolder(dockerComposeService); + return builder.Resource.AsEnvironmentPlaceholder(dockerComposeService); } /// @@ -91,7 +91,7 @@ public static string AsEnvironmentPlaceHolder(this IResourceBuilderThe parameter resource for which to create the environment variable placeholder. /// The Docker Compose service resource to associate the environment variable with. /// A string representing the environment variable placeholder in Docker Compose syntax (e.g., ${ENV_VAR}). - public static string AsEnvironmentPlaceHolder(this ParameterResource parameter, DockerComposeServiceResource dockerComposeService) + public static string AsEnvironmentPlaceholder(this ParameterResource parameter, DockerComposeServiceResource dockerComposeService) { // Placeholder for resolving the actual parameter value // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs index 12682c8258a..94fe6cde8ae 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceResourceExtensions.cs @@ -32,7 +32,7 @@ internal static async Task ProcessValueAsync(this DockerComposeServiceRe if (value is ParameterResource param) { - return param.AsEnvironmentPlaceHolder(resource); + return param.AsEnvironmentPlaceholder(resource); } if (value is ConnectionStringReference cs) @@ -82,7 +82,7 @@ internal static async Task ProcessValueAsync(this DockerComposeServiceRe // If we don't know how to process the value, we just return it as an external reference if (value is IManifestExpressionProvider r) { - return r.AsEnvironmentPlaceHolder(resource); + return r.AsEnvironmentPlaceholder(resource); } return value; // todo: we need to never get here really... diff --git a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs index 6fb1f942e2f..13686ff37ee 100644 --- a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs +++ b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs @@ -162,7 +162,7 @@ public async Task DockerComposeAppliesServiceCustomizations() // Set a restart policy composeService.Restart = "always"; - composeService.ContainerName = containerNameParam.AsEnvironmentPlaceHolder(serviceResource); + composeService.ContainerName = containerNameParam.AsEnvironmentPlaceholder(serviceResource); // Add a custom network composeService.Networks.Add("custom-network"); From 9ed7724ed48af793ec93feee4c5310991c062b4e Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 9 May 2025 07:03:13 -0700 Subject: [PATCH 7/7] Remove comment --- .../DockerComposeServiceExtensions.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs index 0e6cae1c8ca..93a8643e78e 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeServiceExtensions.cs @@ -57,11 +57,6 @@ public static IResourceBuilder PublishAsDockerComposeService(this IResourc /// A string representing the environment variable placeholder in Docker Compose syntax (e.g., ${ENV_VAR}). public static string AsEnvironmentPlaceholder(this IManifestExpressionProvider manifestExpressionProvider, DockerComposeServiceResource dockerComposeService) { - // Placeholder for resolving the actual parameter value - // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax - - // Treat secrets as environment variable placeholders as for now - // this doesn't handle generation of parameter values with defaults var env = manifestExpressionProvider.ValueExpression.Replace("{", "") .Replace("}", "") .Replace(".", "_") @@ -96,10 +91,10 @@ public static string AsEnvironmentPlaceholder(this ParameterResource parameter, // Placeholder for resolving the actual parameter value // https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax - // Treat secrets as environment variable placeholders as for now - // this doesn't handle generation of parameter values with defaults var env = parameter.Name.ToUpperInvariant().Replace("-", "_"); + // Treat secrets as environment variable placeholders as for now + // this doesn't handle generation of parameter values with defaults return dockerComposeService.Parent.AddEnvironmentVariable( env, description: $"Parameter {parameter.Name}",