Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/generate_terraform_docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2026 Canonical Ltd.
# See LICENSE file for licensing details.

name: Update Terraform documentation

# Keep the `paths` filters below and the `terraform-directory` input in sync with
# the directories where the Terraform modules live.
on:
pull_request:
paths:
- '**/terraform/**'
- '.github/workflows/generate_terraform_docs.yaml'
push:
branches:
- main
paths:
- '**/terraform/**'
- '.github/workflows/generate_terraform_docs.yaml'

permissions:
contents: write
pull-requests: write

jobs:
terraform-docs:
name: Update Terraform documentation
uses: canonical/operator-workflows/.github/workflows/generate_terraform_docs.yaml@030c5b20a0bb880dcf1ed326bff0fc16b6eb6ac1 # main
secrets: inherit
with:
terraform-directory: terraform/charm/haproxy,terraform/charm/haproxy_ddos_protection_configurator,terraform/charm/haproxy_spoe_auth,terraform/product
18 changes: 0 additions & 18 deletions .github/workflows/terraform-docs.yaml

This file was deleted.

17 changes: 17 additions & 0 deletions .github/workflows/terraform_modules_compliance.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2026 Canonical Ltd.
# See LICENSE file for licensing details.

name: Terraform modules compliance

on:
workflow_dispatch:
pull_request:
paths:
- '**/terraform/**'
- '.github/workflows/terraform_modules_compliance.yaml'

jobs:
terraform-compliance:
uses: canonical/operator-workflows/.github/workflows/terraform_modules_compliance.yaml@030c5b20a0bb880dcf1ed326bff0fc16b6eb6ac1 # main
with:
terraform-directories: '["terraform/charm/haproxy", "terraform/charm/haproxy_ddos_protection_configurator", "terraform/charm/haproxy_spoe_auth", "terraform/product"]'
22 changes: 22 additions & 0 deletions .github/workflows/terraform_modules_release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2026 Canonical Ltd.
# See LICENSE file for licensing details.

name: Terraform modules release
on:
workflow_dispatch:
pull_request:
paths:
- '**/terraform/**'
- '.github/workflows/terraform_modules_release.yaml'
push:
branches:
- main
paths:
- '**/terraform/**'
- '.github/workflows/terraform_modules_release.yaml'

jobs:
terraform:
uses: canonical/operator-workflows/.github/workflows/terraform_modules_release.yaml@030c5b20a0bb880dcf1ed326bff0fc16b6eb6ac1 # main
permissions:
contents: write
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ on:
workflow_dispatch:
pull_request:
paths:
- 'terraform/**'
- '**/terraform/**'
- '.github/workflows/test_terraform_modules.yaml'

jobs:
terraform-tests:
uses: canonical/operator-workflows/.github/workflows/terraform_modules_test.yaml@main
uses: canonical/operator-workflows/.github/workflows/terraform_modules_test.yaml@030c5b20a0bb880dcf1ed326bff0fc16b6eb6ac1 # main
secrets: inherit
with:
lxd-controller: true
Expand Down
1 change: 1 addition & 0 deletions .licenserc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ header:
- 'docs/**'
- '.lycheeignore'
- 'haproxy-route-policy/policy/migrations/*.py'
- '**/MAJOR_VERSION'
comment: on-failure
23 changes: 23 additions & 0 deletions docs/release-notes/artifacts/pr0674.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version_schema: 2

