diff --git a/database-monitoring/terraform/README.md b/database-monitoring/terraform/README.md index 864b844..c99c02c 100644 --- a/database-monitoring/terraform/README.md +++ b/database-monitoring/terraform/README.md @@ -186,6 +186,8 @@ No modules. | [aws_security_group.efs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_vpc_security_group_ingress_rule.efs_from_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | | [random_id.hash_secret](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | +| [aws_ecs_service.runtime](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ecs_service) | data source | +| [aws_ecs_task_definition.runtime](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ecs_task_definition) | data source | | [aws_subnet.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | ## Inputs @@ -202,6 +204,7 @@ No modules. | [name](#input_name) | What the product shows for this agent. Also the ECS cluster, service and task family name, sanitised to the characters ECS allows. | `string` | n/a | yes | | [persistent_ledger](#input_persistent_ledger) | Keep the agent's state on an encrypted EFS file system so it persists across task replacement. When false, the state is on the task's ephemeral storage and every replacement starts it empty. | `bool` | `true` | no | | [publication](#input_publication) | Name of the publication whose tables are streamed. | `string` | `"elastio_monitor"` | no | +| [runtime_updates](#input_runtime_updates) | Preserve images selected by Elastio when applying infrastructure/size changes. Enable after the ECS service has first been created. | `bool` | `false` | no | | [security_group_ids](#input_security_group_ids) | Security groups attached to the task. The database's own security group must admit them on its port. | `list(string)` | n/a | yes | | [server_url](#input_server_url) | Base URL of the server the agent reports to. | `string` | n/a | yes | | [slot](#input_slot) | Name of the existing pgoutput replication slot. The agent never creates or drops one. | `string` | `"elastio_monitor"` | no | @@ -223,3 +226,19 @@ No modules. | [task_role_arn](#output_task_role_arn) | IAM role the running container assumes. The agent has no AWS code; with persistent state the role may mount the state file system, and nothing else. | + +## Runtime image upgrades + +After the first deployment, set `runtime_updates = true` when installing the +Elastio deployment updater. Manual updates and optional automatic upgrades are +then controlled through Elastio. Terraform continues to own infrastructure and +CPU/memory sizing. During a size change or later apply, the module reads the +service's current task definition and preserves its agent and detector images, +including immutable digests selected by the updater. It does not reset them to +the module's older `image` defaults. Do not enable this option on a first-ever +apply: the service must exist for its deployed images to be read. + +The updater runs separately from the monitored task with permission to update +only its configured ECS service and pass the existing task/execution roles. +Installation and Docker/VM equivalents are documented in the +[agent updater guide](https://github.com/elastio/database-monitoring-agent/tree/master/deploy/updater). diff --git a/database-monitoring/terraform/main.tf b/database-monitoring/terraform/main.tf index 8f90615..3bc6419 100644 --- a/database-monitoring/terraform/main.tf +++ b/database-monitoring/terraform/main.tf @@ -65,7 +65,7 @@ locals { agent_container_base = { name = "${local.prefix}-agent" - image = var.image + image = lookup(local.runtime_images, "elastio-dbmon-agent", var.image) essential = true user = "${local.uid}:${local.gid}" @@ -134,7 +134,7 @@ locals { # metrics to every interface). The agent needs no inbound rule at all. ed_container = { name = "${local.prefix}-ed" - image = var.ed_image + image = lookup(local.runtime_images, "elastio-dbmon-ed", var.ed_image) essential = false user = "${local.uid}:${local.gid}" memory = local.ed_memory diff --git a/database-monitoring/terraform/runtime-updates.tf b/database-monitoring/terraform/runtime-updates.tf new file mode 100644 index 0000000..99b6990 --- /dev/null +++ b/database-monitoring/terraform/runtime-updates.tf @@ -0,0 +1,25 @@ +# After bootstrap, Elastio owns image selection. Terraform reads the service's +# currently deployed images before changing size or infrastructure, so a later +# apply cannot restore an obsolete module image pin. +variable "runtime_updates" { + description = "Preserve images selected by Elastio when applying infrastructure/size changes. Enable after the ECS service has first been created." + type = bool + default = false +} + +data "aws_ecs_service" "runtime" { + count = var.runtime_updates ? 1 : 0 + service_name = local.ecs_name + cluster_arn = aws_ecs_cluster.this.arn +} + +data "aws_ecs_task_definition" "runtime" { + count = var.runtime_updates ? 1 : 0 + task_definition = data.aws_ecs_service.runtime[0].task_definition +} + +locals { + runtime_images = var.runtime_updates ? { + for container in jsondecode(data.aws_ecs_task_definition.runtime[0].container_definitions) : container.name => container.image + } : {} +} diff --git a/database-monitoring/terraform/tests/module.tftest.hcl b/database-monitoring/terraform/tests/module.tftest.hcl index ab1fbda..5248e0b 100644 --- a/database-monitoring/terraform/tests/module.tftest.hcl +++ b/database-monitoring/terraform/tests/module.tftest.hcl @@ -362,3 +362,27 @@ run "content_analysis_needs_room_for_the_detector" { expect_failures = [var.content_analysis] } + +run "runtime_update_images_survive_size_changes" { + command = plan + variables { + runtime_updates = true + content_analysis = true + task_cpu = 1024 + task_memory = 2048 + image = "old-agent:0.1.9" + ed_image = "old-python:0.1.9" + } + override_data { + target = data.aws_ecs_service.runtime[0] + values = { task_definition = "arn:aws:ecs:us-east-1:123456789012:task-definition/runtime:7" } + } + override_data { + target = data.aws_ecs_task_definition.runtime[0] + values = { container_definitions = "[{\"name\":\"elastio-dbmon-agent\",\"image\":\"agent@sha256:new\"},{\"name\":\"elastio-dbmon-ed\",\"image\":\"native@sha256:new\"}]" } + } + assert { + condition = local.agent_container.image == "agent@sha256:new" && local.ed_container.image == "native@sha256:new" && aws_ecs_task_definition.this.cpu == "1024" + error_message = "Sizing must preserve the images selected by Elastio, including native ED." + } +}