diff --git a/src/Aspire.Hosting.Kubernetes/KubernetesResource.cs b/src/Aspire.Hosting.Kubernetes/KubernetesResource.cs index 88fe890846e..6899fb7b105 100644 --- a/src/Aspire.Hosting.Kubernetes/KubernetesResource.cs +++ b/src/Aspire.Hosting.Kubernetes/KubernetesResource.cs @@ -386,6 +386,13 @@ private async Task ProcessValueAsync(KubernetesEnvironmentContext contex return s; } + // Handle scalar/primitive types (bool, int, long, double, etc.) + // These can appear when third-party integrations set environment variables to non-string values. + if (value is IConvertible) + { + return string.Format(CultureInfo.InvariantCulture, "{0}", value); + } + if (value is EndpointReference ep) { var referencedResource = ep.Resource == this diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs index 92d7f73d92e..33f82f2f66c 100644 --- a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs +++ b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs @@ -619,6 +619,55 @@ public async Task PublishAsync_ConditionalWithParameterBranch_UsesIfElseSyntax() await settingsTask; } + [Fact] + public async Task PublishAsync_HandlesScalarEnvironmentVariableTypes() + { + using var tempDir = new TestTempDirectory(); + var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); + + builder.AddKubernetesEnvironment("env"); + + var api = builder.AddContainer("myapp", "mcr.microsoft.com/dotnet/aspnet:8.0") + .WithEnvironment(context => + { + context.EnvironmentVariables["BOOL_TRUE"] = true; + context.EnvironmentVariables["BOOL_FALSE"] = false; + context.EnvironmentVariables["INT_VALUE"] = 42; + context.EnvironmentVariables["DOUBLE_VALUE"] = 3.14; + }) + .WithEnvironment("STRING_VALUE", "hello"); + + var app = builder.Build(); + app.Run(); + + var expectedFiles = new[] + { + "Chart.yaml", + "values.yaml", + "templates/myapp/deployment.yaml", + "templates/myapp/config.yaml", + }; + + SettingsTask settingsTask = default!; + + foreach (var expectedFile in expectedFiles) + { + var filePath = Path.Combine(tempDir.Path, expectedFile); + var fileExtension = Path.GetExtension(filePath)[1..]; + + if (settingsTask is null) + { + settingsTask = Verify(File.ReadAllText(filePath), fileExtension); + } + else + { + settingsTask = settingsTask.AppendContentAsFile(File.ReadAllText(filePath), fileExtension); + } + } + + await settingsTask; + } + private sealed class TestConditionProvider(string value) : IValueProvider, IManifestExpressionProvider { public string ValueExpression => "test-condition"; diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#00.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#00.verified.yaml new file mode 100644 index 00000000000..d05c0dbf228 --- /dev/null +++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#00.verified.yaml @@ -0,0 +1,11 @@ +apiVersion: "v2" +name: "aspire-hosting-tests" +version: "0.1.0" +kubeVersion: ">= 1.18.0-0" +description: "Aspire Helm Chart" +type: "application" +keywords: + - "aspire" + - "kubernetes" +appVersion: "0.1.0" +deprecated: false diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#01.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#01.verified.yaml new file mode 100644 index 00000000000..68352bf6708 --- /dev/null +++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#01.verified.yaml @@ -0,0 +1,9 @@ +parameters: {} +secrets: {} +config: + myapp: + BOOL_TRUE: "True" + BOOL_FALSE: "False" + INT_VALUE: "42" + DOUBLE_VALUE: "3.14" + STRING_VALUE: "hello" diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#02.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#02.verified.yaml new file mode 100644 index 00000000000..37de00c4947 --- /dev/null +++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#02.verified.yaml @@ -0,0 +1,36 @@ +--- +apiVersion: "apps/v1" +kind: "Deployment" +metadata: + name: "myapp-deployment" + labels: + app.kubernetes.io/name: "aspire-hosting-tests" + app.kubernetes.io/component: "myapp" + app.kubernetes.io/instance: "{{ .Release.Name }}" +spec: + template: + metadata: + labels: + app.kubernetes.io/name: "aspire-hosting-tests" + app.kubernetes.io/component: "myapp" + app.kubernetes.io/instance: "{{ .Release.Name }}" + spec: + containers: + - image: "mcr.microsoft.com/dotnet/aspnet:8.0" + name: "myapp" + envFrom: + - configMapRef: + name: "myapp-config" + imagePullPolicy: "IfNotPresent" + selector: + matchLabels: + app.kubernetes.io/name: "aspire-hosting-tests" + app.kubernetes.io/component: "myapp" + app.kubernetes.io/instance: "{{ .Release.Name }}" + replicas: 1 + revisionHistoryLimit: 3 + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: "RollingUpdate" diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#03.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#03.verified.yaml new file mode 100644 index 00000000000..936d96906e3 --- /dev/null +++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesScalarEnvironmentVariableTypes#03.verified.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: "v1" +kind: "ConfigMap" +metadata: + name: "myapp-config" + labels: + app.kubernetes.io/name: "aspire-hosting-tests" + app.kubernetes.io/component: "myapp" + app.kubernetes.io/instance: "{{ .Release.Name }}" +data: + BOOL_TRUE: "{{ .Values.config.myapp.BOOL_TRUE }}" + BOOL_FALSE: "{{ .Values.config.myapp.BOOL_FALSE }}" + INT_VALUE: "{{ .Values.config.myapp.INT_VALUE }}" + DOUBLE_VALUE: "{{ .Values.config.myapp.DOUBLE_VALUE }}" + STRING_VALUE: "{{ .Values.config.myapp.STRING_VALUE }}"