changes:
- title: Migrated Terraform modules to the CC008 Charm Terraform Standards
author: copilot
type: minor
description: >
All Terraform modules under `terraform/` (the `haproxy`,
`haproxy-ddos-protection-configurator` and `haproxy-spoe-auth` charm
modules, and the product module) were migrated to the CC008 Charm
Terraform Standards. Charm module `base` and `constraints` variables
now default to `null` instead of a fixed value, and `app_name`,
`channel` and `model_uuid` are no longer nullable. The product module
gained a new `risk` input variable. CI now runs the CC008 compliance
checker and releases Terraform modules with `tf-X.Y.Z` tags via the
canonical/operator-workflows reusable workflows.
urls:
pr:
- ""
related_doc:
related_issue:
visibility: internal
highlight: false
1 change: 1 addition & 0 deletions terraform/MAJOR_VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
4 changes: 2 additions & 2 deletions terraform/charm/haproxy/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
resource "juju_application" "haproxy" {
name = var.app_name
model_uuid = var.model_uuid
units = var.machines == null ? coalesce(var.units, 1) : null
machines = var.machines
units = length(coalesce(var.machines, [])) == 0 ? coalesce(var.units, 1) : null
machines = length(coalesce(var.machines, [])) == 0 ? null : var.machines

charm {
name = "haproxy"
Expand Down
28 changes: 13 additions & 15 deletions terraform/charm/haproxy/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ variable "app_name" {
description = "Application name of the deployed haproxy charm."
type = string
default = "haproxy"
nullable = false
}

variable "base" {
description = "Base of the haproxy charm."
type = string
default = "ubuntu@24.04"
default = null
}

variable "channel" {
description = "Channel of the haproxy charm."
type = string
default = "2.8/edge"
nullable = false
}

variable "config" {
Expand All @@ -28,12 +30,19 @@ variable "config" {
variable "constraints" {
description = "Haproxy constraints."
type = string
default = "arch=amd64"
default = null
}

variable "machines" {
description = "Machines to deploy haproxy units to. Takes precedence over units when set."
type = set(string)
default = []
}

variable "model_uuid" {
description = "ID of the Juju model to deploy to."
type = string
nullable = false
}

variable "revision" {
Expand All @@ -43,19 +52,8 @@ variable "revision" {
}

variable "units" {
description = "Number of haproxy units. If hacluster is enabled, it is recommended to use a value > 3 to ensure a quorum."
description = "Number of haproxy units. If hacluster is enabled, it is recommended to use a value > 3 to ensure a quorum. Ignored when machines is set."
type = number
default = null
}

variable "machines" {
description = "Machines to deploy haproxy units to. Mutually exclusive with units."
type = set(string)
default = null

validation {
condition = var.machines == null || var.units == null
error_message = "machines and units cannot both be set."
}
default = 1
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ variable "app_name" {
description = "Application name of the deployed haproxy-ddos-protection-configurator charm."
type = string
default = "haproxy-ddos-protection-configurator"
nullable = false
}

variable "base" {
description = "Base of the haproxy-ddos-protection-configurator charm."
type = string
default = "ubuntu@24.04"
default = null
}

variable "channel" {
description = "Channel of the haproxy-ddos-protection-configurator charm."
type = string
default = "latest/edge"
nullable = false
}

variable "config" {
Expand All @@ -28,12 +30,13 @@ variable "config" {
variable "constraints" {
description = "haproxy-ddos-protection-configurator constraints."
type = string
default = "arch=amd64"
default = null
}

variable "model_uuid" {
description = "ID of the Juju model to deploy to."
type = string
nullable = false
}

variable "revision" {
Expand Down
7 changes: 5 additions & 2 deletions terraform/charm/haproxy_spoe_auth/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ variable "app_name" {
description = "Application name of the deployed haproxy-spoe-auth charm."
type = string
default = "haproxy-spoe-auth"
nullable = false
}

variable "base" {
description = "Base of the haproxy-spoe-auth charm."
type = string
default = "ubuntu@24.04"
default = null
}

variable "channel" {
description = "Channel of the haproxy-spoe-auth charm."
type = string
default = "latest/edge"
nullable = false
}

variable "config" {
Expand All @@ -28,12 +30,13 @@ variable "config" {
variable "constraints" {
description = "haproxy-spoe-auth constraints."
type = string
default = "arch=amd64"
default = null
}

variable "model_uuid" {
description = "ID of the Juju model to deploy to."
type = string
nullable = false
}

variable "revision" {
Expand Down
13 changes: 7 additions & 6 deletions terraform/product/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_grafana_agent"></a> [grafana\_agent](#input\_grafana\_agent) | n/a | <pre>object({<br/> app_name = optional(string, "grafana-agent")<br/> channel = optional(string, "2/stable")<br/> config = optional(map(string), {})<br/> revision = optional(number, null)<br/> })</pre> | `{}` | no |
| <a name="input_hacluster"></a> [hacluster](#input\_hacluster) | n/a | <pre>object({<br/> app_name = optional(string, "hacluster")<br/> channel = optional(string, "2.4/edge")<br/> config = optional(map(string), {})<br/> revision = optional(number, null)<br/> })</pre> | `null` | no |
| <a name="input_haproxy"></a> [haproxy](#input\_haproxy) | n/a | <pre>object({<br/> app_name = optional(string, "haproxy")<br/> channel = optional(string, "2.8/edge")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number)<br/> base = optional(string, "ubuntu@24.04")<br/> units = optional(number)<br/> machines = optional(set(string))<br/> })</pre> | `{}` | no |
| <a name="input_haproxy_ddos_protection_configurator"></a> [haproxy\_ddos\_protection\_configurator](#input\_haproxy\_ddos\_protection\_configurator) | Configuration for haproxy-ddos-protection-configurator charm deployment. | <pre>object({<br/> app_name = optional(string, "haproxy-ddos-protection-configurator")<br/> channel = optional(string, "latest/edge")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number, null)<br/> base = optional(string, "ubuntu@24.04")<br/> units = optional(number, 1)<br/> })</pre> | `{}` | no |
| <a name="input_keepalived"></a> [keepalived](#input\_keepalived) | n/a | <pre>object({<br/> app_name = optional(string, "keepalived")<br/> channel = optional(string, "latest/edge")<br/> config = optional(map(string), {})<br/> revision = optional(number, null)<br/> })</pre> | `null` | no |
| <a name="input_grafana_agent"></a> [grafana\_agent](#input\_grafana\_agent) | n/a | <pre>object({<br/> app_name = optional(string, "grafana-agent")<br/> config = optional(map(string), {})<br/> revision = optional(number, null)<br/> })</pre> | `{}` | no |
| <a name="input_hacluster"></a> [hacluster](#input\_hacluster) | n/a | <pre>object({<br/> app_name = optional(string, "hacluster")<br/> config = optional(map(string), {})<br/> revision = optional(number, null)<br/> })</pre> | `null` | no |
| <a name="input_haproxy"></a> [haproxy](#input\_haproxy) | n/a | <pre>object({<br/> app_name = optional(string, "haproxy")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number)<br/> base = optional(string, "ubuntu@24.04")<br/> units = optional(number)<br/> machines = optional(set(string))<br/> })</pre> | `{}` | no |
| <a name="input_haproxy_ddos_protection_configurator"></a> [haproxy\_ddos\_protection\_configurator](#input\_haproxy\_ddos\_protection\_configurator) | Configuration for haproxy-ddos-protection-configurator charm deployment. | <pre>object({<br/> app_name = optional(string, "haproxy-ddos-protection-configurator")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number, null)<br/> base = optional(string, "ubuntu@24.04")<br/> units = optional(number, 1)<br/> })</pre> | `{}` | no |
| <a name="input_keepalived"></a> [keepalived](#input\_keepalived) | n/a | <pre>object({<br/> app_name = optional(string, "keepalived")<br/> config = optional(map(string), {})<br/> revision = optional(number, null)<br/> })</pre> | `null` | no |
| <a name="input_metadata_version"></a> [metadata\_version](#input\_metadata\_version) | Version string reported in the product module 'metadata' output. | `string` | `"1.0.0"` | no |
| <a name="input_model_uuid"></a> [model\_uuid](#input\_model\_uuid) | ID of the model to deploy to | `string` | `""` | no |
| <a name="input_protected_hostnames_configuration"></a> [protected\_hostnames\_configuration](#input\_protected\_hostnames\_configuration) | Configuration for each protected hostname.<br/>For each hostname, a haproxy-spoe-auth application will be deployed and integrated to haproxy.<br/>Optionally a oauth-external-idp-integrator application can be deployed and integrated to haproxy-spoe-auth.<br/>The hostnames to protect have to be provided through the haproxy\_route relation. | <pre>list(object({<br/> hostname = string<br/> haproxy_spoe_auth = optional(object({<br/> # The hostname will be added automatically<br/> config = optional(map(string), {})<br/> # A random number will be appended to each app_name<br/> app_name = optional(string, "haproxy-spoe-auth")<br/> channel = optional(string, "latest/stable")<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number)<br/> base = optional(string, "ubuntu@24.04")<br/> units = optional(number, 1)<br/> }), {})<br/> oauth_external_idp_integrator = optional(object({<br/> # A number will be appended to the app_name<br/> app_name = optional(string, "oauth-external-idp-integrator")<br/> channel = optional(string, "latest/edge")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number)<br/> base = optional(string, "ubuntu@22.04")<br/> units = optional(number, 1)<br/> }), null)<br/> }))</pre> | n/a | yes |
| <a name="input_protected_hostnames_configuration"></a> [protected\_hostnames\_configuration](#input\_protected\_hostnames\_configuration) | Configuration for each protected hostname.<br/>For each hostname, a haproxy-spoe-auth application will be deployed and integrated to haproxy.<br/>Optionally a oauth-external-idp-integrator application can be deployed and integrated to haproxy-spoe-auth.<br/>The hostnames to protect have to be provided through the haproxy\_route relation. | <pre>list(object({<br/> hostname = string<br/> haproxy_spoe_auth = optional(object({<br/> # The hostname will be added automatically<br/> config = optional(map(string), {})<br/> # A random number will be appended to each app_name<br/> app_name = optional(string, "haproxy-spoe-auth")<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number)<br/> base = optional(string, "ubuntu@24.04")<br/> units = optional(number, 1)<br/> }), {})<br/> oauth_external_idp_integrator = optional(object({<br/> # A number will be appended to the app_name<br/> app_name = optional(string, "oauth-external-idp-integrator")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number)<br/> base = optional(string, "ubuntu@22.04")<br/> units = optional(number, 1)<br/> }), null)<br/> }))</pre> | n/a | yes |
| <a name="input_risk"></a> [risk](#input\_risk) | Risk level controlling the channel risk of the charms in the solution. | `string` | `"stable"` | no |
Comment thread
seb4stien marked this conversation as resolved.
Resolved

## Outputs

Expand Down
16 changes: 16 additions & 0 deletions terraform/product/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2025 Canonical Ltd.
# See LICENSE file for licensing details.

locals {
use_hacluster = var.hacluster != null
use_keepalived = var.keepalived != null

# Product owns each charm's track; `risk` selects the channel risk solution-wide.
haproxy_channel = "2.8/${var.risk}"
hacluster_channel = "2.4/${var.risk}"
keepalived_channel = "latest/${var.risk}"
grafana_agent_channel = "2/${var.risk}"
haproxy_ddos_protection_configurator_channel = "latest/${var.risk}"
haproxy_spoe_auth_channel = "latest/${var.risk}"
oauth_external_idp_integrator_channel = "latest/${var.risk}"
}
18 changes: 7 additions & 11 deletions terraform/product/main.tf
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
# Copyright 2025 Canonical Ltd.
# See LICENSE file for licensing details.
locals {
use_hacluster = var.hacluster != null
use_keepalived = var.keepalived != null
}

module "haproxy" {
source = "../charm/haproxy"

model_uuid = var.model_uuid
app_name = var.haproxy.app_name
channel = var.haproxy.channel
channel = local.haproxy_channel
revision = var.haproxy.revision
base = var.haproxy.base
units = var.haproxy.units
Expand All @@ -28,7 +24,7 @@ resource "juju_application" "hacluster" {
charm {
name = "hacluster"
revision = var.hacluster.revision
channel = var.hacluster.channel
channel = local.hacluster_channel
base = var.haproxy.base
}

Expand Down Expand Up @@ -59,7 +55,7 @@ resource "juju_application" "keepalived" {
charm {
name = "keepalived"
revision = var.keepalived.revision
channel = var.keepalived.channel
channel = local.keepalived_channel
base = var.haproxy.base
}

Expand Down Expand Up @@ -89,7 +85,7 @@ resource "juju_application" "grafana_agent" {
charm {
name = "grafana-agent"
revision = var.grafana_agent.revision
channel = var.grafana_agent.channel
channel = local.grafana_agent_channel
base = var.haproxy.base
}

Expand All @@ -115,7 +111,7 @@ module "haproxy_ddos_protection_configurator" {

model_uuid = var.model_uuid
app_name = var.haproxy_ddos_protection_configurator.app_name
channel = var.haproxy_ddos_protection_configurator.channel
channel = local.haproxy_ddos_protection_configurator_channel
revision = var.haproxy_ddos_protection_configurator.revision
base = var.haproxy_ddos_protection_configurator.base
units = var.haproxy_ddos_protection_configurator.units
Expand Down Expand Up @@ -147,7 +143,7 @@ module "haproxy_spoe_auth" {

model_uuid = var.model_uuid
app_name = format("%s%s", each.value.app_name, substr(md5(each.key), 0, 8))
channel = each.value.channel
channel = local.haproxy_spoe_auth_channel
revision = each.value.revision
base = each.value.base
units = each.value.units
Expand Down Expand Up @@ -184,7 +180,7 @@ resource "juju_application" "oauth_external_idp_integrator" {
charm {
name = "oauth-external-idp-integrator"
revision = each.value.revision
channel = each.value.channel
channel = local.oauth_external_idp_integrator_channel
base = each.value.base
}
config = each.value.config
Expand Down
Loading
Loading