diff --git a/examples/hello-node/http.tf b/examples/hello-node/http.tf index d7d796a..76c92d1 100644 --- a/examples/hello-node/http.tf +++ b/examples/hello-node/http.tf @@ -14,7 +14,6 @@ data "archive_file" "handler_http" { } resource "sless_function" "hello_http" { - namespace = "default" name = "hello-http" runtime = "nodejs20" entrypoint = "handler-http.handle" @@ -28,7 +27,6 @@ resource "sless_function" "hello_http" { } resource "sless_trigger" "hello_http" { - namespace = "default" name = "hello-http-trigger" type = "http" function = sless_function.hello_http.name diff --git a/examples/hello-node/job.tf b/examples/hello-node/job.tf index 30ac4f0..0c8fc5a 100644 --- a/examples/hello-node/job.tf +++ b/examples/hello-node/job.tf @@ -16,7 +16,6 @@ data "archive_file" "handler_job" { } resource "sless_function" "hello_job" { - namespace = "default" name = "hello-job" runtime = "nodejs20" entrypoint = "handler-job.handle" @@ -31,7 +30,6 @@ resource "sless_function" "hello_job" { # Одноразовый запуск. Все поля immutable — изменение любого пересоздаёт джоб. # run_id: 0 = не запускать, 1+ = запустить. Для повторного запуска увеличь run_id (1→2→3...). resource "sless_job" "hello_run" { - namespace = "default" name = "hello-run" function = sless_function.hello_job.name event_json = jsonencode({ numbers = [100, 200, 300] }) diff --git a/examples/hello-node/main.tf b/examples/hello-node/main.tf index f38a1c2..271f13b 100644 --- a/examples/hello-node/main.tf +++ b/examples/hello-node/main.tf @@ -8,7 +8,7 @@ terraform { required_providers { sless = { source = "terra.k8c.ru/naeel/sless" - version = "~> 0.1.7" + version = "~> 0.1.9" } archive = { source = "hashicorp/archive" @@ -18,7 +18,8 @@ terraform { } provider "sless" { - endpoint = "https://sless-api.kube5s.ru" - token = "dev-token-change-me" + endpoint = "https://sless-api.kube5s.ru" + token = "dev-token-change-me" + namespace = "default" } diff --git a/examples/notes-python/init.tf b/examples/notes-python/init.tf index 0924819..85cf5fb 100644 --- a/examples/notes-python/init.tf +++ b/examples/notes-python/init.tf @@ -14,7 +14,6 @@ # Джоб создания таблицы notes. # CREATE TABLE IF NOT EXISTS — безопасно запускать повторно, таблица не пересоздаётся. resource "sless_job" "notes_table_init" { - namespace = "default" name = "notes-create-table" function = sless_function.sql_runner.name wait_timeout_sec = 120 @@ -32,7 +31,6 @@ resource "sless_job" "notes_table_init" { resource "sless_job" "notes_index_init" { depends_on = [sless_job.notes_table_init] - namespace = "default" name = "notes-create-index" function = sless_function.sql_runner.name wait_timeout_sec = 60 diff --git a/examples/notes-python/main.tf b/examples/notes-python/main.tf index 65124e0..62af59a 100644 --- a/examples/notes-python/main.tf +++ b/examples/notes-python/main.tf @@ -14,7 +14,7 @@ terraform { # Провайдер для управления serverless функциями через sless API sless = { source = "terra.k8c.ru/naeel/sless" - version = "~> 0.1.7" + version = "~> 0.1.9" } # hashicorp/archive — для упаковки исходников в zip перед загрузкой archive = { @@ -27,6 +27,7 @@ terraform { # sless провайдер подключается к API кластера. # В продакшне token следует передавать через TF_VAR или secrets. provider "sless" { - endpoint = "https://sless-api.kube5s.ru" - token = "dev-token-change-me" + endpoint = "https://sless-api.kube5s.ru" + token = "dev-token-change-me" + namespace = "default" } diff --git a/examples/notes-python/notes-list.tf b/examples/notes-python/notes-list.tf index 5ed81f5..1b76e13 100644 --- a/examples/notes-python/notes-list.tf +++ b/examples/notes-python/notes-list.tf @@ -18,7 +18,6 @@ data "archive_file" "notes_list_zip" { # Read-only функция в кластере. # entrypoint = "notes_list.list_notes" → файл notes_list.py, функция list_notes(). resource "sless_function" "notes_list" { - namespace = "default" name = "notes-list" runtime = "python3.11" entrypoint = "notes_list.list_notes" @@ -36,7 +35,6 @@ resource "sless_function" "notes_list" { # HTTP-триггер для read-only функции. # Создаёт Ingress, URL доступен в outputs.tf. resource "sless_trigger" "notes_list_http" { - namespace = "default" name = "notes-list-http" type = "http" function = sless_function.notes_list.name diff --git a/examples/notes-python/notes.tf b/examples/notes-python/notes.tf index 8e2a17c..d7f2b8e 100644 --- a/examples/notes-python/notes.tf +++ b/examples/notes-python/notes.tf @@ -20,7 +20,6 @@ data "archive_file" "notes_crud_zip" { # CRUD функция в кластере. # entrypoint = "notes_crud.crud" → файл notes_crud.py, функция crud(). resource "sless_function" "notes_crud" { - namespace = "default" name = "notes" runtime = "python3.11" entrypoint = "notes_crud.crud" @@ -39,7 +38,6 @@ resource "sless_function" "notes_crud" { # Создаёт Ingress в кластере, URL доступен в outputs.tf. # Базовый URL: https://sless-api.kube5s.ru/fn/default/notes resource "sless_trigger" "notes_crud_http" { - namespace = "default" name = "notes-http" type = "http" function = sless_function.notes_crud.name diff --git a/examples/notes-python/sql-runner.tf b/examples/notes-python/sql-runner.tf index 9800b6d..92bc8ad 100644 --- a/examples/notes-python/sql-runner.tf +++ b/examples/notes-python/sql-runner.tf @@ -17,7 +17,6 @@ data "archive_file" "sql_runner_zip" { # entrypoint = "sql_runner.run_sql" → файл sql_runner.py, функция run_sql(). # memory_mb=128 достаточно — DDL запросы не требуют памяти на вычисления. resource "sless_function" "sql_runner" { - namespace = "default" name = "sql-runner" runtime = "python3.11" entrypoint = "sql_runner.run_sql" diff --git a/examples/pg-query/main.tf b/examples/pg-query/main.tf index 42f18db..787cea3 100644 --- a/examples/pg-query/main.tf +++ b/examples/pg-query/main.tf @@ -6,7 +6,7 @@ terraform { required_providers { sless = { source = "terra.k8c.ru/naeel/sless" - version = "~> 0.1.7" + version = "~> 0.1.9" } archive = { source = "hashicorp/archive" @@ -16,6 +16,7 @@ terraform { } provider "sless" { - endpoint = "https://sless-api.kube5s.ru" - token = "dev-token-change-me" + endpoint = "https://sless-api.kube5s.ru" + token = "dev-token-change-me" + namespace = "default" } diff --git a/examples/pg-query/pg-query.tf b/examples/pg-query/pg-query.tf index f3b876b..e12048a 100644 --- a/examples/pg-query/pg-query.tf +++ b/examples/pg-query/pg-query.tf @@ -13,7 +13,6 @@ data "archive_file" "handler_pg_query" { } resource "sless_function" "pg_query" { - namespace = "default" name = "pg-query" runtime = "python3.11" entrypoint = "handler.handle" @@ -31,7 +30,6 @@ resource "sless_function" "pg_query" { } resource "sless_trigger" "pg_query_http" { - namespace = "default" name = "pg-query-http" type = "http" function = sless_function.pg_query.name diff --git a/examples/simple-node/main.tf b/examples/simple-node/main.tf index 76bb8eb..fc6d96b 100644 --- a/examples/simple-node/main.tf +++ b/examples/simple-node/main.tf @@ -1,13 +1,26 @@ # Создано: 2026-03-09 -# main.tf — точка входа для примера simple-node. -# Демонстрирует цепочку: sless_job (one-shot) → sless_function (http). -# Аналог simple-python, но на Node.js 20. +# main.tf — пример: запустить один раз скрипт при деплое и передать его результат в функцию. +# То же самое что simple-python, но на Node.js 20. +# +# Как это работает: +# 1. При «terraform apply» запускается скрипт-джоб (time_getter) +# 2. Скрипт возвращает JSON с текущим временем +# 3. Terraform подхватывает этот JSON и передаёт в переменную окружения HTTP-функции (time_display) +# 4. Функция отдаёт время при каждом запросе +# +# Зачем такое нужно: +# Если данные нужны функции, но считаются один раз при деплое — +# напишите логику в джоб, а результат передайте через env_vars. +# Например: получить токен, версию схемы БД, время деплоя и т.д. +# +# namespace задаётся один раз здесь, в блоке provider. +# В ресурсах (sless_function, sless_trigger, sless_job) namespace НЕ указывается. terraform { required_providers { sless = { source = "terra.k8c.ru/naeel/sless" - version = "~> 0.1.8" + version = "~> 0.1.9" } archive = { source = "hashicorp/archive" @@ -17,6 +30,7 @@ terraform { } provider "sless" { - endpoint = "https://sless-api.kube5s.ru" - token = "dev-token-change-me" + endpoint = "https://sless-api.kube5s.ru" + token = "dev-token-change-me" + namespace = "default" } diff --git a/examples/simple-node/outputs.tf b/examples/simple-node/outputs.tf index 06204e2..4d59ef6 100644 --- a/examples/simple-node/outputs.tf +++ b/examples/simple-node/outputs.tf @@ -1,12 +1,14 @@ # Создано: 2026-03-09 -# outputs.tf — полезные значения после terraform apply. +# outputs.tf — что выводит terraform после apply. +# Адрес вашей функции — откройте в браузере или вставьте в curl output "display_url" { - description = "URL HTTP-триггера функции time_display" + description = "URL функции time_display" value = sless_trigger.display_http.url } +# Что вернул скрипт-джоб — именно это передано в функцию как JOB_TIME output "job_result" { - description = "Stdout джоба (return value функции getTime)" + description = "Результат выполнения скрипта time_getter" value = sless_job.run_getter.message } diff --git a/examples/simple-node/time-display.tf b/examples/simple-node/time-display.tf index 33ee0d3..0779f74 100644 --- a/examples/simple-node/time-display.tf +++ b/examples/simple-node/time-display.tf @@ -1,35 +1,35 @@ # Создано: 2026-03-09 -# time-display.tf — постоянная HTTP-функция, получающая данные от джоба. +# time-display.tf — HTTP-функция, доступная по URL после apply. +# Получает результат джоба (из time-getter.tf) через переменную окружения JOB_TIME. -# Упаковываем код функции в zip +# Упаковываем код функции в zip для загрузки data "archive_file" "time_display_zip" { type = "zip" source_dir = "${path.module}/code/time_display" output_path = "${path.module}/dist/time_display.zip" } -# HTTP-функция: постоянный Deployment, читает JOB_TIME из env +# HTTP-функция — отвечает на запросы по URL из outputs.tf resource "sless_function" "time_display" { - namespace = "default" - name = "simple-node-time-display" + name = "simple-node-time-display" # уникальное имя в namespace runtime = "nodejs20" - entrypoint = "time_display.showTime" + entrypoint = "time_display.showTime" # файл.функция в code/time_display/ memory_mb = 64 - # Значение вычислено джобом при apply и зафиксировано в state + # Передаём результат джоба в функцию через переменную окружения. + # В коде функции: process.env.JOB_TIME env_vars = { JOB_TIME = sless_job.run_getter.message } code_path = data.archive_file.time_display_zip.output_path - code_hash = filesha256("${path.module}/code/time_display/time_display.js") + code_hash = filesha256("${path.module}/code/time_display/time_display.js") # для пересборки при изменении кода - depends_on = [sless_job.run_getter] + depends_on = [sless_job.run_getter] # ждём завершения джоба перед деплоем функции } -# HTTP-триггер — публикует функцию по URL +# Публикуем функцию по HTTP — URL будет в outputs.tf resource "sless_trigger" "display_http" { - namespace = "default" name = "simple-node-display-http" type = "http" function = sless_function.time_display.name diff --git a/examples/simple-node/time-getter.tf b/examples/simple-node/time-getter.tf index 2edea62..b1212c2 100644 --- a/examples/simple-node/time-getter.tf +++ b/examples/simple-node/time-getter.tf @@ -1,36 +1,35 @@ # Создано: 2026-03-09 -# time-getter.tf — одноразовая функция + джоб запускающий её при apply. -# sless_job.run_getter.message после apply содержит stdout runner-а: -# {"time":"2026-03-09T12:34:56.789Z"} -# Это значение terraform записывает в env JOB_TIME функции time_display. +# time-getter.tf — скрипт который запускается ОДИН РАЗ при terraform apply. +# После запуска его результат доступен через: sless_job.run_getter.message +# Смотри time-display.tf — там этот результат передаётся в функцию. -# Упаковываем код функции в zip +# Упаковываем код скрипта в zip для загрузки data "archive_file" "time_getter_zip" { type = "zip" source_dir = "${path.module}/code/time_getter" output_path = "${path.module}/dist/time_getter.zip" } -# Функция-вычислитель: запускается только джобом, не имеет HTTP-триггера +# Функция для скрипта — без HTTP-триггера, вызывается только через джоб ниже resource "sless_function" "time_getter" { - namespace = "default" - name = "simple-node-time-getter" + name = "simple-node-time-getter" # уникальное имя в namespace runtime = "nodejs20" - entrypoint = "time_getter.getTime" + entrypoint = "time_getter.getTime" # файл.функция в code/time_getter/ memory_mb = 64 code_path = data.archive_file.time_getter_zip.output_path - code_hash = filesha256("${path.module}/code/time_getter/time_getter.js") + code_hash = filesha256("${path.module}/code/time_getter/time_getter.js") # для пересборки при изменении кода } -# Джоб: запускает time_getter один раз при terraform apply. +# Джоб — запускает функцию time_getter один раз прямо при apply. +# run_id = 1 означает «запустить». Если увеличить (2, 3...) — запустится снова. +# После завершения: sless_job.run_getter.message = то что вернула функция resource "sless_job" "run_getter" { - namespace = "default" name = "simple-node-getter-run" function = sless_function.time_getter.name run_id = 1 - wait_timeout_sec = 120 - event_json = "{}" + wait_timeout_sec = 120 # сколько секунд ждать завершения скрипта + event_json = "{}" # входные данные для скрипта (пусто — данные не нужны) depends_on = [sless_function.time_getter] } diff --git a/examples/simple-python/main.tf b/examples/simple-python/main.tf index 4b7c748..90381fa 100644 --- a/examples/simple-python/main.tf +++ b/examples/simple-python/main.tf @@ -1,14 +1,25 @@ # Создано: 2026-03-09 -# main.tf — точка входа для примера simple-python. -# Демонстрирует цепочку: sless_job (one-shot) → sless_function (http). -# Джоб запускается при terraform apply, его stdout (JSON) попадает в -# sless_job.run_getter.message и передаётся функции через env_vars. +# main.tf — пример: запустить один раз скрипт при деплое и передать его результат в функцию. +# +# Как это работает: +# 1. При «terraform apply» запускается скрипт-джоб (time_getter) +# 2. Скрипт возвращает JSON с текущим временем +# 3. Terraform подхватывает этот JSON и передаёт в переменную окружения HTTP-функции (time_display) +# 4. Функция отдаёт время при каждом запросе +# +# Зачем такое нужно: +# Если данные нужны функции, но считаются один раз при деплое — +# напишите логику в джоб, а результат передайте через env_vars. +# Например: получить токен, версию схемы БД, время деплоя и т.д. +# +# namespace задаётся один раз здесь, в блоке provider. +# В ресурсах (sless_function, sless_trigger, sless_job) namespace НЕ указывается. terraform { required_providers { sless = { source = "terra.k8c.ru/naeel/sless" - version = "~> 0.1.8" + version = "~> 0.1.9" } archive = { source = "hashicorp/archive" @@ -18,6 +29,7 @@ terraform { } provider "sless" { - endpoint = "https://sless-api.kube5s.ru" - token = "dev-token-change-me" + endpoint = "https://sless-api.kube5s.ru" + token = "dev-token-change-me" + namespace = "default" } diff --git a/examples/simple-python/outputs.tf b/examples/simple-python/outputs.tf index b4c03b3..4d59ef6 100644 --- a/examples/simple-python/outputs.tf +++ b/examples/simple-python/outputs.tf @@ -1,14 +1,14 @@ # Создано: 2026-03-09 -# outputs.tf — полезные значения после terraform apply. +# outputs.tf — что выводит terraform после apply. -# URL HTTP-триггера для тестирования функции display +# Адрес вашей функции — откройте в браузере или вставьте в curl output "display_url" { - description = "URL HTTP-триггера функции time_display" + description = "URL функции time_display" value = sless_trigger.display_http.url } -# Результат джоба — JSON строка {"time": "..."} из stdout функции get_time() +# Что вернул скрипт-джоб — именно это передано в функцию как JOB_TIME output "job_result" { - description = "Stdout джоба (return value функции get_time)" + description = "Результат выполнения скрипта time_getter" value = sless_job.run_getter.message } diff --git a/examples/simple-python/time-display.tf b/examples/simple-python/time-display.tf index 861c938..236a0de 100644 --- a/examples/simple-python/time-display.tf +++ b/examples/simple-python/time-display.tf @@ -1,39 +1,35 @@ # Создано: 2026-03-09 -# time-display.tf — постоянная HTTP-функция, получающая данные от джоба. -# JOB_TIME берётся из sless_job.run_getter.message (stdout джоба) — -# это JSON строка {"time": "..."}, terraform передаёт её в env целиком. -# Функция парсит её через os.environ, а не через event — демонстрирует -# паттерн "данные вычислены один раз при деплое, используются на каждый запрос". +# time-display.tf — HTTP-функция, доступная по URL после apply. +# Получает результат джоба (из time-getter.tf) через переменную окружения JOB_TIME. -# Упаковываем код функции в zip +# Упаковываем код функции в zip для загрузки data "archive_file" "time_display_zip" { type = "zip" source_dir = "${path.module}/code/time_display" output_path = "${path.module}/dist/time_display.zip" } -# HTTP-функция: постоянный Deployment, читает JOB_TIME из env +# HTTP-функция — отвечает на запросы по URL из outputs.tf resource "sless_function" "time_display" { - namespace = "default" - name = "simple-py-time-display" + name = "simple-py-time-display" # уникальное имя в namespace runtime = "python3.11" - entrypoint = "time_display.show_time" + entrypoint = "time_display.show_time" # файл.функция в code/time_display/ memory_mb = 64 - # Значение вычислено джобом при apply и зафиксировано в state + # Передаём результат джоба в функцию через переменную окружения. + # В коде функции: os.environ.get("JOB_TIME") env_vars = { JOB_TIME = sless_job.run_getter.message } code_path = data.archive_file.time_display_zip.output_path - code_hash = filesha256("${path.module}/code/time_display/time_display.py") + code_hash = filesha256("${path.module}/code/time_display/time_display.py") # для пересборки при изменении кода - depends_on = [sless_job.run_getter] + depends_on = [sless_job.run_getter] # ждём завершения джоба перед деплоем функции } -# HTTP-триггер — публикует функцию по URL +# Публикуем функцию по HTTP — URL будет в outputs.tf resource "sless_trigger" "display_http" { - namespace = "default" name = "simple-py-display-http" type = "http" function = sless_function.time_display.name diff --git a/examples/simple-python/time-getter.tf b/examples/simple-python/time-getter.tf index b833025..3295ef0 100644 --- a/examples/simple-python/time-getter.tf +++ b/examples/simple-python/time-getter.tf @@ -1,38 +1,35 @@ # Создано: 2026-03-09 -# time-getter.tf — одноразовая функция + джоб запускающий её при apply. -# sless_job.run_getter.message после apply содержит stdout runner-а: -# {"time": "2026-03-09T12:34:56.789012+00:00"} -# Это значение terraform записывает в env JOB_TIME функции time_display. +# time-getter.tf — скрипт который запускается ОДИН РАЗ при terraform apply. +# После запуска его результат доступен через: sless_job.run_getter.message +# Смотри time-display.tf — там этот результат передаётся в функцию. -# Упаковываем код функции в zip +# Упаковываем код скрипта в zip для загрузки data "archive_file" "time_getter_zip" { type = "zip" source_dir = "${path.module}/code/time_getter" output_path = "${path.module}/dist/time_getter.zip" } -# Функция-вычислитель: запускается только джобом, не имеет HTTP-триггера +# Функция для скрипта — без HTTP-триггера, вызывается только через джоб ниже resource "sless_function" "time_getter" { - namespace = "default" - name = "simple-py-time-getter" + name = "simple-py-time-getter" # уникальное имя в namespace runtime = "python3.11" - entrypoint = "time_getter.get_time" + entrypoint = "time_getter.get_time" # файл.функция в code/time_getter/ memory_mb = 64 code_path = data.archive_file.time_getter_zip.output_path - code_hash = filesha256("${path.module}/code/time_getter/time_getter.py") + code_hash = filesha256("${path.module}/code/time_getter/time_getter.py") # для пересборки при изменении кода } -# Джоб: запускает time_getter один раз при terraform apply. -# run_id > 0 — разрешение на запуск (run_id=0 пропускается оператором). -# После завершения message = stdout пода = json возвращённый get_time(). +# Джоб — запускает функцию time_getter один раз прямо при apply. +# run_id = 1 означает «запустить». Если увеличить (2, 3...) — запустится снова. +# После завершения: sless_job.run_getter.message = то что вернула функция resource "sless_job" "run_getter" { - namespace = "default" name = "simple-py-getter-run" function = sless_function.time_getter.name run_id = 1 - wait_timeout_sec = 120 - event_json = "{}" + wait_timeout_sec = 120 # сколько секунд ждать завершения скрипта + event_json = "{}" # входные данные для скрипта (пусто — данные не нужны) depends_on = [sless_function.time_getter] } diff --git a/terraform/provider/internal/client/client.go b/terraform/provider/internal/client/client.go index 5bda76d..3d00e0e 100644 --- a/terraform/provider/internal/client/client.go +++ b/terraform/provider/internal/client/client.go @@ -23,14 +23,16 @@ type Client struct { httpClient *http.Client endpoint string token string + Namespace string // берётся из provider {}, пользователь не касается } // New создаёт клиент. endpoint — базовый URL оператора (без trailing slash). -func New(endpoint, token string) *Client { +func New(endpoint, token, namespace string) *Client { return &Client{ httpClient: &http.Client{Timeout: 30 * time.Second}, endpoint: endpoint, token: token, + Namespace: namespace, } } diff --git a/terraform/provider/internal/provider/provider.go b/terraform/provider/internal/provider/provider.go index 3528966..b3a2803 100644 --- a/terraform/provider/internal/provider/provider.go +++ b/terraform/provider/internal/provider/provider.go @@ -28,8 +28,9 @@ type SlessProvider struct { // SlessProviderModel — конфигурация блока provider {} в .tf файле. type SlessProviderModel struct { - Endpoint types.String `tfsdk:"endpoint"` - Token types.String `tfsdk:"token"` + Endpoint types.String `tfsdk:"endpoint"` + Token types.String `tfsdk:"token"` + Namespace types.String `tfsdk:"namespace"` } // New возвращает фабрику провайдера — точная копия паттерна nubes. @@ -56,6 +57,12 @@ func (p *SlessProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp Optional: true, Sensitive: true, }, + // namespace — окружение/проект в кластере. Задаётся один раз здесь. + // В ресурсах (sless_function, sless_trigger, sless_job) namespace НЕ указывается. + "namespace": schema.StringAttribute{ + MarkdownDescription: "Пространство имён (окружение). Задаётся один раз для всех ресурсов.", + Required: true, + }, }, } } @@ -87,7 +94,9 @@ func (p *SlessProvider) Configure(ctx context.Context, req provider.ConfigureReq } } - c := client.New(endpoint, token) + namespace := config.Namespace.ValueString() + + c := client.New(endpoint, token, namespace) resp.ResourceData = c resp.DataSourceData = c } diff --git a/terraform/provider/internal/resources/function_resource.go b/terraform/provider/internal/resources/function_resource.go index 4f1d4b9..ed1718b 100644 --- a/terraform/provider/internal/resources/function_resource.go +++ b/terraform/provider/internal/resources/function_resource.go @@ -51,7 +51,6 @@ func NewFunctionResource() resource.Resource { // FunctionModel — модель состояния terraform для sless_function. type FunctionModel struct { - Namespace types.String `tfsdk:"namespace"` Name types.String `tfsdk:"name"` Runtime types.String `tfsdk:"runtime"` Entrypoint types.String `tfsdk:"entrypoint"` @@ -75,13 +74,7 @@ func (r *FunctionResource) Metadata(_ context.Context, req resource.MetadataRequ func (r *FunctionResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - // namespace + name — immutable, смена требует пересоздания ресурса - "namespace": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, + // name — immutable, смена требует пересоздания ресурса "name": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -175,7 +168,7 @@ func (r *FunctionResource) Create(ctx context.Context, req resource.CreateReques return } - ns := plan.Namespace.ValueString() + ns := r.client.Namespace envVars, d := mapToStringMap(ctx, plan.EnvVars) resp.Diagnostics.Append(d...) if resp.Diagnostics.HasError() { @@ -221,7 +214,7 @@ func (r *FunctionResource) Read(ctx context.Context, req resource.ReadRequest, r return } - fn, err := r.client.GetFunction(ctx, state.Namespace.ValueString(), state.Name.ValueString()) + fn, err := r.client.GetFunction(ctx, r.client.Namespace, state.Name.ValueString()) if err != nil { resp.Diagnostics.AddError("read function", err.Error()) return @@ -243,7 +236,7 @@ func (r *FunctionResource) Update(ctx context.Context, req resource.UpdateReques return } - ns := plan.Namespace.ValueString() + ns := r.client.Namespace name := plan.Name.ValueString() envVars, d := mapToStringMap(ctx, plan.EnvVars) @@ -293,7 +286,7 @@ func (r *FunctionResource) Delete(ctx context.Context, req resource.DeleteReques return } - if err := r.client.DeleteFunction(ctx, state.Namespace.ValueString(), state.Name.ValueString()); err != nil { + if err := r.client.DeleteFunction(ctx, r.client.Namespace, state.Name.ValueString()); err != nil { resp.Diagnostics.AddError("delete function", err.Error()) } } @@ -307,7 +300,6 @@ func fnToModel(plan FunctionModel, fn *client.FunctionResponse) FunctionModel { buildTimeoutSec = types.Int64Value(defaultBuildTimeoutSec) } return FunctionModel{ - Namespace: types.StringValue(fn.Namespace), Name: types.StringValue(fn.Name), Runtime: types.StringValue(fn.Runtime), Entrypoint: types.StringValue(fn.Entrypoint), diff --git a/terraform/provider/internal/resources/job_resource.go b/terraform/provider/internal/resources/job_resource.go index 1e3f4ac..33c54e9 100644 --- a/terraform/provider/internal/resources/job_resource.go +++ b/terraform/provider/internal/resources/job_resource.go @@ -49,7 +49,6 @@ func NewJobResource() resource.Resource { // JobModel — модель состояния terraform для sless_job. type JobModel struct { - Namespace types.String `tfsdk:"namespace"` Name types.String `tfsdk:"name"` Function types.String `tfsdk:"function"` EventJSON types.String `tfsdk:"event_json"` @@ -73,12 +72,7 @@ func (r *JobResource) Schema(_ context.Context, _ resource.SchemaRequest, resp * MarkdownDescription: "Одноразовый запуск serverless функции. terraform apply блокируется до завершения джоба.", Attributes: map[string]schema.Attribute{ // Все input-поля immutable — джоб нельзя "изменить", только пересоздать. - "namespace": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, + "name": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -160,7 +154,7 @@ func (r *JobResource) Create(ctx context.Context, req resource.CreateRequest, re return } - ns := plan.Namespace.ValueString() + ns := r.client.Namespace eventJSON := plan.EventJSON.ValueString() if eventJSON == "" { eventJSON = "{}" @@ -188,7 +182,6 @@ func (r *JobResource) Create(ctx context.Context, req resource.CreateRequest, re waitTimeoutSec = types.Int64Value(defaultWaitTimeoutSec) } resp.Diagnostics.Append(resp.State.Set(ctx, JobModel{ - Namespace: types.StringValue(plan.Namespace.ValueString()), Name: types.StringValue(plan.Name.ValueString()), Function: types.StringValue(plan.Function.ValueString()), EventJSON: plan.EventJSON, @@ -223,7 +216,7 @@ func (r *JobResource) Read(ctx context.Context, req resource.ReadRequest, resp * return } - j, err := r.client.GetJob(ctx, state.Namespace.ValueString(), state.Name.ValueString()) + j, err := r.client.GetJob(ctx, r.client.Namespace, state.Name.ValueString()) if err != nil { resp.Diagnostics.AddError("read job", err.Error()) return @@ -250,7 +243,7 @@ func (r *JobResource) Delete(ctx context.Context, req resource.DeleteRequest, re return } - if err := r.client.DeleteJob(ctx, state.Namespace.ValueString(), state.Name.ValueString()); err != nil { + if err := r.client.DeleteJob(ctx, r.client.Namespace, state.Name.ValueString()); err != nil { resp.Diagnostics.AddError("delete job", err.Error()) } } @@ -262,7 +255,6 @@ func jobToModel(plan JobModel, j *client.JobResponse) JobModel { waitTimeoutSec = types.Int64Value(defaultWaitTimeoutSec) } return JobModel{ - Namespace: types.StringValue(j.Namespace), Name: types.StringValue(j.Name), Function: types.StringValue(j.FunctionRef), EventJSON: plan.EventJSON, diff --git a/terraform/provider/internal/resources/trigger_resource.go b/terraform/provider/internal/resources/trigger_resource.go index e94e0fe..3133119 100644 --- a/terraform/provider/internal/resources/trigger_resource.go +++ b/terraform/provider/internal/resources/trigger_resource.go @@ -35,7 +35,6 @@ func NewTriggerResource() resource.Resource { // TriggerModel — модель состояния terraform для sless_trigger. type TriggerModel struct { - Namespace types.String `tfsdk:"namespace"` Name types.String `tfsdk:"name"` Type types.String `tfsdk:"type"` FunctionRef types.String `tfsdk:"function"` @@ -54,12 +53,6 @@ func (r *TriggerResource) Metadata(_ context.Context, req resource.MetadataReque func (r *TriggerResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "namespace": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, "name": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -136,7 +129,7 @@ func (r *TriggerResource) Create(ctx context.Context, req resource.CreateRequest return } - tr, err := r.client.CreateTrigger(ctx, plan.Namespace.ValueString(), client.TriggerRequest{ + tr, err := r.client.CreateTrigger(ctx, r.client.Namespace, client.TriggerRequest{ Name: plan.Name.ValueString(), Type: plan.Type.ValueString(), FunctionRef: plan.FunctionRef.ValueString(), @@ -151,7 +144,7 @@ func (r *TriggerResource) Create(ctx context.Context, req resource.CreateRequest // Контроллер заполняет status.URL асинхронно — ждём до 30 секунд. // Без этого URL остаётся пустым в state и требует ручного terraform refresh. if tr.URL == "" && plan.Type.ValueString() == "http" { - ns := plan.Namespace.ValueString() + ns := r.client.Namespace name := plan.Name.ValueString() deadline := time.Now().Add(30 * time.Second) for time.Now().Before(deadline) { @@ -174,7 +167,7 @@ func (r *TriggerResource) Read(ctx context.Context, req resource.ReadRequest, re return } - tr, err := r.client.GetTrigger(ctx, state.Namespace.ValueString(), state.Name.ValueString()) + tr, err := r.client.GetTrigger(ctx, r.client.Namespace, state.Name.ValueString()) if err != nil { resp.Diagnostics.AddError("read trigger", err.Error()) return @@ -198,7 +191,7 @@ func (r *TriggerResource) Update(ctx context.Context, req resource.UpdateRequest } enabled := plan.Enabled.ValueBool() - tr, err := r.client.UpdateTrigger(ctx, plan.Namespace.ValueString(), plan.Name.ValueString(), client.TriggerUpdateRequest{ + tr, err := r.client.UpdateTrigger(ctx, r.client.Namespace, plan.Name.ValueString(), client.TriggerUpdateRequest{ Enabled: &enabled, }) if err != nil { @@ -216,7 +209,7 @@ func (r *TriggerResource) Delete(ctx context.Context, req resource.DeleteRequest return } - if err := r.client.DeleteTrigger(ctx, state.Namespace.ValueString(), state.Name.ValueString()); err != nil { + if err := r.client.DeleteTrigger(ctx, r.client.Namespace, state.Name.ValueString()); err != nil { resp.Diagnostics.AddError("delete trigger", err.Error()) } } @@ -230,7 +223,6 @@ func trToModel(tr *client.TriggerResponse) TriggerModel { schedule = types.StringValue(tr.Schedule) } return TriggerModel{ - Namespace: types.StringValue(tr.Namespace), Name: types.StringValue(tr.Name), Type: types.StringValue(tr.Type), FunctionRef: types.StringValue(tr.FunctionRef),