From 6b30ac10d152b853965f2850fec0ec30a34dccfb Mon Sep 17 00:00:00 2001 From: Weii Wang Date: Wed, 14 Oct 2026 20:16:40 +0800 Subject: [PATCH 1/4] chore: migrate terraform modules to CC008 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/generate_terraform_docs.yaml | 30 +++++++++ .../terraform_modules_compliance.yaml | 17 +++++ .../workflows/terraform_modules_release.yaml | 22 +++++++ .github/workflows/test_terraform_modules.yaml | 13 ++-- .gitignore | 2 + .licenserc.yaml | 1 + terraform/.tflint.hcl | 6 ++ terraform/MAJOR_VERSION | 1 + .../cloudflare_configurator/MAJOR_VERSION | 1 + .../charm/cloudflare_configurator/README.md | 64 +++++++++++++++++++ .../charm/cloudflare_configurator/main.tf | 18 ++++++ .../charm/cloudflare_configurator/outputs.tf | 28 ++++++++ .../cloudflare_configurator/terraform.tf | 12 ++++ .../tests/main.tftest.hcl | 63 ++++++++++++++++++ .../cloudflare_configurator/variables.tf | 52 +++++++++++++++ terraform/charm/cloudflared/MAJOR_VERSION | 1 + terraform/charm/cloudflared/README.md | 64 +++++++++++++++++++ terraform/charm/cloudflared/main.tf | 17 +++++ terraform/charm/cloudflared/outputs.tf | 34 ++++++++++ terraform/charm/cloudflared/terraform.tf | 12 ++++ .../charm/cloudflared/tests/main.tftest.hcl | 63 ++++++++++++++++++ terraform/charm/cloudflared/variables.tf | 46 +++++++++++++ 22 files changed, 560 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/generate_terraform_docs.yaml create mode 100644 .github/workflows/terraform_modules_compliance.yaml create mode 100644 .github/workflows/terraform_modules_release.yaml create mode 100644 terraform/.tflint.hcl create mode 100644 terraform/MAJOR_VERSION create mode 120000 terraform/charm/cloudflare_configurator/MAJOR_VERSION create mode 100644 terraform/charm/cloudflare_configurator/README.md create mode 100644 terraform/charm/cloudflare_configurator/main.tf create mode 100644 terraform/charm/cloudflare_configurator/outputs.tf create mode 100644 terraform/charm/cloudflare_configurator/terraform.tf create mode 100644 terraform/charm/cloudflare_configurator/tests/main.tftest.hcl create mode 100644 terraform/charm/cloudflare_configurator/variables.tf create mode 120000 terraform/charm/cloudflared/MAJOR_VERSION create mode 100644 terraform/charm/cloudflared/README.md create mode 100644 terraform/charm/cloudflared/main.tf create mode 100644 terraform/charm/cloudflared/outputs.tf create mode 100644 terraform/charm/cloudflared/terraform.tf create mode 100644 terraform/charm/cloudflared/tests/main.tftest.hcl create mode 100644 terraform/charm/cloudflared/variables.tf diff --git a/.github/workflows/generate_terraform_docs.yaml b/.github/workflows/generate_terraform_docs.yaml new file mode 100644 index 0000000..41578be --- /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/cloudflared,terraform/charm/cloudflare_configurator diff --git a/.github/workflows/terraform_modules_compliance.yaml b/.github/workflows/terraform_modules_compliance.yaml new file mode 100644 index 0000000..b9eca7f --- /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/cloudflared", "terraform/charm/cloudflare_configurator"]' diff --git a/.github/workflows/terraform_modules_release.yaml b/.github/workflows/terraform_modules_release.yaml new file mode 100644 index 0000000..1ef660b --- /dev/null +++ b/.github/workflows/terraform_modules_release.yaml @@ -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 diff --git a/.github/workflows/test_terraform_modules.yaml b/.github/workflows/test_terraform_modules.yaml index 40aef55..e21f64e 100644 --- a/.github/workflows/test_terraform_modules.yaml +++ b/.github/workflows/test_terraform_modules.yaml @@ -1,4 +1,4 @@ -# Copyright 2025 Canonical Ltd. +# Copyright 2026 Canonical Ltd. # See LICENSE file for licensing details. name: Terraform modules tests @@ -7,16 +7,15 @@ on: workflow_dispatch: pull_request: paths: - - 'terraform/**' - - 'terraform-product/**' + - '**/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 permissions: contents: read secrets: inherit with: - k8s-controller: true - #lxd-controller: true - terraform-directories: '["terraform"]' + lxd-controller: true + terraform-directories: '["terraform/charm/cloudflared", "terraform/charm/cloudflare_configurator"]' diff --git a/.gitignore b/.gitignore index 39d01a7..e651169 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ __pycache__/ terraform/**/.terraform* terraform/**/.tfvars terraform/**/*.tfstate* +**/.terraform/ +**/.terraform.lock.hcl # opcli/spread generated files .spread-reuse.* .spread-run-*/ diff --git a/.licenserc.yaml b/.licenserc.yaml index 15c2d10..3cd8980 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -34,4 +34,5 @@ header: - 'docs/release-notes/template/*.yaml' - 'uv.lock' - '**/uv.lock' + - '**/MAJOR_VERSION' comment: on-failure diff --git a/terraform/.tflint.hcl b/terraform/.tflint.hcl new file mode 100644 index 0000000..ebfb77b --- /dev/null +++ b/terraform/.tflint.hcl @@ -0,0 +1,6 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +rule "terraform_required_version" { + enabled = true +} 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/charm/cloudflare_configurator/MAJOR_VERSION b/terraform/charm/cloudflare_configurator/MAJOR_VERSION new file mode 120000 index 0000000..a80b14b --- /dev/null +++ b/terraform/charm/cloudflare_configurator/MAJOR_VERSION @@ -0,0 +1 @@ +../../MAJOR_VERSION \ No newline at end of file diff --git a/terraform/charm/cloudflare_configurator/README.md b/terraform/charm/cloudflare_configurator/README.md new file mode 100644 index 0000000..73beff2 --- /dev/null +++ b/terraform/charm/cloudflare_configurator/README.md @@ -0,0 +1,64 @@ + +# Cloudflare-configurator Terraform module + + +This folder contains a base [Terraform][Terraform] module for the cloudflare-configurator charm. + +The module uses the [Terraform Juju provider][Terraform Juju provider] to model the charm +deployment onto any machine environment managed by [Juju][Juju]. + +## Module structure + +- **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 UUID, channel or application name). +- **outputs.tf** - Integrates the module with other Terraform modules, primarily + by defining potential integration endpoints (charm integrations), but also by exposing + the deployed Juju application. +- **terraform.tf** - Defines the Terraform and Juju provider versions. + +## Using cloudflare-configurator base module in higher level modules + +If you want to use the `cloudflare-configurator` base module as part of your Terraform module, +import it 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" { + name = "my-model" +} + +module "cloudflare_configurator" { + source = "git::https://github.com/canonical/cloudflared-operators//terraform/charm/cloudflare_configurator?ref=tf-1.0.0" + + model_uuid = data.juju_model.my_model.uuid + # (Customize configuration variables here if needed) +} +``` + +Create integrations, for instance: + +```text +resource "juju_integration" "cloudflared-route" { + model_uuid = data.juju_model.my_model.uuid + application { + name = module.cloudflare_configurator.application.name + endpoint = module.cloudflare_configurator.provides.cloudflared_route.endpoint + } + application { + name = "cloudflared" + endpoint = "cloudflared-route" + } +} +``` + +The complete list of available integrations can be found [in the Integrations tab][cloudflare-configurator-integrations]. + + + + + +[Terraform]: https://www.terraform.io/ +[Terraform Juju provider]: https://registry.terraform.io/providers/juju/juju/latest +[Juju]: https://juju.is +[cloudflare-configurator-integrations]: https://charmhub.io/cloudflare-configurator/integrations diff --git a/terraform/charm/cloudflare_configurator/main.tf b/terraform/charm/cloudflare_configurator/main.tf new file mode 100644 index 0000000..87c2a21 --- /dev/null +++ b/terraform/charm/cloudflare_configurator/main.tf @@ -0,0 +1,18 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +resource "juju_application" "cloudflare_configurator" { + name = var.app_name + model_uuid = var.model_uuid + + charm { + name = "cloudflare-configurator" + channel = var.channel + revision = var.revision + base = var.base + } + + config = var.config + constraints = var.constraints + units = var.units +} diff --git a/terraform/charm/cloudflare_configurator/outputs.tf b/terraform/charm/cloudflare_configurator/outputs.tf new file mode 100644 index 0000000..7a0257c --- /dev/null +++ b/terraform/charm/cloudflare_configurator/outputs.tf @@ -0,0 +1,28 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +output "application" { + description = "The deployed application object." + value = juju_application.cloudflare_configurator +} + +output "provides" { + description = "Map of the provided integration endpoints." + value = { + cloudflared_route = { + kind = "endpoint" + name = juju_application.cloudflare_configurator.name + endpoint = "cloudflared-route" + } + ingress = { + kind = "endpoint" + name = juju_application.cloudflare_configurator.name + endpoint = "ingress" + } + } +} + +output "requires" { + description = "Map of the required integration endpoints." + value = {} +} diff --git a/terraform/charm/cloudflare_configurator/terraform.tf b/terraform/charm/cloudflare_configurator/terraform.tf new file mode 100644 index 0000000..af5d151 --- /dev/null +++ b/terraform/charm/cloudflare_configurator/terraform.tf @@ -0,0 +1,12 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +terraform { + required_version = "~> 1.12" + required_providers { + juju = { + source = "juju/juju" + version = ">= 1.0, < 3.0" + } + } +} diff --git a/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl b/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl new file mode 100644 index 0000000..11ccf9f --- /dev/null +++ b/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl @@ -0,0 +1,63 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +variables { + app_name = "cloudflare-configurator" + base = "ubuntu@24.04" + channel = "latest/edge" + config = {} + constraints = "arch=amd64" + model_uuid = "00000000-0000-0000-0000-000000000001" + revision = 10 + units = 2 +} + +mock_provider "juju" { + mock_resource "juju_application" { + override_during = plan + defaults = { + id = "cloudflare-configurator" + model_type = "iaas" + } + } +} + +run "deploys_application" { + command = plan + + assert { + condition = output.application.name == "cloudflare-configurator" && output.application.model_uuid == "00000000-0000-0000-0000-000000000001" && output.application.charm[0].name == "cloudflare-configurator" && output.application.charm[0].channel == "latest/edge" && output.application.charm[0].base == "ubuntu@24.04" && output.application.charm[0].revision == 10 + error_message = "application identity did not match the expected deployment" + } + + assert { + condition = output.application.constraints == "arch=amd64" + error_message = "application constraints did not match expected" + } + + assert { + condition = output.application.units == 2 + error_message = "application units did not match expected" + } + + assert { + condition = output.provides == { + cloudflared_route = { + kind = "endpoint" + name = "cloudflare-configurator" + endpoint = "cloudflared-route" + } + ingress = { + kind = "endpoint" + name = "cloudflare-configurator" + endpoint = "ingress" + } + } + error_message = "provides outputs did not match expected structure" + } + + assert { + condition = output.requires == {} + error_message = "requires outputs did not match expected structure" + } +} diff --git a/terraform/charm/cloudflare_configurator/variables.tf b/terraform/charm/cloudflare_configurator/variables.tf new file mode 100644 index 0000000..2339b2f --- /dev/null +++ b/terraform/charm/cloudflare_configurator/variables.tf @@ -0,0 +1,52 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +variable "app_name" { + description = "Name of the application in the Juju model." + type = string + default = "cloudflare-configurator" + nullable = false +} + +variable "base" { + description = "The operating system on which to deploy." + type = string + default = null +} + +variable "channel" { + description = "The channel to use when deploying a charm." + type = string + default = "latest/edge" + nullable = false +} + +variable "config" { + description = "Application config. Details about available options can be found at https://charmhub.io/cloudflare-configurator/configurations" + type = map(string) + 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" { + description = "Revision number of the charm." + type = number + default = null +} + +variable "units" { + description = "The number of units to deploy." + type = number + default = 1 +} diff --git a/terraform/charm/cloudflared/MAJOR_VERSION b/terraform/charm/cloudflared/MAJOR_VERSION new file mode 120000 index 0000000..a80b14b --- /dev/null +++ b/terraform/charm/cloudflared/MAJOR_VERSION @@ -0,0 +1 @@ +../../MAJOR_VERSION \ No newline at end of file diff --git a/terraform/charm/cloudflared/README.md b/terraform/charm/cloudflared/README.md new file mode 100644 index 0000000..43078ac --- /dev/null +++ b/terraform/charm/cloudflared/README.md @@ -0,0 +1,64 @@ + +# Cloudflared Terraform module + + +This folder contains a base [Terraform][Terraform] module for the cloudflared charm. + +The module uses the [Terraform Juju provider][Terraform Juju provider] to model the charm +deployment onto any machine environment managed by [Juju][Juju]. + +## Module structure + +- **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 UUID, channel or application name). +- **outputs.tf** - Integrates the module with other Terraform modules, primarily + by defining potential integration endpoints (charm integrations), but also by exposing + the deployed Juju application. +- **terraform.tf** - Defines the Terraform and Juju provider versions. + +## Using cloudflared base module in higher level modules + +If you want to use the `cloudflared` base module as part of your Terraform module, import it +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" { + name = "my-model" +} + +module "cloudflared" { + source = "git::https://github.com/canonical/cloudflared-operators//terraform/charm/cloudflared?ref=tf-1.0.0" + + model_uuid = data.juju_model.my_model.uuid + # (Customize configuration variables here if needed) +} +``` + +Create integrations, for instance: + +```text +resource "juju_integration" "cloudflared-route" { + model_uuid = data.juju_model.my_model.uuid + application { + name = module.cloudflared.application.name + endpoint = module.cloudflared.requires.cloudflared_route.endpoint + } + application { + name = "cloudflare-configurator" + endpoint = "cloudflared-route" + } +} +``` + +The complete list of available integrations can be found [in the Integrations tab][cloudflared-integrations]. + + + + + +[Terraform]: https://www.terraform.io/ +[Terraform Juju provider]: https://registry.terraform.io/providers/juju/juju/latest +[Juju]: https://juju.is +[cloudflared-integrations]: https://charmhub.io/cloudflared/integrations diff --git a/terraform/charm/cloudflared/main.tf b/terraform/charm/cloudflared/main.tf new file mode 100644 index 0000000..05c4116 --- /dev/null +++ b/terraform/charm/cloudflared/main.tf @@ -0,0 +1,17 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +resource "juju_application" "cloudflared" { + name = var.app_name + model_uuid = var.model_uuid + + charm { + name = "cloudflared" + channel = var.channel + revision = var.revision + base = var.base + } + + config = var.config + constraints = var.constraints +} diff --git a/terraform/charm/cloudflared/outputs.tf b/terraform/charm/cloudflared/outputs.tf new file mode 100644 index 0000000..bacd527 --- /dev/null +++ b/terraform/charm/cloudflared/outputs.tf @@ -0,0 +1,34 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +output "application" { + description = "The deployed application object." + value = juju_application.cloudflared +} + +output "provides" { + description = "Map of the provided integration endpoints." + value = { + cos_agent = { + kind = "endpoint" + name = juju_application.cloudflared.name + endpoint = "cos-agent" + } + } +} + +output "requires" { + description = "Map of the required integration endpoints." + value = { + cloudflared_route = { + kind = "endpoint" + name = juju_application.cloudflared.name + endpoint = "cloudflared-route" + } + juju_info = { + kind = "endpoint" + name = juju_application.cloudflared.name + endpoint = "juju-info" + } + } +} diff --git a/terraform/charm/cloudflared/terraform.tf b/terraform/charm/cloudflared/terraform.tf new file mode 100644 index 0000000..af5d151 --- /dev/null +++ b/terraform/charm/cloudflared/terraform.tf @@ -0,0 +1,12 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +terraform { + required_version = "~> 1.12" + required_providers { + juju = { + source = "juju/juju" + version = ">= 1.0, < 3.0" + } + } +} diff --git a/terraform/charm/cloudflared/tests/main.tftest.hcl b/terraform/charm/cloudflared/tests/main.tftest.hcl new file mode 100644 index 0000000..14109b2 --- /dev/null +++ b/terraform/charm/cloudflared/tests/main.tftest.hcl @@ -0,0 +1,63 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +variables { + app_name = "cloudflared" + base = "ubuntu@24.04" + channel = "latest/edge" + config = {} + constraints = "arch=amd64" + model_uuid = "00000000-0000-0000-0000-000000000001" + revision = 10 +} + +mock_provider "juju" { + mock_resource "juju_application" { + override_during = plan + defaults = { + id = "cloudflared" + model_type = "iaas" + } + } +} + +run "deploys_application" { + command = plan + + assert { + condition = output.application.name == "cloudflared" && output.application.model_uuid == "00000000-0000-0000-0000-000000000001" && output.application.charm[0].name == "cloudflared" && output.application.charm[0].channel == "latest/edge" && output.application.charm[0].base == "ubuntu@24.04" && output.application.charm[0].revision == 10 + error_message = "application identity did not match the expected deployment" + } + + assert { + condition = output.application.constraints == "arch=amd64" + error_message = "application constraints did not match expected" + } + + assert { + condition = output.provides == { + cos_agent = { + kind = "endpoint" + name = "cloudflared" + endpoint = "cos-agent" + } + } + error_message = "provides outputs did not match expected structure" + } + + assert { + condition = output.requires == { + cloudflared_route = { + kind = "endpoint" + name = "cloudflared" + endpoint = "cloudflared-route" + } + juju_info = { + kind = "endpoint" + name = "cloudflared" + endpoint = "juju-info" + } + } + error_message = "requires outputs did not match expected structure" + } +} diff --git a/terraform/charm/cloudflared/variables.tf b/terraform/charm/cloudflared/variables.tf new file mode 100644 index 0000000..5826577 --- /dev/null +++ b/terraform/charm/cloudflared/variables.tf @@ -0,0 +1,46 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +variable "app_name" { + description = "Name of the application in the Juju model." + type = string + default = "cloudflared" + nullable = false +} + +variable "base" { + description = "The operating system on which to deploy." + type = string + default = null +} + +variable "channel" { + description = "The channel to use when deploying a charm." + type = string + default = "latest/stable" + nullable = false +} + +variable "config" { + description = "Application config. Details about available options can be found at https://charmhub.io/cloudflared/configurations" + type = map(string) + 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" { + description = "Revision number of the charm." + type = number + default = null +} From 10f3741292bd2dfdb476521b274b5012982ecb07 Mon Sep 17 00:00:00 2001 From: Weii Wang Date: Wed, 14 Oct 2026 20:52:43 +0800 Subject: [PATCH 2/4] chore: align terraform modules with falco-operators conventions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/generate_terraform_docs.yaml | 8 +-- .../terraform_modules_compliance.yaml | 4 +- .../workflows/terraform_modules_release.yaml | 21 +++++-- .github/workflows/test_terraform_modules.yaml | 9 ++- docs/release-notes/artifacts/pr0030.yaml | 17 +++++ .../charm/cloudflare_configurator/README.md | 4 +- .../cloudflare_configurator/terraform.tf | 2 +- .../tests/main.tftest.hcl | 62 +++++------------- .../tests/setup/main.tf | 22 +++++++ terraform/charm/cloudflared/README.md | 4 +- terraform/charm/cloudflared/terraform.tf | 2 +- .../charm/cloudflared/tests/main.tftest.hcl | 63 ++++++------------- .../charm/cloudflared/tests/setup/main.tf | 22 +++++++ 13 files changed, 130 insertions(+), 110 deletions(-) create mode 100644 docs/release-notes/artifacts/pr0030.yaml create mode 100644 terraform/charm/cloudflare_configurator/tests/setup/main.tf create mode 100644 terraform/charm/cloudflared/tests/setup/main.tf diff --git a/.github/workflows/generate_terraform_docs.yaml b/.github/workflows/generate_terraform_docs.yaml index 41578be..d0c6788 100644 --- a/.github/workflows/generate_terraform_docs.yaml +++ b/.github/workflows/generate_terraform_docs.yaml @@ -8,14 +8,14 @@ name: Update Terraform documentation on: pull_request: paths: - - '**/terraform/**' - - '.github/workflows/generate_terraform_docs.yaml' + - "terraform/**" + - ".github/workflows/generate_terraform_docs.yaml" push: branches: - main paths: - - '**/terraform/**' - - '.github/workflows/generate_terraform_docs.yaml' + - "terraform/**" + - ".github/workflows/generate_terraform_docs.yaml" permissions: contents: write diff --git a/.github/workflows/terraform_modules_compliance.yaml b/.github/workflows/terraform_modules_compliance.yaml index b9eca7f..1fc179f 100644 --- a/.github/workflows/terraform_modules_compliance.yaml +++ b/.github/workflows/terraform_modules_compliance.yaml @@ -7,8 +7,8 @@ on: workflow_dispatch: pull_request: paths: - - '**/terraform/**' - - '.github/workflows/terraform_modules_compliance.yaml' + - "**/terraform/**" + - ".github/workflows/terraform_modules_compliance.yaml" jobs: terraform-compliance: diff --git a/.github/workflows/terraform_modules_release.yaml b/.github/workflows/terraform_modules_release.yaml index 1ef660b..1b030d7 100644 --- a/.github/workflows/terraform_modules_release.yaml +++ b/.github/workflows/terraform_modules_release.yaml @@ -2,21 +2,32 @@ # See LICENSE file for licensing details. name: Terraform modules release + on: workflow_dispatch: pull_request: paths: - - '**/terraform/**' - - '.github/workflows/terraform_modules_release.yaml' + - "terraform/**" + - ".github/workflows/terraform_modules_release.yaml" push: branches: - main paths: - - '**/terraform/**' - - '.github/workflows/terraform_modules_release.yaml' + - "terraform/**" + - ".github/workflows/terraform_modules_release.yaml" jobs: - terraform: + cloudflared-terraform: + uses: canonical/operator-workflows/.github/workflows/terraform_modules_release.yaml@030c5b20a0bb880dcf1ed326bff0fc16b6eb6ac1 # main + permissions: + contents: write + with: + tag-prefix: cloudflared-tf- + major-version-file: terraform/charm/cloudflared/MAJOR_VERSION + cloudflare-configurator-terraform: uses: canonical/operator-workflows/.github/workflows/terraform_modules_release.yaml@030c5b20a0bb880dcf1ed326bff0fc16b6eb6ac1 # main permissions: contents: write + with: + tag-prefix: cloudflare-configurator-tf- + major-version-file: terraform/charm/cloudflare_configurator/MAJOR_VERSION diff --git a/.github/workflows/test_terraform_modules.yaml b/.github/workflows/test_terraform_modules.yaml index e21f64e..6ec389f 100644 --- a/.github/workflows/test_terraform_modules.yaml +++ b/.github/workflows/test_terraform_modules.yaml @@ -7,15 +7,14 @@ on: workflow_dispatch: pull_request: paths: - - '**/terraform/**' - - '.github/workflows/test_terraform_modules.yaml' + - "**/terraform/**" + - ".github/workflows/test_terraform_modules.yaml" jobs: - terraform-tests: + terraform-tests-lxd: uses: canonical/operator-workflows/.github/workflows/terraform_modules_test.yaml@030c5b20a0bb880dcf1ed326bff0fc16b6eb6ac1 # main - permissions: - contents: read secrets: inherit with: + k8s-controller: false lxd-controller: true terraform-directories: '["terraform/charm/cloudflared", "terraform/charm/cloudflare_configurator"]' diff --git a/docs/release-notes/artifacts/pr0030.yaml b/docs/release-notes/artifacts/pr0030.yaml new file mode 100644 index 0000000..e02a1bf --- /dev/null +++ b/docs/release-notes/artifacts/pr0030.yaml @@ -0,0 +1,17 @@ +version_schema: 2 + +changes: + - title: Added CC008-compliant Terraform modules + author: weiiwang01 + type: minor + description: > + Added Terraform modules for the cloudflared and cloudflare-configurator + charms following the CC008 Charm Terraform Module standard, together with + the Terraform test, compliance, release and documentation workflows. + urls: + pr: + - https://github.com/canonical/cloudflared-operators/pull/30 + related_doc: + related_issue: + visibility: public + highlight: false diff --git a/terraform/charm/cloudflare_configurator/README.md b/terraform/charm/cloudflare_configurator/README.md index 73beff2..6b1cde4 100644 --- a/terraform/charm/cloudflare_configurator/README.md +++ b/terraform/charm/cloudflare_configurator/README.md @@ -29,7 +29,7 @@ data "juju_model" "my_model" { } module "cloudflare_configurator" { - source = "git::https://github.com/canonical/cloudflared-operators//terraform/charm/cloudflare_configurator?ref=tf-1.0.0" + source = "git::https://github.com/canonical/cloudflared-operators//terraform/charm/cloudflare_configurator?ref=cloudflare-configurator-tf-1.0.0" model_uuid = data.juju_model.my_model.uuid # (Customize configuration variables here if needed) @@ -55,8 +55,10 @@ resource "juju_integration" "cloudflared-route" { The complete list of available integrations can be found [in the Integrations tab][cloudflare-configurator-integrations]. + + [Terraform]: https://www.terraform.io/ [Terraform Juju provider]: https://registry.terraform.io/providers/juju/juju/latest diff --git a/terraform/charm/cloudflare_configurator/terraform.tf b/terraform/charm/cloudflare_configurator/terraform.tf index af5d151..637cc0a 100644 --- a/terraform/charm/cloudflare_configurator/terraform.tf +++ b/terraform/charm/cloudflare_configurator/terraform.tf @@ -6,7 +6,7 @@ terraform { required_providers { juju = { source = "juju/juju" - version = ">= 1.0, < 3.0" + version = "~> 1.0" } } } diff --git a/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl b/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl index 11ccf9f..e47d320 100644 --- a/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl +++ b/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl @@ -1,63 +1,33 @@ # Copyright 2026 Canonical Ltd. # See LICENSE file for licensing details. -variables { - app_name = "cloudflare-configurator" - base = "ubuntu@24.04" - channel = "latest/edge" - config = {} - constraints = "arch=amd64" - model_uuid = "00000000-0000-0000-0000-000000000001" - revision = 10 - units = 2 -} - -mock_provider "juju" { - mock_resource "juju_application" { - override_during = plan - defaults = { - id = "cloudflare-configurator" - model_type = "iaas" - } +run "setup_tests" { + module { + source = "./tests/setup" } } -run "deploys_application" { - command = plan - - assert { - condition = output.application.name == "cloudflare-configurator" && output.application.model_uuid == "00000000-0000-0000-0000-000000000001" && output.application.charm[0].name == "cloudflare-configurator" && output.application.charm[0].channel == "latest/edge" && output.application.charm[0].base == "ubuntu@24.04" && output.application.charm[0].revision == 10 - error_message = "application identity did not match the expected deployment" - } - - assert { - condition = output.application.constraints == "arch=amd64" - error_message = "application constraints did not match expected" +run "basic_deploy" { + variables { + model_uuid = run.setup_tests.model_uuid + base = "ubuntu@24.04" + channel = "latest/edge" + # renovate: depName="cloudflare-configurator" + revision = 11 } assert { - condition = output.application.units == 2 - error_message = "application units did not match expected" + condition = output.application.name == "cloudflare-configurator" + error_message = "cloudflare-configurator application name did not match expected" } assert { - condition = output.provides == { - cloudflared_route = { - kind = "endpoint" - name = "cloudflare-configurator" - endpoint = "cloudflared-route" - } - ingress = { - kind = "endpoint" - name = "cloudflare-configurator" - endpoint = "ingress" - } - } - error_message = "provides outputs did not match expected structure" + condition = output.provides.value.endpoint == "cloudflared-route" + error_message = "cloudflare-configurator module should provide 'provides.value' output" } assert { - condition = output.requires == {} - error_message = "requires outputs did not match expected structure" + condition = output.provides.ingress.endpoint == "ingress" + error_message = "cloudflare-configurator module should provide 'provides.ingress' output" } } diff --git a/terraform/charm/cloudflare_configurator/tests/setup/main.tf b/terraform/charm/cloudflare_configurator/tests/setup/main.tf new file mode 100644 index 0000000..0236105 --- /dev/null +++ b/terraform/charm/cloudflare_configurator/tests/setup/main.tf @@ -0,0 +1,22 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +terraform { + required_version = "~> 1.12" + required_providers { + juju = { + source = "juju/juju" + version = "~> 1.0" + } + } +} + +provider "juju" {} + +resource "juju_model" "test_model" { + name = "tf-testing-${formatdate("YYYYMMDDhhmmss", timestamp())}" +} + +output "model_uuid" { + value = juju_model.test_model.uuid +} diff --git a/terraform/charm/cloudflared/README.md b/terraform/charm/cloudflared/README.md index 43078ac..381f449 100644 --- a/terraform/charm/cloudflared/README.md +++ b/terraform/charm/cloudflared/README.md @@ -29,7 +29,7 @@ data "juju_model" "my_model" { } module "cloudflared" { - source = "git::https://github.com/canonical/cloudflared-operators//terraform/charm/cloudflared?ref=tf-1.0.0" + source = "git::https://github.com/canonical/cloudflared-operators//terraform/charm/cloudflared?ref=cloudflared-tf-1.0.0" model_uuid = data.juju_model.my_model.uuid # (Customize configuration variables here if needed) @@ -55,8 +55,10 @@ resource "juju_integration" "cloudflared-route" { The complete list of available integrations can be found [in the Integrations tab][cloudflared-integrations]. + + [Terraform]: https://www.terraform.io/ [Terraform Juju provider]: https://registry.terraform.io/providers/juju/juju/latest diff --git a/terraform/charm/cloudflared/terraform.tf b/terraform/charm/cloudflared/terraform.tf index af5d151..637cc0a 100644 --- a/terraform/charm/cloudflared/terraform.tf +++ b/terraform/charm/cloudflared/terraform.tf @@ -6,7 +6,7 @@ terraform { required_providers { juju = { source = "juju/juju" - version = ">= 1.0, < 3.0" + version = "~> 1.0" } } } diff --git a/terraform/charm/cloudflared/tests/main.tftest.hcl b/terraform/charm/cloudflared/tests/main.tftest.hcl index 14109b2..288bccf 100644 --- a/terraform/charm/cloudflared/tests/main.tftest.hcl +++ b/terraform/charm/cloudflared/tests/main.tftest.hcl @@ -1,63 +1,38 @@ # Copyright 2026 Canonical Ltd. # See LICENSE file for licensing details. -variables { - app_name = "cloudflared" - base = "ubuntu@24.04" - channel = "latest/edge" - config = {} - constraints = "arch=amd64" - model_uuid = "00000000-0000-0000-0000-000000000001" - revision = 10 -} - -mock_provider "juju" { - mock_resource "juju_application" { - override_during = plan - defaults = { - id = "cloudflared" - model_type = "iaas" - } +run "setup_tests" { + module { + source = "./tests/setup" } } -run "deploys_application" { - command = plan +run "basic_deploy" { + variables { + model_uuid = run.setup_tests.model_uuid + base = "ubuntu@24.04" + channel = "latest/edge" + # renovate: depName="cloudflared" + revision = 40 + } assert { - condition = output.application.name == "cloudflared" && output.application.model_uuid == "00000000-0000-0000-0000-000000000001" && output.application.charm[0].name == "cloudflared" && output.application.charm[0].channel == "latest/edge" && output.application.charm[0].base == "ubuntu@24.04" && output.application.charm[0].revision == 10 - error_message = "application identity did not match the expected deployment" + condition = output.application.name == "cloudflared" + error_message = "cloudflared application name did not match expected" } assert { - condition = output.application.constraints == "arch=amd64" - error_message = "application constraints did not match expected" + condition = output.provides.value.endpoint == "cos-agent" + error_message = "cloudflared module should provide 'provides.value' output" } assert { - condition = output.provides == { - cos_agent = { - kind = "endpoint" - name = "cloudflared" - endpoint = "cos-agent" - } - } - error_message = "provides outputs did not match expected structure" + condition = output.requires.value.endpoint == "cloudflared-route" + error_message = "cloudflared module should provide 'requires.value' output" } assert { - condition = output.requires == { - cloudflared_route = { - kind = "endpoint" - name = "cloudflared" - endpoint = "cloudflared-route" - } - juju_info = { - kind = "endpoint" - name = "cloudflared" - endpoint = "juju-info" - } - } - error_message = "requires outputs did not match expected structure" + condition = output.requires.juju_info.endpoint == "juju-info" + error_message = "cloudflared module should provide 'requires.juju_info' output" } } diff --git a/terraform/charm/cloudflared/tests/setup/main.tf b/terraform/charm/cloudflared/tests/setup/main.tf new file mode 100644 index 0000000..0236105 --- /dev/null +++ b/terraform/charm/cloudflared/tests/setup/main.tf @@ -0,0 +1,22 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +terraform { + required_version = "~> 1.12" + required_providers { + juju = { + source = "juju/juju" + version = "~> 1.0" + } + } +} + +provider "juju" {} + +resource "juju_model" "test_model" { + name = "tf-testing-${formatdate("YYYYMMDDhhmmss", timestamp())}" +} + +output "model_uuid" { + value = juju_model.test_model.uuid +} From 5283091a22cbba3b0d562dd611fb037be78efa1f Mon Sep 17 00:00:00 2001 From: Weii Wang Date: Wed, 14 Oct 2026 21:51:32 +0800 Subject: [PATCH 3/4] fix: correct terraform test assertions and serialize per-charm releases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/artifacts/pr0030.yaml | 3 +++ .../charm/cloudflare_configurator/tests/main.tftest.hcl | 4 ++-- terraform/charm/cloudflared/tests/main.tftest.hcl | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/release-notes/artifacts/pr0030.yaml b/docs/release-notes/artifacts/pr0030.yaml index e02a1bf..037b686 100644 --- a/docs/release-notes/artifacts/pr0030.yaml +++ b/docs/release-notes/artifacts/pr0030.yaml @@ -1,3 +1,6 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + version_schema: 2 changes: diff --git a/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl b/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl index e47d320..4147ac2 100644 --- a/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl +++ b/terraform/charm/cloudflare_configurator/tests/main.tftest.hcl @@ -22,8 +22,8 @@ run "basic_deploy" { } assert { - condition = output.provides.value.endpoint == "cloudflared-route" - error_message = "cloudflare-configurator module should provide 'provides.value' output" + condition = output.provides.cloudflared_route.endpoint == "cloudflared-route" + error_message = "cloudflare-configurator module should provide 'provides.cloudflared_route' output" } assert { diff --git a/terraform/charm/cloudflared/tests/main.tftest.hcl b/terraform/charm/cloudflared/tests/main.tftest.hcl index 288bccf..ea39e70 100644 --- a/terraform/charm/cloudflared/tests/main.tftest.hcl +++ b/terraform/charm/cloudflared/tests/main.tftest.hcl @@ -22,13 +22,13 @@ run "basic_deploy" { } assert { - condition = output.provides.value.endpoint == "cos-agent" - error_message = "cloudflared module should provide 'provides.value' output" + condition = output.provides.cos_agent.endpoint == "cos-agent" + error_message = "cloudflared module should provide 'provides.cos_agent' output" } assert { - condition = output.requires.value.endpoint == "cloudflared-route" - error_message = "cloudflared module should provide 'requires.value' output" + condition = output.requires.cloudflared_route.endpoint == "cloudflared-route" + error_message = "cloudflared module should provide 'requires.cloudflared_route' output" } assert { From 2b72c988a8e31acbe0f05de889ede30575aba4cc Mon Sep 17 00:00:00 2001 From: Weii Wang Date: Thu, 15 Oct 2026 13:35:43 +0800 Subject: [PATCH 4/4] docs: point the Juju links at canonical.com/juju Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- terraform/charm/cloudflare_configurator/README.md | 2 +- terraform/charm/cloudflared/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/charm/cloudflare_configurator/README.md b/terraform/charm/cloudflare_configurator/README.md index 6b1cde4..db4a400 100644 --- a/terraform/charm/cloudflare_configurator/README.md +++ b/terraform/charm/cloudflare_configurator/README.md @@ -62,5 +62,5 @@ The complete list of available integrations can be found [in the Integrations ta [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 [cloudflare-configurator-integrations]: https://charmhub.io/cloudflare-configurator/integrations diff --git a/terraform/charm/cloudflared/README.md b/terraform/charm/cloudflared/README.md index 381f449..ce7a962 100644 --- a/terraform/charm/cloudflared/README.md +++ b/terraform/charm/cloudflared/README.md @@ -62,5 +62,5 @@ The complete list of available integrations can be found [in the Integrations ta [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 [cloudflared-integrations]: https://charmhub.io/cloudflared/integrations