diff --git a/terraform/cos-lite/README.md b/terraform/cos-lite/README.md index 404bc531..e1e3b4ac 100644 --- a/terraform/cos-lite/README.md +++ b/terraform/cos-lite/README.md @@ -26,8 +26,8 @@ This is a Terraform module facilitating the deployment of the COS Lite solution, | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [alertmanager](#input\_alertmanager) | Application configuration for Alertmanager. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "alertmanager")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | +| [base](#input\_base) | The operating system on which to deploy. E.g. ubuntu@22.04. Changing this value for machine charms will trigger a replace by terraform. Check Charmhub for per-charm base support. | `string` | `"ubuntu@24.04"` | no | | [catalogue](#input\_catalogue) | Application configuration for Catalogue. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "catalogue")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | -| [channel](#input\_channel) | Channel that the applications are (unless overwritten by individual channels) deployed from | `string` | `"dev/edge"` | no | | [external\_ca\_cert\_offer\_url](#input\_external\_ca\_cert\_offer\_url) | A Juju offer URL (e.g. admin/external-ca.send-ca-cert) of a CA providing the 'certificate\_transfer' integration for applications to trust ingress via Traefik. | `string` | `null` | no | | [external\_certificates\_offer\_url](#input\_external\_certificates\_offer\_url) | A Juju offer URL (e.g. admin/external-ca.certificates) of a CA providing the 'tls\_certificates' integration for Traefik to supply it with server certificates. | `string` | `null` | no | | [grafana](#input\_grafana) | Application configuration for Grafana. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "grafana")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | @@ -36,6 +36,7 @@ This is a Terraform module facilitating the deployment of the COS Lite solution, | [loki](#input\_loki) | Application configuration for Loki. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "loki")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [model\_uuid](#input\_model\_uuid) | Reference to an existing model resource or data source for the model to deploy to | `string` | n/a | yes | | [prometheus](#input\_prometheus) | Application configuration for Prometheus. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "prometheus")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | +| [risk](#input\_risk) | Risk level that the applications are (unless overwritten by individual channels) deployed from | `string` | `"edge"` | no | | [ssc](#input\_ssc) | Application configuration for self-signed-certificates. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "ca")
channel = optional(string, "1/stable")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [traefik](#input\_traefik) | Application configuration for Traefik. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "traefik")
channel = optional(string, "latest/stable")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | diff --git a/terraform/cos-lite/applications.tf b/terraform/cos-lite/applications.tf index dd1bd5ab..72d34dd1 100644 --- a/terraform/cos-lite/applications.tf +++ b/terraform/cos-lite/applications.tf @@ -1,11 +1,11 @@ module "alertmanager" { source = "git::https://github.com/canonical/alertmanager-k8s-operator//terraform" app_name = var.alertmanager.app_name - channel = var.channel + channel = local.channels.alertmanager config = var.alertmanager.config constraints = var.alertmanager.constraints model_uuid = var.model_uuid - revision = var.alertmanager.revision + revision = local.revisions.alertmanager storage_directives = var.alertmanager.storage_directives units = var.alertmanager.units } @@ -13,11 +13,11 @@ module "alertmanager" { module "catalogue" { source = "git::https://github.com/canonical/catalogue-k8s-operator//terraform" app_name = var.catalogue.app_name - channel = var.channel + channel = local.channels.catalogue config = var.catalogue.config constraints = var.catalogue.constraints model_uuid = var.model_uuid - revision = var.catalogue.revision + revision = local.revisions.catalogue storage_directives = var.catalogue.storage_directives units = var.catalogue.units } @@ -25,11 +25,11 @@ module "catalogue" { module "grafana" { source = "git::https://github.com/canonical/grafana-k8s-operator//terraform" app_name = var.grafana.app_name - channel = var.channel + channel = local.channels.grafana config = var.grafana.config constraints = var.grafana.constraints model_uuid = var.model_uuid - revision = var.grafana.revision + revision = local.revisions.grafana storage_directives = var.grafana.storage_directives units = var.grafana.units } @@ -37,24 +37,24 @@ module "grafana" { module "loki" { source = "git::https://github.com/canonical/loki-k8s-operator//terraform" app_name = var.loki.app_name - channel = var.channel + channel = local.channels.loki config = var.loki.config constraints = var.loki.constraints model_uuid = var.model_uuid storage_directives = var.loki.storage_directives - revision = var.loki.revision + revision = local.revisions.loki units = var.loki.units } module "prometheus" { source = "git::https://github.com/canonical/prometheus-k8s-operator//terraform" app_name = var.prometheus.app_name - channel = var.channel + channel = local.channels.prometheus config = var.prometheus.config constraints = var.prometheus.constraints model_uuid = var.model_uuid storage_directives = var.prometheus.storage_directives - revision = var.prometheus.revision + revision = local.revisions.prometheus units = var.prometheus.units } @@ -62,22 +62,22 @@ module "ssc" { count = var.internal_tls ? 1 : 0 source = "git::https://github.com/canonical/self-signed-certificates-operator//terraform" app_name = var.ssc.app_name - channel = var.ssc.channel + channel = local.channels.ssc config = var.ssc.config constraints = var.ssc.constraints model_uuid = var.model_uuid - revision = var.ssc.revision + revision = local.revisions.ssc units = var.ssc.units } module "traefik" { source = "git::https://github.com/canonical/traefik-k8s-operator//terraform" app_name = var.traefik.app_name - channel = var.traefik.channel + channel = local.channels.traefik config = var.traefik.config constraints = var.traefik.constraints model_uuid = var.model_uuid - revision = var.traefik.revision + revision = local.revisions.traefik storage_directives = var.traefik.storage_directives units = var.traefik.units } diff --git a/terraform/cos-lite/locals.tf b/terraform/cos-lite/locals.tf new file mode 100644 index 00000000..e1204dd0 --- /dev/null +++ b/terraform/cos-lite/locals.tf @@ -0,0 +1,30 @@ +locals { + tls_termination = var.external_certificates_offer_url != null ? true : false + tracks = { + alertmanager = "0.31" + catalogue = "3.0" + grafana = "12.4" + loki = "3.7" + prometheus = "3.10" + ssc = "latest" + traefik = "latest" + } + channels = { + alertmanager = "${local.tracks.alertmanager}/${var.risk}" + catalogue = "${local.tracks.catalogue}/${var.risk}" + grafana = "${local.tracks.grafana}/${var.risk}" + loki = "${local.tracks.loki}/${var.risk}" + prometheus = "${local.tracks.prometheus}/${var.risk}" + ssc = "${local.tracks.ssc}/${var.risk}" + traefik = "${local.tracks.traefik}/${var.risk}" + } + revisions = { + alertmanager = var.alertmanager.revision != null ? var.alertmanager.revision : data.juju_charm.alertmanager_info.revision + catalogue = var.catalogue.revision != null ? var.catalogue.revision : data.juju_charm.catalogue_info.revision + grafana = var.grafana.revision != null ? var.grafana.revision : data.juju_charm.grafana_info.revision + loki = var.loki.revision != null ? var.loki.revision : data.juju_charm.loki_info.revision + prometheus = var.prometheus.revision != null ? var.prometheus.revision : data.juju_charm.prometheus_info.revision + ssc = var.ssc.revision != null ? var.ssc.revision : data.juju_charm.ssc_info.revision + traefik = var.traefik.revision != null ? var.traefik.revision : data.juju_charm.traefik_info.revision + } +} \ No newline at end of file diff --git a/terraform/cos-lite/tests/revision_pin.tftest.hcl b/terraform/cos-lite/tests/revision_pin.tftest.hcl new file mode 100644 index 00000000..82c61e73 --- /dev/null +++ b/terraform/cos-lite/tests/revision_pin.tftest.hcl @@ -0,0 +1,95 @@ +mock_provider "juju" {} + +variables { model_uuid = "00000000-0000-0000-0000-000000000000" } + +# --- User revision pin is respected and not overridden by juju_charm datasource --- + +run "user_revision_pin_is_respected" { + command = plan + + variables { + alertmanager = { revision = 1 } + catalogue = { revision = 2 } + grafana = { revision = 3 } + loki = { revision = 4 } + prometheus = { revision = 5 } + ssc = { revision = 6 } + traefik = { revision = 7 } + } + + assert { + condition = local.revisions.alertmanager == 1 + error_message = "Expected alertmanager revision 1, got ${local.revisions.alertmanager}" + } + + assert { + condition = local.revisions.catalogue == 2 + error_message = "Expected catalogue revision 2, got ${local.revisions.catalogue}" + } + + assert { + condition = local.revisions.grafana == 3 + error_message = "Expected grafana revision 3, got ${local.revisions.grafana}" + } + + assert { + condition = local.revisions.loki == 4 + error_message = "Expected loki revision 4, got ${local.revisions.loki}" + } + + assert { + condition = local.revisions.prometheus == 5 + error_message = "Expected prometheus revision 5, got ${local.revisions.prometheus}" + } + + assert { + condition = local.revisions.ssc == 6 + error_message = "Expected ssc revision 6, got ${local.revisions.ssc}" + } + + assert { + condition = local.revisions.traefik == 7 + error_message = "Expected traefik revision 7, got ${local.revisions.traefik}" + } +} + +# --- Without a revision pin, the juju_charm datasource determines the revision --- + +run "no_pin_uses_datasource" { + command = plan + + assert { + condition = local.revisions.alertmanager == data.juju_charm.alertmanager_info.revision + error_message = "alertmanager revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.catalogue == data.juju_charm.catalogue_info.revision + error_message = "catalogue revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.grafana == data.juju_charm.grafana_info.revision + error_message = "grafana revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.loki == data.juju_charm.loki_info.revision + error_message = "loki revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.prometheus == data.juju_charm.prometheus_info.revision + error_message = "prometheus revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.ssc == data.juju_charm.ssc_info.revision + error_message = "ssc revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.traefik == data.juju_charm.traefik_info.revision + error_message = "traefik revision should come from datasource when no pin is set" + } +} diff --git a/terraform/cos-lite/upgrades.tf b/terraform/cos-lite/upgrades.tf new file mode 100644 index 00000000..1d1d0587 --- /dev/null +++ b/terraform/cos-lite/upgrades.tf @@ -0,0 +1,43 @@ +# -------------- # CharmHub API -------------- # + +data "juju_charm" "alertmanager_info" { + charm = "alertmanager-k8s" + channel = local.channels.alertmanager + base = var.base +} + +data "juju_charm" "catalogue_info" { + charm = "catalogue-k8s" + channel = local.channels.catalogue + base = var.base +} + +data "juju_charm" "grafana_info" { + charm = "grafana-k8s" + channel = local.channels.grafana + base = var.base +} + +data "juju_charm" "loki_info" { + charm = "loki-k8s" + channel = local.channels.loki + base = var.base +} + +data "juju_charm" "prometheus_info" { + charm = "prometheus-k8s" + channel = local.channels.prometheus + base = var.base +} + +data "juju_charm" "ssc_info" { + charm = "self-signed-certificates" + channel = local.channels.ssc + base = var.base +} + +data "juju_charm" "traefik_info" { + charm = "traefik-k8s" + channel = local.channels.traefik + base = var.base +} diff --git a/terraform/cos-lite/variables.tf b/terraform/cos-lite/variables.tf index 5421f6c6..40176124 100644 --- a/terraform/cos-lite/variables.tf +++ b/terraform/cos-lite/variables.tf @@ -5,15 +5,16 @@ # causes the operation to fail due to https://github.com/juju/terraform-provider-juju/issues/344 # Therefore, we set a default value of "arch=amd64" for all applications. -locals { - # https://github.com/juju/terraform-provider-juju/issues/972 - tls_termination = var.external_certificates_offer_url != null ? true : false +variable "risk" { + description = "Risk level that the applications are (unless overwritten by individual channels) deployed from" + type = string + default = "edge" } -variable "channel" { - description = "Channel that the applications are (unless overwritten by individual channels) deployed from" +variable "base" { + description = "The operating system on which to deploy. E.g. ubuntu@22.04. Changing this value for machine charms will trigger a replace by terraform. Check Charmhub for per-charm base support." + default = "ubuntu@24.04" type = string - default = "dev/edge" } variable "model_uuid" { diff --git a/terraform/cos/README.md b/terraform/cos/README.md index bd294b6a..e9ab7ac8 100644 --- a/terraform/cos/README.md +++ b/terraform/cos/README.md @@ -32,8 +32,8 @@ This is a Terraform module facilitating the deployment of the COS solution, usin |------|-------------|------|---------|:--------:| | [alertmanager](#input\_alertmanager) | Application configuration for Alertmanager. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "alertmanager")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [anti\_affinity](#input\_anti\_affinity) | Enable anti-affinity constraints across all HA modules (Mimir, Loki, Tempo) | `bool` | `true` | no | +| [base](#input\_base) | The operating system on which to deploy. E.g. ubuntu@22.04. Changing this value for machine charms will trigger a replace by terraform. Check Charmhub for per-charm base support. | `string` | `"ubuntu@24.04"` | no | | [catalogue](#input\_catalogue) | Application configuration for Catalogue. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "catalogue")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | -| [channel](#input\_channel) | Channel that the applications are (unless overwritten by individual channels) deployed from | `string` | `"dev/edge"` | no | | [cloud](#input\_cloud) | Kubernetes cloud or environment where this COS module will be deployed (e.g self-managed, aws) | `string` | `"self-managed"` | no | | [external\_ca\_cert\_offer\_url](#input\_external\_ca\_cert\_offer\_url) | A Juju offer URL (e.g. admin/external-ca.send-ca-cert) of a CA providing the 'certificate\_transfer' integration for applications to trust ingress via Traefik. | `string` | `null` | no | | [external\_certificates\_offer\_url](#input\_external\_certificates\_offer\_url) | A Juju offer URL of a CA providing the 'tls\_certificates' integration for Traefik to supply it with server certificates | `string` | `null` | no | @@ -48,6 +48,7 @@ This is a Terraform module facilitating the deployment of the COS solution, usin | [mimir\_worker](#input\_mimir\_worker) | Application configuration for all Mimir Workers. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
backend_config = optional(map(string), {})
read_config = optional(map(string), {})
write_config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
backend_storage_directives = optional(map(string), {})
read_storage_directives = optional(map(string), {})
write_storage_directives = optional(map(string), {})
backend_units = optional(number, 3)
read_units = optional(number, 3)
write_units = optional(number, 3)
})
| `{}` | no | | [model\_uuid](#input\_model\_uuid) | Reference to an existing model resource or data source for the model to deploy to | `string` | n/a | yes | | [opentelemetry\_collector](#input\_opentelemetry\_collector) | Application configuration for OpenTelemetry Collector. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "otelcol")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | +| [risk](#input\_risk) | Risk level that the applications are (unless overwritten by individual channels) deployed from | `string` | `"edge"` | no | | [s3\_access\_key](#input\_s3\_access\_key) | S3 access-key credential | `string` | n/a | yes | | [s3\_endpoint](#input\_s3\_endpoint) | S3 endpoint | `string` | n/a | yes | | [s3\_integrator](#input\_s3\_integrator) | Application configuration for all S3-integrators in coordinated workers. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
channel = optional(string, "2/edge")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | diff --git a/terraform/cos/applications.tf b/terraform/cos/applications.tf index 8420484e..deaec9aa 100644 --- a/terraform/cos/applications.tf +++ b/terraform/cos/applications.tf @@ -1,11 +1,11 @@ module "alertmanager" { source = "git::https://github.com/canonical/alertmanager-k8s-operator//terraform" app_name = var.alertmanager.app_name - channel = var.channel + channel = local.channels.alertmanager config = var.alertmanager.config constraints = var.alertmanager.constraints model_uuid = var.model_uuid - revision = var.alertmanager.revision + revision = local.revisions.alertmanager storage_directives = var.alertmanager.storage_directives units = var.alertmanager.units } @@ -13,11 +13,11 @@ module "alertmanager" { module "catalogue" { source = "git::https://github.com/canonical/catalogue-k8s-operator//terraform" app_name = var.catalogue.app_name - channel = var.channel + channel = local.channels.catalogue config = var.catalogue.config constraints = var.catalogue.constraints model_uuid = var.model_uuid - revision = var.catalogue.revision + revision = local.revisions.catalogue storage_directives = var.catalogue.storage_directives units = var.catalogue.units } @@ -25,11 +25,11 @@ module "catalogue" { module "grafana" { source = "git::https://github.com/canonical/grafana-k8s-operator//terraform" app_name = var.grafana.app_name - channel = var.channel + channel = local.channels.grafana config = var.grafana.config constraints = var.grafana.constraints model_uuid = var.model_uuid - revision = var.grafana.revision + revision = local.revisions.grafana storage_directives = var.grafana.storage_directives units = var.grafana.units } @@ -37,28 +37,28 @@ module "grafana" { module "loki" { source = "git::https://github.com/canonical/loki-operators//terraform" anti_affinity = var.anti_affinity - channel = var.channel + channel = local.channels.loki model_uuid = var.model_uuid s3_endpoint = var.s3_endpoint s3_secret_key = var.s3_secret_key s3_access_key = var.s3_access_key s3_bucket = var.loki_bucket - s3_integrator_channel = var.s3_integrator.channel + s3_integrator_channel = local.channels.s3_integrator s3_integrator_config = var.s3_integrator.config s3_integrator_constraints = var.s3_integrator.constraints - s3_integrator_revision = var.s3_integrator.revision + s3_integrator_revision = local.revisions.s3_integrator s3_integrator_storage_directives = var.s3_integrator.storage_directives s3_integrator_units = var.s3_integrator.units coordinator_config = var.loki_coordinator.config coordinator_constraints = var.loki_coordinator.constraints - coordinator_revision = var.loki_coordinator.revision + coordinator_revision = local.revisions.loki_coordinator coordinator_storage_directives = var.loki_coordinator.storage_directives coordinator_units = var.loki_coordinator.units backend_config = var.loki_worker.backend_config read_config = var.loki_worker.read_config write_config = var.loki_worker.write_config worker_constraints = var.loki_worker.constraints - worker_revision = var.loki_worker.revision + worker_revision = local.revisions.loki_worker backend_worker_storage_directives = var.loki_worker.backend_storage_directives read_worker_storage_directives = var.loki_worker.read_storage_directives write_worker_storage_directives = var.loki_worker.write_storage_directives @@ -68,38 +68,30 @@ module "loki" { } module "mimir" { - source = "git::https://github.com/canonical/mimir-operators//terraform" - anti_affinity = var.anti_affinity - channel = var.channel - model_uuid = var.model_uuid - s3_endpoint = var.s3_endpoint - s3_secret_key = var.s3_secret_key - s3_access_key = var.s3_access_key - s3_bucket = var.mimir_bucket - s3_integrator_channel = var.s3_integrator.channel - s3_integrator_config = var.s3_integrator.config - s3_integrator_constraints = var.s3_integrator.constraints - s3_integrator_revision = var.s3_integrator.revision - s3_integrator_storage_directives = var.s3_integrator.storage_directives - s3_integrator_units = var.s3_integrator.units - coordinator_config = merge( - var.mimir_coordinator.config, - # enable exemplar storage (required for metrics-to-traces). - # This config option is not supported in track `1`, so we'll set it only - # for newer tracks to maintain backward compatibility. - can(regex("^1/", var.channel)) ? {} : { - "max_global_exemplars_per_user" = "100000" - } - ) + source = "git::https://github.com/canonical/mimir-operators//terraform" + anti_affinity = var.anti_affinity + channel = local.channels.mimir + model_uuid = var.model_uuid + s3_endpoint = var.s3_endpoint + s3_secret_key = var.s3_secret_key + s3_access_key = var.s3_access_key + s3_bucket = var.mimir_bucket + s3_integrator_channel = local.channels.s3_integrator + s3_integrator_config = var.s3_integrator.config + s3_integrator_constraints = var.s3_integrator.constraints + s3_integrator_revision = local.revisions.s3_integrator + s3_integrator_storage_directives = var.s3_integrator.storage_directives + s3_integrator_units = var.s3_integrator.units + coordinator_config = { "max_global_exemplars_per_user" = "100000" } coordinator_constraints = var.mimir_coordinator.constraints - coordinator_revision = var.mimir_coordinator.revision + coordinator_revision = local.revisions.mimir_coordinator coordinator_storage_directives = var.mimir_coordinator.storage_directives coordinator_units = var.mimir_coordinator.units backend_config = var.mimir_worker.backend_config read_config = var.mimir_worker.read_config write_config = var.mimir_worker.write_config worker_constraints = var.mimir_worker.constraints - worker_revision = var.mimir_worker.revision + worker_revision = local.revisions.mimir_worker backend_worker_storage_directives = var.mimir_worker.backend_storage_directives read_worker_storage_directives = var.mimir_worker.read_storage_directives write_worker_storage_directives = var.mimir_worker.write_storage_directives @@ -111,11 +103,11 @@ module "mimir" { module "opentelemetry_collector" { source = "git::https://github.com/canonical/opentelemetry-collector-k8s-operator//terraform" app_name = var.opentelemetry_collector.app_name - channel = var.channel + channel = local.channels.otelcol config = var.opentelemetry_collector.config constraints = var.opentelemetry_collector.constraints model_uuid = var.model_uuid - revision = var.opentelemetry_collector.revision + revision = local.revisions.otelcol storage_directives = var.opentelemetry_collector.storage_directives units = var.opentelemetry_collector.units } @@ -124,32 +116,33 @@ module "ssc" { count = var.internal_tls ? 1 : 0 source = "git::https://github.com/canonical/self-signed-certificates-operator//terraform" app_name = var.ssc.app_name - channel = var.ssc.channel + channel = local.channels.ssc config = var.ssc.config constraints = var.ssc.constraints model_uuid = var.model_uuid - revision = var.ssc.revision + revision = local.revisions.ssc units = var.ssc.units } module "tempo" { - source = "git::https://github.com/canonical/tempo-operators//terraform" - anti_affinity = var.anti_affinity - channel = var.channel - model_uuid = var.model_uuid - s3_endpoint = var.s3_endpoint - s3_access_key = var.s3_access_key - s3_secret_key = var.s3_secret_key - s3_bucket = var.tempo_bucket - s3_integrator_channel = var.s3_integrator.channel + source = "git::https://github.com/canonical/tempo-operators//terraform" + anti_affinity = var.anti_affinity + channel = local.channels.tempo + model_uuid = var.model_uuid + s3_endpoint = var.s3_endpoint + s3_access_key = var.s3_access_key + s3_secret_key = var.s3_secret_key + s3_bucket = var.tempo_bucket + # TODO: The same s3_integrator channel for all coordinated-workers? + s3_integrator_channel = local.channels.s3_integrator s3_integrator_config = var.s3_integrator.config s3_integrator_constraints = var.s3_integrator.constraints - s3_integrator_revision = var.s3_integrator.revision + s3_integrator_revision = local.revisions.s3_integrator s3_integrator_storage_directives = var.s3_integrator.storage_directives s3_integrator_units = var.s3_integrator.units coordinator_config = var.tempo_coordinator.config coordinator_constraints = var.tempo_coordinator.constraints - coordinator_revision = var.tempo_coordinator.revision + coordinator_revision = local.revisions.tempo_coordinator coordinator_storage_directives = var.tempo_coordinator.storage_directives coordinator_units = var.tempo_coordinator.units querier_config = var.tempo_worker.querier_config @@ -159,7 +152,7 @@ module "tempo" { compactor_config = var.tempo_worker.compactor_config metrics_generator_config = var.tempo_worker.metrics_generator_config worker_constraints = var.tempo_worker.constraints - worker_revision = var.tempo_worker.revision + worker_revision = local.revisions.tempo_worker compactor_worker_storage_directives = var.tempo_worker.compactor_worker_storage_directives distributor_worker_storage_directives = var.tempo_worker.distributor_worker_storage_directives ingester_worker_storage_directives = var.tempo_worker.ingester_worker_storage_directives @@ -177,11 +170,11 @@ module "tempo" { module "traefik" { source = "git::https://github.com/canonical/traefik-k8s-operator//terraform" app_name = var.traefik.app_name - channel = var.traefik.channel + channel = local.channels.traefik config = var.cloud == "aws" ? { "loadbalancer_annotations" = "service.beta.kubernetes.io/aws-load-balancer-scheme=internet-facing" } : var.traefik.config constraints = var.traefik.constraints model_uuid = var.model_uuid - revision = var.traefik.revision + revision = local.revisions.traefik storage_directives = var.traefik.storage_directives units = var.traefik.units } diff --git a/terraform/cos/locals.tf b/terraform/cos/locals.tf new file mode 100644 index 00000000..bae1f726 --- /dev/null +++ b/terraform/cos/locals.tf @@ -0,0 +1,43 @@ +locals { + clouds = ["aws", "self-managed"] # list of k8s clouds where this COS module can be deployed. + tls_termination = var.external_certificates_offer_url != null ? true : false + tracks = { + alertmanager = "0.31" + catalogue = "3.0" + grafana = "12.4" + loki = "3.7" + mimir = "3.0" + otelcol = "0.130" + s3_integrator = "2" + ssc = "latest" + tempo = "2.10" + traefik = "latest" + } + channels = { + alertmanager = "${local.tracks.alertmanager}/${var.risk}" + catalogue = "${local.tracks.catalogue}/${var.risk}" + grafana = "${local.tracks.grafana}/${var.risk}" + loki = "${local.tracks.loki}/${var.risk}" + mimir = "${local.tracks.mimir}/${var.risk}" + otelcol = "${local.tracks.otelcol}/${var.risk}" + s3_integrator = "${local.tracks.s3_integrator}/${var.risk}" + ssc = "${local.tracks.ssc}/${var.risk}" + tempo = "${local.tracks.tempo}/${var.risk}" + traefik = "${local.tracks.traefik}/${var.risk}" + } + revisions = { + alertmanager = var.alertmanager.revision != null ? var.alertmanager.revision : data.juju_charm.alertmanager_info.revision + catalogue = var.catalogue.revision != null ? var.catalogue.revision : data.juju_charm.catalogue_info.revision + grafana = var.grafana.revision != null ? var.grafana.revision : data.juju_charm.grafana_info.revision + loki_coordinator = var.loki_coordinator.revision != null ? var.loki_coordinator.revision : data.juju_charm.loki_coordinator_info.revision + loki_worker = var.loki_worker.revision != null ? var.loki_worker.revision : data.juju_charm.loki_worker_info.revision + mimir_coordinator = var.mimir_coordinator.revision != null ? var.mimir_coordinator.revision : data.juju_charm.mimir_coordinator_info.revision + mimir_worker = var.mimir_worker.revision != null ? var.mimir_worker.revision : data.juju_charm.mimir_worker_info.revision + otelcol = var.opentelemetry_collector.revision != null ? var.opentelemetry_collector.revision : data.juju_charm.otelcol_info.revision + s3_integrator = var.s3_integrator.revision != null ? var.s3_integrator.revision : data.juju_charm.s3_integrator_info.revision + ssc = var.ssc.revision != null ? var.ssc.revision : data.juju_charm.ssc_info.revision + tempo_coordinator = var.tempo_coordinator.revision != null ? var.tempo_coordinator.revision : data.juju_charm.tempo_coordinator_info.revision + tempo_worker = var.tempo_worker.revision != null ? var.tempo_worker.revision : data.juju_charm.tempo_worker_info.revision + traefik = var.traefik.revision != null ? var.traefik.revision : data.juju_charm.traefik_info.revision + } +} diff --git a/terraform/cos/tests/revision_pin.tftest.hcl b/terraform/cos/tests/revision_pin.tftest.hcl new file mode 100644 index 00000000..c9536cf5 --- /dev/null +++ b/terraform/cos/tests/revision_pin.tftest.hcl @@ -0,0 +1,166 @@ +mock_provider "juju" {} + +variables { + model_uuid = "00000000-0000-0000-0000-000000000000" + s3_endpoint = "foo" + s3_access_key = "foo" + s3_secret_key = "foo" +} + +# --- User revision pin is respected and not overridden by juju_charm datasource --- + +run "user_revision_pin_is_respected" { + command = plan + + variables { + alertmanager = { revision = 1 } + catalogue = { revision = 2 } + grafana = { revision = 3 } + loki_coordinator = { revision = 4 } + loki_worker = { revision = 5 } + mimir_coordinator = { revision = 6 } + mimir_worker = { revision = 7 } + opentelemetry_collector = { revision = 8 } + ssc = { revision = 9 } + s3_integrator = { revision = 10 } + tempo_coordinator = { revision = 11 } + tempo_worker = { revision = 12 } + traefik = { revision = 13 } + } + + assert { + condition = local.revisions.alertmanager == 1 + error_message = "Expected alertmanager revision 1, got ${local.revisions.alertmanager}" + } + + assert { + condition = local.revisions.catalogue == 2 + error_message = "Expected catalogue revision 2, got ${local.revisions.catalogue}" + } + + assert { + condition = local.revisions.grafana == 3 + error_message = "Expected grafana revision 3, got ${local.revisions.grafana}" + } + + assert { + condition = local.revisions.loki_coordinator == 4 + error_message = "Expected loki_coordinator revision 4, got ${local.revisions.loki_coordinator}" + } + + assert { + condition = local.revisions.loki_worker == 5 + error_message = "Expected loki_worker revision 5, got ${local.revisions.loki_worker}" + } + + assert { + condition = local.revisions.mimir_coordinator == 6 + error_message = "Expected mimir_coordinator revision 6, got ${local.revisions.mimir_coordinator}" + } + + assert { + condition = local.revisions.mimir_worker == 7 + error_message = "Expected mimir_worker revision 7, got ${local.revisions.mimir_worker}" + } + + assert { + condition = local.revisions.otelcol == 8 + error_message = "Expected otelcol revision 8, got ${local.revisions.otelcol}" + } + + assert { + condition = local.revisions.ssc == 9 + error_message = "Expected ssc revision 9, got ${local.revisions.ssc}" + } + + assert { + condition = local.revisions.s3_integrator == 10 + error_message = "Expected s3_integrator revision 10, got ${local.revisions.s3_integrator}" + } + + assert { + condition = local.revisions.tempo_coordinator == 11 + error_message = "Expected tempo_coordinator revision 11, got ${local.revisions.tempo_coordinator}" + } + + assert { + condition = local.revisions.tempo_worker == 12 + error_message = "Expected tempo_worker revision 12, got ${local.revisions.tempo_worker}" + } + + assert { + condition = local.revisions.traefik == 13 + error_message = "Expected traefik revision 13, got ${local.revisions.traefik}" + } +} + +# --- Without a revision pin, the juju_charm datasource determines the revision --- + +run "no_pin_uses_datasource" { + command = plan + + assert { + condition = local.revisions.alertmanager == data.juju_charm.alertmanager_info.revision + error_message = "alertmanager revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.catalogue == data.juju_charm.catalogue_info.revision + error_message = "catalogue revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.grafana == data.juju_charm.grafana_info.revision + error_message = "grafana revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.loki_coordinator == data.juju_charm.loki_coordinator_info.revision + error_message = "loki_coordinator revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.loki_worker == data.juju_charm.loki_worker_info.revision + error_message = "loki_worker revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.mimir_coordinator == data.juju_charm.mimir_coordinator_info.revision + error_message = "mimir_coordinator revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.mimir_worker == data.juju_charm.mimir_worker_info.revision + error_message = "mimir_worker revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.otelcol == data.juju_charm.otelcol_info.revision + error_message = "otelcol revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.ssc == data.juju_charm.ssc_info.revision + error_message = "ssc revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.s3_integrator == data.juju_charm.s3_integrator_info.revision + error_message = "s3_integrator revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.tempo_coordinator == data.juju_charm.tempo_coordinator_info.revision + error_message = "tempo_coordinator revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.tempo_worker == data.juju_charm.tempo_worker_info.revision + error_message = "tempo_worker revision should come from datasource when no pin is set" + } + + assert { + condition = local.revisions.traefik == data.juju_charm.traefik_info.revision + error_message = "traefik revision should come from datasource when no pin is set" + } +} diff --git a/terraform/cos/upgrades.tf b/terraform/cos/upgrades.tf new file mode 100644 index 00000000..429bcaa4 --- /dev/null +++ b/terraform/cos/upgrades.tf @@ -0,0 +1,79 @@ +# -------------- # CharmHub API -------------- # + +data "juju_charm" "alertmanager_info" { + charm = "alertmanager-k8s" + channel = local.channels.alertmanager + base = var.base +} + +data "juju_charm" "catalogue_info" { + charm = "catalogue-k8s" + channel = local.channels.catalogue + base = var.base +} + +data "juju_charm" "grafana_info" { + charm = "grafana-k8s" + channel = local.channels.grafana + base = var.base +} + +data "juju_charm" "loki_coordinator_info" { + charm = "loki-coordinator-k8s" + channel = local.channels.loki + base = var.base +} + +data "juju_charm" "loki_worker_info" { + charm = "loki-worker-k8s" + channel = local.channels.loki + base = var.base +} + +data "juju_charm" "mimir_coordinator_info" { + charm = "mimir-coordinator-k8s" + channel = local.channels.mimir + base = var.base +} + +data "juju_charm" "mimir_worker_info" { + charm = "mimir-worker-k8s" + channel = local.channels.mimir + base = var.base +} + +data "juju_charm" "otelcol_info" { + charm = "opentelemetry-collector-k8s" + channel = local.channels.otelcol + base = var.base +} + +data "juju_charm" "tempo_coordinator_info" { + charm = "tempo-coordinator-k8s" + channel = local.channels.tempo + base = var.base +} + +data "juju_charm" "tempo_worker_info" { + charm = "tempo-worker-k8s" + channel = local.channels.tempo + base = var.base +} + +data "juju_charm" "ssc_info" { + charm = "self-signed-certificates" + channel = local.channels.ssc + base = var.base +} + +data "juju_charm" "s3_integrator_info" { + charm = "s3-integrator-k8s" + channel = local.channels.s3_integrator + base = var.base +} + +data "juju_charm" "traefik_info" { + charm = "traefik-k8s" + channel = local.channels.traefik + base = var.base +} diff --git a/terraform/cos/variables.tf b/terraform/cos/variables.tf index 2c45184d..3a337536 100644 --- a/terraform/cos/variables.tf +++ b/terraform/cos/variables.tf @@ -5,15 +5,16 @@ # causes the operation to fail due to https://github.com/juju/terraform-provider-juju/issues/344 # Therefore, we set a default value of "arch=amd64" for all applications. -locals { - clouds = ["aws", "self-managed"] # list of k8s clouds where this COS module can be deployed. - tls_termination = var.external_certificates_offer_url != null ? true : false +variable "risk" { + description = "Risk level that the applications are (unless overwritten by individual channels) deployed from" + type = string + default = "edge" } -variable "channel" { - description = "Channel that the applications are (unless overwritten by individual channels) deployed from" +variable "base" { + description = "The operating system on which to deploy. E.g. ubuntu@22.04. Changing this value for machine charms will trigger a replace by terraform. Check Charmhub for per-charm base support." + default = "ubuntu@24.04" type = string - default = "dev/edge" } variable "model_uuid" { @@ -234,7 +235,6 @@ variable "opentelemetry_collector" { description = "Application configuration for OpenTelemetry Collector. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application" } - variable "ssc" { type = object({ app_name = optional(string, "ca")