diff --git a/terraform/litellm/aws/.terraform.lock.hcl b/terraform/litellm/aws/.terraform.lock.hcl index 30b2a194c4c..79097043254 100644 --- a/terraform/litellm/aws/.terraform.lock.hcl +++ b/terraform/litellm/aws/.terraform.lock.hcl @@ -6,6 +6,7 @@ provider "registry.terraform.io/hashicorp/aws" { constraints = "~> 5.60" hashes = [ "h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=", + "h1:edXOJWE4ORX8Fm+dpVpICzMZJat4AX0VRCAy/xkcOc0=", "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", @@ -28,6 +29,7 @@ provider "registry.terraform.io/hashicorp/random" { version = "3.8.1" constraints = "~> 3.6" hashes = [ + "h1:Eexl06+6J+s75uD46+WnZtpJZYRVUMB0AiuPBifK6Jc=", "h1:u8AKlWVDTH5r9YLSeswoVEjiY72Rt4/ch7U+61ZDkiQ=", "zh:08dd03b918c7b55713026037c5400c48af5b9f468f483463321bd18e17b907b4", "zh:0eee654a5542dc1d41920bbf2419032d6f0d5625b03bd81339e5b33394a3e0ae", diff --git a/terraform/litellm/aws/README.md b/terraform/litellm/aws/README.md index 6fcbdc2f500..bedb6f4e98b 100644 --- a/terraform/litellm/aws/README.md +++ b/terraform/litellm/aws/README.md @@ -419,8 +419,9 @@ To run *many* tenants from a single config, call the module with ```hcl module "litellm" { for_each = toset(["acme", "globex"]) - source = "github.com/BerriAI/litellm//terraform/litellm/aws?ref=" - tenant = each.key + source = "github.com/BerriAI/litellm//terraform/litellm/aws?ref=" + image_tag = "" + tenant = each.key env = "prod" region = "us-west-2" azs = ["us-west-2a", "us-west-2b"] @@ -485,6 +486,8 @@ provider "aws" { module "litellm" { source = "github.com/BerriAI/litellm//terraform/litellm/aws?ref=" + image_tag = "" # a git ref ships no image default, a registry version does + region = "us-west-2" tenant = "acme" env = "prod" @@ -499,12 +502,20 @@ top — set org-wide tags there, per-deployment tags via the `tags` input. ## Image pulls -The defaults pull from `ghcr.io/berriai/litellm-:v1.86.0-dev`, +The defaults pull `ghcr.io/berriai/litellm-:${image_tag}`, which is anonymous-readable. There are four images: `litellm-gateway`, `litellm-backend`, `litellm-ui`, and `litellm-migrations` (slim image used only by the one-off migration task — runs `prisma migrate deploy` against -the writer DB and exits). Bump them together when bumping LiteLLM. To pull -from a private registry: +the writer DB and exits). A published module version (Terraform Registry +`BerriAI/litellm/aws`) ships with `image_tag` stamped to its own release +tag, so bumping the module version bumps all four images with it. A git +ref of this monorepo has no published images of its own, so there +`image_tag` is required. Set `image_tag` to pin a different LiteLLM +release, or +`image_registry` to pull the same four images from a mirror. Set +`gateway_image` / `backend_image` / `ui_image` / `migrations_image` only +for a per-component override (custom build, different tag). To pull from a +private registry: - **ECR (same account)**: the execution role already has `AmazonECSTaskExecutionRolePolicy`, which grants ECR pull for repos in diff --git a/terraform/litellm/aws/ecs.tf b/terraform/litellm/aws/ecs.tf index 2b235c2bad5..412e6c423f5 100644 --- a/terraform/litellm/aws/ecs.tf +++ b/terraform/litellm/aws/ecs.tf @@ -229,7 +229,7 @@ locals { gateway_metrics_container = local.metrics_enabled ? [ { name = "metrics" - image = var.gateway_image + image = local.gateway_image essential = false entryPoint = ["python", "-m", "litellm.proxy.prometheus_metrics_server"] command = ["--port", tostring(var.gateway_metrics_port)] @@ -306,7 +306,7 @@ locals { collector_container = var.collector_enabled ? [{ name = "collector" - image = var.gateway_image + image = local.gateway_image essential = false cpu = var.collector_cpu memory = var.collector_memory @@ -389,7 +389,7 @@ resource "aws_ecs_task_definition" "gateway" { merge( { name = "gateway" - image = var.gateway_image + image = local.gateway_image essential = true portMappings = [{ containerPort = 4000, protocol = "tcp" }] @@ -499,7 +499,7 @@ resource "aws_ecs_task_definition" "backend" { merge( { name = "backend" - image = var.backend_image + image = local.backend_image essential = true portMappings = [{ containerPort = 4001, protocol = "tcp" }] @@ -591,7 +591,7 @@ resource "aws_ecs_task_definition" "ui" { container_definitions = jsonencode([ { name = "ui" - image = var.ui_image + image = local.ui_image essential = true portMappings = [{ containerPort = 3000, protocol = "tcp" }] diff --git a/terraform/litellm/aws/examples/default/.terraform.lock.hcl b/terraform/litellm/aws/examples/default/.terraform.lock.hcl index 4a059b2b268..ce530fa767b 100644 --- a/terraform/litellm/aws/examples/default/.terraform.lock.hcl +++ b/terraform/litellm/aws/examples/default/.terraform.lock.hcl @@ -6,6 +6,7 @@ provider "registry.terraform.io/hashicorp/aws" { constraints = "~> 5.60" hashes = [ "h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=", + "h1:edXOJWE4ORX8Fm+dpVpICzMZJat4AX0VRCAy/xkcOc0=", "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", @@ -29,6 +30,7 @@ provider "registry.terraform.io/hashicorp/random" { constraints = "~> 3.6" hashes = [ "h1:OO+IuvQJSPmWdN8AyyIEvPJbLvDQpgX/zbktoa9KsJE=", + "h1:UlBuNVuCGJ39tTv2c5gz2NRZnQbXfbIWbTzWcth5o74=", "zh:161ad0bd9a75768c82f53fb6e7172a9d8be2d4889b012645a34795031aaf1bf1", "zh:19dc9a5b17729725ccfc4f45b0500af0ee5bc6b6b160c7adb8f2bf617d2c80ea", "zh:269eda8fe42daa7974d5a34d166c3ba9defe80cde86c01e4dadcfdf2e1f05e5f", diff --git a/terraform/litellm/aws/examples/default/main.tf b/terraform/litellm/aws/examples/default/main.tf index 115003f7ddd..5e7a65f907f 100644 --- a/terraform/litellm/aws/examples/default/main.tf +++ b/terraform/litellm/aws/examples/default/main.tf @@ -38,6 +38,9 @@ module "litellm" { litellm_license = var.litellm_license ui_password = var.ui_password + image_registry = var.image_registry + image_tag = var.image_tag != "" ? var.image_tag : null + acm_certificate_arn = var.acm_certificate_arn allow_plaintext_alb = var.allow_plaintext_alb s3_force_destroy = var.s3_force_destroy diff --git a/terraform/litellm/aws/examples/default/terraform.tfvars.example b/terraform/litellm/aws/examples/default/terraform.tfvars.example index 880ecf56555..dd392d9ecc5 100644 --- a/terraform/litellm/aws/examples/default/terraform.tfvars.example +++ b/terraform/litellm/aws/examples/default/terraform.tfvars.example @@ -58,12 +58,15 @@ env = "stage" # s3_force_destroy = false # skip_final_snapshot = false -# Component images and per-task sizing/autoscaling are NOT exposed as -# variables in this example (it keeps the curated surface small). They -# default to working public GHCR images. To pin images or tune -# CPU/memory/workers/autoscaling, set those inputs directly on the -# `module "litellm"` block in main.tf — the full list is in -# ../../variables.tf — or call the module from your own root config. +# Images. image_tag is applied to all four litellm-* images. A published +# module version ships with it stamped to its own release tag, a checkout of +# the monorepo has no default and needs it set. image_registry can point at +# an ECR pull-through mirror. Per-component *_image overrides and per-task +# sizing/autoscaling are NOT exposed here. Set them directly on the +# `module "litellm"` block in main.tf (full list in ../../variables.tf) or +# call the module from your own root config. +# image_registry = "ghcr.io/berriai" +# image_tag = "" # ---------- proxy_config (mirrors helm gateway.config.proxy_config) ---------- # proxy_config = { diff --git a/terraform/litellm/aws/examples/default/variables.tf b/terraform/litellm/aws/examples/default/variables.tf index f8140266fca..27c6c95e553 100644 --- a/terraform/litellm/aws/examples/default/variables.tf +++ b/terraform/litellm/aws/examples/default/variables.tf @@ -105,6 +105,18 @@ variable "ui_password" { } # TLS — provide an ACM cert for production, or opt into HTTP-only for dev. +variable "image_registry" { + description = "Registry path prefix; images composed as /litellm-:." + type = string + default = "ghcr.io/berriai" +} + +variable "image_tag" { + description = "Tag applied to all four litellm-* images. Bump in lockstep." + type = string + default = "" +} + variable "acm_certificate_arn" { description = "ACM cert ARN for the ALB HTTPS listener. Empty → no TLS." type = string diff --git a/terraform/litellm/aws/examples/default/versions.tf b/terraform/litellm/aws/examples/default/versions.tf index 73b88e91dce..06ebe5bd2c5 100644 --- a/terraform/litellm/aws/examples/default/versions.tf +++ b/terraform/litellm/aws/examples/default/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 1.6.0" + required_version = ">= 1.9.0" required_providers { aws = { diff --git a/terraform/litellm/aws/locals.tf b/terraform/litellm/aws/locals.tf index b89bd486d02..c06b171d6bd 100644 --- a/terraform/litellm/aws/locals.tf +++ b/terraform/litellm/aws/locals.tf @@ -11,6 +11,11 @@ locals { # the stack can reference local.name. name = "${var.tenant}-litellm-${var.env}" + gateway_image = var.gateway_image != "" ? var.gateway_image : "${var.image_registry}/litellm-gateway:${var.image_tag}" + backend_image = var.backend_image != "" ? var.backend_image : "${var.image_registry}/litellm-backend:${var.image_tag}" + ui_image = var.ui_image != "" ? var.ui_image : "${var.image_registry}/litellm-ui:${var.image_tag}" + migrations_image = var.migrations_image != "" ? var.migrations_image : "${var.image_registry}/litellm-migrations:${var.image_tag}" + # This is a reusable module — it declares no `provider` block, so the AWS # provider's `default_tags` is the caller's concern, not ours. To keep the # same per-resource tagging the stack had when it owned the provider, the diff --git a/terraform/litellm/aws/migrations.tf b/terraform/litellm/aws/migrations.tf index e924b29eba0..57c33f69a8b 100644 --- a/terraform/litellm/aws/migrations.tf +++ b/terraform/litellm/aws/migrations.tf @@ -28,7 +28,7 @@ resource "aws_ecs_task_definition" "migrations" { container_definitions = jsonencode([{ name = "migrations" - image = var.migrations_image + image = local.migrations_image essential = true # No entryPoint/command override — the image's ENTRYPOINT runs run.py. diff --git a/terraform/litellm/aws/tests/byo_infrastructure.tftest.hcl b/terraform/litellm/aws/tests/byo_infrastructure.tftest.hcl index 5a619bc98b1..702ba37184c 100644 --- a/terraform/litellm/aws/tests/byo_infrastructure.tftest.hcl +++ b/terraform/litellm/aws/tests/byo_infrastructure.tftest.hcl @@ -14,6 +14,7 @@ mock_provider "aws" { mock_provider "random" {} variables { + image_tag = "v9.9.9" region = "us-east-1" tenant = "acme" env = "test" diff --git a/terraform/litellm/aws/tests/collector.tftest.hcl b/terraform/litellm/aws/tests/collector.tftest.hcl index 1465130232f..082f17b2c08 100644 --- a/terraform/litellm/aws/tests/collector.tftest.hcl +++ b/terraform/litellm/aws/tests/collector.tftest.hcl @@ -13,6 +13,7 @@ mock_provider "aws" { mock_provider "random" {} variables { + image_tag = "v9.9.9" region = "us-east-1" tenant = "acme" env = "test" @@ -66,7 +67,7 @@ run "enabled_adds_a_sidecar_that_shares_the_gateway_transport" { assert { condition = ( - local.collector_container[0].image == var.gateway_image && + local.collector_container[0].image == local.gateway_image && local.collector_container[0].entryPoint == ["sh", "-c"] && local.collector_container[0].command == ["exec python -m litellm.proxy.collector"] && local.collector_container[0].essential == false && diff --git a/terraform/litellm/aws/tests/connection_pool.tftest.hcl b/terraform/litellm/aws/tests/connection_pool.tftest.hcl index 7408b1179ed..c618bd11768 100644 --- a/terraform/litellm/aws/tests/connection_pool.tftest.hcl +++ b/terraform/litellm/aws/tests/connection_pool.tftest.hcl @@ -12,6 +12,7 @@ mock_provider "aws" { mock_provider "random" {} variables { + image_tag = "v9.9.9" region = "us-east-1" tenant = "acme" env = "test" diff --git a/terraform/litellm/aws/tests/images.tftest.hcl b/terraform/litellm/aws/tests/images.tftest.hcl new file mode 100644 index 00000000000..2d03a46b7a3 --- /dev/null +++ b/terraform/litellm/aws/tests/images.tftest.hcl @@ -0,0 +1,104 @@ +mock_provider "aws" { + mock_data "aws_iam_policy_document" { + defaults = { + json = "{\"Version\":\"2012-10-17\",\"Statement\":[]}" + } + } +} +mock_provider "random" {} + +variables { + image_tag = "v9.9.9" + region = "us-east-1" + tenant = "acme" + env = "test" + allow_plaintext_alb = true + azs = ["us-east-1a", "us-east-1b"] +} + +run "every_component_follows_image_registry_and_image_tag" { + command = plan + + variables { + image_registry = "123456789012.dkr.ecr.us-east-1.amazonaws.com/mirror" + image_tag = "v9.9.9" + } + + assert { + condition = ( + local.gateway_image == "123456789012.dkr.ecr.us-east-1.amazonaws.com/mirror/litellm-gateway:v9.9.9" && + local.backend_image == "123456789012.dkr.ecr.us-east-1.amazonaws.com/mirror/litellm-backend:v9.9.9" && + local.ui_image == "123456789012.dkr.ecr.us-east-1.amazonaws.com/mirror/litellm-ui:v9.9.9" && + local.migrations_image == "123456789012.dkr.ecr.us-east-1.amazonaws.com/mirror/litellm-migrations:v9.9.9" + ) + error_message = "All four images must be composed as /litellm-:." + } +} + +run "an_empty_image_tag_fails_closed_unless_every_image_is_overridden" { + command = plan + + variables { + image_tag = "" + } + + expect_failures = [var.image_tag] +} + +run "a_null_image_tag_takes_the_module_default" { + command = plan + + variables { + image_tag = null + } + + expect_failures = [var.image_tag] +} + +run "four_explicit_uris_need_no_image_tag" { + command = plan + + variables { + image_tag = "" + gateway_image = "example.com/custom/gateway:nightly" + backend_image = "example.com/custom/backend:nightly" + ui_image = "example.com/custom/ui:nightly" + migrations_image = "example.com/custom/migrations:nightly" + } + + assert { + condition = local.migrations_image == "example.com/custom/migrations:nightly" + error_message = "Explicit per-component URIs must satisfy the module without an image_tag." + } +} + +run "composed_defaults_pull_from_ghcr_at_the_image_tag" { + command = plan + + assert { + condition = alltrue([ + for image in [local.gateway_image, local.backend_image, local.ui_image, local.migrations_image] : + startswith(image, "ghcr.io/berriai/litellm-") && endswith(image, ":${var.image_tag}") + ]) + error_message = "With no overrides every component must come from ghcr.io/berriai at image_tag." + } +} + +run "a_per_component_uri_wins_over_the_composed_image" { + command = plan + + variables { + image_tag = "v9.9.9" + gateway_image = "example.com/custom/gateway:nightly" + } + + assert { + condition = ( + local.gateway_image == "example.com/custom/gateway:nightly" && + local.backend_image == "ghcr.io/berriai/litellm-backend:v9.9.9" && + local.ui_image == "ghcr.io/berriai/litellm-ui:v9.9.9" && + local.migrations_image == "ghcr.io/berriai/litellm-migrations:v9.9.9" + ) + error_message = "A per-component override must replace only that component's image." + } +} diff --git a/terraform/litellm/aws/tests/metrics_sidecar.tftest.hcl b/terraform/litellm/aws/tests/metrics_sidecar.tftest.hcl index de839d7e5be..b330d4b91c3 100644 --- a/terraform/litellm/aws/tests/metrics_sidecar.tftest.hcl +++ b/terraform/litellm/aws/tests/metrics_sidecar.tftest.hcl @@ -11,6 +11,7 @@ mock_provider "aws" { mock_provider "random" {} variables { + image_tag = "v9.9.9" region = "us-east-1" tenant = "acme" env = "test" diff --git a/terraform/litellm/aws/tests/workload_autoscaling.tftest.hcl b/terraform/litellm/aws/tests/workload_autoscaling.tftest.hcl index 94e281faf94..ea6c343767c 100644 --- a/terraform/litellm/aws/tests/workload_autoscaling.tftest.hcl +++ b/terraform/litellm/aws/tests/workload_autoscaling.tftest.hcl @@ -11,6 +11,7 @@ mock_provider "aws" { mock_provider "random" {} variables { + image_tag = "v9.9.9" region = "us-east-1" tenant = "acme" env = "test" diff --git a/terraform/litellm/aws/variables.tf b/terraform/litellm/aws/variables.tf index 580a0cc657a..9b7a279a907 100644 --- a/terraform/litellm/aws/variables.tf +++ b/terraform/litellm/aws/variables.tf @@ -135,38 +135,53 @@ variable "azs" { # ---------- Component images ---------- # -# Defaults pin the four componentized images at the same release tag on -# GHCR. Override on a per-component basis in tfvars when bumping; bump them -# together when bumping the LiteLLM release. +variable "image_registry" { + description = "Registry path prefix used to compose the four LiteLLM image URIs as `/litellm-:`. Per-component overrides (`gateway_image`, `backend_image`, `ui_image`, `migrations_image`) bypass this entirely when set." + type = string + default = "ghcr.io/berriai" +} + +variable "image_tag" { + description = "Tag applied to all four litellm-* images when composed from `image_registry`. A published module version ships with this stamped to its own release tag, which matches the `v`-prefixed tag published to GHCR for the same LiteLLM release. Set it to pin a different LiteLLM release. When sourcing the module from a git ref of the monorepo it has no default and must be set." + type = string + default = "" + nullable = false + + validation { + condition = var.image_tag != "" || (var.gateway_image != "" && var.backend_image != "" && var.ui_image != "" && var.migrations_image != "") + error_message = "image_tag is empty. Published module versions (registry.terraform.io/modules/BerriAI/litellm) ship with it stamped to their own release. A git ref of the monorepo has no published images of its own, so set image_tag to a published LiteLLM release tag or set all four *_image URIs." + } +} variable "gateway_image" { - description = "Container image for the gateway (data plane, port 4000). Tag must match a tag actually published to GHCR — the split images use the `v`-prefixed semver convention." + description = "Full image URI for the gateway (data plane, port 4000). Empty (default) composes from `image_registry` + `image_tag`." type = string - default = "ghcr.io/berriai/litellm-gateway:v1.86.0-dev" + default = "" } variable "backend_image" { - description = "Container image for the backend (management API, port 4001)." + description = "Full image URI for the backend (management API, port 4001). Empty (default) composes from `image_registry` + `image_tag`." type = string - default = "ghcr.io/berriai/litellm-backend:v1.86.0-dev" + default = "" } variable "ui_image" { - description = "Container image for the UI (nginx static export, port 3000)." + description = "Full image URI for the UI (nginx static export, port 3000). Empty (default) composes from `image_registry` + `image_tag`." type = string - default = "ghcr.io/berriai/litellm-ui:v1.86.0-dev" + default = "" } variable "migrations_image" { description = <<-EOT - Container image for the one-off prisma migration task. Built from + Full image URI for the one-off prisma migration task. Empty (default) + composes from `image_registry` + `image_tag`. Built from `migrations/Dockerfile` — slim image whose ENTRYPOINT runs `python3 /app/run.py` (assembles DATABASE_URL from DATABASE_* env vars via DatabaseURLSettings, then runs `prisma migrate deploy`). Should track the same release tag as gateway/backend/ui. EOT type = string - default = "ghcr.io/berriai/litellm-migrations:v1.86.0-dev" + default = "" } # ---------- Service sizing ---------- diff --git a/terraform/litellm/aws/versions.tf b/terraform/litellm/aws/versions.tf index 73b88e91dce..06ebe5bd2c5 100644 --- a/terraform/litellm/aws/versions.tf +++ b/terraform/litellm/aws/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 1.6.0" + required_version = ">= 1.9.0" required_providers { aws = { diff --git a/terraform/litellm/gcp/.terraform.lock.hcl b/terraform/litellm/gcp/.terraform.lock.hcl index 013391b53d4..ad8b77ce422 100644 --- a/terraform/litellm/gcp/.terraform.lock.hcl +++ b/terraform/litellm/gcp/.terraform.lock.hcl @@ -6,6 +6,7 @@ provider "registry.terraform.io/hashicorp/google" { constraints = "~> 6.10" hashes = [ "h1:79CwMTsp3Ud1nOl5hFS5mxQHyT0fGVye7pqpU0PPlHI=", + "h1:faTJQOetP9/RYuHwA3r2SWnuYoyzQNm4tUWZrZggcgY=", "zh:1f3513fcfcbf7ca53d667a168c5067a4dd91a4d4cccd19743e248ff31065503c", "zh:3da7db8fc2c51a77dd958ea8baaa05c29cd7f829bd8941c26e2ea9cb3aadc1e5", "zh:3e09ac3f6ca8111cbb659d38c251771829f4347ab159a12db195e211c76068bb", @@ -26,6 +27,7 @@ provider "registry.terraform.io/hashicorp/google-beta" { constraints = "~> 6.10" hashes = [ "h1:P2GiUJM1frlPtBViwKn1A9V2dVBdGuWcX80w9TdH8ZE=", + "h1:uxh4ME3hhSzVjmiWgA1IQqYqg25MV6FMVArHyA6Ki5o=", "zh:18b442bd0a05321d39dda1e9e3f1bdede4e61bc2ac62cc7a67037a3864f75101", "zh:2e387c51455862828bec923a3ec81abf63a4d998da470cf00e09003bda53d668", "zh:3942e708fa84ebe54996086f4b1398cb747fe19cbcd0be07ace528291fb35dee", @@ -45,6 +47,7 @@ provider "registry.terraform.io/hashicorp/random" { version = "3.8.1" constraints = "~> 3.6" hashes = [ + "h1:Eexl06+6J+s75uD46+WnZtpJZYRVUMB0AiuPBifK6Jc=", "h1:u8AKlWVDTH5r9YLSeswoVEjiY72Rt4/ch7U+61ZDkiQ=", "zh:08dd03b918c7b55713026037c5400c48af5b9f468f483463321bd18e17b907b4", "zh:0eee654a5542dc1d41920bbf2419032d6f0d5625b03bd81339e5b33394a3e0ae", diff --git a/terraform/litellm/gcp/README.md b/terraform/litellm/gcp/README.md index c90af26ebf2..3227dcac179 100644 --- a/terraform/litellm/gcp/README.md +++ b/terraform/litellm/gcp/README.md @@ -54,11 +54,15 @@ Then point the stack at it via `image_registry`: ```hcl image_registry = "us-central1-docker.pkg.dev/my-gcp-project/litellm/berriai" -image_tag = "v1.86.0-dev" ``` -The four `litellm-:${image_tag}` URIs are composed from those -two vars. Set `gateway_image` / `backend_image` / `ui_image` / +The four `litellm-:${image_tag}` URIs are composed from +`image_registry` and `image_tag`. A published module version (Terraform +Registry `BerriAI/litellm/google`) ships with `image_tag` stamped to its +own release tag, so bumping the module version bumps all four images with +it. A git ref of this monorepo has no published images of its own, so +there `image_tag` is required. Set `image_tag` to pin a different LiteLLM +release. Set `gateway_image` / `backend_image` / `ui_image` / `migrations_image` only if you need a per-component override (custom build, different tag). @@ -485,6 +489,8 @@ provider "google-beta" { module "litellm" { source = "github.com/BerriAI/litellm//terraform/litellm/gcp?ref=" + image_tag = "" # a git ref ships no image default, a registry version does + project = "my-gcp-project" region = "us-central1" tenant = "acme" diff --git a/terraform/litellm/gcp/examples/default/.terraform.lock.hcl b/terraform/litellm/gcp/examples/default/.terraform.lock.hcl index e6285567315..55568afb834 100644 --- a/terraform/litellm/gcp/examples/default/.terraform.lock.hcl +++ b/terraform/litellm/gcp/examples/default/.terraform.lock.hcl @@ -6,6 +6,7 @@ provider "registry.terraform.io/hashicorp/google" { constraints = "~> 6.10" hashes = [ "h1:79CwMTsp3Ud1nOl5hFS5mxQHyT0fGVye7pqpU0PPlHI=", + "h1:faTJQOetP9/RYuHwA3r2SWnuYoyzQNm4tUWZrZggcgY=", "zh:1f3513fcfcbf7ca53d667a168c5067a4dd91a4d4cccd19743e248ff31065503c", "zh:3da7db8fc2c51a77dd958ea8baaa05c29cd7f829bd8941c26e2ea9cb3aadc1e5", "zh:3e09ac3f6ca8111cbb659d38c251771829f4347ab159a12db195e211c76068bb", @@ -26,6 +27,7 @@ provider "registry.terraform.io/hashicorp/google-beta" { constraints = "~> 6.10" hashes = [ "h1:P2GiUJM1frlPtBViwKn1A9V2dVBdGuWcX80w9TdH8ZE=", + "h1:uxh4ME3hhSzVjmiWgA1IQqYqg25MV6FMVArHyA6Ki5o=", "zh:18b442bd0a05321d39dda1e9e3f1bdede4e61bc2ac62cc7a67037a3864f75101", "zh:2e387c51455862828bec923a3ec81abf63a4d998da470cf00e09003bda53d668", "zh:3942e708fa84ebe54996086f4b1398cb747fe19cbcd0be07ace528291fb35dee", @@ -46,6 +48,7 @@ provider "registry.terraform.io/hashicorp/random" { constraints = "~> 3.6" hashes = [ "h1:OO+IuvQJSPmWdN8AyyIEvPJbLvDQpgX/zbktoa9KsJE=", + "h1:UlBuNVuCGJ39tTv2c5gz2NRZnQbXfbIWbTzWcth5o74=", "zh:161ad0bd9a75768c82f53fb6e7172a9d8be2d4889b012645a34795031aaf1bf1", "zh:19dc9a5b17729725ccfc4f45b0500af0ee5bc6b6b160c7adb8f2bf617d2c80ea", "zh:269eda8fe42daa7974d5a34d166c3ba9defe80cde86c01e4dadcfdf2e1f05e5f", diff --git a/terraform/litellm/gcp/examples/default/deploystack.json b/terraform/litellm/gcp/examples/default/deploystack.json index 47d1fd914ce..732d4a187b1 100644 --- a/terraform/litellm/gcp/examples/default/deploystack.json +++ b/terraform/litellm/gcp/examples/default/deploystack.json @@ -25,7 +25,7 @@ { "name": "image_tag", "description": "Tag for the four litellm-* images (gateway, backend, ui, migrations). Bump together when bumping LiteLLM", - "default": "v1.86.0-dev" + "default": "" }, { "name": "image_registry", diff --git a/terraform/litellm/gcp/examples/default/main.tf b/terraform/litellm/gcp/examples/default/main.tf index 782d4ee4b65..9b18a6df863 100644 --- a/terraform/litellm/gcp/examples/default/main.tf +++ b/terraform/litellm/gcp/examples/default/main.tf @@ -41,7 +41,7 @@ module "litellm" { ui_password = var.ui_password image_registry = var.image_registry - image_tag = var.image_tag + image_tag = var.image_tag != "" ? var.image_tag : null lb_domains = var.lb_domains allow_plaintext_lb = var.allow_plaintext_lb diff --git a/terraform/litellm/gcp/examples/default/terraform.tfvars.example b/terraform/litellm/gcp/examples/default/terraform.tfvars.example index ec6206734e2..6456b525d08 100644 --- a/terraform/litellm/gcp/examples/default/terraform.tfvars.example +++ b/terraform/litellm/gcp/examples/default/terraform.tfvars.example @@ -38,12 +38,13 @@ env = "stage" # Images. Cloud Run rejects ghcr.io, so a real deploy must point # image_registry at an Artifact Registry remote repo (see README "Image -# pulls"); image_tag is applied to all four litellm-* images. Per-component -# *_image overrides are NOT exposed here — set them directly on the -# `module "litellm"` block in main.tf (see ../../variables.tf) if you need -# to mix-and-match versions. +# pulls"); image_tag is applied to all four litellm-* images. A published +# module version ships with it stamped to its own release tag, a checkout of +# the monorepo has no default and needs it set. Per-component *_image overrides are +# NOT exposed here. Set them directly on the `module "litellm"` block in +# main.tf (see ../../variables.tf) if you need to mix-and-match versions. # image_registry = "us-central1-docker.pkg.dev/my-gcp-project/litellm/berriai" -# image_tag = "v1.86.0-dev" +# image_tag = "" # ---------- proxy_config (mirrors helm gateway.config.proxy_config) ---------- # proxy_config = { diff --git a/terraform/litellm/gcp/examples/default/variables.tf b/terraform/litellm/gcp/examples/default/variables.tf index 08b78346df3..dcf84572fd7 100644 --- a/terraform/litellm/gcp/examples/default/variables.tf +++ b/terraform/litellm/gcp/examples/default/variables.tf @@ -85,7 +85,7 @@ variable "image_registry" { variable "image_tag" { description = "Tag applied to all four litellm-* images. Bump in lockstep." type = string - default = "v1.86.0-dev" + default = "" } # TLS — provide DNS names for a managed cert, or opt into HTTP-only for dev. diff --git a/terraform/litellm/gcp/examples/default/versions.tf b/terraform/litellm/gcp/examples/default/versions.tf index a630c59afd0..55214b5c51a 100644 --- a/terraform/litellm/gcp/examples/default/versions.tf +++ b/terraform/litellm/gcp/examples/default/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 1.6.0" + required_version = ">= 1.9.0" required_providers { google = { diff --git a/terraform/litellm/gcp/tests/collector.tftest.hcl b/terraform/litellm/gcp/tests/collector.tftest.hcl index 7a5ea781acb..f16d02d9c7e 100644 --- a/terraform/litellm/gcp/tests/collector.tftest.hcl +++ b/terraform/litellm/gcp/tests/collector.tftest.hcl @@ -7,6 +7,7 @@ mock_provider "google-beta" {} mock_provider "random" {} variables { + image_tag = "v9.9.9" project_id = "acme-test" region = "us-central1" tenant = "acme" diff --git a/terraform/litellm/gcp/tests/connection_pool.tftest.hcl b/terraform/litellm/gcp/tests/connection_pool.tftest.hcl index 999e4f0ff95..c0ad842ff95 100644 --- a/terraform/litellm/gcp/tests/connection_pool.tftest.hcl +++ b/terraform/litellm/gcp/tests/connection_pool.tftest.hcl @@ -18,6 +18,7 @@ mock_provider "google-beta" {} mock_provider "random" {} variables { + image_tag = "v9.9.9" project_id = "test-project" tenant = "tenant" env = "test" diff --git a/terraform/litellm/gcp/tests/deps_only.tftest.hcl b/terraform/litellm/gcp/tests/deps_only.tftest.hcl index 610c49d5b52..11815af9051 100644 --- a/terraform/litellm/gcp/tests/deps_only.tftest.hcl +++ b/terraform/litellm/gcp/tests/deps_only.tftest.hcl @@ -14,6 +14,7 @@ mock_provider "google-beta" {} mock_provider "random" {} variables { + image_tag = "v9.9.9" project_id = "test-project" tenant = "tenant" env = "test" @@ -112,6 +113,20 @@ run "deps_only_creates_no_runtime" { } } +run "deps_only_needs_no_image_tag" { + command = plan + + variables { + create_runtime = false + image_tag = "" + } + + assert { + condition = length(google_cloud_run_v2_service.gateway) == 0 + error_message = "deps-only mode must plan without an image tag since it deploys no images" + } +} + run "existing_network_attaches_data_stores" { command = plan diff --git a/terraform/litellm/gcp/tests/metrics_sidecar.tftest.hcl b/terraform/litellm/gcp/tests/metrics_sidecar.tftest.hcl index 7de62065d23..fb5446b4c56 100644 --- a/terraform/litellm/gcp/tests/metrics_sidecar.tftest.hcl +++ b/terraform/litellm/gcp/tests/metrics_sidecar.tftest.hcl @@ -14,6 +14,7 @@ mock_provider "google-beta" {} mock_provider "random" {} variables { + image_tag = "v9.9.9" project_id = "test-project" tenant = "tenant" env = "test" diff --git a/terraform/litellm/gcp/variables.tf b/terraform/litellm/gcp/variables.tf index 412f919ab89..6161e893e02 100644 --- a/terraform/litellm/gcp/variables.tf +++ b/terraform/litellm/gcp/variables.tf @@ -144,9 +144,15 @@ variable "image_registry" { } variable "image_tag" { - description = "Tag applied to all four litellm-* images when composed from `image_registry`. Bump in lockstep when bumping LiteLLM. Must match a tag actually published to GHCR — the split images use the `v`-prefixed semver convention (e.g. `v1.86.0-dev`)." + description = "Tag applied to all four litellm-* images when composed from `image_registry`. A published module version ships with this stamped to its own release tag, which matches the `v`-prefixed tag published to GHCR for the same LiteLLM release. Set it to pin a different LiteLLM release. When sourcing the module from a git ref of the monorepo it has no default and must be set." type = string - default = "v1.86.0-dev" + default = "" + nullable = false + + validation { + condition = !var.create_runtime || var.image_tag != "" || (var.gateway_image != "" && var.backend_image != "" && var.ui_image != "" && var.migrations_image != "") + error_message = "image_tag is empty. Published module versions (registry.terraform.io/modules/BerriAI/litellm) ship with it stamped to their own release. A git ref of the monorepo has no published images of its own, so set image_tag to a published LiteLLM release tag or set all four *_image URIs." + } } variable "gateway_image" { diff --git a/terraform/litellm/gcp/versions.tf b/terraform/litellm/gcp/versions.tf index a630c59afd0..55214b5c51a 100644 --- a/terraform/litellm/gcp/versions.tf +++ b/terraform/litellm/gcp/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 1.6.0" + required_version = ">= 1.9.0" required_providers { google = {