diff --git a/.github/workflows/generate_terraform_docs.yaml b/.github/workflows/generate_terraform_docs.yaml new file mode 100644 index 0000000..3a78108 --- /dev/null +++ b/.github/workflows/generate_terraform_docs.yaml @@ -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 diff --git a/.github/workflows/terraform_modules_compliance.yaml b/.github/workflows/terraform_modules_compliance.yaml new file mode 100644 index 0000000..39c04ed --- /dev/null +++ b/.github/workflows/terraform_modules_compliance.yaml @@ -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"]' diff --git a/.github/workflows/terraform_modules_release.yaml b/.github/workflows/terraform_modules_release.yaml new file mode 100644 index 0000000..b470385 --- /dev/null +++ b/.github/workflows/terraform_modules_release.yaml @@ -0,0 +1,23 @@ +# 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-release: + uses: canonical/operator-workflows/.github/workflows/terraform_modules_release.yaml@030c5b20a0bb880dcf1ed326bff0fc16b6eb6ac1 # main + permissions: + contents: write diff --git a/.github/workflows/test_terraform_module.yaml b/.github/workflows/test_terraform_module.yaml deleted file mode 100644 index aa92d51..0000000 --- a/.github/workflows/test_terraform_module.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2025 Canonical Ltd. -# See LICENSE file for licensing details. - -name: Test Terraform Module -on: - workflow_dispatch: - pull_request: - paths: - - "terraform/**" - -permissions: - contents: read - -jobs: - test-terraform: - uses: canonical/operator-workflows/.github/workflows/terraform_modules_test.yaml@main - with: - lxd-controller: true - k8s-controller: false - terraform-directories: '["terraform"]' diff --git a/.github/workflows/test_terraform_modules.yaml b/.github/workflows/test_terraform_modules.yaml new file mode 100644 index 0000000..e74cb0e --- /dev/null +++ b/.github/workflows/test_terraform_modules.yaml @@ -0,0 +1,20 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +name: Terraform modules tests + +on: + workflow_dispatch: + pull_request: + paths: + - "**/terraform/**" + - ".github/workflows/test_terraform_modules.yaml" + +jobs: + terraform-tests-lxd: + uses: canonical/operator-workflows/.github/workflows/terraform_modules_test.yaml@030c5b20a0bb880dcf1ed326bff0fc16b6eb6ac1 # main + secrets: inherit + with: + k8s-controller: false + lxd-controller: true + terraform-directories: '["terraform/charm"]' diff --git a/.gitignore b/.gitignore index 61820f7..2099862 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,9 @@ __pycache__/ .mypy_cache *.egg-info/ */*.rock -terraform/tests/.terraform +terraform/charm/tests/.terraform +**/.terraform/ +**/.terraform.lock.hcl # BEGIN VALE WORKFLOW IGNORE .vale/styles/* diff --git a/.licenserc.yaml b/.licenserc.yaml index c960196..34cb6b1 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -19,6 +19,7 @@ header: - '.jujuignore' - '.gitignore' - '.licenserc.yaml' + - '**/MAJOR_VERSION' - 'CODEOWNERS' - 'LICENSE' - 'trivy.yaml' diff --git a/docs/changelog.md b/docs/changelog.md index 2052832..569a3ed 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -13,6 +13,14 @@ Place any unreleased changes here, that are subject to release in coming version - Added support for base ubuntu@26.04. Aproxy now supports 20.04, 22.04, 24.04, and 26.04. - Load the netfilter kernel modules required by the nftables rules before applying them. - Organize the documentation home page by user needs and explain its structure. +- Migrated the Terraform module to the CC008 Charm Terraform Standard and moved it from `terraform/` to + `terraform/charm/`: renamed `versions.tf` to + `terraform.tf`, added the mandatory `constraints` variable and the `application`/`provides`/`requires` + outputs (replacing the deprecated `app_name` and `endpoints` outputs), added a `terraform/MAJOR_VERSION` + file, and wired the `terraform_modules_compliance`, `terraform_modules_release` and + `generate_terraform_docs` CI workflows. Note that the `base` variable now defaults to `null` (the + charm's default base) instead of `ubuntu@24.04`; pass `base = "ubuntu@24.04"` explicitly to keep the + previous default. # 2026-05-19 - Add `aproxy-port` charm configuration to allow customizing the port that aproxy listens on. diff --git a/terraform/MAJOR_VERSION b/terraform/MAJOR_VERSION new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/terraform/MAJOR_VERSION @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/terraform/.tflint.hcl b/terraform/charm/.tflint.hcl similarity index 100% rename from terraform/.tflint.hcl rename to terraform/charm/.tflint.hcl diff --git a/terraform/charm/MAJOR_VERSION b/terraform/charm/MAJOR_VERSION new file mode 120000 index 0000000..89ac4b6 --- /dev/null +++ b/terraform/charm/MAJOR_VERSION @@ -0,0 +1 @@ +../MAJOR_VERSION diff --git a/terraform/README.md b/terraform/charm/README.md similarity index 66% rename from terraform/README.md rename to terraform/charm/README.md index 217b6fc..a037c60 100644 --- a/terraform/README.md +++ b/terraform/charm/README.md @@ -10,15 +10,16 @@ deployment onto any Kubernetes environment managed by [Juju][Juju]. - **main.tf** - Defines the Juju application to be deployed. - **variables.tf** - Allows customization of the deployment. Also models the charm configuration, except for exposing the deployment options (Juju model name, channel or application name). -- **output.tf** - Integrates the module with other Terraform modules, primarily +- **outputs.tf** - Integrates the module with other Terraform modules, primarily by defining potential integration endpoints (charm integrations), but also by exposing - the Juju application name. -- **versions.tf** - Defines the Terraform provider version. + the deployed Juju application. +- **terraform.tf** - Defines the Terraform and Juju provider versions. ## Using aproxy base module in higher level modules If you want to use `aproxy` base module as part of your Terraform module, import it -like shown below: +like shown below. Always pin the `?ref=` to a released tag or a commit hash: CC008 does +not allow floating references such as branches. ```text data "juju_model" "my_model" { @@ -26,9 +27,9 @@ data "juju_model" "my_model" { } module "aproxy" { - source = "git::https://github.com/canonical/aproxy-operator//terraform" - - model = juju_model.my_model.name + source = "git::https://github.com/canonical/aproxy-operator//terraform/charm?ref=tf-1.0.0" + + model_uuid = data.juju_model.my_model.uuid # (Customize configuration variables here if needed) } ``` @@ -37,10 +38,10 @@ Create integrations, for instance: ```text resource "juju_integration" "aproxy_to_principal" { - model = juju_model.my_model.name + model_uuid = data.juju_model.my_model.uuid application { - name = module.aproxy.app_name - endpoint = module.aproxy.endpoints.juju_info + name = module.aproxy.application.name + endpoint = module.aproxy.requires.juju_info.endpoint } application { name = "principal-app" @@ -51,7 +52,13 @@ resource "juju_integration" "aproxy_to_principal" { The complete list of available integrations can be found [in the Integrations tab][aproxy-integrations]. + + + + + + [Terraform]: https://www.terraform.io/ [Terraform Juju provider]: https://registry.terraform.io/providers/juju/juju/latest -[Juju]: https://juju.is +[Juju]: https://canonical.com/juju [aproxy-integrations]: https://charmhub.io/aproxy/integrations diff --git a/terraform/main.tf b/terraform/charm/main.tf similarity index 70% rename from terraform/main.tf rename to terraform/charm/main.tf index 1aedb5b..ad34f84 100644 --- a/terraform/main.tf +++ b/terraform/charm/main.tf @@ -10,7 +10,8 @@ resource "juju_application" "aproxy" { base = var.base } - model_uuid = var.model_uuid - config = var.config - units = 1 + model_uuid = var.model_uuid + config = var.config + constraints = var.constraints + units = 1 } diff --git a/terraform/charm/outputs.tf b/terraform/charm/outputs.tf new file mode 100644 index 0000000..d52b94d --- /dev/null +++ b/terraform/charm/outputs.tf @@ -0,0 +1,23 @@ +# Copyright 2025 Canonical Ltd. +# See LICENSE file for licensing details. + +output "application" { + description = "The deployed application object." + value = juju_application.aproxy +} + +output "provides" { + description = "Map of the provided integration endpoints." + value = {} +} + +output "requires" { + description = "Map of the required integration endpoints." + value = { + juju_info = { + kind = "endpoint" + name = juju_application.aproxy.name + endpoint = "juju-info" + } + } +} diff --git a/terraform/versions.tf b/terraform/charm/terraform.tf similarity index 74% rename from terraform/versions.tf rename to terraform/charm/terraform.tf index 70b645f..bbca7d5 100644 --- a/terraform/versions.tf +++ b/terraform/charm/terraform.tf @@ -2,10 +2,12 @@ # See LICENSE file for licensing details. terraform { + required_version = "~> 1.12" + required_providers { juju = { source = "juju/juju" - version = "> 1.1.0" + version = "~> 1.0" } } } diff --git a/terraform/tests/.tflint.hcl b/terraform/charm/tests/.tflint.hcl similarity index 100% rename from terraform/tests/.tflint.hcl rename to terraform/charm/tests/.tflint.hcl diff --git a/terraform/charm/tests/main.tftest.hcl b/terraform/charm/tests/main.tftest.hcl new file mode 100644 index 0000000..65fa177 --- /dev/null +++ b/terraform/charm/tests/main.tftest.hcl @@ -0,0 +1,28 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +run "setup_tests" { + module { + source = "./tests/setup" + } +} + +run "basic_deploy" { + variables { + model_uuid = run.setup_tests.model_uuid + base = "ubuntu@24.04" + channel = "latest/edge" + # renovate: depName="aproxy" + revision = 303 + } + + assert { + condition = output.application.name == "aproxy" + error_message = "aproxy application name did not match expected" + } + + assert { + condition = output.requires.juju_info.endpoint == "juju-info" + error_message = "aproxy module should provide 'requires.juju_info' output" + } +} diff --git a/terraform/tests/setup/main.tf b/terraform/charm/tests/setup/main.tf similarity index 62% rename from terraform/tests/setup/main.tf rename to terraform/charm/tests/setup/main.tf index 9c958bc..0236105 100644 --- a/terraform/tests/setup/main.tf +++ b/terraform/charm/tests/setup/main.tf @@ -1,12 +1,12 @@ -# Copyright 2025 Canonical Ltd. +# Copyright 2026 Canonical Ltd. # See LICENSE file for licensing details. terraform { - required_version = ">= 1.0" + required_version = "~> 1.12" required_providers { juju = { - version = "> 1.1.0" source = "juju/juju" + version = "~> 1.0" } } } @@ -14,7 +14,7 @@ terraform { provider "juju" {} resource "juju_model" "test_model" { - name = "tf-testing-aproxy" + name = "tf-testing-${formatdate("YYYYMMDDhhmmss", timestamp())}" } output "model_uuid" { diff --git a/terraform/variables.tf b/terraform/charm/variables.tf similarity index 79% rename from terraform/variables.tf rename to terraform/charm/variables.tf index b19b6cb..0544243 100644 --- a/terraform/variables.tf +++ b/terraform/charm/variables.tf @@ -5,18 +5,20 @@ variable "app_name" { description = "Application name for aproxy." type = string default = "aproxy" + nullable = false } variable "base" { description = "The operating system on which to deploy" type = string - default = "ubuntu@24.04" + default = null } variable "channel" { description = "The channel to use when deploying a charm." type = string default = "latest/stable" + nullable = false } variable "config" { @@ -25,9 +27,16 @@ variable "config" { default = {} } +variable "constraints" { + description = "Juju constraints to apply for this application." + type = string + default = null +} + variable "model_uuid" { description = "UUID of the Juju model where the application will be deployed." type = string + nullable = false } variable "revision" { diff --git a/terraform/outputs.tf b/terraform/outputs.tf deleted file mode 100644 index d995d82..0000000 --- a/terraform/outputs.tf +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2025 Canonical Ltd. -# See LICENSE file for licensing details. - -output "app_name" { - description = "Name of the deployed application." - value = juju_application.aproxy.name -} - -output "endpoints" { - value = { - juju_info = "juju-info" - } -} diff --git a/terraform/tests/integration/main.tf b/terraform/tests/integration/main.tf deleted file mode 100644 index dd0822d..0000000 --- a/terraform/tests/integration/main.tf +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2025 Canonical Ltd. -# See LICENSE file for licensing details. - -terraform { - required_version = ">= 1.0" - required_providers { - juju = { - version = "> 1.1.0" - source = "juju/juju" - } - external = { - version = "> 2" - source = "hashicorp/external" - } - } -} - -provider "juju" {} - -variable "model_uuid" { - type = string -} - -resource "juju_application" "ubuntu" { - model_uuid = var.model_uuid - charm { - name = "ubuntu" - } -} - -resource "juju_integration" "aproxy_ubuntu" { - model_uuid = var.model_uuid - - application { - name = juju_application.ubuntu.name - } - - application { - name = "aproxy" - } -} - -# tflint-ignore: terraform_unused_declarations -data "external" "aproxy_status" { - program = ["bash", "${path.module}/wait-for-status.sh", var.model_uuid] - - depends_on = [ - juju_integration.aproxy_ubuntu - ] -} diff --git a/terraform/tests/integration/wait-for-status.sh b/terraform/tests/integration/wait-for-status.sh deleted file mode 100755 index cbce262..0000000 --- a/terraform/tests/integration/wait-for-status.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# Copyright 2026 Canonical Ltd. -# See LICENSE file for licensing details. - -MODEL_UUID=$1 - -juju wait-for application aproxy --query='status=="maintenance" || status=="blocked"' --timeout=10m &> /dev/null -STATUS=$(juju status aproxy --model "$MODEL_UUID" --format=json | jq -r '.applications.aproxy["application-status"].current') - -echo '{"status": "'"$STATUS"'"}' diff --git a/terraform/tests/main.tftest.hcl b/terraform/tests/main.tftest.hcl deleted file mode 100644 index fcbb264..0000000 --- a/terraform/tests/main.tftest.hcl +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2025 Canonical Ltd. -# See LICENSE file for licensing details. - -run "setup_tests" { - module { - source = "./tests/setup" - } -} - -run "basic_deploy" { - variables { - model_uuid = run.setup_tests.model_uuid - # renovate: depName="aproxy" - revision = 270 - } - - assert { - condition = output.app_name == "aproxy" - error_message = "aproxy app_name did not match expected" - } -} - -run "integration" { - variables { - model_uuid = run.setup_tests.model_uuid - } - - module { - source = "./tests/integration" - } - - assert { - condition = data.external.aproxy_status.result.status == "maintenance" || data.external.aproxy_status.result.status == "blocked" - error_message = "aproxy is not in the expected status after integration." - } -}