From 076624531308bb5d0592c7882e20e7c2ade715db Mon Sep 17 00:00:00 2001 From: Rene Orozco Martinez Date: Thu, 5 Mar 2026 18:23:14 -0700 Subject: [PATCH 1/7] breaking(terraform-server): update testflinger terraform module --- server/terraform/.gitignore | 20 --- server/terraform/README.md | 126 ++++++------------ server/terraform/cloud-init.yaml | 30 ----- server/terraform/locals.tf | 0 server/terraform/main.tf | 80 +---------- server/terraform/outputs.tf | 22 +++ server/terraform/providers.tf | 0 .../terraform/{versions.tf => terraform.tf} | 2 +- server/terraform/variables.tf | 105 ++++----------- 9 files changed, 96 insertions(+), 289 deletions(-) delete mode 100644 server/terraform/.gitignore delete mode 100644 server/terraform/cloud-init.yaml create mode 100644 server/terraform/locals.tf create mode 100644 server/terraform/outputs.tf create mode 100644 server/terraform/providers.tf rename server/terraform/{versions.tf => terraform.tf} (75%) diff --git a/server/terraform/.gitignore b/server/terraform/.gitignore deleted file mode 100644 index f2b3b69bd..000000000 --- a/server/terraform/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -.terraform/* - -*.tfstate -*.tfstate.* - -crash.log -crash.*.log - -*.tfvars -*.tfvars.json - -override.tf -override.tf.json -*_override.tf -*_override.tf.json - -.terraform.tfstate.lock.info - -.terraformrc -terraform.rc diff --git a/server/terraform/README.md b/server/terraform/README.md index 0c95ec87c..c52631de3 100644 --- a/server/terraform/README.md +++ b/server/terraform/README.md @@ -1,103 +1,57 @@ -# Juju deployment +# Terraform module for Testflinger Server -Local Juju and charm deployment via microk8s and terraform. +This is a Terraform module facilitating the deployment of the Testflinger Server charm, +using the [Terraform Juju provider][juju-provider]. For more information, refer +to the provider [documentation][juju-provider-docs]. -## Setup a juju environment +## Requirements -It is recommended to install the pre-requisites on a VM rather than your host machine. To do so, first install multipass: +This module requires a Juju model UUID to be available. Refer to the [usage section](#usage) +below for more details. -```bash -sudo snap install multipass -``` - -Then launch a new VM instance using (this will take a while): - -```bash -multipass launch noble --disk 50G --memory 4G --cpus 2 --name testflinger-juju --mount /path/to/testflinger:/home/ubuntu/testflinger --cloud-init /path/to/testflinger/server/terraform/cloud-init.yaml --timeout 1200 -``` - -Feel free to increase the storage, memory, cpu limits or change the VM name. - -Note that the initialization may timeout. That's fine as long as the setup actually completed. You can tell that the setup completed by checking if there are juju models created already: - -```bash -multipass exec testflinger-juju -- juju models -``` - -## Initialize project's terraform - -Now that everything has been set up, you can initialize the project's terraform. - -In the terraform directory on your host machine, run: - -```bash -multipass exec testflinger-juju -- terraform init -``` - -## Deploy everything - -In the terraform directory on your host machine, run: - -```bash -multipass exec testflinger-juju -- terraform apply -auto-approve -``` +## API -Then wait for the deployment to settle and all the statuses to become active. You can watch the statuses via: +### Inputs -```bash -multipass exec testflinger-juju -- juju status --storage --relations --watch 5s -``` +The module offers the following configurable inputs: -## Connect to your deployment +| Name | Type | Description | Required | +| - | - | - | - | +| `app_name` | string | Name of the Testflinger server application | True | +| `base` | string | Operating system base to use for the Testflinger server charm | False | +| `channel` | string | Channel to use for the charm | False | +| `config` | map(string) | Map of charm config options | False | +| `constraints` | string | Constraints to use for the agent host application | False | +| `model_uuid` | string | UUID of the Juju model to deploy into | True | +| `revision` | number | Revision of the charm to use | False | +| `units` | number | Number of units for the agent host application (maximum: 1) | False | -Look at the IPv4 addresses of your testflinger-juju vm through: -```bash -multipass info testflinger-juju -``` +### Outputs -One of these connect to the ingress enabled inside the VM. To figure out which one try the following command on each IP address until you get response: +| Name | Type | Description | +| - | - | - | +| `application` | object | The deployed application object | +| `provides` | map(string) | Map of provides integration endpoints | +| `requires` | map(string) | Map of requires integration endpoints | -```bash -curl --connect-to :: http://testflinger.local -``` +## Usage +This module is intended to be used as part of a higher-level module. +When defining one, users should ensure that Terraform is aware of the `model_uuid` dependency of the charm module. -Once you find the IP address add the following entry to your host machine's `/etc/hosts` file: - -```text - testflinger.local -``` +### Define a `data` source -After that you should be able to get to Testflinger frontend on your host machine's browser through the url `http://testflinger.local`. You should also be able to access the API through `http://testflinger.local/v1/`. +Define a `data` source and pass to the `model_uuid` input a reference to the `data.juju_model` resource's name. +This will enable Terraform to look for a `model_uuid` resource with a name attribute equal to the one provided, +and apply only if this is present. Otherwise, it will fail before applying anything. -## Teardown - -To take everything down you can start with terraform: - -```bash -multipass exec testflinger-juju -- terraform destroy -auto-approve +```hcl +data "juju_model" "agent-host" { + name = "" +} ``` -The above step can take a while and may even get stuck with some applications in error state. You can watch it through: - -```bash -multipass exec testflinger-juju -- juju status --storage --relations --watch 5s -``` +Pending TBD -To forcefully remove applications stuck in error state: - -```bash -multipass exec testflinger-juju -- juju remove-application --destroy-storage --force -``` - -Once everything is down and the juju model has been deleted you can stop the multipass VM: - -```bash -multipass stop testflinger-juju -``` - -Optionally, delete the VM: - -```bash -multipass delete --purge testflinger-juju -``` +[juju-provider]: https://github.com/juju/terraform-provider-juju/ +[juju-provider-docs]: https://registry.terraform.io/providers/juju/juju/latest/docs \ No newline at end of file diff --git a/server/terraform/cloud-init.yaml b/server/terraform/cloud-init.yaml deleted file mode 100644 index 57f96ecd0..000000000 --- a/server/terraform/cloud-init.yaml +++ /dev/null @@ -1,30 +0,0 @@ -#cloud-config -package_update: true -package_upgrade: true -package_reboot_if_required: true -snap: - commands: - - snap install lxd --channel=6/stable - - snap install juju --channel=3.6/stable - - snap install charmcraft --channel=3.x/stable --classic - - snap install terraform --classic - - snap install microk8s --channel=1.32-strict/stable - - snap alias microk8s.kubectl kubectl -runcmd: - - lxd init --auto - - adduser ubuntu snap_microk8s - - microk8s status --wait-ready - - microk8s enable dns - - microk8s enable hostpath-storage - - microk8s enable ingress - - sudo -i -u ubuntu snap run juju bootstrap microk8s microk8s-controller - - sudo -i -u ubuntu snap run juju bootstrap localhost localhost-controller - - | - # workaround to get the localhost-controller to connect to microk8s - server_url=$(microk8s config | grep server | awk '{print $2;}') - sed -i "s|server: .*|server: $server_url|g" /var/snap/microk8s/current/credentials/client.config - - sudo -i -u ubuntu snap run juju add-cloud microk8s --controller localhost-controller - - sudo -i -u ubuntu snap run juju add-model testflinger-dev-db localhost - - sudo -i -u ubuntu snap run juju deploy mongodb --channel 5/edge - - sudo -i -u ubuntu snap run juju offer testflinger-dev-db.mongodb:database mongodb - - sudo -i -u ubuntu snap run juju add-model testflinger-dev microk8s diff --git a/server/terraform/locals.tf b/server/terraform/locals.tf new file mode 100644 index 000000000..e69de29bb diff --git a/server/terraform/main.tf b/server/terraform/main.tf index a6bdda94b..3933561b2 100644 --- a/server/terraform/main.tf +++ b/server/terraform/main.tf @@ -1,80 +1,14 @@ resource "juju_application" "testflinger" { - name = "testflinger" - model = local.app_model - - units = var.application_units + name = var.app_name + model_uuid = var.model_uuid + constraints = var.constraints + units = var.units + config = var.config charm { name = "testflinger-k8s" - base = "ubuntu@22.04" - channel = local.channel + base = var.base + channel = var.channel revision = var.revision } - - config = { - external_hostname = var.external_ingress_hostname - max_pool_size = var.max_pool_size - jwt_signing_key = var.jwt_signing_key - testflinger_secrets_master_key = var.testflinger_secrets_master_key - http_proxy = var.http_proxy - https_proxy = var.https_proxy - no_proxy = var.no_proxy - } -} - -resource "juju_application" "ingress" { - name = "ingress" - model = local.app_model - trust = true - - charm { - name = "nginx-ingress-integrator" - channel = "latest/stable" - } - - config = { - tls-secret-name = var.tls_secret_name - whitelist-source-range = var.nginx_ingress_integrator_charm_whitelist_source_range - max-body-size = var.nginx_ingress_integrator_charm_max_body_size - } -} - -resource "juju_integration" "testflinger-database-relation" { - model = local.app_model - - application { - name = juju_application.testflinger.name - endpoint = "mongodb_client" - } - - application { - offer_url = var.db_offer - } -} - -resource "juju_integration" "testflinger_encryption_relation" { - model = local.app_model - - application { - name = juju_application.testflinger.name - endpoint = "mongodb_keyvault" - } - - application { - offer_url = var.encryption_offer - } } - -resource "juju_integration" "testflinger-ingress-relation" { - model = local.app_model - - application { - name = juju_application.testflinger.name - } - - application { - name = juju_application.ingress.name - } -} - - diff --git a/server/terraform/outputs.tf b/server/terraform/outputs.tf new file mode 100644 index 000000000..b75261c56 --- /dev/null +++ b/server/terraform/outputs.tf @@ -0,0 +1,22 @@ +output "application" { + description = "The deployed application" + value = juju_application.testflinger +} + +output "provides" { + description = "Map of provided integration endpoints" + value = { + grafana_dashboard = "grafana-dashboard" + metrics_endpoint = "metrics-endpoint" + } +} + +output "requires" { + description = "Map of requires integration endpoints" + value = { + mongodb_client = "mongodb_client" + mongodb_keyvault = "mongodb_client" + nginx_route = "nginx-route" + traefik_route = "traefik_route" + } +} diff --git a/server/terraform/providers.tf b/server/terraform/providers.tf new file mode 100644 index 000000000..e69de29bb diff --git a/server/terraform/versions.tf b/server/terraform/terraform.tf similarity index 75% rename from server/terraform/versions.tf rename to server/terraform/terraform.tf index c4e2edc16..9d78d4bf3 100644 --- a/server/terraform/versions.tf +++ b/server/terraform/terraform.tf @@ -1,7 +1,7 @@ terraform { required_providers { juju = { - version = "~> 0.17.0" + version = "~> 1.0" source = "juju/juju" } } diff --git a/server/terraform/variables.tf b/server/terraform/variables.tf index 51218fa2b..c0c03f48b 100644 --- a/server/terraform/variables.tf +++ b/server/terraform/variables.tf @@ -1,102 +1,49 @@ -variable "environment" { - description = "The environment to deploy to. When the \"revision\" variable is not set, the value of \"environment\" determines the channel to deploy from, either \"latest/stable\" (for production) or \"latest/edge\" channel otherwise." +variable "app_name" { + description = "Name of the Testflinger application to deploy" type = string - default = "dev" - validation { - condition = contains(["dev", "staging", "prod", "production"], var.environment) - error_message = "The environment must be one of 'dev', 'staging', or 'prod'." - } + default = "testflinger" } -variable "revision" { - description = "Revision of the charm to use" - type = number +variable "base" { + description = "Operating system base to use for the Testflinger charm" + type = string nullable = true default = null } -variable "external_ingress_hostname" { - description = "External hostname for the ingress" +variable "channel" { + description = "Channel to use for the Testflinger charm." type = string - default = "testflinger.local" + default = "latest/stable" } -variable "tls_secret_name" { - description = "Secret where the TLS certificate for ingress is stored" - type = string - default = "" +variable "config" { + description = "Map of charm config options" + type = map(string) + default = {} } -variable "db_offer" { - description = "Name of the juju offer for mongodb to use for the cross-model relation" +variable "constraints" { + description = "Constraints to apply to the Testflinger application" type = string - default = "admin/testflinger-dev-db.mongodb" -} - -variable "encryption_offer" { - description = "Name of the juju offer for encryption database to use for the cross-model relation" - type = string - default = "admin/testflinger-dev-db.mongodb" + nullable = true + default = null } -variable "nginx_ingress_integrator_charm_whitelist_source_range" { - description = "Allowed client IP source ranges. The value is a comma separated list of CIDRs." +variable "model_uuid" { + description = "UUID of the Juju model to deploy into" type = string - default = "" } -variable "nginx_ingress_integrator_charm_max_body_size" { - description = "Max allowed body-size (for file uploads) in megabytes, set to 0 to disable limits." - type = number - default = 20 -} - - -variable "application_units" { - description = "Number of units (pods) to start" +variable "revision" { + description = "Revision of the charm to use" type = number - default = 2 + nullable = true + default = null } -variable "max_pool_size" { - description = "Maximum number of concurrent connections to the database" +variable "units" { + description = "Number of units for the server application" type = number - default = 100 -} - -variable "jwt_signing_key" { - description = "The signing key for JWT tokens" - sensitive = true - type = string - default = "secret" -} - -variable "testflinger_secrets_master_key" { - description = "Master key for Testflinger secrets encryption" - type = string - sensitive = true - default = "" -} - -variable "http_proxy" { - description = "HTTP proxy for accessing external HTTP resources" - type = string - default = "" -} - -variable "https_proxy" { - description = "HTTPS proxy for accessing external HTTPS resources" - type = string - default = "" -} - -variable "no_proxy" { - description = "Resources that we should abe able to access bypassing proxy" - type = string - default = "localhost,127.0.0.1,::1" -} - -locals { - app_model = "testflinger-${var.environment}" - channel = var.revision == null ? (startswith(var.environment, "prod") ? "latest/stable" : "latest/edge") : null + default = 1 } From 5ce1ece65b1fda6cafdf550fe7480cd9fad98432 Mon Sep 17 00:00:00 2001 From: Rene Orozco Martinez Date: Thu, 5 Mar 2026 18:51:09 -0700 Subject: [PATCH 2/7] dev: update development modules --- server/terraform/CONTRIBUTING.md | 154 +++++++++++++++++++++++++++ server/terraform/README.md | 27 ++++- server/terraform/dev/.gitignore | 22 ++++ server/terraform/dev/cloud-init.yaml | 30 ++++++ server/terraform/dev/main.tf | 70 ++++++++++++ server/terraform/dev/terraform.tf | 8 ++ server/terraform/dev/variables.tf | 13 +++ 7 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 server/terraform/CONTRIBUTING.md create mode 100644 server/terraform/dev/.gitignore create mode 100644 server/terraform/dev/cloud-init.yaml create mode 100644 server/terraform/dev/main.tf create mode 100644 server/terraform/dev/terraform.tf create mode 100644 server/terraform/dev/variables.tf diff --git a/server/terraform/CONTRIBUTING.md b/server/terraform/CONTRIBUTING.md new file mode 100644 index 000000000..44034cb8b --- /dev/null +++ b/server/terraform/CONTRIBUTING.md @@ -0,0 +1,154 @@ +# Contributing to Testflinger Terraform modules + +This document outlines contribution guidelines specific to the Testflinger Server +Terraform module. + +To learn more about the general contribution guidelines for the Testflinger project, +refer to the [Testflinger contribution guide](../../CONTRIBUTING.md). + +## Pull Requests + +Any change to the terraform modules should be semantically versioned, for automation +to automatically tag your commits, you should consider the following patterns: +- `breaking(terraform-server):` - Creates a major release +- `feat(terraform-server):` - Creates a minor release + +By default, any commit without the above patterns will create a patch version bump. +Refer to [Terraform versioning guide][versioning-guide] to understand more about +semantic versioning Terraform modules. + +## Testflinger Server Terraform Deployment + +The following instructions are meant to provide developers a guide on how to +deploy a Testflinger Server by using [Terraform]. + +### Set up a Juju environment + +It is recommended to install the pre-requisites on a VM rather than your host +machine. To do so, first install [Multipass]: + +```shell +sudo snap install multipass +``` + +Then launch a new VM instance (this may take a while): + +```shell +multipass launch noble --disk 50G --memory 4G --cpus 2 --name testflinger-juju --mount /path/to/testflinger:/home/ubuntu/testflinger --cloud-init /path/to/testflinger/server/terraform/dev/cloud-init.yaml --timeout 1200 +``` + +Feel free to increase the storage, memory, CPU, or VM name. + +> [!NOTE] +> The initialization may time out. That's fine as long as the setup actually completes. +> You can tell that the setup completed by checking if the Juju models were created. + +Check that the models were created: + +```shell +multipass exec testflinger-juju -- juju models +``` + +### Initialize project's terraform + +Now that everything has been set up, you can initialize the project's terraform. +The following guide setups a higher-level module to configure the Testflinger +Server Charm deployment. + +Change your directory on your host machine to the terraform dev directory: + +```shell +cd /path/to/testflinger/server/terraform/dev +``` + +Then run: + +```shell +multipass exec testflinger-juju -- terraform init +``` + +### Set up variables + +Refer to the [README](README.md#api) for the full list of required variables. + +`terraform/dev` directory contains the required terraform files for deployment, +modify them accordingly with the necessary variables such as the JWT signing key +and secrets master key. + +### Deploy + +In the terraform directory on your host machine, run: + +```shell +multipass exec testflinger-juju -- terraform apply -auto-approve +``` + +Then wait for the deployment to settle and all the statuses to become active. +You can watch the statuses via: + +```shell +multipass exec testflinger-juju -- juju status --storage --relations --watch 5s +``` + +### Connect to your deployment + +Look at the IPv4 addresses of your testflinger-juju VM through: + +```shell +multipass info testflinger-juju +``` + +One of these connects to the ingress enabled inside the VM. To figure out which one, +try the following command on each IP address until you get a response: + +```shell +curl --connect-to :: http://testflinger.local/v1 +``` + +Once you find the IP address, add the following entry to your host machine's `/etc/hosts` file: + +```text + testflinger.local +``` + +After that you should be able to reach the Testflinger frontend on your host machine's +browser through `http://testflinger.local`. You should also be able to access the API +through `http://testflinger.local/v1/`. + +### Teardown + +To take everything down, you can start with terraform: + +```shell +multipass exec testflinger-juju -- terraform destroy -auto-approve +``` + +The above step can take a while and may even get stuck with some applications +in error state. You can watch it through: + +```shell +multipass exec testflinger-juju -- juju status --storage --relations --watch 5s +``` + +To forcefully remove applications stuck in error state: + +```shell +multipass exec testflinger-juju -- juju remove-application --destroy-storage --force +``` + +Once everything is down and the juju model has been deleted you can stop the +multipass VM: + +```shell +multipass stop testflinger-juju +``` + +Optionally, delete the VM: + +```shell +multipass delete --purge testflinger-juju +``` + +[Multipass]: https://canonical.com/multipass +[Terraform]: https://developer.hashicorp.com/terraform +[versioning-guide]: https://developer.hashicorp.com/terraform/plugin/best-practices/versioning diff --git a/server/terraform/README.md b/server/terraform/README.md index c52631de3..c66e1d86e 100644 --- a/server/terraform/README.md +++ b/server/terraform/README.md @@ -21,10 +21,10 @@ The module offers the following configurable inputs: | `base` | string | Operating system base to use for the Testflinger server charm | False | | `channel` | string | Channel to use for the charm | False | | `config` | map(string) | Map of charm config options | False | -| `constraints` | string | Constraints to use for the agent host application | False | +| `constraints` | string | Constraints to use for the Testflinger server application | False | | `model_uuid` | string | UUID of the Juju model to deploy into | True | | `revision` | number | Revision of the charm to use | False | -| `units` | number | Number of units for the agent host application (maximum: 1) | False | +| `units` | number | Number of units for the Testflinger server application | False | ### Outputs @@ -46,12 +46,31 @@ This will enable Terraform to look for a `model_uuid` resource with a name attri and apply only if this is present. Otherwise, it will fail before applying anything. ```hcl -data "juju_model" "agent-host" { +data "juju_model" "testflinger_dev" { name = "" } ``` -Pending TBD +### Create module + +Then call the module: + +```hcl +module "testflinger" { + source = "git::https://github.com/canonical/testflinger.git//server/terraform?ref=" + model_uuid = data.juju_model.testflinger_dev.uuid + app_name = "" + config = { + external_hostname = "testflinger.local" + http_proxy = "" + https_proxy = "" + no_proxy = "localhost,127.0.0.1,::1" + max_pool_size = "100" + jwt_signing_key = var.jwt_signing_key + testflinger_secrets_master_key = var.testflinger_secrets_master_key + } +} +``` [juju-provider]: https://github.com/juju/terraform-provider-juju/ [juju-provider-docs]: https://registry.terraform.io/providers/juju/juju/latest/docs \ No newline at end of file diff --git a/server/terraform/dev/.gitignore b/server/terraform/dev/.gitignore new file mode 100644 index 000000000..b468dd82c --- /dev/null +++ b/server/terraform/dev/.gitignore @@ -0,0 +1,22 @@ +.terraform.lock.hcl + +.terraform/* + +*.tfstate +*.tfstate.* + +crash.log +crash.*.log + +*.tfvars +*.tfvars.json + +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +.terraform.tfstate.lock.info + +.terraformrc +terraform.rc diff --git a/server/terraform/dev/cloud-init.yaml b/server/terraform/dev/cloud-init.yaml new file mode 100644 index 000000000..4103df7e6 --- /dev/null +++ b/server/terraform/dev/cloud-init.yaml @@ -0,0 +1,30 @@ +#cloud-config +package_update: true +package_upgrade: true +package_reboot_if_required: true +snap: + commands: + - snap install lxd --channel=6/stable + - snap install juju --channel=3.6/stable + - snap install charmcraft --channel=3.x/stable --classic + - snap install terraform --classic + - snap install microk8s --channel=1.32-strict/stable + - snap alias microk8s.kubectl kubectl +runcmd: + - lxd init --auto + - adduser ubuntu snap_microk8s + - microk8s status --wait-ready + - microk8s enable dns + - microk8s enable hostpath-storage + - microk8s enable ingress + - sudo -i -u ubuntu snap run juju bootstrap microk8s microk8s-controller + - sudo -i -u ubuntu snap run juju bootstrap localhost localhost-controller + - | + # workaround to get the localhost-controller to connect to microk8s + server_url=$(microk8s config | grep server | awk '{print $2;}') + sed -i "s|server: .*|server: $server_url|g" /var/snap/microk8s/current/credentials/client.config + - sudo -i -u ubuntu snap run juju add-cloud microk8s --controller localhost-controller + - sudo -i -u ubuntu snap run juju add-model testflinger-dev-db localhost + - sudo -i -u ubuntu snap run juju deploy mongodb --channel 6/edge + - sudo -i -u ubuntu snap run juju offer testflinger-dev-db.mongodb:database mongodb + - sudo -i -u ubuntu snap run juju add-model testflinger-dev microk8s diff --git a/server/terraform/dev/main.tf b/server/terraform/dev/main.tf new file mode 100644 index 000000000..24be4e461 --- /dev/null +++ b/server/terraform/dev/main.tf @@ -0,0 +1,70 @@ +# Testflinger Terraform module +module "testflinger" { + source = "../" + app_name = "testflinger" + model_uuid = data.juju_model.testflinger_dev_model.uuid + units = 2 + base = "ubuntu@22.04" + channel = "latest/beta" + config = { + external_hostname = "testflinger.local" + http_proxy = "" + https_proxy = "" + no_proxy = "localhost,127.0.0.1,::1" + max_pool_size = "100" + jwt_signing_key = var.jwt_signing_key + testflinger_secrets_master_key = var.testflinger_secrets_master_key + } +} + +# Data Source for juju model +data "juju_model" "testflinger_dev_model" { + name = "testflinger-dev" + owner = "admin" +} + +# Nginx Ingress Integrator Terraform resource +resource "juju_application" "ingress" { + name = "ingress" + model_uuid = data.juju_model.testflinger_dev_model.uuid + trust = true + + charm { + name = "nginx-ingress-integrator" + channel = "latest/stable" + } + + config = { + tls-secret-name = "" + whitelist-source-range = "" + max-body-size = "20" + } +} + +# Juju integration between MongoDB and Testflinger application +resource "juju_integration" "testflinger-database-relation" { + model_uuid = data.juju_model.testflinger_dev_model.uuid + + application { + name = module.testflinger.application.name + endpoint = "mongodb_client" + } + + application { + offer_url = "admin/testflinger-dev-db.mongodb" + endpoint = "database" + } +} + +# Juju integration between Nginx Ingress and Testflinger application +resource "juju_integration" "testflinger-ingress-relation" { + model_uuid = data.juju_model.testflinger_dev_model.uuid + + application { + name = module.testflinger.application.name + } + + application { + name = juju_application.ingress.name + } +} diff --git a/server/terraform/dev/terraform.tf b/server/terraform/dev/terraform.tf new file mode 100644 index 000000000..7fa9f8b51 --- /dev/null +++ b/server/terraform/dev/terraform.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + juju = { + version = "~> 1.0" + source = "juju/juju" + } + } +} \ No newline at end of file diff --git a/server/terraform/dev/variables.tf b/server/terraform/dev/variables.tf new file mode 100644 index 000000000..69be3ccd2 --- /dev/null +++ b/server/terraform/dev/variables.tf @@ -0,0 +1,13 @@ +variable "jwt_signing_key" { + description = "The signing key for JWT tokens" + sensitive = true + type = string + default = "secret" +} + +variable "testflinger_secrets_master_key" { + description = "Master key for Testflinger secrets encryption" + type = string + sensitive = true + default = "" +} From 7fceaca4ba48c4b77907cdf2df10af5c75755ad8 Mon Sep 17 00:00:00 2001 From: Rene Orozco Martinez Date: Tue, 7 Apr 2026 17:00:39 -0600 Subject: [PATCH 3/7] ci: add workflow for tagging terraform module --- .github/workflows/server-terraform-tags.yml | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/server-terraform-tags.yml diff --git a/.github/workflows/server-terraform-tags.yml b/.github/workflows/server-terraform-tags.yml new file mode 100644 index 000000000..7d16b75ad --- /dev/null +++ b/.github/workflows/server-terraform-tags.yml @@ -0,0 +1,51 @@ +name: Create version tag for testflinger server terraform module +permissions: + contents: read +on: + push: + branches: + - main + paths: + - server/terraform/** + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + auto-tag-terraform-module: + name: Create version tag for the terraform module + runs-on: ubuntu-latest + permissions: + contents: write # necessary for writing tag + + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 # necessary for semantic versioning + filter: blob:none # exclude file contents for faster checkout + persist-credentials: true # necessary for pushing tags + + - name: Generate semantic version + id: version + uses: PaulHatch/semantic-version@f29500c9d60a99ed5168e39ee367e0976884c46e # v6.0.1 + with: + tag_prefix: "testflinger-k8s-" + major_pattern: "breaking(terraform-server):" + minor_pattern: "feat(terraform-server):" + change_path: "server/terraform/" + search_commit_body: true + enable_prerelease_mode: false + + - name: Create and push tag + env: + TAG: ${{ steps.version.outputs.version_tag }} + run: | + echo ::group::Create tag + git tag "$TAG" + echo ::endgroup:: + echo ::notice::"$TAG" + echo ::group::Push tag + git push origin $TAG + echo ::endgroup:: From 949308ccc02fb918f714007fbf074305cb7339cf Mon Sep 17 00:00:00 2001 From: Rene Orozco Martinez Date: Tue, 7 Apr 2026 17:26:26 -0600 Subject: [PATCH 4/7] add copilot suggestions --- server/terraform/CONTRIBUTING.md | 19 ++++++++++++++++--- server/terraform/README.md | 19 +++++++++++-------- server/terraform/dev/variables.tf | 1 - server/terraform/outputs.tf | 8 ++++---- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/server/terraform/CONTRIBUTING.md b/server/terraform/CONTRIBUTING.md index 44034cb8b..c3c7ba2eb 100644 --- a/server/terraform/CONTRIBUTING.md +++ b/server/terraform/CONTRIBUTING.md @@ -71,9 +71,22 @@ multipass exec testflinger-juju -- terraform init Refer to the [README](README.md#api) for the full list of required variables. -`terraform/dev` directory contains the required terraform files for deployment, -modify them accordingly with the necessary variables such as the JWT signing key -and secrets master key. +The `terraform/dev` directory has sensitive values to be provided before +deployment. Create a `terraform.tfvars` file inside `terraform/dev` with the +required secrets: + +```hcl +jwt_signing_key = "" +testflinger_secrets_master_key = "" +``` + + +> [!NOTE] +> Only `jwt_signing_key` is required. + +> [!WARNING] +> Never commit `terraform.tfvars` to version control. The file is excluded via +> `.gitignore` for this reason. ### Deploy diff --git a/server/terraform/README.md b/server/terraform/README.md index c66e1d86e..d00ba7ef0 100644 --- a/server/terraform/README.md +++ b/server/terraform/README.md @@ -1,8 +1,8 @@ # Terraform module for Testflinger Server -This is a Terraform module facilitating the deployment of the Testflinger Server charm, -using the [Terraform Juju provider][juju-provider]. For more information, refer -to the provider [documentation][juju-provider-docs]. +This is a Terraform module facilitating the deployment of the Testflinger Server +charm, using the [Terraform Juju provider][juju-provider]. For more information, +refer to the provider [documentation][juju-provider-docs]. ## Requirements @@ -17,7 +17,7 @@ The module offers the following configurable inputs: | Name | Type | Description | Required | | - | - | - | - | -| `app_name` | string | Name of the Testflinger server application | True | +| `app_name` | string | Name of the Testflinger server application | False | | `base` | string | Operating system base to use for the Testflinger server charm | False | | `channel` | string | Channel to use for the charm | False | | `config` | map(string) | Map of charm config options | False | @@ -37,13 +37,16 @@ The module offers the following configurable inputs: ## Usage This module is intended to be used as part of a higher-level module. -When defining one, users should ensure that Terraform is aware of the `model_uuid` dependency of the charm module. +When defining one, users should ensure that Terraform is aware of the `model_uuid` +dependency of the charm module. ### Define a `data` source -Define a `data` source and pass to the `model_uuid` input a reference to the `data.juju_model` resource's name. -This will enable Terraform to look for a `model_uuid` resource with a name attribute equal to the one provided, -and apply only if this is present. Otherwise, it will fail before applying anything. +Define a `data.juju_model` source that looks up the target Juju model by name, +and pass the resulting UUID to the `model_uuid` input using `data.juju_model..uuid`. +This ensures Terraform resolves the model data source before applying the module. +If the named model cannot be found, Terraform will fail during planning or apply +before creating any resources. ```hcl data "juju_model" "testflinger_dev" { diff --git a/server/terraform/dev/variables.tf b/server/terraform/dev/variables.tf index 69be3ccd2..fabda929f 100644 --- a/server/terraform/dev/variables.tf +++ b/server/terraform/dev/variables.tf @@ -2,7 +2,6 @@ variable "jwt_signing_key" { description = "The signing key for JWT tokens" sensitive = true type = string - default = "secret" } variable "testflinger_secrets_master_key" { diff --git a/server/terraform/outputs.tf b/server/terraform/outputs.tf index b75261c56..014ed26e5 100644 --- a/server/terraform/outputs.tf +++ b/server/terraform/outputs.tf @@ -14,9 +14,9 @@ output "provides" { output "requires" { description = "Map of requires integration endpoints" value = { - mongodb_client = "mongodb_client" - mongodb_keyvault = "mongodb_client" - nginx_route = "nginx-route" - traefik_route = "traefik_route" + mongodb_client = "mongodb_client" + mongodb_keyvault = "mongodb_keyvault" + nginx_route = "nginx-route" + traefik_route = "traefik-route" } } From 11eecb26a9556048d879cae9a005ea73ea92f30a Mon Sep 17 00:00:00 2001 From: Rene Orozco Martinez Date: Wed, 8 Apr 2026 08:21:42 -0600 Subject: [PATCH 5/7] add uncomitted change --- server/terraform/CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/terraform/CONTRIBUTING.md b/server/terraform/CONTRIBUTING.md index c3c7ba2eb..4f54c60aa 100644 --- a/server/terraform/CONTRIBUTING.md +++ b/server/terraform/CONTRIBUTING.md @@ -82,7 +82,8 @@ testflinger_secrets_master_key = "" > [!NOTE] -> Only `jwt_signing_key` is required. +> Only `jwt_signing_key` is required. In case `terraform.tfvars` is not provided +> terraform will prompt for the value. > [!WARNING] > Never commit `terraform.tfvars` to version control. The file is excluded via From 22391ba0446a79b5b7a8c4b8cd5c178b858055f0 Mon Sep 17 00:00:00 2001 From: Rene Orozco Martinez Date: Thu, 9 Apr 2026 12:45:35 -0600 Subject: [PATCH 6/7] dev: change kebab-case for snake_case --- server/terraform/CONTRIBUTING.md | 11 ++++++----- server/terraform/dev/main.tf | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/server/terraform/CONTRIBUTING.md b/server/terraform/CONTRIBUTING.md index 4f54c60aa..7cdfe4a21 100644 --- a/server/terraform/CONTRIBUTING.md +++ b/server/terraform/CONTRIBUTING.md @@ -73,7 +73,7 @@ Refer to the [README](README.md#api) for the full list of required variables. The `terraform/dev` directory has sensitive values to be provided before deployment. Create a `terraform.tfvars` file inside `terraform/dev` with the -required secrets: +following secrets: ```hcl jwt_signing_key = "" @@ -119,15 +119,16 @@ try the following command on each IP address until you get a response: curl --connect-to :: http://testflinger.local/v1 ``` -Once you find the IP address, add the following entry to your host machine's `/etc/hosts` file: +Once you find the IP address, add the following entry to your host machine's +`/etc/hosts` file: ```text testflinger.local ``` -After that you should be able to reach the Testflinger frontend on your host machine's -browser through `http://testflinger.local`. You should also be able to access the API -through `http://testflinger.local/v1/`. +After that you should be able to reach the Testflinger frontend on your host +machine's browser through `http://testflinger.local`. You should also be able +to access the API through `http://testflinger.local/v1/`. ### Teardown diff --git a/server/terraform/dev/main.tf b/server/terraform/dev/main.tf index 24be4e461..600e54398 100644 --- a/server/terraform/dev/main.tf +++ b/server/terraform/dev/main.tf @@ -42,7 +42,7 @@ resource "juju_application" "ingress" { } # Juju integration between MongoDB and Testflinger application -resource "juju_integration" "testflinger-database-relation" { +resource "juju_integration" "testflinger_database_relation" { model_uuid = data.juju_model.testflinger_dev_model.uuid application { @@ -57,7 +57,7 @@ resource "juju_integration" "testflinger-database-relation" { } # Juju integration between Nginx Ingress and Testflinger application -resource "juju_integration" "testflinger-ingress-relation" { +resource "juju_integration" "testflinger_ingress_relation" { model_uuid = data.juju_model.testflinger_dev_model.uuid application { From 8a0599ff2abca659fb7810dd88d7d65837715fba Mon Sep 17 00:00:00 2001 From: Rene Orozco Martinez Date: Mon, 13 Apr 2026 15:20:51 -0600 Subject: [PATCH 7/7] add reviewer comments --- server/terraform/dev/main.tf | 2 +- server/terraform/dev/terraform.tf | 2 +- server/terraform/variables.tf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/terraform/dev/main.tf b/server/terraform/dev/main.tf index 600e54398..caf377142 100644 --- a/server/terraform/dev/main.tf +++ b/server/terraform/dev/main.tf @@ -56,7 +56,7 @@ resource "juju_integration" "testflinger_database_relation" { } } -# Juju integration between Nginx Ingress and Testflinger application +# Juju integration between Ingress and Testflinger application resource "juju_integration" "testflinger_ingress_relation" { model_uuid = data.juju_model.testflinger_dev_model.uuid diff --git a/server/terraform/dev/terraform.tf b/server/terraform/dev/terraform.tf index 7fa9f8b51..9d78d4bf3 100644 --- a/server/terraform/dev/terraform.tf +++ b/server/terraform/dev/terraform.tf @@ -5,4 +5,4 @@ terraform { source = "juju/juju" } } -} \ No newline at end of file +} diff --git a/server/terraform/variables.tf b/server/terraform/variables.tf index c0c03f48b..8e7f0820c 100644 --- a/server/terraform/variables.tf +++ b/server/terraform/variables.tf @@ -5,7 +5,7 @@ variable "app_name" { } variable "base" { - description = "Operating system base to use for the Testflinger charm" + description = "Operating system base to use for the Testflinger Server charm" type = string nullable = true default = null