diff --git a/src/Aspire.Hosting.Kubernetes/Extensions/HelmExtensions.cs b/src/Aspire.Hosting.Kubernetes/Extensions/HelmExtensions.cs
index 421cfd1d4f8..53a510e5a39 100644
--- a/src/Aspire.Hosting.Kubernetes/Extensions/HelmExtensions.cs
+++ b/src/Aspire.Hosting.Kubernetes/Extensions/HelmExtensions.cs
@@ -74,4 +74,14 @@ public static bool ContainsHelmExpression(this string value)
public static bool ContainsHelmSecretExpression(this string value)
=> value.Contains($"{{{{ {ValuesSegment}.{SecretsKey}.", StringComparison.Ordinal);
+
+ ///
+ /// Converts the specified environment variable key into a valid Helm key.
+ ///
+ ///
+ /// The environment variable names in Helm values files can not contain hyphens ('-').
+ /// https://helm.sh/docs/chart_best_practices/values/
+ ///
+ public static string ToHelmEnvironmentVariable(this string key)
+ => $"{key.Replace("-", "_")}";
}
diff --git a/src/Aspire.Hosting.Kubernetes/KubernetesResource.cs b/src/Aspire.Hosting.Kubernetes/KubernetesResource.cs
index 45f5e57ccc4..b097a74e943 100644
--- a/src/Aspire.Hosting.Kubernetes/KubernetesResource.cs
+++ b/src/Aspire.Hosting.Kubernetes/KubernetesResource.cs
@@ -260,7 +260,7 @@ private async Task ProcessEnvironmentAsync(KubernetesEnvironmentContext environm
foreach (var environmentVariable in context.EnvironmentVariables)
{
- var key = environmentVariable.Key;
+ var key = environmentVariable.Key.ToHelmEnvironmentVariable();
var value = await this.ProcessValueAsync(environmentContext, executionContext, environmentVariable.Value).ConfigureAwait(false);
switch (value)
diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs
index f522d302337..0378611c172 100644
--- a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs
+++ b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs
@@ -184,9 +184,23 @@ public async Task PublishAsync_HandlesSpecialResourceName()
var cs = builder.AddConnectionString("api-cs", ReferenceExpression.Create($"Url={param0}, Secret={param1}"));
var param3 = builder.AddResource(ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, "param3"));
- builder.AddProject("SpeciaL-ApP", launchProfileName: null)
+
+ var containerResource = builder.AddContainer("Special-resource-with-hyphens", "my-image")
+ .AsHttp2Service()
+ .WithEnvironment("ORIGINAL_ENV", "value")
+ .WithHttpEndpoint(port: 80, targetPort: 80)
+ .WithHttpsEndpoint(port: 443, targetPort: 443)
+ .PublishAsKubernetesService(serviceResource =>
+ {
+ serviceResource.Workload!.PodTemplate.Spec.Containers[0].ImagePullPolicy = "Always";
+ (serviceResource.Workload as Deployment)!.Spec.RevisionHistoryLimit = 5;
+ });
+
+ var projectResource = builder.AddProject("SpeciaL-ApP", launchProfileName: null)
.WithEnvironment("param3", param3)
- .WithReference(cs);
+ .WithReference(cs)
+ .WithReference(containerResource.GetEndpoint("http"))
+ .WithReference(containerResource.GetEndpoint("https"));
var app = builder.Build();
@@ -195,12 +209,15 @@ public async Task PublishAsync_HandlesSpecialResourceName()
// Assert
var expectedFiles = new[]
{
- "Chart.yaml",
- "values.yaml",
- "templates/SpeciaL-ApP/deployment.yaml",
- "templates/SpeciaL-ApP/config.yaml",
- "templates/SpeciaL-ApP/secrets.yaml"
- };
+ "Chart.yaml",
+ "values.yaml",
+ "templates/SpeciaL-ApP/deployment.yaml",
+ "templates/SpeciaL-ApP/config.yaml",
+ "templates/SpeciaL-ApP/secrets.yaml",
+ "templates/Special-resource-with-hyphens/deployment.yaml",
+ "templates/Special-resource-with-hyphens/service.yaml",
+ "templates/Special-resource-with-hyphens/config.yaml"
+ };
SettingsTask settingsTask = default!;
diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#01.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#01.verified.yaml
index d85636467fd..4f84aba059e 100644
--- a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#01.verified.yaml
+++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#01.verified.yaml
@@ -1,11 +1,15 @@
-parameters:
+parameters:
SpeciaL_ApP:
SpeciaL_ApP_image: "SpeciaL-ApP:latest"
secrets:
SpeciaL_ApP:
param3: ""
config:
+ Special_resource_with_hyphens:
+ ORIGINAL_ENV: "value"
SpeciaL_ApP:
OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "true"
OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "true"
OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "in_memory"
+ services__Special_resource_with_hyphens__http__0: "http://special-resource-with-hyphens-service:80"
+ services__Special_resource_with_hyphens__https__0: "https://special-resource-with-hyphens-service:443"
diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#03.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#03.verified.yaml
index 4d50167c5b9..827b9086912 100644
--- a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#03.verified.yaml
+++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#03.verified.yaml
@@ -1,4 +1,4 @@
----
+---
apiVersion: "v1"
kind: "ConfigMap"
metadata:
@@ -11,3 +11,5 @@ data:
OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: "{{ .Values.config.SpeciaL_ApP.OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES }}"
OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: "{{ .Values.config.SpeciaL_ApP.OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES }}"
OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: "{{ .Values.config.SpeciaL_ApP.OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY }}"
+ services__Special_resource_with_hyphens__http__0: "{{ .Values.config.SpeciaL_ApP.services__Special_resource_with_hyphens__http__0 }}"
+ services__Special_resource_with_hyphens__https__0: "{{ .Values.config.SpeciaL_ApP.services__Special_resource_with_hyphens__https__0 }}"
diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#04.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#04.verified.yaml
index b959b404a96..3574b4bab9b 100644
--- a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#04.verified.yaml
+++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#04.verified.yaml
@@ -1,4 +1,4 @@
----
+---
apiVersion: "v1"
kind: "Secret"
metadata:
@@ -9,5 +9,5 @@ metadata:
app.kubernetes.io/instance: "{{.Release.Name}}"
stringData:
param3: "{{ .Values.secrets.SpeciaL_ApP.param3 }}"
- ConnectionStrings__api-cs: "Url={{ .Values.config.SpeciaL_ApP.param0 }}, Secret={{ .Values.secrets.SpeciaL_ApP.param1 }}"
+ ConnectionStrings__api_cs: "Url={{ .Values.config.SpeciaL_ApP.param0 }}, Secret={{ .Values.secrets.SpeciaL_ApP.param1 }}"
type: "Opaque"
diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#05.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#05.verified.yaml
new file mode 100644
index 00000000000..a6d9456f19e
--- /dev/null
+++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#05.verified.yaml
@@ -0,0 +1,43 @@
+---
+apiVersion: "apps/v1"
+kind: "Deployment"
+metadata:
+ name: "special-resource-with-hyphens-deployment"
+ labels:
+ app.kubernetes.io/name: "my-chart"
+ app.kubernetes.io/component: "Special-resource-with-hyphens"
+ app.kubernetes.io/instance: "{{.Release.Name}}"
+spec:
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: "my-chart"
+ app.kubernetes.io/component: "Special-resource-with-hyphens"
+ app.kubernetes.io/instance: "{{.Release.Name}}"
+ spec:
+ containers:
+ - image: "my-image:latest"
+ name: "Special-resource-with-hyphens"
+ envFrom:
+ - configMapRef:
+ name: "special-resource-with-hyphens-config"
+ ports:
+ - name: "http"
+ protocol: "TCP"
+ containerPort: 80
+ - name: "https"
+ protocol: "TCP"
+ containerPort: 443
+ imagePullPolicy: "Always"
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: "my-chart"
+ app.kubernetes.io/component: "Special-resource-with-hyphens"
+ app.kubernetes.io/instance: "{{.Release.Name}}"
+ replicas: 1
+ revisionHistoryLimit: 5
+ strategy:
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 1
+ type: "RollingUpdate"
diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#06.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#06.verified.yaml
new file mode 100644
index 00000000000..09368ba146c
--- /dev/null
+++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#06.verified.yaml
@@ -0,0 +1,24 @@
+---
+apiVersion: "v1"
+kind: "Service"
+metadata:
+ name: "special-resource-with-hyphens-service"
+ labels:
+ app.kubernetes.io/name: "my-chart"
+ app.kubernetes.io/component: "Special-resource-with-hyphens"
+ app.kubernetes.io/instance: "{{.Release.Name}}"
+spec:
+ type: "ClusterIP"
+ selector:
+ app.kubernetes.io/name: "my-chart"
+ app.kubernetes.io/component: "Special-resource-with-hyphens"
+ app.kubernetes.io/instance: "{{.Release.Name}}"
+ ports:
+ - name: "http"
+ protocol: "TCP"
+ port: 80
+ targetPort: 80
+ - name: "https"
+ protocol: "TCP"
+ port: 443
+ targetPort: 443
diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#07.verified.yaml b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#07.verified.yaml
new file mode 100644
index 00000000000..46646f44863
--- /dev/null
+++ b/tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#07.verified.yaml
@@ -0,0 +1,11 @@
+---
+apiVersion: "v1"
+kind: "ConfigMap"
+metadata:
+ name: "special-resource-with-hyphens-config"
+ labels:
+ app.kubernetes.io/name: "my-chart"
+ app.kubernetes.io/component: "Special-resource-with-hyphens"
+ app.kubernetes.io/instance: "{{.Release.Name}}"
+data:
+ ORIGINAL_ENV: "{{ .Values.config.Special_resource_with_hyphens.ORIGINAL_ENV }}"