diff --git a/terraform/cos-dev/applications.tf b/terraform/cos-dev/applications.tf index b76149c0..24518092 100644 --- a/terraform/cos-dev/applications.tf +++ b/terraform/cos-dev/applications.tf @@ -35,6 +35,32 @@ module "grafana" { replace_triggers = [terraform_data.grafana_litestream_resource.id] } +module "istio-ingress" { + count = var.mesh_enabled ? 1 : 0 + source = "git::https://github.com/canonical/istio-ingress-k8s-operator//terraform" + app_name = var.catalogue.app_name + channel = local.channels.catalogue + config = var.catalogue.config + constraints = var.catalogue.constraints + model_uuid = var.model_uuid + revision = local.revisions.catalogue + storage_directives = var.catalogue.storage_directives + units = var.catalogue.units +} + +module "istio-beacon" { + count = var.mesh_enabled ? 1 : 0 + source = "git::https://github.com/canonical/istio-beacon-k8s-operator//terraform" + app_name = var.catalogue.app_name + channel = local.channels.catalogue + config = var.catalogue.config + constraints = var.catalogue.constraints + model_uuid = var.model_uuid + revision = local.revisions.catalogue + storage_directives = var.catalogue.storage_directives + units = var.catalogue.units +} + module "loki_coordinator" { source = "git::https://github.com/canonical/loki-operators//coordinator/terraform" app_name = var.loki_coordinator.app_name @@ -195,8 +221,6 @@ module "opentelemetry_collector" { units = var.opentelemetry_collector.units } -# -------------- # SeaweedFS (storage_backend = "seaweedfs") -------------- - module "seaweedfs" { count = var.storage_backend == "seaweedfs" ? 1 : 0 source = "git::https://github.com/canonical/observability-stack//terraform/seaweedfs" @@ -210,126 +234,6 @@ module "seaweedfs" { units = var.seaweedfs.units } -# -------------- # S3-integrators (storage_backend = "s3") -------------- - -resource "juju_secret" "loki_s3_credentials" { - count = var.storage_backend == "s3" ? 1 : 0 - model_uuid = var.model_uuid - name = "loki-s3-credentials" - value = { - access-key = var.s3_access_key - secret-key = var.s3_secret_key - } - info = "S3 credentials for Loki" -} - -resource "juju_access_secret" "loki_s3_credentials_access" { - count = var.storage_backend == "s3" ? 1 : 0 - model_uuid = var.model_uuid - applications = [juju_application.s3_integrator_loki[0].name] - secret_id = juju_secret.loki_s3_credentials[0].secret_id -} - -# TODO: Replace with a remote terraform module once the s3-integrator charm exposes one. -resource "juju_application" "s3_integrator_loki" { - count = var.storage_backend == "s3" ? 1 : 0 - config = merge({ - endpoint = var.s3_endpoint - bucket = var.loki_bucket - credentials = "secret:${juju_secret.loki_s3_credentials[0].secret_id}" - }, var.s3_integrator.config) - constraints = var.s3_integrator.constraints - model_uuid = var.model_uuid - name = "${var.loki_coordinator.app_name}-s3-integrator" - storage_directives = var.s3_integrator.storage_directives - trust = true - units = var.s3_integrator.units - - charm { - name = "s3-integrator" - channel = local.channels.s3_integrator - revision = local.revisions.s3_integrator - } -} - -resource "juju_secret" "mimir_s3_credentials" { - count = var.storage_backend == "s3" ? 1 : 0 - model_uuid = var.model_uuid - name = "mimir-s3-credentials" - value = { - access-key = var.s3_access_key - secret-key = var.s3_secret_key - } - info = "S3 credentials for Mimir" -} - -resource "juju_access_secret" "mimir_s3_credentials_access" { - count = var.storage_backend == "s3" ? 1 : 0 - model_uuid = var.model_uuid - applications = [juju_application.s3_integrator_mimir[0].name] - secret_id = juju_secret.mimir_s3_credentials[0].secret_id -} - -resource "juju_application" "s3_integrator_mimir" { - count = var.storage_backend == "s3" ? 1 : 0 - config = merge({ - endpoint = var.s3_endpoint - bucket = var.mimir_bucket - credentials = "secret:${juju_secret.mimir_s3_credentials[0].secret_id}" - }, var.s3_integrator.config) - constraints = var.s3_integrator.constraints - model_uuid = var.model_uuid - name = "${var.mimir_coordinator.app_name}-s3-integrator" - storage_directives = var.s3_integrator.storage_directives - trust = true - units = var.s3_integrator.units - - charm { - name = "s3-integrator" - channel = local.channels.s3_integrator - revision = local.revisions.s3_integrator - } -} - -resource "juju_secret" "tempo_s3_credentials" { - count = var.storage_backend == "s3" ? 1 : 0 - model_uuid = var.model_uuid - name = "tempo-s3-credentials" - value = { - access-key = var.s3_access_key - secret-key = var.s3_secret_key - } - info = "S3 credentials for Tempo" -} - -resource "juju_access_secret" "tempo_s3_credentials_access" { - count = var.storage_backend == "s3" ? 1 : 0 - model_uuid = var.model_uuid - applications = [juju_application.s3_integrator_tempo[0].name] - secret_id = juju_secret.tempo_s3_credentials[0].secret_id -} - -resource "juju_application" "s3_integrator_tempo" { - count = var.storage_backend == "s3" ? 1 : 0 - config = merge({ - endpoint = var.s3_endpoint - bucket = var.tempo_bucket - credentials = "secret:${juju_secret.tempo_s3_credentials[0].secret_id}" - }, var.s3_integrator.config) - constraints = var.s3_integrator.constraints - model_uuid = var.model_uuid - name = "${var.tempo_coordinator.app_name}-s3-integrator" - storage_directives = var.s3_integrator.storage_directives - trust = true - units = var.s3_integrator.units - - charm { - name = "s3-integrator" - channel = local.channels.s3_integrator - revision = local.revisions.s3_integrator - } -} - module "ssc" { count = var.internal_tls ? 1 : 0 source = "git::https://github.com/canonical/self-signed-certificates-operator//terraform" @@ -472,3 +376,123 @@ module "traefik" { storage_directives = var.traefik.storage_directives units = var.traefik.units } + +# -------------- # S3-integrator resources (storage_backend = "s3") -------------- + +resource "juju_secret" "loki_s3_credentials" { + count = var.storage_backend == "s3" ? 1 : 0 + model_uuid = var.model_uuid + name = "loki-s3-credentials" + value = { + access-key = var.s3_access_key + secret-key = var.s3_secret_key + } + info = "S3 credentials for Loki" +} + +resource "juju_access_secret" "loki_s3_credentials_access" { + count = var.storage_backend == "s3" ? 1 : 0 + model_uuid = var.model_uuid + applications = [juju_application.s3_integrator_loki[0].name] + secret_id = juju_secret.loki_s3_credentials[0].secret_id +} + +# TODO: Replace with a remote terraform module once the s3-integrator charm exposes one. +resource "juju_application" "s3_integrator_loki" { + count = var.storage_backend == "s3" ? 1 : 0 + config = merge({ + endpoint = var.s3_endpoint + bucket = var.loki_bucket + credentials = "secret:${juju_secret.loki_s3_credentials[0].secret_id}" + }, var.s3_integrator.config) + constraints = var.s3_integrator.constraints + model_uuid = var.model_uuid + name = "${var.loki_coordinator.app_name}-s3-integrator" + storage_directives = var.s3_integrator.storage_directives + trust = true + units = var.s3_integrator.units + + charm { + name = "s3-integrator" + channel = local.channels.s3_integrator + revision = local.revisions.s3_integrator + } +} + +resource "juju_secret" "mimir_s3_credentials" { + count = var.storage_backend == "s3" ? 1 : 0 + model_uuid = var.model_uuid + name = "mimir-s3-credentials" + value = { + access-key = var.s3_access_key + secret-key = var.s3_secret_key + } + info = "S3 credentials for Mimir" +} + +resource "juju_access_secret" "mimir_s3_credentials_access" { + count = var.storage_backend == "s3" ? 1 : 0 + model_uuid = var.model_uuid + applications = [juju_application.s3_integrator_mimir[0].name] + secret_id = juju_secret.mimir_s3_credentials[0].secret_id +} + +resource "juju_application" "s3_integrator_mimir" { + count = var.storage_backend == "s3" ? 1 : 0 + config = merge({ + endpoint = var.s3_endpoint + bucket = var.mimir_bucket + credentials = "secret:${juju_secret.mimir_s3_credentials[0].secret_id}" + }, var.s3_integrator.config) + constraints = var.s3_integrator.constraints + model_uuid = var.model_uuid + name = "${var.mimir_coordinator.app_name}-s3-integrator" + storage_directives = var.s3_integrator.storage_directives + trust = true + units = var.s3_integrator.units + + charm { + name = "s3-integrator" + channel = local.channels.s3_integrator + revision = local.revisions.s3_integrator + } +} + +resource "juju_secret" "tempo_s3_credentials" { + count = var.storage_backend == "s3" ? 1 : 0 + model_uuid = var.model_uuid + name = "tempo-s3-credentials" + value = { + access-key = var.s3_access_key + secret-key = var.s3_secret_key + } + info = "S3 credentials for Tempo" +} + +resource "juju_access_secret" "tempo_s3_credentials_access" { + count = var.storage_backend == "s3" ? 1 : 0 + model_uuid = var.model_uuid + applications = [juju_application.s3_integrator_tempo[0].name] + secret_id = juju_secret.tempo_s3_credentials[0].secret_id +} + +resource "juju_application" "s3_integrator_tempo" { + count = var.storage_backend == "s3" ? 1 : 0 + config = merge({ + endpoint = var.s3_endpoint + bucket = var.tempo_bucket + credentials = "secret:${juju_secret.tempo_s3_credentials[0].secret_id}" + }, var.s3_integrator.config) + constraints = var.s3_integrator.constraints + model_uuid = var.model_uuid + name = "${var.tempo_coordinator.app_name}-s3-integrator" + storage_directives = var.s3_integrator.storage_directives + trust = true + units = var.s3_integrator.units + + charm { + name = "s3-integrator" + channel = local.channels.s3_integrator + revision = local.revisions.s3_integrator + } +} diff --git a/terraform/cos-dev/integrations.tf b/terraform/cos-dev/integrations.tf index 966ef8a2..9efba9a7 100644 --- a/terraform/cos-dev/integrations.tf +++ b/terraform/cos-dev/integrations.tf @@ -649,7 +649,7 @@ resource "juju_integration" "ingress" { app_name = module.mimir_coordinator.app_name endpoint = "ingress" } - } : k => v if var.ingress[k] + } : k => v if var.ingress[k] && !var.mesh_enabled } model_uuid = var.model_uuid @@ -666,7 +666,7 @@ resource "juju_integration" "ingress" { } resource "juju_integration" "grafana_ingress" { - count = var.ingress.grafana ? 1 : 0 + count = var.ingress.grafana && !var.mesh_enabled ? 1 : 0 model_uuid = var.model_uuid @@ -694,7 +694,7 @@ resource "juju_integration" "traefik_route" { app_name = module.tempo_coordinator.app_name endpoint = module.tempo_coordinator.requires.ingress } - } : k => v if var.ingress[k] + } : k => v if var.ingress[k] && !var.mesh_enabled } model_uuid = var.model_uuid @@ -710,6 +710,86 @@ resource "juju_integration" "traefik_route" { } } +resource "juju_integration" "istio_ingress" { + for_each = { + for k, v in { + alertmanager = { + app_name = module.alertmanager.app_name + endpoint = module.alertmanager.requires.ingress + } + catalogue = { + app_name = module.catalogue.app_name + endpoint = module.catalogue.requires.ingress + } + loki = { + app_name = module.loki_coordinator.app_name + endpoint = "ingress" + } + mimir = { + app_name = module.mimir_coordinator.app_name + endpoint = "ingress" + } + } : k => v if var.ingress[k] && var.mesh_enabled + } + + model_uuid = var.model_uuid + + application { + name = module.istio_ingress[0].app_name + endpoint = module.istio_ingress[0].provides.ingress + } + + application { + name = each.value.app_name + endpoint = each.value.endpoint + } +} + +resource "juju_integration" "grafana_istio_ingress" { + count = var.ingress.grafana && var.mesh_enabled ? 1 : 0 + + model_uuid = var.model_uuid + + application { + name = module.grafana.app_name + endpoint = module.grafana.requires.ingress + } + + application { + name = module.istio_ingress[0].app_name + endpoint = module.istio_ingress[0].provides.ingress + } + + lifecycle { replace_triggered_by = [terraform_data.grafana_ingress_interface, terraform_data.grafana_litestream_resource] } +} + +resource "juju_integration" "istio_ingress_route" { + for_each = { + for k, v in { + opentelemetry_collector = { + app_name = module.opentelemetry_collector.app_name + endpoint = module.opentelemetry_collector.requires.ingress + } + tempo = { + app_name = module.tempo_coordinator.app_name + endpoint = module.tempo_coordinator.requires.ingress + } + } : k => v if var.ingress[k] && var.mesh_enabled + } + + model_uuid = var.model_uuid + + application { + name = module.istio_ingress[0].app_name + endpoint = module.istio_ingress[0].provides["istio-ingress-route"] + } + + application { + name = each.value.app_name + endpoint = each.value.endpoint + } +} + # -------------- # Provided by OpenTelemetry Collector -------------- resource "juju_integration" "opentelemetry_collector_mimir_metrics" { @@ -862,3 +942,50 @@ resource "juju_integration" "traces_and_metrics_correlation" { } } +# -------------- # Service Mesh --------------------- + +resource "juju_integration" "istio_beacon" { + for_each = var.mesh_enabled ? { + alertmanager = { + app_name = module.alertmanager.app_name + endpoint = module.alertmanager.requires.service_mesh + } + catalogue = { + app_name = module.catalogue.app_name + endpoint = module.catalogue.requires.service_mesh + } + grafana = { + app_name = module.grafana.app_name + endpoint = module.grafana.requires.service_mesh + } + loki = { + app_name = module.loki_coordinator.app_name + endpoint = module.loki_coordinator.requires.service_mesh + } + mimir = { + app_name = module.mimir_coordinator.app_name + endpoint = module.mimir_coordinator.requires.service_mesh + } + opentelemetry_collector = { + app_name = module.opentelemetry_collector.app_name + endpoint = module.opentelemetry_collector.requires.service_mesh + } + tempo = { + app_name = module.tempo_coordinator.app_name + endpoint = module.tempo_coordinator.requires.service_mesh + } + } : {} + + model_uuid = var.model_uuid + + application { + name = module.istio_beacon[0].app_name + endpoint = module.istio_beacon[0].provides.service_mesh + } + + application { + name = each.value.app_name + endpoint = each.value.endpoint + } +} + diff --git a/terraform/cos-dev/offers.tf b/terraform/cos-dev/offers.tf index a5937c88..37e047c1 100644 --- a/terraform/cos-dev/offers.tf +++ b/terraform/cos-dev/offers.tf @@ -34,3 +34,107 @@ resource "juju_offer" "tempo_tracing" { application_name = module.tempo_coordinator.app_name endpoints = [module.tempo_coordinator.provides.tracing] } + +# -------------- # CMR Mesh offers -------------- # + +resource "juju_offer" "alertmanager_provide_cmr_mesh" { + name = "alertmanager-provide-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.alertmanager.app_name + endpoints = [module.alertmanager.provides.provide_cmr_mesh] +} + +resource "juju_offer" "alertmanager_require_cmr_mesh" { + name = "alertmanager-require-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.alertmanager.app_name + endpoints = [module.alertmanager.requires.require_cmr_mesh] +} + +resource "juju_offer" "catalogue_provide_cmr_mesh" { + name = "catalogue-provide-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.catalogue.app_name + endpoints = [module.catalogue.provides.provide_cmr_mesh] +} + +resource "juju_offer" "catalogue_require_cmr_mesh" { + name = "catalogue-require-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.catalogue.app_name + endpoints = [module.catalogue.requires.require_cmr_mesh] +} + +resource "juju_offer" "grafana_provide_cmr_mesh" { + name = "grafana-provide-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.grafana.app_name + endpoints = [module.grafana.provides.provide_cmr_mesh] + + lifecycle { replace_triggered_by = [terraform_data.grafana_litestream_resource] } +} + +resource "juju_offer" "grafana_require_cmr_mesh" { + name = "grafana-require-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.grafana.app_name + endpoints = [module.grafana.requires.require_cmr_mesh] + + lifecycle { replace_triggered_by = [terraform_data.grafana_litestream_resource] } +} + +resource "juju_offer" "loki_provide_cmr_mesh" { + name = "loki-provide-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.loki_coordinator.app_name + endpoints = [module.loki_coordinator.provides.provide_cmr_mesh] +} + +resource "juju_offer" "loki_require_cmr_mesh" { + name = "loki-require-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.loki_coordinator.app_name + endpoints = [module.loki_coordinator.requires.require_cmr_mesh] +} + +resource "juju_offer" "mimir_provide_cmr_mesh" { + name = "mimir-provide-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.mimir_coordinator.app_name + endpoints = [module.mimir_coordinator.provides.provide_cmr_mesh] +} + +resource "juju_offer" "mimir_require_cmr_mesh" { + name = "mimir-require-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.mimir_coordinator.app_name + endpoints = [module.mimir_coordinator.requires.require_cmr_mesh] +} + +resource "juju_offer" "opentelemetry_collector_provide_cmr_mesh" { + name = "opentelemetry-collector-provide-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.opentelemetry_collector.app_name + endpoints = [module.opentelemetry_collector.provides.provide_cmr_mesh] +} + +resource "juju_offer" "opentelemetry_collector_require_cmr_mesh" { + name = "opentelemetry-collector-require-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.opentelemetry_collector.app_name + endpoints = [module.opentelemetry_collector.requires.require_cmr_mesh] +} + +resource "juju_offer" "tempo_provide_cmr_mesh" { + name = "tempo-provide-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.tempo_coordinator.app_name + endpoints = [module.tempo_coordinator.provides.provide_cmr_mesh] +} + +resource "juju_offer" "tempo_require_cmr_mesh" { + name = "tempo-require-cmr-mesh" + model_uuid = var.model_uuid + application_name = module.tempo_coordinator.app_name + endpoints = [module.tempo_coordinator.requires.require_cmr_mesh] +} diff --git a/terraform/cos-dev/outputs.tf b/terraform/cos-dev/outputs.tf index a9cfbd75..74d663ba 100644 --- a/terraform/cos-dev/outputs.tf +++ b/terraform/cos-dev/outputs.tf @@ -7,6 +7,22 @@ output "offers" { loki_logging = juju_offer.loki_logging mimir_receive_remote_write = juju_offer.mimir_receive_remote_write tempo_tracing = juju_offer.tempo_tracing + + # CMR Mesh + alertmanager_provide_cmr_mesh = juju_offer.alertmanager_provide_cmr_mesh + alertmanager_require_cmr_mesh = juju_offer.alertmanager_require_cmr_mesh + catalogue_provide_cmr_mesh = juju_offer.catalogue_provide_cmr_mesh + catalogue_require_cmr_mesh = juju_offer.catalogue_require_cmr_mesh + grafana_provide_cmr_mesh = juju_offer.grafana_provide_cmr_mesh + grafana_require_cmr_mesh = juju_offer.grafana_require_cmr_mesh + loki_provide_cmr_mesh = juju_offer.loki_provide_cmr_mesh + loki_require_cmr_mesh = juju_offer.loki_require_cmr_mesh + mimir_provide_cmr_mesh = juju_offer.mimir_provide_cmr_mesh + mimir_require_cmr_mesh = juju_offer.mimir_require_cmr_mesh + opentelemetry_collector_provide_cmr_mesh = juju_offer.opentelemetry_collector_provide_cmr_mesh + opentelemetry_collector_require_cmr_mesh = juju_offer.opentelemetry_collector_require_cmr_mesh + tempo_provide_cmr_mesh = juju_offer.tempo_provide_cmr_mesh + tempo_require_cmr_mesh = juju_offer.tempo_require_cmr_mesh } description = "All Juju offers which are exposed by this product module" } diff --git a/terraform/cos-dev/tests/conditional_ingress.tftest.hcl b/terraform/cos-dev/tests/conditional_ingress.tftest.hcl index bb4f421a..bc9f5762 100644 --- a/terraform/cos-dev/tests/conditional_ingress.tftest.hcl +++ b/terraform/cos-dev/tests/conditional_ingress.tftest.hcl @@ -162,3 +162,119 @@ run "ingress_partial_override" { error_message = "Expected traefik_route to contain 'opentelemetry_collector' key" } } + +# --- mesh_enabled: all ingress via istio, none via traefik --- + +run "mesh_ingress_all_enabled" { + command = plan + + variables { + mesh_enabled = true + internal_tls = false + } + + # Traefik ingress resources should be empty + assert { + condition = length(juju_integration.ingress) == 0 + error_message = "Expected 0 traefik ingress integrations when mesh is enabled, got ${length(juju_integration.ingress)}" + } + + assert { + condition = length(juju_integration.grafana_ingress) == 0 + error_message = "Expected 0 grafana_ingress integrations when mesh is enabled, got ${length(juju_integration.grafana_ingress)}" + } + + assert { + condition = length(juju_integration.traefik_route) == 0 + error_message = "Expected 0 traefik_route integrations when mesh is enabled, got ${length(juju_integration.traefik_route)}" + } + + # Istio ingress resources should be populated + assert { + condition = length(juju_integration.istio_ingress) == 4 + error_message = "Expected 4 istio_ingress integrations (alertmanager, catalogue, loki, mimir), got ${length(juju_integration.istio_ingress)}" + } + + assert { + condition = length(juju_integration.grafana_istio_ingress) == 1 + error_message = "Expected 1 grafana_istio_ingress integration, got ${length(juju_integration.grafana_istio_ingress)}" + } + + assert { + condition = length(juju_integration.istio_ingress_route) == 2 + error_message = "Expected 2 istio_ingress_route integrations (opentelemetry_collector, tempo), got ${length(juju_integration.istio_ingress_route)}" + } +} + +# --- mesh_enabled with partial ingress override --- + +run "mesh_ingress_partial" { + command = plan + + variables { + mesh_enabled = true + internal_tls = false + ingress = { + alertmanager = false + catalogue = true + grafana = false + loki = true + mimir = false + opentelemetry_collector = true + tempo = false + } + } + + # Traefik resources should all be empty + assert { + condition = length(juju_integration.ingress) == 0 + error_message = "Expected 0 traefik ingress integrations when mesh is enabled, got ${length(juju_integration.ingress)}" + } + + assert { + condition = length(juju_integration.grafana_ingress) == 0 + error_message = "Expected 0 grafana_ingress integrations when mesh is enabled" + } + + assert { + condition = length(juju_integration.traefik_route) == 0 + error_message = "Expected 0 traefik_route integrations when mesh is enabled" + } + + # Istio resources should respect the ingress toggles + assert { + condition = length(juju_integration.istio_ingress) == 2 + error_message = "Expected 2 istio_ingress integrations (catalogue, loki), got ${length(juju_integration.istio_ingress)}" + } + + assert { + condition = length(juju_integration.grafana_istio_ingress) == 0 + error_message = "Expected 0 grafana_istio_ingress integrations when grafana ingress is disabled" + } + + assert { + condition = length(juju_integration.istio_ingress_route) == 1 + error_message = "Expected 1 istio_ingress_route integration (opentelemetry_collector), got ${length(juju_integration.istio_ingress_route)}" + } +} + +# --- mesh disabled (default): no istio ingress resources --- + +run "no_mesh_no_istio_ingress" { + command = plan + + assert { + condition = length(juju_integration.istio_ingress) == 0 + error_message = "Expected 0 istio_ingress integrations when mesh is disabled, got ${length(juju_integration.istio_ingress)}" + } + + assert { + condition = length(juju_integration.grafana_istio_ingress) == 0 + error_message = "Expected 0 grafana_istio_ingress integrations when mesh is disabled" + } + + assert { + condition = length(juju_integration.istio_ingress_route) == 0 + error_message = "Expected 0 istio_ingress_route integrations when mesh is disabled" + } +} diff --git a/terraform/cos-dev/tests/mesh_enabled.tftest.hcl b/terraform/cos-dev/tests/mesh_enabled.tftest.hcl new file mode 100644 index 00000000..54f7448b --- /dev/null +++ b/terraform/cos-dev/tests/mesh_enabled.tftest.hcl @@ -0,0 +1,65 @@ +mock_provider "juju" {} + +variables { + model_uuid = "00000000-0000-0000-0000-000000000000" +} + +# --- mesh_enabled=false (default): no istio modules or integrations deployed --- + +run "mesh_disabled_by_default" { + command = plan + + assert { + condition = length(module.istio-beacon) == 0 + error_message = "Expected no istio-beacon module when mesh is disabled" + } + + assert { + condition = length(module.istio-ingress) == 0 + error_message = "Expected no istio-ingress module when mesh is disabled" + } + + assert { + condition = length(juju_integration.istio_beacon) == 0 + error_message = "Expected no istio_beacon integrations when mesh is disabled" + } +} + +# --- mesh_enabled=true with internal_tls=false: istio modules and integrations deployed --- + +run "mesh_enabled_without_tls" { + command = plan + + variables { + mesh_enabled = true + internal_tls = false + } + + assert { + condition = length(module.istio-beacon) == 1 + error_message = "Expected istio-beacon module to be deployed when mesh is enabled" + } + + assert { + condition = length(module.istio-ingress) == 1 + error_message = "Expected istio-ingress module to be deployed when mesh is enabled" + } + + assert { + condition = length(juju_integration.istio_beacon) == 7 + error_message = "Expected 7 istio_beacon integrations (one per component) when mesh is enabled" + } +} + +# --- mesh_enabled=true with internal_tls=true (default): validation error --- + +run "mesh_enabled_with_internal_tls_fails" { + command = plan + + variables { + mesh_enabled = true + internal_tls = true + } + + expect_failures = [var.mesh_enabled] +} diff --git a/terraform/cos-dev/variables.tf b/terraform/cos-dev/variables.tf index d1d871fb..9c5c6de2 100644 --- a/terraform/cos-dev/variables.tf +++ b/terraform/cos-dev/variables.tf @@ -46,6 +46,18 @@ variable "storage_backend" { } } +# -------------- # Mesh configurations -------------- +variable "mesh_enabled" { + description = "Specify whether to enable the service mesh or not." + type = bool + default = false + + validation { + condition = !(var.mesh_enabled && var.internal_tls) + error_message = "mesh_enabled and internal_tls cannot both be enabled at the same time." + } +} + # -------------- # TLS configurations -------------- variable "internal_tls" { @@ -183,6 +195,32 @@ variable "grafana" { description = "Application configuration for Grafana. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application" } +variable "istio_beacon" { + type = object({ + app_name = optional(string, "istio-beacon") + config = optional(map(string), {}) + constraints = optional(string, "arch=amd64") + revision = optional(number, null) + storage_directives = optional(map(string), {}) + units = optional(number, 1) + }) + default = {} + description = "Application configuration for istio-beacon. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application" +} + +variable "istio_ingress" { + type = object({ + app_name = optional(string, "istio-ingress") + config = optional(map(string), {}) + constraints = optional(string, "arch=amd64") + revision = optional(number, null) + storage_directives = optional(map(string), {}) + units = optional(number, 1) + }) + default = {} + description = "Application configuration for istio-ingress. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application" +} + variable "loki_coordinator" { type = object({ app_name = optional(string, "loki")