From de401addcb87ad8f65d25f934f9a981b0177713e Mon Sep 17 00:00:00 2001 From: Sepehr Javid Date: Mon, 22 Dec 2025 10:59:50 +0100 Subject: [PATCH 1/6] add service attachments for cross regional load balancer add docs and cleanup add missing toc link in readme add domain_name field in the service attachment docs update variable table with tfdoc.py --- modules/net-lb-app-int-cross-region/README.md | 77 ++++++++++++++++++- modules/net-lb-app-int-cross-region/main.tf | 31 ++++++++ .../net-lb-app-int-cross-region/variables.tf | 15 ++++ 3 files changed, 119 insertions(+), 4 deletions(-) diff --git a/modules/net-lb-app-int-cross-region/README.md b/modules/net-lb-app-int-cross-region/README.md index a1e4a8766..cf6438980 100644 --- a/modules/net-lb-app-int-cross-region/README.md +++ b/modules/net-lb-app-int-cross-region/README.md @@ -18,6 +18,7 @@ Due to the complexity of the underlying resources, changes to the configuration - [Serverless NEG creation](#serverless-neg-creation) - [Private Service Connect NEG creation](#private-service-connect-neg-creation) - [URL Map](#url-map) + - [PSC service attachment](#psc-service-attachment) - [Complex example](#complex-example) - [Deploying changes to load balancer configurations](#deploying-changes-to-load-balancer-configurations) - [Recipes](#recipes) @@ -424,7 +425,9 @@ module "ilb-l7" { backend_service_configs = { default = { backends = [{ - group = "neg" + group = "neg-ew1" + }, { + group = "neg-ew4" }] health_checks = [] } @@ -556,6 +559,71 @@ module "ilb-l7" { # tftest modules=1 resources=7 ``` +### PSC service attachment +The optional `service_attachment` variable allows [publishing Private Service Connect service](https://cloud.google.com/vpc/docs/configure-private-service-connect-producer) by configuring service attachment for all forwarding rules in every configured region. + +```hcl +module "ilb-l7" { + source = "./fabric/modules/net-lb-app-int-cross-region" + name = "ilb-test" + project_id = var.project_id + backend_service_configs = { + default = { + backends = [{ + group = "neg-ew1" + }, { + group = "neg-ew4" + }] + health_checks = [] + } + } + health_check_configs = {} + neg_configs = { + neg-ew1 = { + cloudrun = { + region = "europe-west1" + target_service = { + name = "my-run-service-ew1" + } + } + } + neg-ew4 = { + cloudrun = { + region = "europe-west4" + target_service = { + name = "my-run-service-ew4" + } + } + } + } + vpc_config = { + network = var.vpc.self_link + subnetworks = { + europe-west1 = var.subnet1.self_link + europe-west4 = var.subnet2.self_link + } + } + + service_attachment = { + nat_subnets = { + europe-west1 = [var.subnet_psc_ew1.self_link] + europe-west4 = [var.subnet_psc_ew4.self_link] + } + reconcile_connections = false + consumer_accept_lists = { + # map of `project_id` => `connection_limit` + (var.project_id) = 10 + } + domain_name = { + europe-west1 = var.psc_domain_name_ew1 + europe-west4 = var.psc_domain_name_ew4 + } + } +} +# tftest modules=1 resources=9 +``` + + ### Complex example This example mixes group and NEG backends, and shows how to set HTTPS for specific backends. @@ -738,7 +806,7 @@ When deploying changes to load balancer configuration please refer to [net-lb-ap | [backend-service.tf](./backend-service.tf) | Backend service resources. | google_compute_backend_service | | [groups.tf](./groups.tf) | None | google_compute_instance_group | | [health-check.tf](./health-check.tf) | Health check resource. | google_compute_health_check | -| [main.tf](./main.tf) | Module-level locals and resources. | google_compute_global_forwarding_rule · google_compute_network_endpoint · google_compute_network_endpoint_group · google_compute_region_network_endpoint_group · google_compute_target_http_proxy · google_compute_target_https_proxy | +| [main.tf](./main.tf) | Module-level locals and resources. | google_compute_global_forwarding_rule · google_compute_network_endpoint · google_compute_network_endpoint_group · google_compute_region_network_endpoint_group · google_compute_service_attachment · google_compute_target_http_proxy · google_compute_target_https_proxy | | [outputs.tf](./outputs.tf) | Module outputs. | | | [urlmap.tf](./urlmap.tf) | URL map resources. | google_compute_url_map | | [variables-backend-service.tf](./variables-backend-service.tf) | Backend services variables. | | @@ -753,7 +821,7 @@ When deploying changes to load balancer configuration please refer to [net-lb-ap |---|---|:---:|:---:|:---:| | [name](variables.tf#L72) | Load balancer name. | string | ✓ | | | [project_id](variables.tf#L153) | Project id. | string | ✓ | | -| [vpc_config](variables.tf#L180) | VPC-level configuration. | object({…}) | ✓ | | +| [vpc_config](variables.tf#L195) | VPC-level configuration. | object({…}) | ✓ | | | [addresses](variables.tf#L17) | Optional IP address used for the forwarding rule. | map(string) | | null | | [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} | | [description](variables.tf#L23) | Optional description used for resources. | string | | "Terraform managed." | @@ -765,7 +833,8 @@ When deploying changes to load balancer configuration please refer to [net-lb-ap | [neg_configs](variables.tf#L77) | Optional network endpoint groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} | | [ports](variables.tf#L143) | Optional ports for HTTP load balancer. | list(string) | | null | | [protocol](variables.tf#L158) | Protocol supported by this load balancer. | string | | "HTTP" | -| [service_directory_registration](variables.tf#L171) | Service directory namespace and service used to register this load balancer. | object({…}) | | null | +| [service_attachment](variables.tf#L171) | PSC service attachments. | object({…}) | | null | +| [service_directory_registration](variables.tf#L186) | Service directory namespace and service used to register this load balancer. | object({…}) | | null | | [urlmap_config](variables-urlmap.tf#L19) | The URL map configuration. | object({…}) | | {…} | ## Outputs diff --git a/modules/net-lb-app-int-cross-region/main.tf b/modules/net-lb-app-int-cross-region/main.tf index 28d00b9a4..0d2eec43f 100644 --- a/modules/net-lb-app-int-cross-region/main.tf +++ b/modules/net-lb-app-int-cross-region/main.tf @@ -99,6 +99,37 @@ resource "google_compute_target_https_proxy" "default" { url_map = google_compute_url_map.default.id } +resource "google_compute_service_attachment" "default" { + for_each = var.service_attachment == null ? {} : google_compute_global_forwarding_rule.forwarding_rules + project = var.project_id + region = each.key + name = each.value.name + description = var.service_attachment.description + target_service = each.value.id + nat_subnets = var.service_attachment.nat_subnets[each.key] + connection_preference = ( + var.service_attachment.automatic_connection + ? "ACCEPT_AUTOMATIC" + : "ACCEPT_MANUAL" + ) + consumer_reject_lists = var.service_attachment.consumer_reject_lists + domain_names = ( + var.service_attachment.domain_name == null + ? null + : [var.service_attachment.domain_name[each.key]] + ) + enable_proxy_protocol = var.service_attachment.enable_proxy_protocol + reconcile_connections = var.service_attachment.reconcile_connections + dynamic "consumer_accept_lists" { + for_each = var.service_attachment.consumer_accept_lists + iterator = accept + content { + project_id_or_num = accept.key + connection_limit = accept.value + } + } +} + resource "google_compute_network_endpoint_group" "default" { for_each = local.neg_zonal project = ( diff --git a/modules/net-lb-app-int-cross-region/variables.tf b/modules/net-lb-app-int-cross-region/variables.tf index ac9471858..02eadbd9d 100644 --- a/modules/net-lb-app-int-cross-region/variables.tf +++ b/modules/net-lb-app-int-cross-region/variables.tf @@ -168,6 +168,21 @@ variable "protocol" { } } +variable "service_attachment" { + description = "PSC service attachments." + type = object({ + automatic_connection = optional(bool, false) + consumer_accept_lists = optional(map(string), {}) + consumer_reject_lists = optional(list(string)) + description = optional(string) + domain_name = optional(map(string)) + enable_proxy_protocol = optional(bool, false) + nat_subnets = map(list(string)) + reconcile_connections = optional(bool) + }) + default = null +} + variable "service_directory_registration" { description = "Service directory namespace and service used to register this load balancer." type = object({ From d57026028355825ba1603ded7a03d19373551449 Mon Sep 17 00:00:00 2001 From: Sepehr Javid Date: Tue, 30 Dec 2025 16:14:39 +0100 Subject: [PATCH 2/6] fix failed tests for criss regional lb --- modules/net-lb-app-int-cross-region/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/net-lb-app-int-cross-region/README.md b/modules/net-lb-app-int-cross-region/README.md index cf6438980..f12fb801b 100644 --- a/modules/net-lb-app-int-cross-region/README.md +++ b/modules/net-lb-app-int-cross-region/README.md @@ -426,7 +426,7 @@ module "ilb-l7" { default = { backends = [{ group = "neg-ew1" - }, { + }, { group = "neg-ew4" }] health_checks = [] @@ -571,7 +571,7 @@ module "ilb-l7" { default = { backends = [{ group = "neg-ew1" - }, { + }, { group = "neg-ew4" }] health_checks = [] @@ -606,8 +606,8 @@ module "ilb-l7" { service_attachment = { nat_subnets = { - europe-west1 = [var.subnet_psc_ew1.self_link] - europe-west4 = [var.subnet_psc_ew4.self_link] + europe-west1 = [var.subnet_psc_1.self_link] + europe-west4 = [var.subnet_psc_2.self_link] } reconcile_connections = false consumer_accept_lists = { @@ -615,8 +615,8 @@ module "ilb-l7" { (var.project_id) = 10 } domain_name = { - europe-west1 = var.psc_domain_name_ew1 - europe-west4 = var.psc_domain_name_ew4 + europe-west1 = "ew1.p.example.com." + europe-west4 = "ew4.p.example.com." } } } From 8d736804721a5104b171f75355de0030f4d42c0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jan 2026 13:37:32 +0000 Subject: [PATCH 3/6] Bump qs and @google-cloud/functions-framework Bumps [qs](https://github.com/ljharb/qs) to 6.14.1 and updates ancestor dependency [@google-cloud/functions-framework](https://github.com/GoogleCloudPlatform/functions-framework-nodejs). These dependencies need to be updated together. Updates `qs` from 6.13.0 to 6.14.1 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.14.1) Updates `@google-cloud/functions-framework` from 4.0.0 to 4.0.1 - [Release notes](https://github.com/GoogleCloudPlatform/functions-framework-nodejs/releases) - [Changelog](https://github.com/GoogleCloudPlatform/functions-framework-nodejs/blob/main/CHANGELOG.md) - [Commits](https://github.com/GoogleCloudPlatform/functions-framework-nodejs/compare/v4.0.0...v4.0.1) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.1 dependency-type: indirect - dependency-name: "@google-cloud/functions-framework" dependency-version: 4.0.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../function/package-lock.json | 774 +++++++++++------- 1 file changed, 463 insertions(+), 311 deletions(-) diff --git a/modules/api-gateway/recipe-multi-region/function/package-lock.json b/modules/api-gateway/recipe-multi-region/function/package-lock.json index 2ba82adee..e41cc95c7 100644 --- a/modules/api-gateway/recipe-multi-region/function/package-lock.json +++ b/modules/api-gateway/recipe-multi-region/function/package-lock.json @@ -12,7 +12,7 @@ "@google-cloud/functions-framework": "^4.0.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=22.0.0" } }, "node_modules/@babel/code-frame": { @@ -37,14 +37,15 @@ } }, "node_modules/@google-cloud/functions-framework": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/functions-framework/-/functions-framework-4.0.0.tgz", - "integrity": "sha512-CNcYrz0/hw35Oq0D9RipHUB8KzH4ixq7o12L//qoOg0TFYv4953KrzCo0L2VP++19P39RShKTftDKMFmQhCeEw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/functions-framework/-/functions-framework-4.0.1.tgz", + "integrity": "sha512-/ui6HI2yNVPNQXGCR+3FMMUEXqm1vMt+xh7oS+rqpVeDjjAYmFYyER1SDYlwvhau/hE53vSUyaDbicb/ilFzJA==", + "license": "Apache-2.0", "dependencies": { - "@types/express": "^4.17.21", - "body-parser": "1.20.3", - "cloudevents": "^8.0.2", - "express": "^4.21.2", + "@types/express": "^5.0.0", + "body-parser": "2.2.0", + "cloudevents": "^10.0.0", + "express": "^5.0.0", "minimist": "^1.2.8", "on-finished": "^2.3.0", "read-package-up": "^11.0.0", @@ -59,9 +60,10 @@ } }, "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -71,25 +73,27 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/express": { - "version": "4.17.22", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz", - "integrity": "sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "license": "MIT", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -98,21 +102,18 @@ } }, "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", - "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "version": "25.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", + "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", + "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/normalize-package-data": { @@ -123,39 +124,42 @@ "node_modules/@types/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==" + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" }, "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "license": "MIT", "dependencies": { "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" + "@types/node": "*" } }, "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" @@ -165,6 +169,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -180,6 +185,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -192,15 +198,11 @@ } } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -212,40 +214,39 @@ } }, "node_modules/bignumber.js": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", - "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", "engines": { "node": "*" } }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=18" } }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -254,6 +255,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -271,6 +273,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -283,6 +286,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -295,9 +299,10 @@ } }, "node_modules/cloudevents": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/cloudevents/-/cloudevents-8.0.3.tgz", - "integrity": "sha512-wTixKNjfLeyj9HQpESvLVVO4xgdqdvX4dTeg1IZ2SCunu/fxVzCamcIZneEyj31V82YolFCKwVeSkr8zResB0Q==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cloudevents/-/cloudevents-10.0.0.tgz", + "integrity": "sha512-uyzC+PpMMRawbouHO+3mlisr3QfEDObmo2pN4oTTF6dZncZgpIzdasZx0tRBFI1dMsqCLZZXMtz8cUuvYqHdbw==", + "license": "Apache-2.0", "dependencies": { "ajv": "^8.11.0", "ajv-formats": "^2.1.1", @@ -307,53 +312,71 @@ "uuid": "^8.3.2" }, "engines": { - "node": ">=16 <=22" + "node": ">=20 <=24" } }, "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } }, "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -370,23 +393,16 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -399,12 +415,14 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -413,6 +431,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -421,6 +440,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -429,6 +449,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -439,55 +460,95 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/body-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" }, "funding": { "type": "opencollective", @@ -497,12 +558,13 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "funding": [ { "type": "github", @@ -512,23 +574,28 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/find-up-simple": { @@ -546,6 +613,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", "dependencies": { "is-callable": "^1.2.7" }, @@ -560,30 +628,43 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -607,6 +688,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -619,6 +701,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -630,6 +713,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -641,6 +725,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -652,6 +737,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -666,6 +752,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -685,26 +772,32 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -724,12 +817,14 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -738,6 +833,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -753,6 +849,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -761,12 +858,14 @@ } }, "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" }, @@ -777,10 +876,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", @@ -798,6 +904,7 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" }, @@ -817,6 +924,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" } @@ -824,7 +932,8 @@ "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/lru-cache": { "version": "10.4.3", @@ -835,62 +944,55 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/minimist": { @@ -902,14 +1004,16 @@ } }, "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -931,6 +1035,7 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -942,6 +1047,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -949,6 +1055,15 @@ "node": ">= 0.8" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/parse-json": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", @@ -969,14 +1084,20 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, "node_modules/picocolors": { "version": "1.1.1", @@ -987,6 +1108,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -995,6 +1117,7 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", "engines": { "node": ">= 0.6.0" } @@ -1003,6 +1126,7 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -1012,11 +1136,12 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -1029,22 +1154,40 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/read-package-up": { @@ -1085,33 +1228,32 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -1127,7 +1269,8 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/semver": { "version": "7.7.2", @@ -1141,59 +1284,55 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -1209,12 +1348,14 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -1233,6 +1374,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -1248,6 +1390,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -1265,6 +1408,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -1308,9 +1452,10 @@ "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==" }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1319,6 +1464,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -1335,21 +1481,24 @@ } }, "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" }, "node_modules/unicorn-magic": { "version": "0.1.0", @@ -1366,6 +1515,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1374,6 +1524,7 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -1382,18 +1533,11 @@ "which-typed-array": "^1.1.2" } }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -1411,6 +1555,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1419,6 +1564,7 @@ "version": "1.1.19", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -1434,6 +1580,12 @@ "funding": { "url": "https://github.com/sponsors/ljharb" } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" } } } From 5eeb7b51bbcfd075c394665a06ba36bd76f61dd4 Mon Sep 17 00:00:00 2001 From: Suryansh Singhal Date: Fri, 2 Jan 2026 17:17:55 +0530 Subject: [PATCH 4/6] Terraform allow one of X86_64, ARM64 but the validation is for x86_64 has been fixed (#3615) Co-authored-by: suryansh.singhal --- modules/compute-vm/variables.tf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/compute-vm/variables.tf b/modules/compute-vm/variables.tf index 113df157a..01177dba8 100644 --- a/modules/compute-vm/variables.tf +++ b/modules/compute-vm/variables.tf @@ -83,9 +83,9 @@ variable "attached_disks" { } validation { condition = alltrue([for d in var.attached_disks : - (d.options.architecture == null || contains(["ARM64", "x86_64"], d.options.architecture)) + (d.options.architecture == null || contains(["ARM64", "X86_64"], d.options.architecture)) ]) - error_message = "Architecture can be null, 'x86_64' or 'ARM64'." + error_message = "Architecture can be null, 'X86_64' or 'ARM64'." } } @@ -125,9 +125,9 @@ variable "boot_disk" { validation { condition = ( var.boot_disk.initialize_params.architecture == null || - contains(["ARM64", "x86_64"], var.boot_disk.initialize_params.architecture) + contains(["ARM64", "X86_64"], var.boot_disk.initialize_params.architecture) ) - error_message = "Architecture can be null, 'x86_64' or 'ARM64'." + error_message = "Architecture can be null, 'X86_64' or 'ARM64'." } } From c4447993d3a206a24ccb291e6c29731db13750d2 Mon Sep 17 00:00:00 2001 From: RamBSn <45333649+RamBSn@users.noreply.github.com> Date: Fri, 2 Jan 2026 17:05:24 +0000 Subject: [PATCH 5/6] AlloyDB - Enable multiple automated backup per day (#3604) * update alloydb automated backup with multiple times per day * Revert "update alloydb automated backup with multiple times per day" This reverts commit ea42ba7ba1b63492f812e6614cf4d4d558078069. * update alloydb automated backup with multiple times per day * remove unnecessary toset --------- Co-authored-by: Ludovico Magnocavallo --- modules/alloydb/README.md | 60 ++++++++++++++++++------------------ modules/alloydb/main.tf | 13 +++++--- modules/alloydb/variables.tf | 13 +++++--- 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/modules/alloydb/README.md b/modules/alloydb/README.md index 8f7679867..35f2390ac 100644 --- a/modules/alloydb/README.md +++ b/modules/alloydb/README.md @@ -343,37 +343,37 @@ module "alloydb" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [cluster_name](variables.tf#L81) | Name of the primary cluster. | string | ✓ | | -| [instance_name](variables.tf#L208) | Name of primary instance. | string | ✓ | | -| [location](variables.tf#L220) | Region or zone of the cluster and instance. | string | ✓ | | -| [network_config](variables.tf#L265) | Network configuration for cluster and instance. Only one between psa_config and psc_config can be used. | object({…}) | ✓ | | -| [project_id](variables.tf#L300) | The ID of the project where this instances will be created. | string | ✓ | | +| [cluster_name](variables.tf#L84) | Name of the primary cluster. | string | ✓ | | +| [instance_name](variables.tf#L211) | Name of primary instance. | string | ✓ | | +| [location](variables.tf#L223) | Region or zone of the cluster and instance. | string | ✓ | | +| [network_config](variables.tf#L268) | Network configuration for cluster and instance. Only one between psa_config and psc_config can be used. | object({…}) | ✓ | | +| [project_id](variables.tf#L303) | The ID of the project where this instances will be created. | string | ✓ | | | [annotations](variables.tf#L17) | Map FLAG_NAME=>VALUE for annotations which allow client tools to store small amount of arbitrary data. | map(string) | | null | -| [automated_backup_configuration](variables.tf#L23) | Automated backup settings for cluster. | object({…}) | | {} | -| [availability_type](variables.tf#L58) | Availability type for the primary replica. Either `ZONAL` or `REGIONAL`. | string | | "REGIONAL" | -| [client_connection_config](variables.tf#L64) | Client connection config. | object({…}) | | null | -| [cluster_display_name](variables.tf#L75) | Display name of the primary cluster. | string | | null | -| [continuous_backup_configuration](variables.tf#L87) | Continuous backup settings for cluster. | object({…}) | | {} | -| [cross_region_replication](variables.tf#L97) | Cross region replication config. | object({…}) | | {} | -| [database_version](variables.tf#L154) | Database type and version to create. | string | | "POSTGRES_15" | -| [deletion_policy](variables.tf#L160) | AlloyDB cluster and instance deletion policy. | string | | null | -| [deletion_protection](variables.tf#L166) | Whether Terraform will be prevented from destroying the cluster. When the field is set to true or unset in Terraform state, a terraform apply or terraform destroy that would delete the cluster will fail. When the field is set to false, deleting the cluster is allowed. | bool | | null | -| [display_name](variables.tf#L172) | AlloyDB instance display name. | string | | null | -| [encryption_config](variables.tf#L178) | Set encryption configuration. KMS name format: 'projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME]'. | object({…}) | | null | -| [flags](variables.tf#L187) | Map FLAG_NAME=>VALUE for database-specific tuning. | map(string) | | null | -| [gce_zone](variables.tf#L193) | The GCE zone that the instance should serve from. This can ONLY be specified for ZONAL instances. If present for a REGIONAL instance, an error will be thrown. | string | | null | -| [initial_user](variables.tf#L199) | AlloyDB cluster initial user credentials. | object({…}) | | null | -| [labels](variables.tf#L214) | Labels to be attached to all instances. | map(string) | | null | -| [machine_config](variables.tf#L226) | AlloyDB machine config. | object({…}) | | {} | -| [maintenance_config](variables.tf#L240) | Set maintenance window configuration. | object({…}) | | {} | -| [prefix](variables.tf#L290) | Optional prefix used to generate instance names. | string | | null | -| [project_number](variables.tf#L305) | The project number of the project where this instances will be created. Only used for testing purposes. | string | | null | -| [query_insights_config](variables.tf#L311) | Query insights config. | object({…}) | | {} | -| [read_pool](variables.tf#L322) | Map of read pool instances to create in the primary cluster. | map(object({…})) | | {} | -| [skip_await_major_version_upgrade](variables.tf#L367) | Set to true to skip awaiting on the major version upgrade of the cluster. | bool | | true | -| [subscription_type](variables.tf#L373) | The subscription type of cluster. Possible values are: 'STANDARD' or 'TRIAL'. | string | | "STANDARD" | -| [tag_bindings](variables.tf#L379) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} | -| [users](variables.tf#L386) | Map of users to create in the primary instance (and replicated to other replicas). Set PASSWORD to null if you want to get an autogenerated password. The user types available are: 'ALLOYDB_BUILT_IN' or 'ALLOYDB_IAM_USER'. | map(object({…})) | | {} | +| [automated_backup_configuration](variables.tf#L23) | Automated backup settings for cluster. | object({…}) | | {} | +| [availability_type](variables.tf#L61) | Availability type for the primary replica. Either `ZONAL` or `REGIONAL`. | string | | "REGIONAL" | +| [client_connection_config](variables.tf#L67) | Client connection config. | object({…}) | | null | +| [cluster_display_name](variables.tf#L78) | Display name of the primary cluster. | string | | null | +| [continuous_backup_configuration](variables.tf#L90) | Continuous backup settings for cluster. | object({…}) | | {} | +| [cross_region_replication](variables.tf#L100) | Cross region replication config. | object({…}) | | {} | +| [database_version](variables.tf#L157) | Database type and version to create. | string | | "POSTGRES_15" | +| [deletion_policy](variables.tf#L163) | AlloyDB cluster and instance deletion policy. | string | | null | +| [deletion_protection](variables.tf#L169) | Whether Terraform will be prevented from destroying the cluster. When the field is set to true or unset in Terraform state, a terraform apply or terraform destroy that would delete the cluster will fail. When the field is set to false, deleting the cluster is allowed. | bool | | null | +| [display_name](variables.tf#L175) | AlloyDB instance display name. | string | | null | +| [encryption_config](variables.tf#L181) | Set encryption configuration. KMS name format: 'projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME]'. | object({…}) | | null | +| [flags](variables.tf#L190) | Map FLAG_NAME=>VALUE for database-specific tuning. | map(string) | | null | +| [gce_zone](variables.tf#L196) | The GCE zone that the instance should serve from. This can ONLY be specified for ZONAL instances. If present for a REGIONAL instance, an error will be thrown. | string | | null | +| [initial_user](variables.tf#L202) | AlloyDB cluster initial user credentials. | object({…}) | | null | +| [labels](variables.tf#L217) | Labels to be attached to all instances. | map(string) | | null | +| [machine_config](variables.tf#L229) | AlloyDB machine config. | object({…}) | | {} | +| [maintenance_config](variables.tf#L243) | Set maintenance window configuration. | object({…}) | | {} | +| [prefix](variables.tf#L293) | Optional prefix used to generate instance names. | string | | null | +| [project_number](variables.tf#L308) | The project number of the project where this instances will be created. Only used for testing purposes. | string | | null | +| [query_insights_config](variables.tf#L314) | Query insights config. | object({…}) | | {} | +| [read_pool](variables.tf#L325) | Map of read pool instances to create in the primary cluster. | map(object({…})) | | {} | +| [skip_await_major_version_upgrade](variables.tf#L370) | Set to true to skip awaiting on the major version upgrade of the cluster. | bool | | true | +| [subscription_type](variables.tf#L376) | The subscription type of cluster. Possible values are: 'STANDARD' or 'TRIAL'. | string | | "STANDARD" | +| [tag_bindings](variables.tf#L382) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} | +| [users](variables.tf#L389) | Map of users to create in the primary instance (and replicated to other replicas). Set PASSWORD to null if you want to get an autogenerated password. The user types available are: 'ALLOYDB_BUILT_IN' or 'ALLOYDB_IAM_USER'. | map(object({…})) | | {} | ## Outputs diff --git a/modules/alloydb/main.tf b/modules/alloydb/main.tf index 4b623e261..6569c4e81 100644 --- a/modules/alloydb/main.tf +++ b/modules/alloydb/main.tf @@ -102,11 +102,14 @@ resource "google_alloydb_cluster" "primary" { weekly_schedule { days_of_week = var.automated_backup_configuration.weekly_schedule.days_of_week - start_times { - hours = var.automated_backup_configuration.weekly_schedule.start_times.hours - minutes = 0 - seconds = 0 - nanos = 0 + dynamic "start_times" { + for_each = var.automated_backup_configuration.weekly_schedule.start_times + content { + hours = start_times.value.hours + minutes = 0 + seconds = 0 + nanos = 0 + } } } diff --git a/modules/alloydb/variables.tf b/modules/alloydb/variables.tf index 9df621486..c9314a9ca 100644 --- a/modules/alloydb/variables.tf +++ b/modules/alloydb/variables.tf @@ -30,9 +30,9 @@ variable "automated_backup_configuration" { days_of_week = optional(list(string), [ "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY" ]) - start_times = optional(object({ - hours = optional(number, 23) - }), {}) + start_times = optional(list(object({ + hours = number + })), [{ hours = 23 }]) }), {}) retention_count = optional(number, 7) retention_period = optional(string) @@ -45,8 +45,11 @@ variable "automated_backup_configuration" { # Backup window validation below !(var.automated_backup_configuration.retention_count != null && var.automated_backup_configuration.retention_period != null) && # Backup window hours below - var.automated_backup_configuration.weekly_schedule.start_times.hours >= 0 && - var.automated_backup_configuration.weekly_schedule.start_times.hours <= 23 && + (alltrue([ + for v in var.automated_backup_configuration.weekly_schedule.start_times : + v.hours >= 0 && + v.hours <= 23 + ])) && # Backup window day validation setintersection(["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"], var.automated_backup_configuration.weekly_schedule.days_of_week) == toset(var.automated_backup_configuration.weekly_schedule.days_of_week) ) : true From d0d7c646704fc120c07b197988cac8d0f9f85341 Mon Sep 17 00:00:00 2001 From: Luca Prete Date: Sun, 4 Jan 2026 09:11:46 +0100 Subject: [PATCH 6/6] Refactor Agent Engine module to support new source based deployments (#3609) * Refactor Agent Engine module to support new source based deployments * Fix linting * Fix tests * Fix tests * Remove generated_pickle line from examples * Fix indentation * Delete modules/agent-engine/terraform.atfvars --------- Co-authored-by: Ludovico Magnocavallo --- modules/agent-engine/README.md | 150 +++++++++++------- modules/agent-engine/agent-managed.tf | 115 ++++++++++++++ modules/agent-engine/agent-unmanaged.tf | 122 ++++++++++++++ modules/agent-engine/main.tf | 149 ++++------------- modules/agent-engine/outputs.tf | 7 +- modules/agent-engine/tools/requirements.txt | 1 - modules/agent-engine/tools/serialize_agent.py | 95 ----------- .../agent-engine/variables-serviceaccount.tf | 2 +- modules/agent-engine/variables.tf | 62 ++++++-- .../modules/agent_engine/assets/src/agent.py | 2 +- .../agent_engine/assets/src/source.tar.gz | Bin 0 -> 1282 bytes .../agent_engine/examples/encryption.yaml | 102 ++---------- .../agent_engine/examples/environment.yaml | 102 ++---------- .../agent_engine/examples/minimal-pickle.yaml | 16 +- .../agent_engine/examples/minimal.yaml | 102 ++---------- .../agent_engine/examples/sa-custom.yaml | 102 ++---------- .../agent_engine/examples/sa-default.yaml | 102 ++---------- .../agent_engine/examples/unmanaged.yaml | 75 +++++++++ 18 files changed, 562 insertions(+), 744 deletions(-) create mode 100644 modules/agent-engine/agent-managed.tf create mode 100644 modules/agent-engine/agent-unmanaged.tf delete mode 100644 modules/agent-engine/tools/requirements.txt delete mode 100644 modules/agent-engine/tools/serialize_agent.py create mode 100644 tests/modules/agent_engine/assets/src/source.tar.gz create mode 100644 tests/modules/agent_engine/examples/unmanaged.yaml diff --git a/modules/agent-engine/README.md b/modules/agent-engine/README.md index f886a4972..eba288e52 100644 --- a/modules/agent-engine/README.md +++ b/modules/agent-engine/README.md @@ -2,13 +2,16 @@ The module creates Agent Engine and related dependencies. -- It can automatically generate and update the Pickle file for you, given a source file. -- It optionally creates a GCS storage bucket or can use an existing one and loads on it all your dependencies (`pickle`, `dependencies.tar.gz`, `requirements.txt`) -- Manages the service accounts lifecycle +- It supports both source based deployments (aka in-line deployment) and serialized object deployment (aka pickle deployment). +- For serialized object deployment, it optionally creates a GCS storage bucket or can use an existing one and loads on it all your dependencies (`pickle`, `dependencies.tar.gz`, `requirements.txt`). +- Manages custom service accounts lifecycle. -- [Packaging dependencies](#packaging-dependencies) -- [Minimal deployment](#minimal-deployment) +- [Source based deployment](#source-based-deployment) + - [Create the tar.gz package](#create-the-targz-package) + - [Minimal deployment](#minimal-deployment) +- [Serialized Object Deployment](#serialized-object-deployment) + - [Unmanaged deployments](#unmanaged-deployments) - [Service accounts](#service-accounts) - [Specify an encryption key](#specify-an-encryption-key) - [Define environment variables and use secrets](#define-environment-variables-and-use-secrets) @@ -17,20 +20,20 @@ The module creates Agent Engine and related dependencies. - [Outputs](#outputs) -## Packaging dependencies +## Source based deployment -To deploy an agent, you first need package your dependencies. This consists of a folder with +The source based deployment is the newest, most efficient and easiest way to deploy your agents. -- The source Python file defining your agent to be pickled (or the equivalent pickle file). -- The `dependencies.tar.gz`. +### Create the tar.gz package + +First, create a *tar.gz* file with these files: + +- The source Python file defining your agent, called `agent.py`. - The `requirements.txt` file. -By default, the module expects these files to be in an `src` subfolder. +By default, the module expects the `tar.gz` file to be in the `src` subfolder and to be called `source.tar.gz`. -You can decide to **let the module create the pickle file for you**, starting from a source agent file. -In this case, the module expects you to have in `src` a source file called `agent.py` with a variable referencing your agent function definition called `local_agent`. - -This is an example of `agent.py` file for ADK: +This is an example of an `agent.py` file for ADK: ```python from google.adk.agents import LlmAgent @@ -55,19 +58,12 @@ root_agent = LlmAgent( tools=[get_exchange_rate], ) -local_agent = AdkApp(agent=root_agent) +agent = AdkApp(agent=root_agent) ``` -The [tools/serialize_agent.py](tools/serialize_agent.py) is used to generate the `pickle.pkl` file. -You module needs [these packages](tools/requirements.txt) to work. +### Minimal deployment -If you **already have a pickle file**, the module expects you to have in the `src` subfolder a `pickle.pkl` file. - -You can customize these values by using the `source_files` variable. - -## Minimal deployment - -This example assumes you are providing the [source packages](#packaging-dependencies) (`agent.py`, `dependencies.tar.gz` and `requirements.txt`) in the `src` subfolder. Every time you will change the agent definition, the module will generate the new pickle file for you, will update it on the GCS bucket and will update your agent. +You can now deploy the agent. ```hcl module "agent_engine" { @@ -80,34 +76,71 @@ module "agent_engine" { agent_framework = "google-adk" } - source_files = { - path = "assets/src/" + deployment_files = { + source_config = { + source_path = "assets/src/source.tar.gz" + } } } # tftest inventory=minimal.yaml ``` -Alternatively, you can pass a pre-generated `pickle.pkl` file. +You can change the name of the tar.gz package, of the requirement file, the name of the Python file and the name of the agent function by using the `deployment_files.source_config` variable. + +## Serialized Object Deployment + +You can also manually serialize your agent by using the [cloudpickle library](https://github.com/cloudpipe/cloudpickle) and pass the `pickle.pkl`, `dependencies.tar.gz` and `requirements.txt` files to the module. ```hcl module "agent_engine" { - source = "./fabric/modules/agent-engine" - name = "my-agent" - project_id = var.project_id - region = var.region - generate_pickle = false + source = "./fabric/modules/agent-engine" + name = "my-agent" + project_id = var.project_id + region = var.region agent_engine_config = { agent_framework = "google-adk" } - source_files = { - path = "assets/src/" + deployment_files = { + package_config = { + dependencies_path = "assets/src/dependencies.tar.gz" + pickle_path = "assets/src/pickle.pkl" + requirements_path = "assets/src/requirements.txt" + } + source_config = null } } # tftest inventory=minimal-pickle.yaml ``` +### Unmanaged deployments + +By default, this module tracks and controls code updates. This means you can only the agent code via Terraform. +Anyway, you may want to delegate this operation to third-party tools, outside Terraform. +To do so, deploy the first revision of your code by using the module (this can even be a hello world) and set `var.managed` to `false`. + +```hcl +module "agent_engine" { + source = "./fabric/modules/agent-engine" + name = "my-agent" + project_id = var.project_id + region = var.region + managed = false + + agent_engine_config = { + agent_framework = "google-adk" + } + + deployment_files = { + source_config = { + source_path = "assets/src/source.tar.gz" + } + } +} +# tftest inventory=unmanaged.yaml +``` + ## Service accounts By default, the module creates a dedicated service account for your agent and grants it the roles needed to deploy the agent. The default roles are defined in `var.service_account_config.roles`. You can add more roles, as needed. @@ -131,8 +164,10 @@ module "agent_engine" { create = false } - source_files = { - path = "assets/src/" + deployment_files = { + source_config = { + source_path = "assets/src/source.tar.gz" + } } } # tftest inventory=sa-default.yaml @@ -156,8 +191,10 @@ module "agent_engine" { email = "my-sa@${var.project_id}.iam.gserviceaccount.com" } - source_files = { - path = "assets/src/" + deployment_files = { + source_config = { + source_path = "assets/src/source.tar.gz" + } } } # tftest inventory=sa-custom.yaml @@ -181,8 +218,10 @@ module "agent_engine" { agent_framework = "google-adk" } - source_files = { - path = "assets/src/" + deployment_files = { + source_config = { + source_path = "assets/src/source.tar.gz" + } } } # tftest inventory=encryption.yaml @@ -212,8 +251,10 @@ module "agent_engine" { } } - source_files = { - path = "assets/src/" + deployment_files = { + source_config = { + source_path = "assets/src/source.tar.gz" + } } } # tftest inventory=environment.yaml @@ -227,22 +268,23 @@ The module allows you to dynamically reference context values for resources crea | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [agent_engine_config](variables.tf#L17) | The agent configuration. | object({…}) | ✓ | | -| [name](variables.tf#L77) | The name of the agent. | string | ✓ | | -| [project_id](variables.tf#L83) | The id of the project where to deploy the agent. | string | ✓ | | -| [region](variables.tf#L89) | The region where to deploy the agent. | string | ✓ | | -| [bucket_config](variables.tf#L32) | The GCS bucket configuration. | object({…}) | | {} | -| [context](variables.tf#L44) | Context-specific interpolations. | object({…}) | | {} | -| [description](variables.tf#L57) | The Agent Engine description. | string | | "Terraform managed." | -| [encryption_key](variables.tf#L64) | The full resource name of the Cloud KMS CryptoKey. | string | | null | -| [generate_pickle](variables.tf#L70) | Generate the pickle file from a source file. | bool | | true | +| [agent_engine_config](variables.tf#L17) | The agent configuration. | object({…}) | ✓ | | +| [name](variables.tf#L121) | The name of the agent. | string | ✓ | | +| [project_id](variables.tf#L127) | The id of the project where to deploy the agent. | string | ✓ | | +| [region](variables.tf#L133) | The region where to deploy the agent. | string | ✓ | | +| [bucket_config](variables.tf#L40) | The GCS bucket configuration. | object({…}) | | {} | +| [context](variables.tf#L52) | Context-specific interpolations. | object({…}) | | {} | +| [deployment_files](variables.tf#L65) | The to source files path and names. | object({…}) | | {…} | +| [description](variables.tf#L101) | The Agent Engine description. | string | | "Terraform managed." | +| [encryption_key](variables.tf#L108) | The full resource name of the Cloud KMS CryptoKey. | string | | null | +| [managed](variables.tf#L114) | Whether the Terraform module should control the code updates. | bool | | true | | [service_account_config](variables-serviceaccount.tf#L18) | Service account configurations. | object({…}) | | {} | -| [source_files](variables.tf#L95) | The to source files path and names. | object({…}) | | {} | ## Outputs | name | description | sensitive | |---|---|:---:| -| [id](outputs.tf#L17) | Fully qualified Agent Engine id. | | -| [service_account](outputs.tf#L22) | Service account resource. | | +| [agent](outputs.tf#L17) | The Agent Engine object. | | +| [id](outputs.tf#L22) | Fully qualified Agent Engine id. | | +| [service_account](outputs.tf#L27) | Service account resource. | | diff --git a/modules/agent-engine/agent-managed.tf b/modules/agent-engine/agent-managed.tf new file mode 100644 index 000000000..5c1a10948 --- /dev/null +++ b/modules/agent-engine/agent-managed.tf @@ -0,0 +1,115 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_vertex_ai_reasoning_engine" "managed" { + count = var.managed ? 1 : 0 + display_name = var.name + project = local.project_id + description = var.description + region = local.location + + dynamic "encryption_spec" { + for_each = var.encryption_key == null ? {} : { 1 = 1 } + + content { + kms_key_name = lookup( + local.ctx.kms_keys, + var.encryption_key, + var.encryption_key + ) + } + } + + spec { + agent_framework = var.agent_engine_config.agent_framework + class_methods = ( + length(var.agent_engine_config.class_methods) > 0 + ? jsonencode(var.agent_engine_config.class_methods) + : null + ) + service_account = local.service_account_email + + dynamic "deployment_spec" { + for_each = ( + var.agent_engine_config.container_concurrency != null || + var.agent_engine_config.max_instances != null || + var.agent_engine_config.min_instances != null || + var.agent_engine_config.resource_limits != null || + length(var.agent_engine_config.environment_variables) > 0 || + length(var.agent_engine_config.secret_environment_variables) > 0 + ? { 1 = 1 } + : {} + ) + + content { + container_concurrency = var.agent_engine_config.container_concurrency + max_instances = var.agent_engine_config.max_instances + min_instances = var.agent_engine_config.min_instances + resource_limits = var.agent_engine_config.resource_limits + + dynamic "env" { + for_each = var.agent_engine_config.environment_variables + + content { + name = env.key + value = env.value + } + } + + dynamic "secret_env" { + for_each = var.agent_engine_config.secret_environment_variables + + content { + name = secret_env.key + + secret_ref { + secret = secret_env.value.secret_id + version = secret_env.value.version + } + } + } + } + } + + dynamic "package_spec" { + for_each = var.deployment_files.package_config != null ? { 1 = 1 } : {} + + content { + python_version = var.agent_engine_config.python_version + dependency_files_gcs_uri = "gs://${local.bucket_name}/${google_storage_bucket_object.dependencies[0].name}" + requirements_gcs_uri = "gs://${local.bucket_name}/${google_storage_bucket_object.requirements[0].name}" + pickle_object_gcs_uri = "gs://${local.bucket_name}/${google_storage_bucket_object.pickle[0].name}" + } + } + + dynamic "source_code_spec" { + for_each = var.deployment_files.source_config != null ? { 1 = 1 } : {} + + content { + inline_source { + source_archive = filebase64(var.deployment_files.source_config.source_path) + } + + python_spec { + entrypoint_module = var.deployment_files.source_config.entrypoint_module + entrypoint_object = var.deployment_files.source_config.entrypoint_object + requirements_file = var.deployment_files.source_config.requirements_path + version = var.agent_engine_config.python_version + } + } + } + } +} diff --git a/modules/agent-engine/agent-unmanaged.tf b/modules/agent-engine/agent-unmanaged.tf new file mode 100644 index 000000000..9c7b5260d --- /dev/null +++ b/modules/agent-engine/agent-unmanaged.tf @@ -0,0 +1,122 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_vertex_ai_reasoning_engine" "unmanaged" { + count = var.managed ? 0 : 1 + display_name = var.name + project = local.project_id + description = var.description + region = local.location + + dynamic "encryption_spec" { + for_each = var.encryption_key == null ? {} : { 1 = 1 } + + content { + kms_key_name = lookup( + local.ctx.kms_keys, + var.encryption_key, + var.encryption_key + ) + } + } + + spec { + agent_framework = var.agent_engine_config.agent_framework + class_methods = ( + length(var.agent_engine_config.class_methods) > 0 + ? jsonencode(var.agent_engine_config.class_methods) + : null + ) + service_account = local.service_account_email + + dynamic "deployment_spec" { + for_each = ( + var.agent_engine_config.container_concurrency != null || + var.agent_engine_config.max_instances != null || + var.agent_engine_config.min_instances != null || + var.agent_engine_config.resource_limits != null || + length(var.agent_engine_config.environment_variables) > 0 || + length(var.agent_engine_config.secret_environment_variables) > 0 + ? { 1 = 1 } + : {} + ) + + content { + container_concurrency = var.agent_engine_config.container_concurrency + max_instances = var.agent_engine_config.max_instances + min_instances = var.agent_engine_config.min_instances + resource_limits = var.agent_engine_config.resource_limits + + dynamic "env" { + for_each = var.agent_engine_config.environment_variables + + content { + name = env.key + value = env.value + } + } + + dynamic "secret_env" { + for_each = var.agent_engine_config.secret_environment_variables + + content { + name = secret_env.key + + secret_ref { + secret = secret_env.value.secret_id + version = secret_env.value.version + } + } + } + } + } + + dynamic "package_spec" { + for_each = var.deployment_files.package_config == null ? {} : { 1 = 1 } + + content { + python_version = var.agent_engine_config.python_version + dependency_files_gcs_uri = "gs://${local.bucket_name}/${google_storage_bucket_object.dependencies[0].name}" + requirements_gcs_uri = "gs://${local.bucket_name}/${google_storage_bucket_object.requirements[0].name}" + pickle_object_gcs_uri = "gs://${local.bucket_name}/${google_storage_bucket_object.pickle[0].name}" + } + } + + dynamic "source_code_spec" { + for_each = var.deployment_files.source_config == null ? {} : { 1 = 1 } + + content { + inline_source { + source_archive = filebase64(var.deployment_files.source_config.source_path) + } + + python_spec { + entrypoint_module = var.deployment_files.source_config.entrypoint_module + entrypoint_object = var.deployment_files.source_config.entrypoint_object + requirements_file = var.deployment_files.source_config.requirements_path + version = var.agent_engine_config.python_version + } + } + } + } + + lifecycle { + ignore_changes = [ + spec[0].package_spec, + spec[0].source_code_spec[0].inline_source[0].source_archive + ] + } +} diff --git a/modules/agent-engine/main.tf b/modules/agent-engine/main.tf index a50c42c27..a700778c8 100644 --- a/modules/agent-engine/main.tf +++ b/modules/agent-engine/main.tf @@ -16,8 +16,13 @@ locals { _ctx_p = "$" + _resource = ( + var.managed + ? try(google_vertex_ai_reasoning_engine.managed[0], null) + : try(google_vertex_ai_reasoning_engine.unmanaged[0], null) + ) bucket_name = ( - var.bucket_config.create + var.deployment_files.package_config != null && var.bucket_config.create ? google_storage_bucket.default[0].name : coalesce(var.bucket_config.name, var.name) ) @@ -32,79 +37,9 @@ locals { project_id = lookup( local.ctx.project_ids, var.project_id, var.project_id ) -} - -resource "google_vertex_ai_reasoning_engine" "default" { - display_name = var.name - project = local.project_id - description = var.description - region = local.location - - dynamic "encryption_spec" { - for_each = var.encryption_key == null ? {} : { 1 = 1 } - - content { - kms_key_name = lookup( - local.ctx.kms_keys, - var.encryption_key, - var.encryption_key - ) - } - } - - spec { - agent_framework = var.agent_engine_config.agent_framework - class_methods = ( - length(var.agent_engine_config.class_methods) > 0 - ? jsonencode(var.agent_engine_config.class_methods) - : null - ) - service_account = local.service_account_email - - dynamic "deployment_spec" { - for_each = ( - # length(var.container_spec) > 0 || - length(var.agent_engine_config.environment_variables) > 0 || - length(var.agent_engine_config.secret_environment_variables) > 0 - ? { 1 = 1 } - : {} - ) - - content { - dynamic "env" { - for_each = var.agent_engine_config.environment_variables - - content { - name = env.key - value = env.value - } - } - - dynamic "secret_env" { - for_each = var.agent_engine_config.secret_environment_variables - - content { - name = secret_env.key - - secret_ref { - secret = secret_env.value.secret_id - version = secret_env.value.version - } - } - } - } - } - - package_spec { - python_version = var.agent_engine_config.python_version - dependency_files_gcs_uri = "gs://${local.bucket_name}/${google_storage_bucket_object.dependencies.name}" - requirements_gcs_uri = "gs://${local.bucket_name}/${google_storage_bucket_object.requirements.name}" - pickle_object_gcs_uri = ( - var.generate_pickle - ? "gs://${local.bucket_name}/${google_storage_bucket_object.pickle_from_src[0].name}" - : "gs://${local.bucket_name}/${google_storage_bucket_object.pickle[0].name}" - ) - } + resource = { + id = local._resource.id + object = local._resource } } @@ -119,7 +54,11 @@ resource "time_sleep" "wait_5_minutes" { } resource "google_storage_bucket" "default" { - count = var.bucket_config.create ? 1 : 0 + count = ( + var.bucket_config.create + && var.deployment_files.package_config != null + ? 1 : 0 + ) name = coalesce(var.bucket_config.name, var.name) project = local.project_id location = local.location @@ -127,50 +66,32 @@ resource "google_storage_bucket" "default" { force_destroy = !var.bucket_config.deletion_protection } -resource "null_resource" "default" { - count = var.generate_pickle ? 1 : 0 - - provisioner "local-exec" { - command = join(" ", [ - "python", - "./tools/serialize_agent.py", - "${var.source_files.path}/${var.source_files.pickle_src}", - "--output-file ${var.source_files.path}/${var.source_files.pickle_out}", - "--variable-name ${var.source_files.pickle_src_var_name}" - ]) - } -} - resource "google_storage_bucket_object" "dependencies" { - name = "dependencies.tar.gz" - bucket = local.bucket_name - source = "${var.source_files.path}/${var.source_files.dependencies}" - source_md5hash = filemd5("${var.source_files.path}/${var.source_files.dependencies}") -} - -resource "google_storage_bucket_object" "pickle_from_src" { - count = var.generate_pickle ? 1 : 0 - name = "pickle.pkl" - bucket = local.bucket_name - source = "${var.source_files.path}/${var.source_files.pickle_out}" - source_md5hash = filemd5("${var.source_files.path}/${var.source_files.pickle_out}") - - depends_on = [ - null_resource.default - ] + count = var.deployment_files.package_config != null ? 1 : 0 + name = "dependencies.tar.gz" + bucket = local.bucket_name + source = try(var.deployment_files.package_config.dependencies_path, null) + source_md5hash = filemd5( + try(var.deployment_files.package_config.dependencies_path, null) + ) } resource "google_storage_bucket_object" "pickle" { - count = var.generate_pickle ? 0 : 1 - name = "pickle.pkl" - bucket = local.bucket_name - source = "${var.source_files.path}/${var.source_files.pickle_out}" - source_md5hash = filemd5("${var.source_files.path}/${var.source_files.pickle_out}") + count = var.deployment_files.package_config != null ? 1 : 0 + name = "pickle.pkl" + bucket = local.bucket_name + source = try(var.deployment_files.package_config.pickle_path, null) + source_md5hash = filemd5( + try(var.deployment_files.package_config.pickle_path) + ) } resource "google_storage_bucket_object" "requirements" { - name = "requirements.txt" - bucket = local.bucket_name - source = "${var.source_files.path}/${var.source_files.requirements}" - source_md5hash = filemd5("${var.source_files.path}/${var.source_files.requirements}") + count = var.deployment_files.package_config != null ? 1 : 0 + name = "requirements.txt" + bucket = local.bucket_name + source = try(var.deployment_files.package_config.requirements_path, null) + source_md5hash = filemd5( + try(var.deployment_files.package_config.requirements_path) + ) } diff --git a/modules/agent-engine/outputs.tf b/modules/agent-engine/outputs.tf index 0b418d1c1..8eaa48647 100644 --- a/modules/agent-engine/outputs.tf +++ b/modules/agent-engine/outputs.tf @@ -14,9 +14,14 @@ * limitations under the License. */ +output "agent" { + description = "The Agent Engine object." + value = local.resource.object +} + output "id" { description = "Fully qualified Agent Engine id." - value = google_vertex_ai_reasoning_engine.default.id + value = local.resource.id } output "service_account" { diff --git a/modules/agent-engine/tools/requirements.txt b/modules/agent-engine/tools/requirements.txt deleted file mode 100644 index 37d5682cf..000000000 --- a/modules/agent-engine/tools/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -cloudpickle diff --git a/modules/agent-engine/tools/serialize_agent.py b/modules/agent-engine/tools/serialize_agent.py deleted file mode 100644 index 9b5619303..000000000 --- a/modules/agent-engine/tools/serialize_agent.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse -import cloudpickle -import importlib.util -import os -import sys -from google.adk.agents import LlmAgent - - -def serialize_agent_from_file(input_file, variable_name, output_file): - """ - Dynamically loads a Python module from a full file path, accesses a - top-level variable containing an agent object, and serializes that object - to a specified output file. - - Args: - input_file (str): The full path to the Python source file. - variable_name (str): The name of the variable holding the agent object. - output_file (str): The full path for the output pickle file. - """ - try: - output_dir = os.path.dirname(output_file) - if output_dir and not os.path.isdir(output_dir): - print(f"Error: The output directory '{output_dir}' does not exist.", - file=sys.stderr) - return - - module_name = os.path.splitext(os.path.basename(input_file))[0] - - spec = importlib.util.spec_from_file_location(module_name, input_file) - if spec is None or spec.loader is None: - print(f"Error: Could not import module from {input_file}", - file=sys.stderr) - return - - module = importlib.util.module_from_spec(spec) - - spec.loader.exec_module(module) - - local_agent = getattr(module, variable_name) - - with open(output_file, "wb") as f: - cloudpickle.dump(local_agent, f) - - print( - f"Successfully serialized '{variable_name}' from '{input_file}' to '{output_file}'" - ) - - except FileNotFoundError: - print(f"Error: The input file '{input_file}' was not found.", - file=sys.stderr) - except AttributeError: - print( - f"Error: The variable '{variable_name}' was not found in '{input_file}'.", - file=sys.stderr) - except Exception as e: - print(f"An unexpected error occurred: {e}", file=sys.stderr) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description= - "Serialize a dynamically loaded agent from a variable in a specified file." - ) - parser.add_argument( - "input_file", - help="The full path to the Python source file (e.g., 'my_agents/main.py')." - ) - parser.add_argument( - "--variable-name", default="local_agent", help= - "The name of the agent variable to serialize (default: 'local_agent').") - parser.add_argument( - "--output-file", default="pickle.pkl", help= - "The full path for the output pickle file (e.g., 'output/agent.pkl'). Default is 'pickle.pkl' in the current directory." - ) - - args = parser.parse_args() - - serialize_agent_from_file(args.input_file, args.variable_name, - args.output_file) diff --git a/modules/agent-engine/variables-serviceaccount.tf b/modules/agent-engine/variables-serviceaccount.tf index 5f4166c69..8d599bb1c 100644 --- a/modules/agent-engine/variables-serviceaccount.tf +++ b/modules/agent-engine/variables-serviceaccount.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/agent-engine/variables.tf b/modules/agent-engine/variables.tf index c40ae635c..cf96bb908 100644 --- a/modules/agent-engine/variables.tf +++ b/modules/agent-engine/variables.tf @@ -20,13 +20,21 @@ variable "agent_engine_config" { # Add validation once API stabilizes agent_framework = string class_methods = optional(list(any), []) + container_concurrency = optional(number) environment_variables = optional(map(string), {}) + max_instances = optional(number) + min_instances = optional(number) python_version = optional(string, "3.12") + resource_limits = optional(object({ + cpu = string + memory = string + })) secret_environment_variables = optional(map(object({ secret_id = string version = optional(string, "latest") })), {}) }) + nullable = false } variable "bucket_config" { @@ -54,6 +62,42 @@ variable "context" { default = {} } +variable "deployment_files" { + description = "The to source files path and names." + type = object({ + package_config = optional(object({ + dependencies_path = optional(string, "./src/dependencies.tar.gz") + pickle_path = optional(string, "./src/pickle.pkl") + requirements_path = optional(string, "./src/requirements.txt") + }), null) + source_config = optional(object({ + entrypoint_module = optional(string, "agent") + entrypoint_object = optional(string, "agent") + requirements_path = optional(string, "requirements.txt") + source_path = optional(string, "./src/source.tar.gz") + }), null) + }) + nullable = false + default = { + package_config = null + source_config = {} + } + validation { + condition = ( + var.deployment_files.package_config != null || + var.deployment_files.source_config != null + ) + error_message = "You must provide either 'package_config' or 'source_config'." + } + validation { + condition = !( + var.deployment_files.package_config != null && + var.deployment_files.source_config != null + ) + error_message = "You cannot specify both 'package_config' and 'source_config' simultaneously." + } +} + variable "description" { description = "The Agent Engine description." type = string @@ -67,8 +111,8 @@ variable "encryption_key" { default = null } -variable "generate_pickle" { - description = "Generate the pickle file from a source file." +variable "managed" { + description = "Whether the Terraform module should control the code updates." type = bool nullable = false default = true @@ -91,17 +135,3 @@ variable "region" { type = string nullable = false } - -variable "source_files" { - description = "The to source files path and names." - type = object({ - dependencies = optional(string, "dependencies.tar.gz") - path = optional(string, "./src") - pickle_out = optional(string, "pickle.pkl") - pickle_src = optional(string, "agent.py") - pickle_src_var_name = optional(string, "local_agent") - requirements = optional(string, "requirements.txt") - }) - nullable = false - default = {} -} diff --git a/tests/modules/agent_engine/assets/src/agent.py b/tests/modules/agent_engine/assets/src/agent.py index 1cea69c4c..d928375a0 100644 --- a/tests/modules/agent_engine/assets/src/agent.py +++ b/tests/modules/agent_engine/assets/src/agent.py @@ -42,4 +42,4 @@ root_agent = LlmAgent( tools=[get_exchange_rate], ) -local_agent = AdkApp(agent=root_agent) +agent = AdkApp(agent=root_agent) diff --git a/tests/modules/agent_engine/assets/src/source.tar.gz b/tests/modules/agent_engine/assets/src/source.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..d8a8f24bdbc78b8720f54da1578416d33f9c9949 GIT binary patch literal 1282 zcmV+d1^xOTiwFR~luBs;1MOJvZre5xcMA;Lw9nh;oBN>&kjVOLCn-EYO|sRfYiF_3 zt|*Ftk!YKXB&rlu+bH@T`@B8U_5uU;2z!7XW!Z_7{tJ+}SgRjE*y0_Jzax2fq7KO9 z0VM$yHT_u77a0>YqOnYMIgyoXf?*hS$AL;C2UIM#;aWDR{ux{cOxLt5)#@e~mTA|m z3QVqxz`Ky8k_e;{WloX5eXDL6xMhy^vALGGe%xHtpJ5*Vw&B`_ZCr|f%dwZ^-)z_ow*toX zQ@M=KJpLOThk#rkhL z^|Jo20#~X3ZUfq_``+R1gVy7HeL|!Z`kR8c>HXaD%x;hE9`r}u&Lhp)h8_-TKYiDr za}i&@X{-!4U;W=-|ILf>Z#K-jQP%%eV95C}q?>-ovtX03BqVacMf8je@kAWcc*tU! zKL4R<>ZWDrhF0j4;56@ZbyLg7Cd@w#X|rkTO6y~LRpLW&aPIz}@422#uX~Ow-2Y9> zbyfVku2I(iRp6cV-`U!>b=xxVHfFh7WmJEHxVfl5!xirTMf|&N-7f3@N>JYaKWm)T z|C`GLSGfPr>%VO~W&K|Tu2TQY`~Rnho3H-wt^bzoT#SF$cAc{RuL5_zuVtyIov^q@ z%OmandESO~Ra0604kYge!S_xPx z1v|;)(rV4=q0{FTaJ@(jRj&uFUKQ?py;g4nZ9Hl9A9RoU@WeYj^g8|4eh<2bu-onI zwfe1Y2cLcfuk#chwmN$oKyj^b8alxPjr@@$Q|m|rwP-y`mk12_j8K|TpADFge#OHK z^8ySpH;WkEfrN^PrD``*q!*x-kVQ-qnfF|BO4qc3;1SF|7U?87)$=@)Djr-&Xor!f zI%aA(i=-36^reqZb6qbu^^!!>w15s^NKroVM@W$#3nJ;7rZ(2kgrKoMJy!O10GAV* zP(AAHRX3L9k}stDM~4@s0UBJ$Lwrajs;uvzRzXG`1?)|+2bVVCY5|*zULEPp2muVL z^V3EWrVj*(PX`(LDOBaFy;_>{bwLCmA%a9{^OXh*RFzNFon;?4&MQcMUMzVbd##k# zbEYyA@q)1aYs%xbbuABaL2-WY_8vmM_(hm;9;TY$T+VKX=;D0GX6{8ipkcE*q!Ei5 zhKsv72uV6B++Z;-VdksD(X6T%#W=v=9MLctWFZhdNSJhES*XVZUpVeA_%F6MpZe}h sCuh`rc4_;c