diff --git a/DESTROY_ROUTE_CLEANUP_BUG.md b/DESTROY_ROUTE_CLEANUP_BUG.md new file mode 100644 index 0000000..4dba243 --- /dev/null +++ b/DESTROY_ROUTE_CLEANUP_BUG.md @@ -0,0 +1,143 @@ +# Bug Report: HTTP route is not removed after Terraform destroy + +## Summary + +При удалении примера `hello-node` через Terraform команда `terraform destroy` завершается успешно, но публичный HTTP endpoint не удаляется. + +Фактическое поведение после `destroy` такое: + +1. Сразу после удаления endpoint ещё некоторое время отвечает `HTTP 200` и возвращает корректный ответ функции. +2. Затем backend функции действительно исчезает, но публичный маршрут остаётся опубликованным и начинает отвечать `HTTP 502 function unreachable`. +3. Даже через 120 секунд endpoint не исчезает. + +Это выглядит как баг cleanup в platform/backend/provider lifecycle для HTTP trigger/route. + +## Affected Example + +- Example: `hello-node` +- Terraform files: `hello-node/main.tf`, `hello-node/http.tf`, `hello-node/job.tf` +- Public URL: `https://sless-api.kube5s.ru/fn/default/hello-http` +- Function name: `hello-http` +- Trigger name: `hello-http-trigger` + +## Reproduction + +Использовался репозиторий examples и скрипт: + +- Script: `./run_terraform_examples.sh` + +Шаги воспроизведения: + +1. Выполнить `terraform init` в `hello-node` +2. Выполнить `terraform apply` +3. Убедиться, что endpoint живой +4. Выполнить `terraform destroy` +5. Проверять публичный URL после destroy + +Логика проверки встроена в `run_terraform_examples.sh`: + +1. После `apply` endpoint обязан отвечать `200` +2. После `destroy` endpoint должен исчезнуть +3. Скрипт ждёт до 120 секунд и перепроверяет endpoint каждые 5 секунд + +## Expected Result + +После успешного `terraform destroy`: + +1. Публичный URL должен перестать существовать +2. Запрос на URL должен вернуть `404` или другой явный признак отсутствия маршрута +3. Provider не должен возвращать успешный destroy раньше, чем cleanup HTTP route завершён + +## Actual Result + +После успешного `terraform destroy`: + +1. Terraform сообщает `Destroy complete! Resources: 4 destroyed.` +2. Endpoint `https://sless-api.kube5s.ru/fn/default/hello-http` продолжает отвечать `200` +3. Через некоторое время тот же endpoint начинает отвечать `502` +4. Тело ответа на `502`: + +```json +{"error":"function unreachable: Post \"http://hello-http.sless-fn-default.svc.cluster.local:8080\": dial tcp 10.106.128.167:8080: connect: operation not permitted"} +``` + +Это означает: + +1. внешний HTTP маршрут всё ещё существует; +2. запрос по нему всё ещё направляется внутрь платформы; +3. backend функции уже удалён или недоступен; +4. cleanup маршрута не завершён. + +## Timeline From Real Run + +Подтверждённая последовательность из фактического прогона: + +1. `terraform destroy` завершился успешно +2. первые проверки после destroy возвращали `HTTP 200` +3. затем проверки начали возвращать `HTTP 502 function unreachable` +4. в течение всех 24 проверок по 5 секунд endpoint не исчез +5. итоговое время ожидания: 120 секунд + +Итоговый summary из скрипта: + +```text +ERROR SUMMARY +example: hello-node +step: endpoint cleanup after clean destroy +reason: route cleanup bug: public endpoint still exists but backend is already gone (HTTP 502 function unreachable); endpoint was still published after 120s +``` + +## Why This Is A Real Platform Bug + +Это не похоже на проблему тестового скрипта или Terraform CLI по следующим причинам: + +1. `terraform destroy` завершается без ошибки +2. state Terraform очищается как ожидалось +3. сначала endpoint отвечает `200`, значит маршрут реально жив после destroy +4. потом endpoint отвечает `502 function unreachable`, значит backend уже исчез, но route ещё остался +5. скрипт ждёт 120 секунд, то есть это не мгновенная eventual consistency на 1-2 секунды + +Иными словами: удаление backend и удаление публичного маршрута расходятся по времени, а route cleanup либо не выполняется, либо не дожидается завершения. + +## Most Likely Broken Layer + +Наиболее вероятные точки проблемы: + +1. API/backend destroy trigger возвращает success до фактического удаления HTTP route +2. Controller удаляет function workload, но не удаляет route/ingress/virtualservice/gateway mapping +3. Удаление route запускается асинхронно, но его результат не awaited +4. В системе остаётся запись маршрута на имя функции, хотя service/backend уже удалён + +## What To Check In The Development Repo + +Нужно проверить destroy flow именно для HTTP trigger: + +1. Удаляется ли объект trigger только в metadata/storage или реально удаляется и внешний маршрут +2. Какие Kubernetes/ingress объекты создаются для HTTP trigger и все ли они удаляются +3. Есть ли race condition между удалением function/service и удалением route +4. Не возвращает ли provider success раньше, чем backend подтверждает полное удаление маршрута +5. Есть ли финальный polling/wait на исчезновение route перед возвратом успешного destroy + +Если архитектура использует отдельные сущности route/service/function, то destroy должен идти в таком порядке: + +1. disable/remove public routing +2. дождаться, что endpoint больше не публикуется снаружи +3. удалить backend/service/workload +4. завершить destroy success + +Сейчас по фактическому поведению порядок либо обратный, либо неполный. + +## Minimal Acceptance Criteria For Fix + +Исправление можно считать рабочим, если после `terraform destroy` для `hello-node` выполняются все условия: + +1. URL `https://sless-api.kube5s.ru/fn/default/hello-http` перестаёт отвечать как живой маршрут +2. URL не возвращает `502 function unreachable` +3. URL исчезает в разумное время после destroy +4. `./run_terraform_examples.sh` проходит шаг `endpoint cleanup after clean destroy` + +## Current Status + +На данный момент массовый прогон examples корректно останавливается на `hello-node`, потому что это первый воспроизводимый failure. + +Дальше прогонять остальные примеры без исправления destroy cleanup смысла нет: тест уже доказал platform bug на базовом HTTP сценарии. \ No newline at end of file diff --git a/hello-node/dist/handler-http.zip b/hello-node/dist/handler-http.zip deleted file mode 100644 index eb7a08c..0000000 Binary files a/hello-node/dist/handler-http.zip and /dev/null differ diff --git a/hello-node/dist/handler-job.zip b/hello-node/dist/handler-job.zip deleted file mode 100644 index 5074c2f..0000000 Binary files a/hello-node/dist/handler-job.zip and /dev/null differ diff --git a/hello-node/terraform.tfstate.1772954131.backup b/hello-node/terraform.tfstate.1772954131.backup deleted file mode 100644 index f804125..0000000 --- a/hello-node/terraform.tfstate.1772954131.backup +++ /dev/null @@ -1,168 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.12.2", - "serial": 22, - "lineage": "d12fc078-7aee-39d1-629d-358c3c135820", - "outputs": { - "trigger_url": { - "value": "https://sless-api.kube5s.ru/fn/default/hello-http", - "type": "string" - } - }, - "resources": [ - { - "mode": "data", - "type": "archive_file", - "name": "handler_http", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "16650367fe534ed2feb81be322fd4a9d80f77388", - "output_base64sha256": "/fY9RigPle6Yx9R9B9yEYmi5+jgL6PV3fmPlVK5ia3g=", - "output_base64sha512": "yKvMpTCZBynqV3LlE3wTZGMIS0EG0tY8LE+1iIETCuWGoc+bv4+Hlnve14bBWGOnvQHdqE84y4UDi8Pmnz1A2A==", - "output_file_mode": null, - "output_md5": "a74ae4ccb7337659439eacaf1831194d", - "output_path": "./handler-http.zip", - "output_sha": "16650367fe534ed2feb81be322fd4a9d80f77388", - "output_sha256": "fdf63d46280f95ee98c7d47d07dc846268b9fa380be8f5777e63e554ae626b78", - "output_sha512": "c8abcca530990729ea5772e5137c136463084b4106d2d63c2c4fb58881130ae586a1cf9bbf8f87967bded786c15863a7bd01dda84f38cb85038bc3e69f3d40d8", - "output_size": 409, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": null, - "source_file": "./code/handler-http.js", - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "handler_job", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "27327ec4d4ab6d5d6fdc3f82a5ab768c68146a66", - "output_base64sha256": "cI+JbO5lWW+3qo4DSDg5HXLQS7EXHze2W6vGCNk0iOI=", - "output_base64sha512": "4D841Y2OT5EVlRbi/NwGh7SHnRzKgZ6AF1Rx+AIspPKthrbsZi8oGY6qLYe/NJ4t46j1Y8WkZ4tJ6iBpL5g7uw==", - "output_file_mode": null, - "output_md5": "2c5c498c77ec002df7cbeac94f626af8", - "output_path": "./handler-job.zip", - "output_sha": "27327ec4d4ab6d5d6fdc3f82a5ab768c68146a66", - "output_sha256": "708f896cee65596fb7aa8e034838391d72d04bb1171f37b65babc608d93488e2", - "output_sha512": "e03f38d58d8e4f91159516e2fcdc0687b4879d1cca819e80175471f8022ca4f2ad86b6ec662f28198eaa2d87bf349e2de3a8f563c5a4678b49ea20692f983bbb", - "output_size": 489, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": null, - "source_file": "./code/handler-job.js", - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "hello_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "a74ae4ccb7337659439eacaf1831194d", - "code_path": "./handler-http.zip", - "entrypoint": "handler-http.handle", - "env_vars": null, - "image_ref": "pearlharbor.registryk8s.services.ngcloud.ru/sless/sless-default-hello-http:latest", - "memory_mb": 128, - "name": "hello-http", - "namespace": "default", - "phase": "Ready", - "runtime": "nodejs20", - "timeout_sec": 30 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.handler_http" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "hello_job", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "2c5c498c77ec002df7cbeac94f626af8", - "code_path": "./handler-job.zip", - "entrypoint": "handler-job.handle", - "env_vars": null, - "image_ref": "pearlharbor.registryk8s.services.ngcloud.ru/sless/sless-default-hello-job:latest", - "memory_mb": 128, - "name": "hello-job", - "namespace": "default", - "phase": "Ready", - "runtime": "nodejs20", - "timeout_sec": 30 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.handler_job" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "hello_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "hello-http", - "name": "hello-http-trigger", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/hello-http" - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.handler_http", - "sless_function.hello_http" - ] - } - ] - } - ], - "check_results": null -} diff --git a/hello-node/terraform.tfstate.1773029146.backup b/hello-node/terraform.tfstate.1773029146.backup deleted file mode 100644 index 1681c79..0000000 --- a/hello-node/terraform.tfstate.1773029146.backup +++ /dev/null @@ -1,209 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.12.2", - "serial": 68, - "lineage": "d12fc078-7aee-39d1-629d-358c3c135820", - "outputs": { - "job_completion_time": { - "value": "2026-03-08T17:10:15Z", - "type": "string" - }, - "job_message": { - "value": "completed successfully", - "type": "string" - }, - "job_phase": { - "value": "Succeeded", - "type": "string" - }, - "trigger_url": { - "value": "https://sless-api.kube5s.ru/fn/default/hello-http", - "type": "string" - } - }, - "resources": [ - { - "mode": "data", - "type": "archive_file", - "name": "handler_http", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "af56405ad089f54b3382b1f8bbf648e9a4020186", - "output_base64sha256": "5k1JGBxCX5TLgrWRw8F4O3rMWu/4dprgLd9EH+maLx4=", - "output_base64sha512": "E6SsXf62mq4oPTpoiH9LXKdCy0nq4o8bANVfkYqd5JTvIGxXr/ow7DYqotFvJc1GIFMyo4etB54CKVduAEzWDw==", - "output_file_mode": null, - "output_md5": "8f2434a1fe0b8f791c4c39848c1c1db0", - "output_path": "./handler-http.zip", - "output_sha": "af56405ad089f54b3382b1f8bbf648e9a4020186", - "output_sha256": "e64d49181c425f94cb82b591c3c1783b7acc5aeff8769ae02ddf441fe99a2f1e", - "output_sha512": "13a4ac5dfeb69aae283d3a68887f4b5ca742cb49eae28f1b00d55f918a9de494ef206c57affa30ec362aa2d16f25cd46205332a387ad079e0229576e004cd60f", - "output_size": 419, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": null, - "source_file": "./code/handler-http.js", - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "handler_job", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "e9056d193119214fe75cb187ca287199eaf326c9", - "output_base64sha256": "MbwoybKGBrsT3T/MrloAK0g+lu4qPCnAo1M0vAqR83Y=", - "output_base64sha512": "CHrmv338w4YGroUNZC7zR5vlgJvmrep5jToVJlPWjzEVeMO9J+eucD9LXzg89kzDsN/lVgCfQ3TvRE5RsOxugA==", - "output_file_mode": null, - "output_md5": "4aeae366572f35f199937c40c3708120", - "output_path": "./handler-job.zip", - "output_sha": "e9056d193119214fe75cb187ca287199eaf326c9", - "output_sha256": "31bc28c9b28606bb13dd3fccae5a002b483e96ee2a3c29c0a35334bc0a91f376", - "output_sha512": "087ae6bf7dfcc38606ae850d642ef3479be5809be6adea798d3a152653d68f311578c3bd27e7ae703f4b5f383cf64cc3b0dfe556009f4374ef444e51b0ec6e80", - "output_size": 506, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": null, - "source_file": "./code/handler-job.js", - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "hello_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "c9755d3a41cd22734918ed9c6350394cb70ddc4104031bcd8edd5e5fed9a1963", - "code_path": "./handler-http.zip", - "entrypoint": "handler-http.handle", - "env_vars": null, - "image_ref": "naeel/sless-default-hello-http:072b89774248", - "memory_mb": 128, - "name": "hello-http", - "namespace": "default", - "phase": "Ready", - "runtime": "nodejs20", - "timeout_sec": 30 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.handler_http" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "hello_job", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "4155f9367b1999fb0f80460e919dac79b6af559a603cf938d70a2a322fccc7ec", - "code_path": "./handler-job.zip", - "entrypoint": "handler-job.handle", - "env_vars": null, - "image_ref": "naeel/sless-default-hello-job:67f12d5519e3", - "memory_mb": 128, - "name": "hello-job", - "namespace": "default", - "phase": "Ready", - "runtime": "nodejs20", - "timeout_sec": 30 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.handler_job" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_job", - "name": "hello_run", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "completion_time": "2026-03-08T17:10:15Z", - "event_json": "{\"numbers\":[10,20,30,40,50]}", - "function": "hello-job", - "message": "completed successfully", - "name": "hello-run", - "namespace": "default", - "phase": "Succeeded", - "run_id": 8, - "start_time": "2026-03-08T17:10:05Z", - "wait_timeout_sec": 600 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.handler_job", - "sless_function.hello_job" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "hello_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "hello-http", - "name": "hello-http-trigger", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/hello-http" - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.handler_http", - "sless_function.hello_http" - ] - } - ] - } - ], - "check_results": null -} diff --git a/notes-python/dist/notes-list.zip b/notes-python/dist/notes-list.zip deleted file mode 100644 index bcf1f18..0000000 Binary files a/notes-python/dist/notes-list.zip and /dev/null differ diff --git a/notes-python/dist/notes.zip b/notes-python/dist/notes.zip deleted file mode 100644 index 628ff16..0000000 Binary files a/notes-python/dist/notes.zip and /dev/null differ diff --git a/notes-python/dist/sql-runner.zip b/notes-python/dist/sql-runner.zip deleted file mode 100644 index 6ae5e84..0000000 Binary files a/notes-python/dist/sql-runner.zip and /dev/null differ diff --git a/notes-python/terraform.tfstate.1773036471.backup b/notes-python/terraform.tfstate.1773036471.backup deleted file mode 100644 index f0b6146..0000000 --- a/notes-python/terraform.tfstate.1773036471.backup +++ /dev/null @@ -1,369 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.12.2", - "serial": 15, - "lineage": "46b43916-6d6b-060c-ad36-6176e18b5f7b", - "outputs": { - "notes_list_url": { - "value": "https://sless-api.kube5s.ru/fn/default/notes-list", - "type": "string" - }, - "notes_url": { - "value": "https://sless-api.kube5s.ru/fn/default/notes", - "type": "string" - } - }, - "resources": [ - { - "mode": "data", - "type": "archive_file", - "name": "notes", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "d1fed6f90a4b4fde556aa2cd18e6ffaa9877c377", - "output_base64sha256": "HMlgBvSu0gccGVilru2ybzC7v31uzG1Zy96bK4p+C1E=", - "output_base64sha512": "GoV68HbQgbVaBtR4v1kLUhh+kMpJRIoBz30SrgYlVGz13SzV1wZ/lPvedqYvhmirG0fQf63wHYThOmvOR7lvgQ==", - "output_file_mode": null, - "output_md5": "cf96673e4f09c58d2e49c1664cdee1c2", - "output_path": "./dist/notes.zip", - "output_sha": "d1fed6f90a4b4fde556aa2cd18e6ffaa9877c377", - "output_sha256": "1cc96006f4aed2071c1958a5aeedb26f30bbbf7d6ecc6d59cbde9b2b8a7e0b51", - "output_sha512": "1a857af076d081b55a06d478bf590b52187e90ca49448a01cf7d12ae0625546cf5dd2cd5d7067f94fbde76a62f8668ab1b47d07fadf01d84e13a6bce47b96f81", - "output_size": 1226, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/notes", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "notes_list", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "b5730a0d98ae778f80c27881147e14d060151fce", - "output_base64sha256": "1pbtfCE8qjpAP5Ddz9c9afhOUXq3aq56ordi5t4vX00=", - "output_base64sha512": "nTUnbRKT4d5g5g3HANTGxZi5FmUIaM3C660XzZmGQLJIQ+WNYNLJ/no/H30KzafwE1mJf2qkdcZ8cT3xZdciNg==", - "output_file_mode": null, - "output_md5": "090088f39e1ef9d8d7d152944441edb7", - "output_path": "./dist/notes-list.zip", - "output_sha": "b5730a0d98ae778f80c27881147e14d060151fce", - "output_sha256": "d696ed7c213caa3a403f90ddcfd73d69f84e517ab76aae7aa2b762e6de2f5f4d", - "output_sha512": "9d35276d1293e1de60e60dc700d4c6c598b916650868cdc2ebad17cd998640b24843e58d60d2c9fe7a3f1f7d0acda7f01359897f6aa475c67c713df165d72236", - "output_size": 746, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/notes-list", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "sql_runner", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "241f12b31b5d970c697341292954b220e44f8d2a", - "output_base64sha256": "w/IUipIki9V1lf33x06/gHq+KWVdfJO9Ni98DkhC+Xw=", - "output_base64sha512": "aLpBXApQ5Nltm6ODUqQc6VZQCoxgn68mHlcrOF8bFrdem8psnz7dujkJE5PYvB5VDNKmdsViDAKPNTwHxut4XA==", - "output_file_mode": null, - "output_md5": "1fe2ab7afe4730b1141146363d69bd23", - "output_path": "./dist/sql-runner.zip", - "output_sha": "241f12b31b5d970c697341292954b220e44f8d2a", - "output_sha256": "c3f2148a92248bd57595fdf7c74ebf807abe29655d7c93bd362f7c0e4842f97c", - "output_sha512": "68ba415c0a50e4d96d9ba38352a41ce956500a8c609faf261e572b385f1b16b75e9bca6c9f3eddba39091393d8bc1e550cd2a676c5620c028f353c07c6eb785c", - "output_size": 796, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/sql-runner", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "notes", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "aaefd6de16697af6ac7331c7e1795a5446c71e351e6cacc3f4b490b683feb0cb", - "code_path": "./dist/notes.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-notes:80b15a8b73f5", - "memory_mb": 128, - "name": "notes", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "notes_list", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "4091f7c79ca8c9c5663c812eb08f25ef5257af217bf2b412add670a4737b9d7f", - "code_path": "./dist/notes-list.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-notes-list:07f2d5ae5ee4", - "memory_mb": 128, - "name": "notes-list", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes_list" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "sql_runner", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "fcde93e59e6fd7f3db426482da7667d5d402aaaaaf56f79a7a60bb1722ec603a", - "code_path": "./dist/sql-runner.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-sql-runner:22bce581a299", - "memory_mb": 128, - "name": "sql-runner", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_job", - "name": "create_index", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "completion_time": "2026-03-09T05:44:59Z", - "event_json": "{\"statements\":[\"CREATE INDEX IF NOT EXISTS notes_created_idx ON notes(created_at DESC)\"]}", - "function": "sql-runner", - "message": "completed successfully", - "name": "notes-create-index", - "namespace": "default", - "phase": "Succeeded", - "run_id": 1, - "start_time": "2026-03-09T05:44:54Z", - "wait_timeout_sec": 60 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner", - "sless_function.sql_runner", - "sless_job.create_table" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_job", - "name": "create_table", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "completion_time": "2026-03-09T05:44:54Z", - "event_json": "{\"statements\":[\"CREATE TABLE IF NOT EXISTS notes (id serial PRIMARY KEY, title text NOT NULL, body text, created_at timestamp DEFAULT now())\"]}", - "function": "sql-runner", - "message": "completed successfully", - "name": "notes-create-table", - "namespace": "default", - "phase": "Succeeded", - "run_id": 1, - "start_time": "2026-03-09T05:44:39Z", - "wait_timeout_sec": 120 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner", - "sless_function.sql_runner" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "notes_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "notes", - "name": "notes-http", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/notes" - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes", - "sless_function.notes" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "notes_list_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "notes-list", - "name": "notes-list-http", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/notes-list" - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes_list", - "sless_function.notes_list" - ] - } - ] - } - ], - "check_results": null -} diff --git a/notes-python/terraform.tfstate.1773036482.backup b/notes-python/terraform.tfstate.1773036482.backup deleted file mode 100644 index b6402da..0000000 --- a/notes-python/terraform.tfstate.1773036482.backup +++ /dev/null @@ -1,365 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.12.2", - "serial": 16, - "lineage": "46b43916-6d6b-060c-ad36-6176e18b5f7b", - "outputs": { - "notes_list_url": { - "value": "https://sless-api.kube5s.ru/fn/default/notes-list", - "type": "string" - }, - "notes_url": { - "value": "https://sless-api.kube5s.ru/fn/default/notes", - "type": "string" - } - }, - "resources": [ - { - "mode": "data", - "type": "archive_file", - "name": "notes", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "d1fed6f90a4b4fde556aa2cd18e6ffaa9877c377", - "output_base64sha256": "HMlgBvSu0gccGVilru2ybzC7v31uzG1Zy96bK4p+C1E=", - "output_base64sha512": "GoV68HbQgbVaBtR4v1kLUhh+kMpJRIoBz30SrgYlVGz13SzV1wZ/lPvedqYvhmirG0fQf63wHYThOmvOR7lvgQ==", - "output_file_mode": null, - "output_md5": "cf96673e4f09c58d2e49c1664cdee1c2", - "output_path": "./dist/notes.zip", - "output_sha": "d1fed6f90a4b4fde556aa2cd18e6ffaa9877c377", - "output_sha256": "1cc96006f4aed2071c1958a5aeedb26f30bbbf7d6ecc6d59cbde9b2b8a7e0b51", - "output_sha512": "1a857af076d081b55a06d478bf590b52187e90ca49448a01cf7d12ae0625546cf5dd2cd5d7067f94fbde76a62f8668ab1b47d07fadf01d84e13a6bce47b96f81", - "output_size": 1226, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/notes", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "notes_list", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "b5730a0d98ae778f80c27881147e14d060151fce", - "output_base64sha256": "1pbtfCE8qjpAP5Ddz9c9afhOUXq3aq56ordi5t4vX00=", - "output_base64sha512": "nTUnbRKT4d5g5g3HANTGxZi5FmUIaM3C660XzZmGQLJIQ+WNYNLJ/no/H30KzafwE1mJf2qkdcZ8cT3xZdciNg==", - "output_file_mode": null, - "output_md5": "090088f39e1ef9d8d7d152944441edb7", - "output_path": "./dist/notes-list.zip", - "output_sha": "b5730a0d98ae778f80c27881147e14d060151fce", - "output_sha256": "d696ed7c213caa3a403f90ddcfd73d69f84e517ab76aae7aa2b762e6de2f5f4d", - "output_sha512": "9d35276d1293e1de60e60dc700d4c6c598b916650868cdc2ebad17cd998640b24843e58d60d2c9fe7a3f1f7d0acda7f01359897f6aa475c67c713df165d72236", - "output_size": 746, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/notes-list", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "sql_runner", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "241f12b31b5d970c697341292954b220e44f8d2a", - "output_base64sha256": "w/IUipIki9V1lf33x06/gHq+KWVdfJO9Ni98DkhC+Xw=", - "output_base64sha512": "aLpBXApQ5Nltm6ODUqQc6VZQCoxgn68mHlcrOF8bFrdem8psnz7dujkJE5PYvB5VDNKmdsViDAKPNTwHxut4XA==", - "output_file_mode": null, - "output_md5": "1fe2ab7afe4730b1141146363d69bd23", - "output_path": "./dist/sql-runner.zip", - "output_sha": "241f12b31b5d970c697341292954b220e44f8d2a", - "output_sha256": "c3f2148a92248bd57595fdf7c74ebf807abe29655d7c93bd362f7c0e4842f97c", - "output_sha512": "68ba415c0a50e4d96d9ba38352a41ce956500a8c609faf261e572b385f1b16b75e9bca6c9f3eddba39091393d8bc1e550cd2a676c5620c028f353c07c6eb785c", - "output_size": 796, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/sql-runner", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "notes_crud", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "aaefd6de16697af6ac7331c7e1795a5446c71e351e6cacc3f4b490b683feb0cb", - "code_path": "./dist/notes.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-notes:80b15a8b73f5", - "memory_mb": 128, - "name": "notes", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "notes_list", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "4091f7c79ca8c9c5663c812eb08f25ef5257af217bf2b412add670a4737b9d7f", - "code_path": "./dist/notes-list.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-notes-list:07f2d5ae5ee4", - "memory_mb": 128, - "name": "notes-list", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes_list" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "sql_runner", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "fcde93e59e6fd7f3db426482da7667d5d402aaaaaf56f79a7a60bb1722ec603a", - "code_path": "./dist/sql-runner.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-sql-runner:22bce581a299", - "memory_mb": 128, - "name": "sql-runner", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_job", - "name": "create_index", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "completion_time": "2026-03-09T05:44:59Z", - "event_json": "{\"statements\":[\"CREATE INDEX IF NOT EXISTS notes_created_idx ON notes(created_at DESC)\"]}", - "function": "sql-runner", - "message": "completed successfully", - "name": "notes-create-index", - "namespace": "default", - "phase": "Succeeded", - "run_id": 1, - "start_time": "2026-03-09T05:44:54Z", - "wait_timeout_sec": 60 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner", - "sless_function.sql_runner", - "sless_job.create_table" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_job", - "name": "create_table", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "completion_time": "2026-03-09T05:44:54Z", - "event_json": "{\"statements\":[\"CREATE TABLE IF NOT EXISTS notes (id serial PRIMARY KEY, title text NOT NULL, body text, created_at timestamp DEFAULT now())\"]}", - "function": "sql-runner", - "message": "completed successfully", - "name": "notes-create-table", - "namespace": "default", - "phase": "Succeeded", - "run_id": 1, - "start_time": "2026-03-09T05:44:39Z", - "wait_timeout_sec": 120 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner", - "sless_function.sql_runner" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "notes_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "notes", - "name": "notes-http", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/notes" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "notes_list_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "notes-list", - "name": "notes-list-http", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/notes-list" - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes_list", - "sless_function.notes_list" - ] - } - ] - } - ], - "check_results": null -} diff --git a/notes-python/terraform.tfstate.1773036493.backup b/notes-python/terraform.tfstate.1773036493.backup deleted file mode 100644 index a4b3e6a..0000000 --- a/notes-python/terraform.tfstate.1773036493.backup +++ /dev/null @@ -1,365 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.12.2", - "serial": 17, - "lineage": "46b43916-6d6b-060c-ad36-6176e18b5f7b", - "outputs": { - "notes_list_url": { - "value": "https://sless-api.kube5s.ru/fn/default/notes-list", - "type": "string" - }, - "notes_url": { - "value": "https://sless-api.kube5s.ru/fn/default/notes", - "type": "string" - } - }, - "resources": [ - { - "mode": "data", - "type": "archive_file", - "name": "notes", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "d1fed6f90a4b4fde556aa2cd18e6ffaa9877c377", - "output_base64sha256": "HMlgBvSu0gccGVilru2ybzC7v31uzG1Zy96bK4p+C1E=", - "output_base64sha512": "GoV68HbQgbVaBtR4v1kLUhh+kMpJRIoBz30SrgYlVGz13SzV1wZ/lPvedqYvhmirG0fQf63wHYThOmvOR7lvgQ==", - "output_file_mode": null, - "output_md5": "cf96673e4f09c58d2e49c1664cdee1c2", - "output_path": "./dist/notes.zip", - "output_sha": "d1fed6f90a4b4fde556aa2cd18e6ffaa9877c377", - "output_sha256": "1cc96006f4aed2071c1958a5aeedb26f30bbbf7d6ecc6d59cbde9b2b8a7e0b51", - "output_sha512": "1a857af076d081b55a06d478bf590b52187e90ca49448a01cf7d12ae0625546cf5dd2cd5d7067f94fbde76a62f8668ab1b47d07fadf01d84e13a6bce47b96f81", - "output_size": 1226, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/notes", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "notes_list", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "b5730a0d98ae778f80c27881147e14d060151fce", - "output_base64sha256": "1pbtfCE8qjpAP5Ddz9c9afhOUXq3aq56ordi5t4vX00=", - "output_base64sha512": "nTUnbRKT4d5g5g3HANTGxZi5FmUIaM3C660XzZmGQLJIQ+WNYNLJ/no/H30KzafwE1mJf2qkdcZ8cT3xZdciNg==", - "output_file_mode": null, - "output_md5": "090088f39e1ef9d8d7d152944441edb7", - "output_path": "./dist/notes-list.zip", - "output_sha": "b5730a0d98ae778f80c27881147e14d060151fce", - "output_sha256": "d696ed7c213caa3a403f90ddcfd73d69f84e517ab76aae7aa2b762e6de2f5f4d", - "output_sha512": "9d35276d1293e1de60e60dc700d4c6c598b916650868cdc2ebad17cd998640b24843e58d60d2c9fe7a3f1f7d0acda7f01359897f6aa475c67c713df165d72236", - "output_size": 746, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/notes-list", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "sql_runner", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "241f12b31b5d970c697341292954b220e44f8d2a", - "output_base64sha256": "w/IUipIki9V1lf33x06/gHq+KWVdfJO9Ni98DkhC+Xw=", - "output_base64sha512": "aLpBXApQ5Nltm6ODUqQc6VZQCoxgn68mHlcrOF8bFrdem8psnz7dujkJE5PYvB5VDNKmdsViDAKPNTwHxut4XA==", - "output_file_mode": null, - "output_md5": "1fe2ab7afe4730b1141146363d69bd23", - "output_path": "./dist/sql-runner.zip", - "output_sha": "241f12b31b5d970c697341292954b220e44f8d2a", - "output_sha256": "c3f2148a92248bd57595fdf7c74ebf807abe29655d7c93bd362f7c0e4842f97c", - "output_sha512": "68ba415c0a50e4d96d9ba38352a41ce956500a8c609faf261e572b385f1b16b75e9bca6c9f3eddba39091393d8bc1e550cd2a676c5620c028f353c07c6eb785c", - "output_size": 796, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/sql-runner", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "notes_crud", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "aaefd6de16697af6ac7331c7e1795a5446c71e351e6cacc3f4b490b683feb0cb", - "code_path": "./dist/notes.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-notes:80b15a8b73f5", - "memory_mb": 128, - "name": "notes", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "notes_list", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "4091f7c79ca8c9c5663c812eb08f25ef5257af217bf2b412add670a4737b9d7f", - "code_path": "./dist/notes-list.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-notes-list:07f2d5ae5ee4", - "memory_mb": 128, - "name": "notes-list", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes_list" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "sql_runner", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "fcde93e59e6fd7f3db426482da7667d5d402aaaaaf56f79a7a60bb1722ec603a", - "code_path": "./dist/sql-runner.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-sql-runner:22bce581a299", - "memory_mb": 128, - "name": "sql-runner", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_job", - "name": "create_index", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "completion_time": "2026-03-09T05:44:59Z", - "event_json": "{\"statements\":[\"CREATE INDEX IF NOT EXISTS notes_created_idx ON notes(created_at DESC)\"]}", - "function": "sql-runner", - "message": "completed successfully", - "name": "notes-create-index", - "namespace": "default", - "phase": "Succeeded", - "run_id": 1, - "start_time": "2026-03-09T05:44:54Z", - "wait_timeout_sec": 60 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner", - "sless_function.sql_runner", - "sless_job.create_table" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_job", - "name": "create_table", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "completion_time": "2026-03-09T05:44:54Z", - "event_json": "{\"statements\":[\"CREATE TABLE IF NOT EXISTS notes (id serial PRIMARY KEY, title text NOT NULL, body text, created_at timestamp DEFAULT now())\"]}", - "function": "sql-runner", - "message": "completed successfully", - "name": "notes-create-table", - "namespace": "default", - "phase": "Succeeded", - "run_id": 1, - "start_time": "2026-03-09T05:44:39Z", - "wait_timeout_sec": 120 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner", - "sless_function.sql_runner" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "notes_crud_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "notes", - "name": "notes-http", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/notes" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "notes_list_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "notes-list", - "name": "notes-list-http", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/notes-list" - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes_list", - "sless_function.notes_list" - ] - } - ] - } - ], - "check_results": null -} diff --git a/notes-python/terraform.tfstate.1773036504.backup b/notes-python/terraform.tfstate.1773036504.backup deleted file mode 100644 index 13a4818..0000000 --- a/notes-python/terraform.tfstate.1773036504.backup +++ /dev/null @@ -1,360 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.12.2", - "serial": 18, - "lineage": "46b43916-6d6b-060c-ad36-6176e18b5f7b", - "outputs": { - "notes_list_url": { - "value": "https://sless-api.kube5s.ru/fn/default/notes-list", - "type": "string" - }, - "notes_url": { - "value": "https://sless-api.kube5s.ru/fn/default/notes", - "type": "string" - } - }, - "resources": [ - { - "mode": "data", - "type": "archive_file", - "name": "notes", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "d1fed6f90a4b4fde556aa2cd18e6ffaa9877c377", - "output_base64sha256": "HMlgBvSu0gccGVilru2ybzC7v31uzG1Zy96bK4p+C1E=", - "output_base64sha512": "GoV68HbQgbVaBtR4v1kLUhh+kMpJRIoBz30SrgYlVGz13SzV1wZ/lPvedqYvhmirG0fQf63wHYThOmvOR7lvgQ==", - "output_file_mode": null, - "output_md5": "cf96673e4f09c58d2e49c1664cdee1c2", - "output_path": "./dist/notes.zip", - "output_sha": "d1fed6f90a4b4fde556aa2cd18e6ffaa9877c377", - "output_sha256": "1cc96006f4aed2071c1958a5aeedb26f30bbbf7d6ecc6d59cbde9b2b8a7e0b51", - "output_sha512": "1a857af076d081b55a06d478bf590b52187e90ca49448a01cf7d12ae0625546cf5dd2cd5d7067f94fbde76a62f8668ab1b47d07fadf01d84e13a6bce47b96f81", - "output_size": 1226, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/notes", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "notes_list", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "b5730a0d98ae778f80c27881147e14d060151fce", - "output_base64sha256": "1pbtfCE8qjpAP5Ddz9c9afhOUXq3aq56ordi5t4vX00=", - "output_base64sha512": "nTUnbRKT4d5g5g3HANTGxZi5FmUIaM3C660XzZmGQLJIQ+WNYNLJ/no/H30KzafwE1mJf2qkdcZ8cT3xZdciNg==", - "output_file_mode": null, - "output_md5": "090088f39e1ef9d8d7d152944441edb7", - "output_path": "./dist/notes-list.zip", - "output_sha": "b5730a0d98ae778f80c27881147e14d060151fce", - "output_sha256": "d696ed7c213caa3a403f90ddcfd73d69f84e517ab76aae7aa2b762e6de2f5f4d", - "output_sha512": "9d35276d1293e1de60e60dc700d4c6c598b916650868cdc2ebad17cd998640b24843e58d60d2c9fe7a3f1f7d0acda7f01359897f6aa475c67c713df165d72236", - "output_size": 746, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/notes-list", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "data", - "type": "archive_file", - "name": "sql_runner", - "provider": "provider[\"registry.terraform.io/hashicorp/archive\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "exclude_symlink_directories": null, - "excludes": null, - "id": "241f12b31b5d970c697341292954b220e44f8d2a", - "output_base64sha256": "w/IUipIki9V1lf33x06/gHq+KWVdfJO9Ni98DkhC+Xw=", - "output_base64sha512": "aLpBXApQ5Nltm6ODUqQc6VZQCoxgn68mHlcrOF8bFrdem8psnz7dujkJE5PYvB5VDNKmdsViDAKPNTwHxut4XA==", - "output_file_mode": null, - "output_md5": "1fe2ab7afe4730b1141146363d69bd23", - "output_path": "./dist/sql-runner.zip", - "output_sha": "241f12b31b5d970c697341292954b220e44f8d2a", - "output_sha256": "c3f2148a92248bd57595fdf7c74ebf807abe29655d7c93bd362f7c0e4842f97c", - "output_sha512": "68ba415c0a50e4d96d9ba38352a41ce956500a8c609faf261e572b385f1b16b75e9bca6c9f3eddba39091393d8bc1e550cd2a676c5620c028f353c07c6eb785c", - "output_size": 796, - "source": [], - "source_content": null, - "source_content_filename": null, - "source_dir": "./code/sql-runner", - "source_file": null, - "type": "zip" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "notes_crud", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "aaefd6de16697af6ac7331c7e1795a5446c71e351e6cacc3f4b490b683feb0cb", - "code_path": "./dist/notes.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-notes:80b15a8b73f5", - "memory_mb": 128, - "name": "notes", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "notes_list", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "4091f7c79ca8c9c5663c812eb08f25ef5257af217bf2b412add670a4737b9d7f", - "code_path": "./dist/notes-list.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-notes-list:07f2d5ae5ee4", - "memory_mb": 128, - "name": "notes-list", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes_list" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_function", - "name": "sql_runner", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "build_timeout_sec": 300, - "code_hash": "fcde93e59e6fd7f3db426482da7667d5d402aaaaaf56f79a7a60bb1722ec603a", - "code_path": "./dist/sql-runner.zip", - "entrypoint": "handler.handle", - "env_vars": { - "PG_DSN": "postgres://sless:sless-pg-password@postgres.sless.svc.cluster.local:5432/sless?sslmode=disable" - }, - "image_ref": "naeel/sless-default-sql-runner:22bce581a299", - "memory_mb": 128, - "name": "sql-runner", - "namespace": "default", - "phase": "Ready", - "runtime": "python3.11", - "timeout_sec": 30 - }, - "sensitive_attributes": [ - [ - { - "type": "get_attr", - "value": "env_vars" - }, - { - "type": "index", - "value": { - "value": "PG_DSN", - "type": "string" - } - } - ] - ], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_job", - "name": "create_index", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "completion_time": "2026-03-09T05:44:59Z", - "event_json": "{\"statements\":[\"CREATE INDEX IF NOT EXISTS notes_created_idx ON notes(created_at DESC)\"]}", - "function": "sql-runner", - "message": "completed successfully", - "name": "notes-create-index", - "namespace": "default", - "phase": "Succeeded", - "run_id": 1, - "start_time": "2026-03-09T05:44:54Z", - "wait_timeout_sec": 60 - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_job", - "name": "notes_table_init", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "completion_time": "2026-03-09T05:44:54Z", - "event_json": "{\"statements\":[\"CREATE TABLE IF NOT EXISTS notes (id serial PRIMARY KEY, title text NOT NULL, body text, created_at timestamp DEFAULT now())\"]}", - "function": "sql-runner", - "message": "completed successfully", - "name": "notes-create-table", - "namespace": "default", - "phase": "Succeeded", - "run_id": 1, - "start_time": "2026-03-09T05:44:39Z", - "wait_timeout_sec": 120 - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.sql_runner", - "sless_function.sql_runner" - ] - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "notes_crud_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "notes", - "name": "notes-http", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/notes" - }, - "sensitive_attributes": [], - "identity_schema_version": 0 - } - ] - }, - { - "mode": "managed", - "type": "sless_trigger", - "name": "notes_list_http", - "provider": "provider[\"terra.k8c.ru/naeel/sless\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "active": true, - "enabled": true, - "function": "notes-list", - "name": "notes-list-http", - "namespace": "default", - "schedule": null, - "type": "http", - "url": "https://sless-api.kube5s.ru/fn/default/notes-list" - }, - "sensitive_attributes": [], - "identity_schema_version": 0, - "dependencies": [ - "data.archive_file.notes_list", - "sless_function.notes_list" - ] - } - ] - } - ], - "check_results": null -} diff --git a/run_terraform_examples.sh b/run_terraform_examples.sh new file mode 100755 index 0000000..0ea985a --- /dev/null +++ b/run_terraform_examples.sh @@ -0,0 +1,333 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOG_DIR="$ROOT_DIR/.test-logs" +mkdir -p "$LOG_DIR" + +EXAMPLES=( + "hello-node" + "simple-node" + "simple-python" + "notes-python" +) + +declare -A CHECK_METHOD=( + [hello-node]="POST" + [simple-node]="GET" + [simple-python]="GET" + [notes-python]="GET" +) + +declare -A CHECK_URL=( + [hello-node]="https://sless-api.kube5s.ru/fn/default/hello-http" + [simple-node]="https://sless-api.kube5s.ru/fn/default/simple-node-time-display" + [simple-python]="https://sless-api.kube5s.ru/fn/default/simple-py-time-display" + [notes-python]="https://sless-api.kube5s.ru/fn/default/notes-list" +) + +declare -A CHECK_DATA=( + [hello-node]='{"name":"Smoke"}' + [simple-node]='' + [simple-python]='' + [notes-python]='' +) + +declare -a PREEXISTING_EXAMPLES=() +LAST_LOG_FILE="" +CURRENT_EXAMPLE="" +CURRENT_STEP="" +LAST_ERROR_SUMMARY="" + +fail_run() { + local example="$1" + local step="$2" + local details="$3" + + echo + echo "ERROR SUMMARY" + echo "example: $example" + echo "step: $step" + echo "reason: $details" + + if [ -n "$LAST_LOG_FILE" ] && [ -f "$LAST_LOG_FILE" ]; then + echo "log: $LAST_LOG_FILE" + echo "last log lines:" + tail -n 20 "$LAST_LOG_FILE" + fi + + exit 1 +} + +run_step() { + local example="$1" + local step="$2" + shift 2 + + CURRENT_EXAMPLE="$example" + CURRENT_STEP="$step" + LAST_ERROR_SUMMARY="" + + if ! "$@"; then + local details="$LAST_ERROR_SUMMARY" + if [ -z "$details" ]; then + details="step failed without explicit summary" + fi + fail_run "$example" "$step" "$details" + fi +} + +restore_any_backups() { + local backup + while IFS= read -r backup; do + [ -n "$backup" ] || continue + if [ -f "$backup" ]; then + mv "$backup" "${backup%.copilot.bak}" + fi + done < <(find "$ROOT_DIR" -name '*.copilot.bak' | sort) +} + +trap restore_any_backups EXIT + +clean_local_artifacts() { + local example="$1" + rm -rf \ + "$ROOT_DIR/$example/.terraform" \ + "$ROOT_DIR/$example/.terraform.lock.hcl" \ + "$ROOT_DIR/$example/terraform.tfstate" \ + "$ROOT_DIR/$example/terraform.tfstate.backup" \ + "$ROOT_DIR/$example"/terraform.tfstate.*.backup \ + "$ROOT_DIR/$example/dist" +} + +retry_tf() { + local example="$1" + local label="$2" + shift 2 + + local attempt=1 + while [ "$attempt" -le 3 ]; do + LAST_LOG_FILE="$LOG_DIR/${example//\//_}-${label// /_}-${attempt}.log" + echo "==> [$example] $label (attempt $attempt/3)" + + ( + cd "$ROOT_DIR/$example" + "$@" + ) 2>&1 | tee "$LAST_LOG_FILE" + + local status=${PIPESTATUS[0]} + if [ "$status" -eq 0 ]; then + return 0 + fi + + if grep -Eiq 'Unauthorized|401|403' "$LAST_LOG_FILE"; then + LAST_ERROR_SUMMARY="authorization error during $label" + echo "[$example] authorization error during $label" + return 41 + fi + + if grep -Eiq 'TLS handshake timeout|tls:.*timeout|i/o timeout|Client\.Timeout exceeded while awaiting headers|context deadline exceeded|unexpected EOF' "$LAST_LOG_FILE" && [ "$attempt" -lt 3 ]; then + attempt=$((attempt + 1)) + sleep 2 + continue + fi + + if grep -Eiq 'TLS handshake timeout|tls:.*timeout|i/o timeout|Client\.Timeout exceeded while awaiting headers|context deadline exceeded|unexpected EOF' "$LAST_LOG_FILE"; then + LAST_ERROR_SUMMARY="network/provider download failure during $label after retries" + else + LAST_ERROR_SUMMARY="terraform command failed during $label with exit code $status" + fi + + return "$status" + done + + LAST_ERROR_SUMMARY="terraform command failed during $label after exhausting retries" + return 1 +} + +record_preexisting_if_needed() { + local example="$1" + if grep -Fq 'No changes. Your infrastructure matches the configuration.' "$LAST_LOG_FILE"; then + PREEXISTING_EXAMPLES+=("$example") + echo "[$example] detected preexisting remote resources on clean apply" + fi +} + +probe_endpoint() { + local example="$1" + local body_file="$LOG_DIR/${example//\//_}-endpoint-body.txt" + local status_file="$LOG_DIR/${example//\//_}-endpoint-status.txt" + local method="${CHECK_METHOD[$example]}" + local url="${CHECK_URL[$example]}" + local data="${CHECK_DATA[$example]}" + + if [ "$method" = "POST" ]; then + curl -sS -X POST -H 'Content-Type: application/json' -d "$data" -o "$body_file" -w '%{http_code}' "$url" > "$status_file" + else + curl -sS -o "$body_file" -w '%{http_code}' "$url" > "$status_file" + fi +} + +assert_live_endpoint() { + local example="$1" + probe_endpoint "$example" + + local body_file="$LOG_DIR/${example//\//_}-endpoint-body.txt" + local status + status="$(cat "$LOG_DIR/${example//\//_}-endpoint-status.txt")" + + if [ "$status" != "200" ]; then + LAST_ERROR_SUMMARY="live endpoint check failed with HTTP $status" + echo "[$example] live endpoint check failed with HTTP $status" + cat "$body_file" + return 1 + fi + + if grep -Fq 'function unreachable' "$body_file"; then + LAST_ERROR_SUMMARY="live endpoint returned function unreachable" + echo "[$example] live endpoint check returned unreachable function" + cat "$body_file" + return 1 + fi +} + +assert_destroyed_endpoint() { + local example="$1" + probe_endpoint "$example" + + local body_file="$LOG_DIR/${example//\//_}-endpoint-body.txt" + local status + status="$(cat "$LOG_DIR/${example//\//_}-endpoint-status.txt")" + + if [ "$status" = "404" ] || [ "$status" = "000" ]; then + return 0 + fi + + if grep -Eiq 'not found|404 page not found' "$body_file"; then + return 0 + fi + + if [ "$status" = "502" ] && grep -Fq 'function unreachable' "$body_file"; then + LAST_ERROR_SUMMARY="route cleanup bug: public endpoint still exists but backend is already gone (HTTP 502 function unreachable)" + echo "[$example] route still exists after destroy, but backend is already gone (HTTP 502 function unreachable)" + cat "$body_file" + return 1 + fi + + LAST_ERROR_SUMMARY="endpoint still responds after destroy with HTTP $status" + echo "[$example] endpoint still responds after destroy with HTTP $status" + cat "$body_file" + return 1 +} + +wait_for_destroyed_endpoint() { + local example="$1" + local attempts=24 + local sleep_sec=5 + local try=1 + + while [ "$try" -le "$attempts" ]; do + if assert_destroyed_endpoint "$example"; then + echo "[$example] endpoint disappeared after destroy" + return 0 + fi + + echo "[$example] endpoint still present after destroy, waiting (${try}/${attempts})" + try=$((try + 1)) + sleep "$sleep_sec" + done + + echo "[$example] endpoint did not disappear after destroy within $((attempts * sleep_sec))s" + if [ -z "$LAST_ERROR_SUMMARY" ]; then + LAST_ERROR_SUMMARY="endpoint remained reachable for more than $((attempts * sleep_sec))s after destroy" + else + LAST_ERROR_SUMMARY="$LAST_ERROR_SUMMARY; endpoint was still published after $((attempts * sleep_sec))s" + fi + return 1 +} + +backup_and_modify() { + local example="$1" + case "$example" in + hello-node) + cp "$ROOT_DIR/$example/http.tf" "$ROOT_DIR/$example/http.tf.copilot.bak" + perl -0pi -e 's/enabled\s+=\s+true/enabled = false/' "$ROOT_DIR/$example/http.tf" + ;; + simple-node) + cp "$ROOT_DIR/$example/time-display.tf" "$ROOT_DIR/$example/time-display.tf.copilot.bak" + perl -0pi -e 's/memory_mb\s+=\s+64/memory_mb = 96/' "$ROOT_DIR/$example/time-display.tf" + ;; + simple-python) + cp "$ROOT_DIR/$example/time-display.tf" "$ROOT_DIR/$example/time-display.tf.copilot.bak" + perl -0pi -e 's/memory_mb\s+=\s+64/memory_mb = 96/' "$ROOT_DIR/$example/time-display.tf" + ;; + notes-python) + cp "$ROOT_DIR/$example/notes-list.tf" "$ROOT_DIR/$example/notes-list.tf.copilot.bak" + perl -0pi -e 's/memory_mb\s+=\s+128/memory_mb = 160/' "$ROOT_DIR/$example/notes-list.tf" + ;; + esac +} + +restore_modified_files() { + local example="$1" + case "$example" in + hello-node) + mv "$ROOT_DIR/$example/http.tf.copilot.bak" "$ROOT_DIR/$example/http.tf" + ;; + simple-node) + mv "$ROOT_DIR/$example/time-display.tf.copilot.bak" "$ROOT_DIR/$example/time-display.tf" + ;; + simple-python) + mv "$ROOT_DIR/$example/time-display.tf.copilot.bak" "$ROOT_DIR/$example/time-display.tf" + ;; + notes-python) + mv "$ROOT_DIR/$example/notes-list.tf.copilot.bak" "$ROOT_DIR/$example/notes-list.tf" + ;; + esac +} + +run_example() { + local example="$1" + + echo + echo "==== $example ====" + + clean_local_artifacts "$example" + run_step "$example" "terraform init" retry_tf "$example" "terraform init" terraform init -input=false -no-color + run_step "$example" "terraform apply clean" retry_tf "$example" "terraform apply clean" terraform apply -auto-approve -input=false -no-color + record_preexisting_if_needed "$example" + run_step "$example" "endpoint check after clean apply" assert_live_endpoint "$example" + + run_step "$example" "terraform destroy clean" retry_tf "$example" "terraform destroy clean" terraform destroy -auto-approve -input=false -no-color + run_step "$example" "endpoint cleanup after clean destroy" wait_for_destroyed_endpoint "$example" + + run_step "$example" "terraform apply second" retry_tf "$example" "terraform apply second" terraform apply -auto-approve -input=false -no-color + run_step "$example" "endpoint check after second apply" assert_live_endpoint "$example" + + backup_and_modify "$example" + run_step "$example" "terraform apply modified" retry_tf "$example" "terraform apply modified" terraform apply -auto-approve -input=false -no-color + restore_modified_files "$example" + + run_step "$example" "terraform destroy final" retry_tf "$example" "terraform destroy final" terraform destroy -auto-approve -input=false -no-color + run_step "$example" "endpoint cleanup after final destroy" wait_for_destroyed_endpoint "$example" + clean_local_artifacts "$example" +} + +main() { + local example + for example in "${EXAMPLES[@]}"; do + run_example "$example" + done + + if [ "${#PREEXISTING_EXAMPLES[@]}" -gt 0 ]; then + echo + echo "Preexisting remote resources were detected on first apply for: ${PREEXISTING_EXAMPLES[*]}" + exit 2 + fi + + echo + echo "All Terraform example lifecycles completed successfully." +} + +main "$@" \ No newline at end of file diff --git a/simple-node/dist/time_display.zip b/simple-node/dist/time_display.zip deleted file mode 100644 index 1b3ce4e..0000000 Binary files a/simple-node/dist/time_display.zip and /dev/null differ diff --git a/simple-node/dist/time_getter.zip b/simple-node/dist/time_getter.zip deleted file mode 100644 index c85ffec..0000000 Binary files a/simple-node/dist/time_getter.zip and /dev/null differ diff --git a/simple-python/dist/time_display.zip b/simple-python/dist/time_display.zip deleted file mode 100644 index 04a485d..0000000 Binary files a/simple-python/dist/time_display.zip and /dev/null differ diff --git a/simple-python/dist/time_getter.zip b/simple-python/dist/time_getter.zip deleted file mode 100644 index 4f35be2..0000000 Binary files a/simple-python/dist/time_getter.zip and /dev/null differ