Skip to content

Commit

Permalink
Add support for scaling rules
Browse files Browse the repository at this point in the history
fixes #54
  • Loading branch information
x418 authored and lonegunmanb committed Nov 15, 2024
1 parent 3554ab0 commit 09ace40
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ No modules.
| <a name="input_container_app_environment_name"></a> [container\_app\_environment\_name](#input\_container\_app\_environment\_name) | (Required) The name of the container apps managed environment. Changing this forces a new resource to be created. | `string` | n/a | yes |
| <a name="input_container_app_environment_tags"></a> [container\_app\_environment\_tags](#input\_container\_app\_environment\_tags) | A map of the tags to use on the resources that are deployed with this module. | `map(string)` | `{}` | no |
| <a name="input_container_app_secrets"></a> [container\_app\_secrets](#input\_container\_app\_secrets) | (Optional) The secrets of the container apps. The key of the map should be aligned with the corresponding container app. | <pre>map(list(object({<br> name = string<br> value = string<br> })))</pre> | `{}` | no |
| <a name="input_container_apps"></a> [container\_apps](#input\_container\_apps) | The container apps to deploy. | <pre>map(object({<br> name = string<br> tags = optional(map(string))<br> revision_mode = string<br> workload_profile_name = optional(string)<br><br> template = object({<br> init_containers = optional(set(object({<br> args = optional(list(string))<br> command = optional(list(string))<br> cpu = optional(number)<br> image = string<br> name = string<br> memory = optional(string)<br> env = optional(list(object({<br> name = string<br> secret_name = optional(string)<br> value = optional(string)<br> })))<br> volume_mounts = optional(list(object({<br> name = string<br> path = string<br> })))<br> })), [])<br> containers = set(object({<br> name = string<br> image = string<br> args = optional(list(string))<br> command = optional(list(string))<br> cpu = string<br> memory = string<br> env = optional(set(object({<br> name = string<br> secret_name = optional(string)<br> value = optional(string)<br> })))<br> liveness_probe = optional(object({<br> failure_count_threshold = optional(number)<br> header = optional(object({<br> name = string<br> value = string<br> }))<br> host = optional(string)<br> initial_delay = optional(number, 1)<br> interval_seconds = optional(number, 10)<br> path = optional(string)<br> port = number<br> timeout = optional(number, 1)<br> transport = string<br> }))<br> readiness_probe = optional(object({<br> failure_count_threshold = optional(number)<br> header = optional(object({<br> name = string<br> value = string<br> }))<br> host = optional(string)<br> interval_seconds = optional(number, 10)<br> path = optional(string)<br> port = number<br> success_count_threshold = optional(number, 3)<br> timeout = optional(number)<br> transport = string<br> }))<br> startup_probe = optional(object({<br> failure_count_threshold = optional(number)<br> header = optional(object({<br> name = string<br> value = string<br> }))<br> host = optional(string)<br> interval_seconds = optional(number, 10)<br> path = optional(string)<br> port = number<br> timeout = optional(number)<br> transport = string<br> }))<br> volume_mounts = optional(list(object({<br> name = string<br> path = string<br> })))<br> }))<br> max_replicas = optional(number)<br> min_replicas = optional(number)<br> revision_suffix = optional(string)<br><br> volume = optional(set(object({<br> name = string<br> storage_name = optional(string)<br> storage_type = optional(string)<br> })))<br> })<br><br> ingress = optional(object({<br> allow_insecure_connections = optional(bool, false)<br> external_enabled = optional(bool, false)<br> ip_security_restrictions = optional(list(object({<br> action = string<br> ip_address_range = string<br> name = string<br> description = optional(string)<br> })), [])<br> target_port = number<br> transport = optional(string)<br> traffic_weight = object({<br> label = optional(string)<br> latest_revision = optional(string)<br> revision_suffix = optional(string)<br> percentage = number<br> })<br> }))<br><br> identity = optional(object({<br> type = string<br> identity_ids = optional(list(string))<br> }))<br><br> dapr = optional(object({<br> app_id = string<br> app_port = number<br> app_protocol = optional(string)<br> }))<br><br> registry = optional(list(object({<br> server = string<br> username = optional(string)<br> password_secret_name = optional(string)<br> identity = optional(string)<br> })))<br> }))</pre> | n/a | yes |
| <a name="input_container_apps"></a> [container\_apps](#input\_container\_apps) | The container apps to deploy. | <pre>map(object({<br> name = string<br> tags = optional(map(string))<br> revision_mode = string<br> workload_profile_name = optional(string)<br><br> template = object({<br> init_containers = optional(set(object({<br> args = optional(list(string))<br> command = optional(list(string))<br> cpu = optional(number)<br> image = string<br> name = string<br> memory = optional(string)<br> env = optional(list(object({<br> name = string<br> secret_name = optional(string)<br> value = optional(string)<br> })))<br> volume_mounts = optional(list(object({<br> name = string<br> path = string<br> })))<br> })), [])<br> containers = set(object({<br> name = string<br> image = string<br> args = optional(list(string))<br> command = optional(list(string))<br> cpu = string<br> memory = string<br> env = optional(set(object({<br> name = string<br> secret_name = optional(string)<br> value = optional(string)<br> })))<br> liveness_probe = optional(object({<br> failure_count_threshold = optional(number)<br> header = optional(object({<br> name = string<br> value = string<br> }))<br> host = optional(string)<br> initial_delay = optional(number, 1)<br> interval_seconds = optional(number, 10)<br> path = optional(string)<br> port = number<br> timeout = optional(number, 1)<br> transport = string<br> }))<br> readiness_probe = optional(object({<br> failure_count_threshold = optional(number)<br> header = optional(object({<br> name = string<br> value = string<br> }))<br> host = optional(string)<br> interval_seconds = optional(number, 10)<br> path = optional(string)<br> port = number<br> success_count_threshold = optional(number, 3)<br> timeout = optional(number)<br> transport = string<br> }))<br> startup_probe = optional(object({<br> failure_count_threshold = optional(number)<br> header = optional(object({<br> name = string<br> value = string<br> }))<br> host = optional(string)<br> interval_seconds = optional(number, 10)<br> path = optional(string)<br> port = number<br> timeout = optional(number)<br> transport = string<br> }))<br> volume_mounts = optional(list(object({<br> name = string<br> path = string<br> })))<br> }))<br> max_replicas = optional(number)<br> min_replicas = optional(number)<br> revision_suffix = optional(string)<br> http_scale_rules = optional(list(object({<br> name = string<br> concurrent_requests = number<br> authentication = optional(object({<br> secret_name = string<br> trigger_parameter = string<br> }))<br> })))<br> custom_scale_rules = optional(list(object({<br> name = string<br> custom_rule_type = string # See https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/container_app#custom_rule_type<br> metadata = map(string)<br> authentication = optional(object({<br> secret_name = string<br> trigger_parameter = string<br> }))<br> })))<br> volume = optional(set(object({<br> name = string<br> storage_name = optional(string)<br> storage_type = optional(string)<br> })))<br> })<br><br> ingress = optional(object({<br> allow_insecure_connections = optional(bool, false)<br> external_enabled = optional(bool, false)<br> ip_security_restrictions = optional(list(object({<br> action = string<br> ip_address_range = string<br> name = string<br> description = optional(string)<br> })), [])<br> target_port = number<br> transport = optional(string)<br> traffic_weight = object({<br> label = optional(string)<br> latest_revision = optional(string)<br> revision_suffix = optional(string)<br> percentage = number<br> })<br> }))<br><br> identity = optional(object({<br> type = string<br> identity_ids = optional(list(string))<br> }))<br><br> dapr = optional(object({<br> app_id = string<br> app_port = number<br> app_protocol = optional(string)<br> }))<br><br> registry = optional(list(object({<br> server = string<br> username = optional(string)<br> password_secret_name = optional(string)<br> identity = optional(string)<br> })))<br><br> }))</pre> | n/a | yes |
| <a name="input_dapr_component"></a> [dapr\_component](#input\_dapr\_component) | (Optional) The Dapr component to deploy. | <pre>map(object({<br> name = string<br> component_type = string<br> version = string<br> ignore_errors = optional(bool, false)<br> init_timeout = optional(string, "5s")<br> scopes = optional(list(string))<br> metadata = optional(set(object({<br> name = string<br> secret_name = optional(string)<br> value = string<br> })))<br> }))</pre> | `{}` | no |
| <a name="input_dapr_component_secrets"></a> [dapr\_component\_secrets](#input\_dapr\_component\_secrets) | (Optional) The secrets of the Dapr components. The key of the map should be aligned with the corresponding Dapr component. | <pre>map(list(object({<br> name = string<br> value = string<br> })))</pre> | `{}` | no |
| <a name="input_env_storage"></a> [env\_storage](#input\_env\_storage) | (Optional) Manages a Container App Environment Storage, writing files to this file share to make data accessible by other systems. | <pre>map(object({<br> name = string<br> account_name = string<br> share_name = string<br> access_mode = string<br> }))</pre> | `{}` | no |
Expand Down
31 changes: 31 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,36 @@ resource "azurerm_container_app" "container_app" {
storage_type = volume.value.storage_type
}
}
dynamic "http_scale_rule" {
for_each = each.value.template.http_scale_rules
content {
name = http_scale_rule.value.name
concurrent_requests = http_scale_rule.value.concurrent_requests
dynamic "authentication" {
for_each = http_scale_rule.value.authentication != null ? [http_scale_rule.value.authentication] : []
content {
secret_name = authentication.value.secret_name
trigger_parameter = authentication.value.trigger_parameter
}
}
}
}

dynamic "custom_scale_rule" {
for_each = each.value.template.custom_scale_rules
content {
name = custom_scale_rule.value.name
custom_rule_type = custom_scale_rule.value.custom_rule_type
metadata = custom_scale_rule.value.metadata
dynamic "authentication" {
for_each = custom_scale_rule.value.authentication != null ? [custom_scale_rule.value.authentication] : []
content {
secret_name = authentication.value.secret_name
trigger_parameter = authentication.value.trigger_parameter
}
}
}
}
}
dynamic "dapr" {
for_each = each.value.dapr == null ? [] : [each.value.dapr]
Expand Down Expand Up @@ -330,4 +360,5 @@ resource "azurerm_container_app" "container_app" {
value = local.container_app_secrets[each.key][secret.key]
}
}

}
27 changes: 26 additions & 1 deletion variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,23 @@ variable "container_apps" {
max_replicas = optional(number)
min_replicas = optional(number)
revision_suffix = optional(string)

http_scale_rules = optional(list(object({
name = string
concurrent_requests = number
authentication = optional(object({
secret_name = string
trigger_parameter = string
}))
})))
custom_scale_rules = optional(list(object({
name = string
custom_rule_type = string # See https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/container_app#custom_rule_type
metadata = map(string)
authentication = optional(object({
secret_name = string
trigger_parameter = string
}))
})))
volume = optional(set(object({
name = string
storage_name = optional(string)
Expand Down Expand Up @@ -134,6 +150,7 @@ variable "container_apps" {
password_secret_name = optional(string)
identity = optional(string)
})))

}))
description = "The container apps to deploy."
nullable = false
Expand All @@ -146,6 +163,14 @@ variable "container_apps" {
condition = alltrue([for n, c in var.container_apps : c.ingress == null ? true : (c.ingress.ip_security_restrictions == null ? true : (length(distinct([for r in c.ingress.ip_security_restrictions : r.action])) <= 1))])
error_message = "The `action` types in an all `ip_security_restriction` blocks must be the same for the `ingress`, mixing `Allow` and `Deny` rules is not currently supported by the service."
}
validation {
condition = alltrue([for n, c in var.container_apps : c.template.custom_scale_rules == null || alltrue([for _, r in c.template.custom_scale_rules : can(regex("^[a-z0-9][a-z0-9-.]*[a-z0-9]$", r.name))])])
error_message = "The `name` in `custom_scale_rule` must consist of lower case alphanumeric characters, '-', or '.', and should start and end with an alphanumeric character."
}
validation {
condition = alltrue([for n, c in var.container_apps : c.template.http_scale_rules == null || alltrue([for _, r in c.template.http_scale_rules : can(regex("^[a-z0-9][a-z0-9-.]*[a-z0-9]$", r.name))])])
error_message = "The `name` in `http_scale_rule` must consist of lower case alphanumeric characters, '-', or '.', and should start and end with an alphanumeric character."
}
}

variable "location" {
Expand Down

0 comments on commit 09ace40

Please sign in to comment.