Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4669d7b
feat: COS-Dev mesh
MichaelThamm May 14, 2026
05f3fbd
Merge branch 'main' into feat/service-mesh-cos-dev
MichaelThamm May 19, 2026
555e7e3
overhaul for mesh
MichaelThamm May 17, 2026
061d98d
chore
MichaelThamm May 17, 2026
7bada97
chore
MichaelThamm May 18, 2026
18b2641
chore
MichaelThamm May 19, 2026
02ac17a
feat: Generic COS assertions function (#351)
MichaelThamm May 20, 2026
fecf0ee
chore
MichaelThamm May 21, 2026
0c2bdf4
Merge branch 'main' into feat/service-mesh-cos-dev
MichaelThamm May 21, 2026
fccec76
chore
MichaelThamm May 21, 2026
d91bee9
chore
MichaelThamm May 21, 2026
bfffa49
refactor: Traefik and SSC are conditional
MichaelThamm May 21, 2026
567dfce
chore
MichaelThamm May 21, 2026
f5120ab
chore
MichaelThamm May 21, 2026
928012b
Merge branch 'refactor/housekeeping' into feat/service-mesh-cos-dev
MichaelThamm May 22, 2026
5e1c5c4
chore
MichaelThamm May 22, 2026
2be7775
chore
MichaelThamm May 22, 2026
f91048b
chore
MichaelThamm May 22, 2026
34b7a4f
chore
MichaelThamm May 22, 2026
04254ba
chore
MichaelThamm May 22, 2026
1b046e2
chore
MichaelThamm May 22, 2026
75a71a8
Merge branch 'refactor/housekeeping' into feat/service-mesh-cos-dev
MichaelThamm May 22, 2026
9462ac2
chore
MichaelThamm May 22, 2026
0e84219
chore
MichaelThamm May 22, 2026
4ed8950
chore
MichaelThamm May 22, 2026
7da4a86
Merge branch 'main' into feat/service-mesh-cos-dev
MichaelThamm May 26, 2026
5900945
Merge branch 'main' into feat/service-mesh-cos-dev
MichaelThamm May 27, 2026
8ea99f1
Merge branch 'main' into feat/service-mesh-cos-dev
MichaelThamm May 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions terraform/cos-dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ This module is intended for development and testing environments where full HA i
| <a name="module_alertmanager"></a> [alertmanager](#module\_alertmanager) | git::https://github.com/canonical/alertmanager-k8s-operator//terraform | n/a |
| <a name="module_catalogue"></a> [catalogue](#module\_catalogue) | git::https://github.com/canonical/catalogue-k8s-operator//terraform | n/a |
| <a name="module_grafana"></a> [grafana](#module\_grafana) | git::https://github.com/canonical/grafana-k8s-operator//terraform | n/a |
| <a name="module_istio_beacon"></a> [istio\_beacon](#module\_istio\_beacon) | git::https://github.com/canonical/istio-beacon-k8s-operator//terraform | n/a |
| <a name="module_istio_ingress"></a> [istio\_ingress](#module\_istio\_ingress) | git::https://github.com/canonical/istio-ingress-k8s-operator//terraform | n/a |
| <a name="module_loki_coordinator"></a> [loki\_coordinator](#module\_loki\_coordinator) | git::https://github.com/canonical/loki-operators//coordinator/terraform | n/a |
| <a name="module_loki_worker"></a> [loki\_worker](#module\_loki\_worker) | git::https://github.com/canonical/loki-operators//worker/terraform | n/a |
| <a name="module_loki_worker_backend"></a> [loki\_worker\_backend](#module\_loki\_worker\_backend) | git::https://github.com/canonical/loki-operators//worker/terraform | n/a |
Expand Down Expand Up @@ -54,9 +56,12 @@ This module is intended for development and testing environments where full HA i
| <a name="input_grafana"></a> [grafana](#input\_grafana) | Application configuration for Grafana. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application | <pre>object({<br/> app_name = optional(string, "grafana")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number, null)<br/> storage_directives = optional(map(string), {})<br/> units = optional(number, 1)<br/> })</pre> | `{}` | no |
| <a name="input_ingress"></a> [ingress](#input\_ingress) | Per-component toggle for ingress integrations | <pre>object({<br/> alertmanager = optional(bool, true)<br/> catalogue = optional(bool, true)<br/> grafana = optional(bool, true)<br/> loki = optional(bool, true)<br/> mimir = optional(bool, true)<br/> opentelemetry_collector = optional(bool, true)<br/> tempo = optional(bool, true)<br/> })</pre> | `{}` | no |
| <a name="input_internal_tls"></a> [internal\_tls](#input\_internal\_tls) | Specify whether to use TLS or not for internal COS communication. By default, TLS is enabled using self-signed-certificates | `bool` | `true` | no |
| <a name="input_istio_beacon"></a> [istio\_beacon](#input\_istio\_beacon) | Application configuration for istio-beacon. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application | <pre>object({<br/> app_name = optional(string, "istio-beacon")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number, null)<br/> storage_directives = optional(map(string), {})<br/> units = optional(number, 1)<br/> })</pre> | `{}` | no |
| <a name="input_istio_ingress"></a> [istio\_ingress](#input\_istio\_ingress) | Application configuration for istio-ingress. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application | <pre>object({<br/> app_name = optional(string, "istio-ingress")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number, null)<br/> storage_directives = optional(map(string), {})<br/> units = optional(number, 1)<br/> })</pre> | `{}` | no |
| <a name="input_loki_bucket"></a> [loki\_bucket](#input\_loki\_bucket) | Loki S3 bucket name | `string` | `"loki"` | no |
| <a name="input_loki_coordinator"></a> [loki\_coordinator](#input\_loki\_coordinator) | Application configuration for the Loki coordinator. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application | <pre>object({<br/> app_name = optional(string, "loki")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number, null)<br/> storage_directives = optional(map(string), {})<br/> units = optional(number, 1)<br/> })</pre> | `{}` | no |
| <a name="input_loki_worker"></a> [loki\_worker](#input\_loki\_worker) | Application configuration for the Loki worker(s). For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application | <pre>object({<br/> app_name = optional(string, "loki-worker")<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number, null)<br/> # Monolithic mode (role-all)<br/> config = optional(map(string), {})<br/> storage_directives = optional(map(string), {})<br/> units = optional(number, 1)<br/> # Distributed mode<br/> backend_config = optional(map(string), {})<br/> read_config = optional(map(string), {})<br/> write_config = optional(map(string), {})<br/> backend_storage_directives = optional(map(string), {})<br/> read_storage_directives = optional(map(string), {})<br/> write_storage_directives = optional(map(string), {})<br/> backend_units = optional(number, 1)<br/> read_units = optional(number, 1)<br/> write_units = optional(number, 1)<br/> })</pre> | `{}` | no |
| <a name="input_service_mesh"></a> [mesh\_enabled](#input\_mesh\_enabled) | Configure the service mesh. | `bool` | `false` | no |
| <a name="input_mimir_bucket"></a> [mimir\_bucket](#input\_mimir\_bucket) | Mimir S3 bucket name | `string` | `"mimir"` | no |
| <a name="input_mimir_coordinator"></a> [mimir\_coordinator](#input\_mimir\_coordinator) | Application configuration for the Mimir coordinator. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application | <pre>object({<br/> app_name = optional(string, "mimir")<br/> config = optional(map(string), {})<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number, null)<br/> storage_directives = optional(map(string), {})<br/> units = optional(number, 1)<br/> })</pre> | `{}` | no |
| <a name="input_mimir_worker"></a> [mimir\_worker](#input\_mimir\_worker) | Application configuration for the Mimir worker(s). For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application | <pre>object({<br/> app_name = optional(string, "mimir-worker")<br/> constraints = optional(string, "arch=amd64")<br/> revision = optional(number, null)<br/> # Monolithic mode (role-all)<br/> config = optional(map(string), {})<br/> storage_directives = optional(map(string), {})<br/> units = optional(number, 1)<br/> # Distributed mode<br/> backend_config = optional(map(string), {})<br/> read_config = optional(map(string), {})<br/> write_config = optional(map(string), {})<br/> backend_storage_directives = optional(map(string), {})<br/> read_storage_directives = optional(map(string), {})<br/> write_storage_directives = optional(map(string), {})<br/> backend_units = optional(number, 1)<br/> read_units = optional(number, 1)<br/> write_units = optional(number, 1)<br/> })</pre> | `{}` | no |
Expand Down
28 changes: 28 additions & 0 deletions terraform/cos-dev/applications.tf
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,34 @@ module "grafana" {
replace_triggers = [terraform_data.grafana_litestream_resource.id]
}

module "istio_ingress" {
source = "git::https://github.com/canonical/istio-ingress-k8s-operator//terraform"
count = local.istio_ingress_enabled ? 1 : 0

app_name = var.istio_ingress.app_name
channel = local.channels.istio_ingress
config = var.istio_ingress.config
constraints = var.istio_ingress.constraints
model_uuid = var.model_uuid
revision = local.revisions.istio_ingress
storage_directives = var.istio_ingress.storage_directives
units = var.istio_ingress.units
}

module "istio_beacon" {
source = "git::https://github.com/canonical/istio-beacon-k8s-operator//terraform"
count = var.service_mesh ? 1 : 0

app_name = var.istio_beacon.app_name
channel = local.channels.istio_beacon
config = var.istio_beacon.config
constraints = var.istio_beacon.constraints
model_uuid = var.model_uuid
revision = local.revisions.istio_beacon

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to feature resources here like in this PR for example:

This requires the beacon and istio modules to feature this as an input as well, e.g.:

storage_directives = var.istio_beacon.storage_directives
units = var.istio_beacon.units
}

module "loki_coordinator" {
source = "git::https://github.com/canonical/loki-operators//coordinator/terraform"

Expand Down
127 changes: 127 additions & 0 deletions terraform/cos-dev/integrations.tf
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,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 local.istio_ingress_enabled && var.ingress[k]
}

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 = local.istio_ingress_enabled && var.ingress.grafana ? 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 local.istio_ingress_enabled && var.ingress[k]
}

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" {
Expand Down Expand Up @@ -877,3 +957,50 @@ resource "juju_integration" "traces_and_metrics_correlation" {
endpoint = "send-datasource"
}
}

# -------------- # Service Mesh ---------------------

resource "juju_integration" "istio_beacon" {
for_each = var.service_mesh ? {
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
}
}
23 changes: 15 additions & 8 deletions terraform/cos-dev/locals.tf
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
locals {
tls_termination = var.external_certificates_offer_url != null ? true : false
reverse_proxy_enabled = anytrue(values(var.ingress))
traefik_enabled = local.reverse_proxy_enabled
traefik_enabled = local.reverse_proxy_enabled && !var.service_mesh
istio_ingress_enabled = local.reverse_proxy_enabled && var.service_mesh
traefik_base = "ubuntu@20.04"
tracks = {
alertmanager = "dev"
catalogue = "dev"
grafana = "dev"
loki = "dev"
mimir = "dev"
otelcol = "dev"
tempo = "dev"
alertmanager = "dev"
catalogue = "dev"
grafana = "dev"
istio_beacon = "dev"
istio_ingress = "dev"
loki = "dev"
mimir = "dev"
otelcol = "dev"
tempo = "dev"

s3_integrator = "2"
seaweedfs = "latest"
Expand All @@ -21,6 +24,8 @@ locals {
alertmanager = "${local.tracks.alertmanager}/${var.risk}"
catalogue = "${local.tracks.catalogue}/${var.risk}"
grafana = "${local.tracks.grafana}/${var.risk}"
istio_beacon = "${local.tracks.istio_beacon}/${var.risk}"
istio_ingress = "${local.tracks.istio_ingress}/${var.risk}"
loki = "${local.tracks.loki}/${var.risk}"
mimir = "${local.tracks.mimir}/${var.risk}"
otelcol = "${local.tracks.otelcol}/${var.risk}"
Expand All @@ -34,6 +39,8 @@ locals {
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
istio_beacon = var.istio_beacon.revision != null ? var.istio_beacon.revision : data.juju_charm.istio_beacon_info.revision
istio_ingress = var.istio_ingress.revision != null ? var.istio_ingress.revision : data.juju_charm.istio_ingress_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
Expand Down
2 changes: 2 additions & 0 deletions terraform/cos-dev/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ output "components" {
alertmanager = module.alertmanager
catalogue = module.catalogue
grafana = module.grafana
istio_beacon = try(module.istio_beacon[0], null)
istio_ingress = try(module.istio_ingress[0], null)
loki_coordinator = module.loki_coordinator
loki_worker = try(module.loki_worker[0], null)
loki_worker_backend = try(module.loki_worker_backend[0], null)
Expand Down
71 changes: 71 additions & 0 deletions terraform/cos-dev/tests/conditional_ingress.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,74 @@ run "traefik_ingress_disabled" {
error_message = "Unexpected traefik_route integrations when ingress is disabled"
}
}

# --- istio: all ingress enabled ---

run "istio_ingress_enabled" {
command = plan

variables {
internal_tls = false
service_mesh = true
}

assert {
condition = length(module.istio_ingress) == 1
error_message = "Expected an istio_ingress module when ingress is enabled"
}

assert {
condition = length(juju_integration.istio_ingress) == 4
error_message = "Unexpected istio_ingress integrations when ingress is enabled"
}

assert {
condition = length(juju_integration.grafana_istio_ingress) == 1
error_message = "Unexpected grafana_istio_ingress integrations when ingress is enabled"
}

assert {
condition = length(juju_integration.istio_ingress_route) == 2
error_message = "Unexpected istio_ingress_route integrations when ingress is enabled"
}
}

# --- istio: all ingress disabled ---

run "istio_ingress_disabled" {
command = plan

variables {
internal_tls = false
service_mesh = true
ingress = {
alertmanager = false
catalogue = false
grafana = false
loki = false
mimir = false
opentelemetry_collector = false
tempo = false
}
}

assert {
condition = length(module.istio_ingress) == 0
error_message = "Expected no istio_ingress module when ingress is disabled"
}

assert {
condition = length(juju_integration.istio_ingress) == 0
error_message = "Unexpected istio_ingress integrations when ingress is disabled"
}

assert {
condition = length(juju_integration.grafana_istio_ingress) == 0
error_message = "Unexpected grafana_istio_ingress integrations when ingress is disabled"
}

assert {
condition = length(juju_integration.istio_ingress_route) == 0
error_message = "Unexpected istio_ingress_route integrations when ingress is disabled"
}
}
64 changes: 64 additions & 0 deletions terraform/cos-dev/tests/mesh.tftest.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
mock_provider "juju" {}

variables { model_uuid = "00000000-0000-0000-0000-000000000000" }

# --- mesh and internal_tls disabled: no validation error ---

run "mesh_and_internal_tls_disabled" {
command = plan

variables {
service_mesh = false
internal_tls = false
}
}

# --- mesh and internal_tls enabled: validation error ---

run "mesh_and_internal_tls_enabled_fails" {
command = plan

variables {
service_mesh = true
internal_tls = true
}

expect_failures = [var.service_mesh]
}

# --- default: mesh disabled - no istio beacon ---

run "mesh_disabled" {
command = plan

assert {
condition = length(module.istio_beacon) == 0
error_message = "Expected no istio_beacon module when the mesh is disabled"
}

assert {
condition = length(juju_integration.istio_beacon) == 0
error_message = "Expected no istio_beacon integrations when the mesh is disabled"
}
}

# --- mesh enabled: ingress via istio ---

run "service_mesh" {
command = plan

variables {
service_mesh = true
internal_tls = false
}

assert {
condition = length(module.istio_beacon) == 1
error_message = "Expected istio_beacon module when the mesh is enabled"
}

assert {
condition = length(juju_integration.istio_beacon) == 7
error_message = "Unexpected istio_beacon integrations when the mesh is enabled"
}
}
Loading
Loading