From 3021c08ceb12f5255592926fff33f4f80891a5be Mon Sep 17 00:00:00 2001 From: Eugene Tolmachev Date: Mon, 2 May 2022 17:22:45 -0400 Subject: [PATCH 1/3] containerApp evn var collections --- .../api-overview/resources/container-apps.md | 3 +++ src/Farmer/Builders/Builders.ContainerApps.fs | 16 ++++++++++++++++ src/Tests/ContainerApps.fs | 17 +++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/docs/content/api-overview/resources/container-apps.md b/docs/content/api-overview/resources/container-apps.md index 04a0ca1f2..ab7e40cf5 100644 --- a/docs/content/api-overview/resources/container-apps.md +++ b/docs/content/api-overview/resources/container-apps.md @@ -48,8 +48,11 @@ The Container Apps builder (`containerApp`) is used to define one or more contai | add_containers | Adds a list of containers to this container app. All containers in the app share resources and scaling. | | add_simple_container | Adds a single container that references a public docker image and version. | | add_secret_parameter | Adds an application secret to the entire container app. This is passed as a secure parameter to the template, and an environment variable is automatically created which references the secret. | +| add_secret_parameters | Adds application secrets to the entire container app. This is passed as secure parameters to the template, and environment variables are automatically created which reference the secret. | | add_secret_expression | As per `add_secret_parameter`, but the value is sourced from an ARM expression instead of as a parameter. Useful for e.g. storage keys etc. | +| add_secret_expressions | As per `add_secret_parameters`, but the values are sourced from an ARM expressions instead of as parameters. Useful for e.g. storage keys etc. | | add_env_variable | Adds a static, plain text environment variable. | +| add_env_variables | Adds static, plain text environment variables. | ##### Scale Rules diff --git a/src/Farmer/Builders/Builders.ContainerApps.fs b/src/Farmer/Builders/Builders.ContainerApps.fs index 5bddd7ab4..8787f8e48 100644 --- a/src/Farmer/Builders/Builders.ContainerApps.fs +++ b/src/Farmer/Builders/Builders.ContainerApps.fs @@ -280,6 +280,11 @@ type ContainerAppBuilder () = EnvironmentVariables = state.EnvironmentVariables.Add (EnvVar.create key.Value key.Value) } + /// Adds an application secrets to the Azure Container App. + [] + member __.AddSecretParameters (state:ContainerAppConfig, keys:#seq<_>) = + keys |> Seq.fold (fun s k -> __.AddSecretParameter(s,k)) state + /// Adds an application secret to the Azure Container App. [] member _.AddSecretExpression (state:ContainerAppConfig, key, expression) = @@ -293,6 +298,12 @@ type ContainerAppBuilder () = | None -> state.Dependencies } + /// Adds an application secrets to the Azure Container App. + [] + member __.AddSecretExpressions (state:ContainerAppConfig, xs: #seq<_>) = + xs |> Seq.fold (fun s (k,e) -> __.AddSecretExpression(s,k,e)) state + + /// Adds a public environment variable to the Azure Container App environment variables. [] member _.AddEnvironmentVariable (state:ContainerAppConfig, name, value) = @@ -300,6 +311,11 @@ type ContainerAppBuilder () = EnvironmentVariables = state.EnvironmentVariables.Add (EnvVar.create name value) } + /// Adds a public environment variables to the Azure Container App environment variables. + [] + member __.AddEnvironmentVariables (state:ContainerAppConfig, vars:#seq<_>) = + vars |> Seq.fold (fun s (k,v) -> __.AddEnvironmentVariable(s,k,v)) state + [] member this.AddSimpleContainer (state:ContainerAppConfig, dockerImage, dockerVersion) = let container = diff --git a/src/Tests/ContainerApps.fs b/src/Tests/ContainerApps.fs index 7fa3552ff..8d86af74b 100644 --- a/src/Tests/ContainerApps.fs +++ b/src/Tests/ContainerApps.fs @@ -43,6 +43,17 @@ let fullContainerAppDeployment = dapr_app_id "http" add_http_scale_rule "http-rule" { ConcurrentRequests = 100 } } + containerApp { + name "multienv" + add_simple_container "mcr.microsoft.com/dotnet/samples" "aspnetapp" + ingress_target_port 80us + ingress_transport Auto + add_http_scale_rule "http-scaler" { ConcurrentRequests = 10 } + add_cpu_scale_rule "cpu-scaler" { Utilisation = 50 } + add_secret_parameters ["servicebusconnectionkey"] + add_env_variables ["ServiceBusQueueName","wishrequests"] + add_secret_expressions ["containerlogs", containerLogs.PrimarySharedKey] + } containerApp { name "servicebus" active_revision_mode Single @@ -85,6 +96,12 @@ let tests = testList "Container Apps" [ Expect.isNotNull (jobj.SelectToken("parameters.servicebusconnectionkey")) "Missing 'servicebusconnectionkey' parameter" Expect.isNotNull (jobj.SelectToken("parameters['myregistry.azurecr.io-password']")) "Missing 'myregistry.azurecr.io-password' parameter" } + test "Seq container environment parameters" { + let containerApp = fullContainerAppDeployment.Template.Resources |> List.find(fun r -> r.ResourceId.Name.Value = "multienv") :?> Farmer.Arm.App.ContainerApp + containerApp.EnvironmentVariables.["ServiceBusQueueName"] |> ignore + containerApp.EnvironmentVariables.["servicebusconnectionkey"] |> ignore + containerApp.EnvironmentVariables.["containerlogs"] |> ignore + } test "Full container managed environments" { let kubeEnv = jobj.SelectToken("resources[?(@.name=='kubecontainerenv')]") Expect.equal (kubeEnv.["type"] |> string) "Microsoft.App/managedEnvironments" "Incorrect type for kuberenetes environment" From 2b07934e405bb5e0e92e870ffe0119a77f6d2d37 Mon Sep 17 00:00:00 2001 From: Eugene Tolmachev Date: Tue, 3 May 2022 13:37:52 -0400 Subject: [PATCH 2/3] fix for #921 --- src/Farmer/Arm/App.fs | 7 ++++--- src/Tests/ContainerApps.fs | 14 +++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Farmer/Arm/App.fs b/src/Farmer/Arm/App.fs index 0fa6425c7..1141a6290 100644 --- a/src/Farmer/Arm/App.fs +++ b/src/Farmer/Arm/App.fs @@ -55,6 +55,7 @@ type ContainerApp = interface IArmResource with member this.ResourceId = containerApps.resourceId this.Name member this.JsonModel = + let usernameSecretName (resourceId:ResourceId) = $"{resourceId.Name.Value}-username" {| containerApps.Create(this.Name, this.Location, this.dependencies) with kind = "containerapp" identity = @@ -72,7 +73,7 @@ type ContainerApp = {| name = cred.Username value = cred.Password.ArmExpression.Eval() |} | ImageRegistryAuthentication.ListCredentials resourceId -> - {| name = ArmExpression.create($"listCredentials({resourceId.ArmExpression.Value}, '2019-05-01').username").Eval() + {| name = usernameSecretName resourceId value = ArmExpression.create($"listCredentials({resourceId.ArmExpression.Value}, '2019-05-01').passwords[0].value").Eval() |} for setting in this.Secrets do {| name = setting.Key.Value @@ -90,9 +91,9 @@ type ContainerApp = username = cred.Username passwordSecretRef = cred.Username |} | ImageRegistryAuthentication.ListCredentials resourceId -> - {| server = ArmExpression.create($"reference({resourceId.ArmExpression.Value}, '2019-05-01').loginServer").Eval() + {| server = $"{resourceId.Name.Value}.azurecr.io" username = ArmExpression.create($"listCredentials({resourceId.ArmExpression.Value}, '2019-05-01').username").Eval() - passwordSecretRef = ArmExpression.create($"listCredentials({resourceId.ArmExpression.Value}, '2019-05-01').username").Eval() |} + passwordSecretRef = usernameSecretName resourceId |} |] ingress = match this.IngressMode with diff --git a/src/Tests/ContainerApps.fs b/src/Tests/ContainerApps.fs index 8d86af74b..da59a5e4e 100644 --- a/src/Tests/ContainerApps.fs +++ b/src/Tests/ContainerApps.fs @@ -8,11 +8,14 @@ open Farmer.ContainerApp open Farmer.Identity let msi = createUserAssignedIdentity "appUser" +let containerRegistryName = "myregistry" let fullContainerAppDeployment = let containerLogs = logAnalytics { name "containerlogs" } - let containerRegistryDomain = "myregistry.azurecr.io" - let containerRegistryUsername = "myregistry" + let containerRegistryDomain = $"{containerRegistryName}.azurecr.io" + let acr = containerRegistry { + name containerRegistryName + } let version = "1.0.0" let containerEnv = containerEnvironment { @@ -24,7 +27,7 @@ let fullContainerAppDeployment = add_identity msi active_revision_mode Single add_registry_credentials [ - registry containerRegistryDomain containerRegistryUsername + registry containerRegistryDomain containerRegistryName ] add_containers [ container { @@ -57,6 +60,7 @@ let fullContainerAppDeployment = containerApp { name "servicebus" active_revision_mode Single + reference_registry_credentials [(acr :> IBuilder).ResourceId] add_containers [ container { name "servicebus" @@ -150,4 +154,8 @@ let tests = testList "Container Apps" [ Expect.isNonEmpty containerApp.Identity.UserAssigned "Container app did not have identity" Expect.equal containerApp.Identity.UserAssigned.[0] (UserAssignedIdentity(ResourceId.create(Arm.ManagedIdentity.userAssignedIdentities, ResourceName "appUser"))) "Expected user identity named 'appUser'." } + test "Linked ACR references correct secret" { + let containerApp = fullContainerAppDeployment.Template.Resources |> List.find(fun r -> r.ResourceId.Name.Value = "servicebus") :?> Farmer.Arm.App.ContainerApp + Expect.isFalse (containerApp.Secrets |> Map.containsKey (ContainerAppValidation.ContainerAppSettingKey.Create $"{containerRegistryName}-username").OkValue) "Container app did not have linked ACR's secret" + } ] From 6f43f9cdead29163e5f0cf632ff5c682ac2aa48a Mon Sep 17 00:00:00 2001 From: Eugene Tolmachev Date: Wed, 4 May 2022 13:12:56 -0400 Subject: [PATCH 3/3] convenience properties for referencing as dependencies --- RELEASE_NOTES.md | 1 + src/Farmer/Builders/Builders.ContainerApps.fs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 722f06bad..5c16bde8d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,7 @@ Release Notes ============= ## 1.7.1 +* Container Apps: Support for collections of env vars, fix ACR credentials linking * Event Hub: Don't create the `$Default` consumer group explicitly. It will automatically be created by Azure when the resource is created. ## 1.7.0 diff --git a/src/Farmer/Builders/Builders.ContainerApps.fs b/src/Farmer/Builders/Builders.ContainerApps.fs index 8787f8e48..ca80b52d1 100644 --- a/src/Farmer/Builders/Builders.ContainerApps.fs +++ b/src/Farmer/Builders/Builders.ContainerApps.fs @@ -34,7 +34,12 @@ type ContainerAppConfig = ImageRegistryCredentials : ImageRegistryAuthentication list Containers : ContainerConfig list Dependencies : Set } - + member this.ResourceId = containerApps.resourceId this.Name + member this.LatestRevisionFqdn = + ArmExpression + .reference(containerApps, this.ResourceId) + .Map(sprintf "%s.latestRevisionFqdn") + type ContainerEnvironmentConfig = { Name : ResourceName InternalLoadBalancerState : FeatureFlag