From 695f217e131338c77e50d49240f23a5df6f634d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Mon, 9 Mar 2026 14:50:06 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20operator=20v0.1.16=20=E2=80=94=20job=20?= =?UTF-8?q?stdout=20->=20status.Message=20(feature=20B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FunctionJobReconciler: added KubeClient field (kubernetes.Interface) - getJobPodOutput(): reads pod logs via typed client after job succeeds - main.go: inject kubernetes.NewForConfigOrDie into FunctionJobReconciler - rbac.yaml: add pods/pods/log get/list/watch permissions - examples/simple-python/: job->function chain demo (Python) - examples/simple-node/: job->function chain demo (Node.js) sless_job.X.message now contains the return value of the function --- simple-node/code/time_display/time_display.js | 20 +++++++++ simple-node/code/time_getter/time_getter.js | 10 +++++ simple-node/dist/time_display.zip | Bin 0 -> 682 bytes simple-node/dist/time_getter.zip | Bin 0 -> 574 bytes simple-node/main.tf | 22 ++++++++++ simple-node/outputs.tf | 12 ++++++ simple-node/time-display.tf | 36 ++++++++++++++++ simple-node/time-getter.tf | 36 ++++++++++++++++ .../code/time_display/time_display.py | 20 +++++++++ simple-python/code/time_getter/time_getter.py | 18 ++++++++ simple-python/dist/time_display.zip | Bin 0 -> 695 bytes simple-python/dist/time_getter.zip | Bin 0 -> 674 bytes simple-python/main.tf | 23 ++++++++++ simple-python/outputs.tf | 14 ++++++ simple-python/time-display.tf | 40 ++++++++++++++++++ simple-python/time-getter.tf | 38 +++++++++++++++++ 16 files changed, 289 insertions(+) create mode 100644 simple-node/code/time_display/time_display.js create mode 100644 simple-node/code/time_getter/time_getter.js create mode 100644 simple-node/dist/time_display.zip create mode 100644 simple-node/dist/time_getter.zip create mode 100644 simple-node/main.tf create mode 100644 simple-node/outputs.tf create mode 100644 simple-node/time-display.tf create mode 100644 simple-node/time-getter.tf create mode 100644 simple-python/code/time_display/time_display.py create mode 100644 simple-python/code/time_getter/time_getter.py create mode 100644 simple-python/dist/time_display.zip create mode 100644 simple-python/dist/time_getter.zip create mode 100644 simple-python/main.tf create mode 100644 simple-python/outputs.tf create mode 100644 simple-python/time-display.tf create mode 100644 simple-python/time-getter.tf diff --git a/simple-node/code/time_display/time_display.js b/simple-node/code/time_display/time_display.js new file mode 100644 index 0000000..a711b3e --- /dev/null +++ b/simple-node/code/time_display/time_display.js @@ -0,0 +1,20 @@ +// Создано: 2026-03-09 +// time_display.js — HTTP-функция (постоянный Deployment + Trigger). +// Читает env JOB_TIME, которую terraform передаёт из sless_job.run_getter.message. +// Демонстрирует цепочку: Job вычисляет данные → Function использует их через env. + +exports.showTime = function(event) { + // JOB_TIME устанавливается terraform из статуса джоба (JSON строка) + const jobTimeRaw = process.env.JOB_TIME || '{}'; + let jobTime; + try { + const parsed = JSON.parse(jobTimeRaw); + jobTime = parsed.time || jobTimeRaw; + } catch (e) { + jobTime = jobTimeRaw; + } + return { + message: `Сервис запустился в: ${jobTime}`, + path: event._path || '/', + }; +}; diff --git a/simple-node/code/time_getter/time_getter.js b/simple-node/code/time_getter/time_getter.js new file mode 100644 index 0000000..a750c17 --- /dev/null +++ b/simple-node/code/time_getter/time_getter.js @@ -0,0 +1,10 @@ +// Создано: 2026-03-09 +// time_getter.js — функция запускается как Job (одноразово). +// Возвращает JSON со временем запуска. Оператор v0.1.16 захватывает stdout +// и записывает его в sless_job.run_getter.message — оттуда terraform передаёт +// значение в sless_function.display через env_var JOB_TIME. + +exports.getTime = function(event) { + // Возвращаем время в ISO 8601 UTC — без зависимостей, только stdlib + return { time: new Date().toISOString() }; +}; diff --git a/simple-node/dist/time_display.zip b/simple-node/dist/time_display.zip new file mode 100644 index 0000000000000000000000000000000000000000..1b3ce4e78a8426793243cb2623a8420f47a8dd30 GIT binary patch literal 682 zcmWIWW@Zs#-~d7f2E{HQ0SEj*R!L@VYJ5s&aY0UErCwHXOwif9%RVA^e`~&bGF*bPxu-sgpXXP8Ly3N!$cjYaQpN%PjE-bIOOO9T1nK7fqVtv6o?Ju4e ze0-WB;(p&LK4+V5(_hfeBV6O&W4TG_ZO(g}JGbgKZ2f!Uqs^t>X)EMRL#}+$zZ>lO zeQtDB+{!)o9(})aTqamf;i;hL28Uy@?-U;kKAZ8Gx9*a#py}b$(>P52`itINalKV% zSDSeJ#3Ft}p?4EG&SmERFWma+(bLPmheT#ReOWNyexg{=uj31I1jRgdG#{yWdj5{` zk{J`SxR$P3{&1o8`KGMc4BaKSg}i>2Zk)2qwJH9Df%(53%~uSf6Xg^(9&4XbwBM!r zp@@g_|BUnbDmrE zRfx9+X@}2k5ufVjeL+w5xe05b9#iZR?e-I&!vY1q=Pq)yJ|NXU)q-T%8jy*Bm{6qj8TC86O9bZg<~WBV7xcBMoJd+j{-@zl|; zrnxa&%}?Z+TT?g%x$!Lx_=^Y+-XTdB{>fyw0m{{U}xj$`q7y`T*nM9Zo j2@6>cl(1l6Nh64bmdpaYS=m6sj6i4$q{D%RGB5xDUSc3< literal 0 HcmV?d00001 diff --git a/simple-node/dist/time_getter.zip b/simple-node/dist/time_getter.zip new file mode 100644 index 0000000000000000000000000000000000000000..c85ffec457f32832992daa67522b41ffcef696e1 GIT binary patch literal 574 zcmWIWW@Zs#-~d7f2E{HQ0SA0QR!L@VYJ7TXNl9vvURH6=q`O(djv~k3f7d*h%2YNf zgyYtuUU_lO3_YRshmZLko|dxxjL)JVuDiYR9Zec>-1`phD!XZFb~*gV{O}K+Da$mw zzPvJc{`mj=&$BbPa@hO|Hi^C>vy=kTyDJF|Ma#O9e&r%KV^%oJMcdC%cWZ~ zD%_9fzFsSl<@f)kL#O<`nO}UeZ0)?%oOB;$vlXu_wU{c$qLV)72eOE9OAaDM_zCdtCX9>OQisNt8VuX~Nkt#e`e*a_4`YYLU~=zZjTox+tRd zl9%0i@uG@b8JAwx73`A#!^yz#|9^ltJBQ01mxUV`85oM07#IS)8JR?w5wVRd2a0VN bSkee$p~iiHH!B-Rlo1GRfb<)nnG6g7j|T2G literal 0 HcmV?d00001 diff --git a/simple-node/main.tf b/simple-node/main.tf new file mode 100644 index 0000000..76bb8eb --- /dev/null +++ b/simple-node/main.tf @@ -0,0 +1,22 @@ +# Создано: 2026-03-09 +# main.tf — точка входа для примера simple-node. +# Демонстрирует цепочку: sless_job (one-shot) → sless_function (http). +# Аналог simple-python, но на Node.js 20. + +terraform { + required_providers { + sless = { + source = "terra.k8c.ru/naeel/sless" + version = "~> 0.1.8" + } + archive = { + source = "hashicorp/archive" + version = "~> 2.0" + } + } +} + +provider "sless" { + endpoint = "https://sless-api.kube5s.ru" + token = "dev-token-change-me" +} diff --git a/simple-node/outputs.tf b/simple-node/outputs.tf new file mode 100644 index 0000000..06204e2 --- /dev/null +++ b/simple-node/outputs.tf @@ -0,0 +1,12 @@ +# Создано: 2026-03-09 +# outputs.tf — полезные значения после terraform apply. + +output "display_url" { + description = "URL HTTP-триггера функции time_display" + value = sless_trigger.display_http.url +} + +output "job_result" { + description = "Stdout джоба (return value функции getTime)" + value = sless_job.run_getter.message +} diff --git a/simple-node/time-display.tf b/simple-node/time-display.tf new file mode 100644 index 0000000..33ee0d3 --- /dev/null +++ b/simple-node/time-display.tf @@ -0,0 +1,36 @@ +# Создано: 2026-03-09 +# time-display.tf — постоянная HTTP-функция, получающая данные от джоба. + +# Упаковываем код функции в 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 +resource "sless_function" "time_display" { + namespace = "default" + name = "simple-node-time-display" + runtime = "nodejs20" + entrypoint = "time_display.showTime" + memory_mb = 64 + + # Значение вычислено джобом при apply и зафиксировано в state + 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") + + depends_on = [sless_job.run_getter] +} + +# HTTP-триггер — публикует функцию по URL +resource "sless_trigger" "display_http" { + namespace = "default" + name = "simple-node-display-http" + type = "http" + function = sless_function.time_display.name +} diff --git a/simple-node/time-getter.tf b/simple-node/time-getter.tf new file mode 100644 index 0000000..2edea62 --- /dev/null +++ b/simple-node/time-getter.tf @@ -0,0 +1,36 @@ +# Создано: 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. + +# Упаковываем код функции в 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-триггера +resource "sless_function" "time_getter" { + namespace = "default" + name = "simple-node-time-getter" + runtime = "nodejs20" + entrypoint = "time_getter.getTime" + memory_mb = 64 + + code_path = data.archive_file.time_getter_zip.output_path + code_hash = filesha256("${path.module}/code/time_getter/time_getter.js") +} + +# Джоб: запускает time_getter один раз при terraform apply. +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 = "{}" + + depends_on = [sless_function.time_getter] +} diff --git a/simple-python/code/time_display/time_display.py b/simple-python/code/time_display/time_display.py new file mode 100644 index 0000000..789cd44 --- /dev/null +++ b/simple-python/code/time_display/time_display.py @@ -0,0 +1,20 @@ +# Создано: 2026-03-09 +# time_display.py — HTTP-функция (постоянный Deployment + Trigger). +# Читает env JOB_TIME, которую terraform передаёт из sless_job.run_getter.message. +# Демонстрирует цепочку: Job вычисляет данные → Function использует их через env. + +import json +import os + + +def show_time(event): + # JOB_TIME устанавливается terraform из статуса джоба (JSON строка) + job_time_raw = os.environ.get("JOB_TIME", "{}") + try: + job_time = json.loads(job_time_raw).get("time", job_time_raw) + except (json.JSONDecodeError, AttributeError): + job_time = job_time_raw + return { + "message": f"Сервис запустился в: {job_time}", + "path": event.get("_path", "/"), + } diff --git a/simple-python/code/time_getter/time_getter.py b/simple-python/code/time_getter/time_getter.py new file mode 100644 index 0000000..05c47f6 --- /dev/null +++ b/simple-python/code/time_getter/time_getter.py @@ -0,0 +1,18 @@ +# Создано: 2026-03-09 +# time_getter.py — функция запускается как Job (одноразово). +# Возвращает JSON со временем запуска. Оператор v0.1.16 захватывает stdout +# и записывает его в sless_job.run_getter.message — оттуда terraform передаёт +# значение в sless_function.display через env_var JOB_TIME. + +import json +from datetime import datetime, timezone + + +def get_time(event): + # Возвращаем время в ISO 8601 UTC — без зависимостей, только stdlib + return {"time": datetime.now(timezone.utc).isoformat()} + + +if __name__ == "__main__": + # Для локального тестирования без оператора + print(json.dumps(get_time({}))) diff --git a/simple-python/dist/time_display.zip b/simple-python/dist/time_display.zip new file mode 100644 index 0000000000000000000000000000000000000000..04a485d5778a02e267c4c7815e1fd056723f6d00 GIT binary patch literal 695 zcmWIWW@Zs#-~d7f2E{HQ0SEj*R!L@VYJ5s&aY0UErCvd0&ZLW3!u}FR-+$M%Q#0rM zU-ExxQ^EpqFYTxcT+iw}XJzhEy~(+G^0ff2%tdcqk1qJaTJ`j;R8OSv=E~O3d!6RH zq?xT1+;jV;ZSlFEb@#KaXV)J0o^_X=?Z1a<<_6tnH;MA7t@G}` zycqkxEiPnrzS$33{*4EE^e2Am=!#H!%q^$zSWqnDgK6ERzBgM!*6S)e{|^jpKfCOT z>V0j2eM(Omg?uU;jI0*!{hRLi_37%nMu}}_ZhomeG5>_e+U$0(?VR2|JNi3wPtC7% z@GxAuL-nzRMObNk`Qindy%TTcpSDeZ}@t;M5_8pkx?zc@TS$)=pyHCZd?)CEdtWYZ2q-u2Lr}8rG@Fjm5e`H-P&07B1 zRzIhFidl)w>KFeakIs8;`b6=l`zw&HaZQR>>H8|5NA-cHTA$y04@@)v{|9)pbFAL9 zL`j{Afgy^Sfg!+~kx7IZk;;(eK&cD{mNbG`XlX9Mo0SbD%m{?GKsp6zC<6lkLR&TO literal 0 HcmV?d00001 diff --git a/simple-python/dist/time_getter.zip b/simple-python/dist/time_getter.zip new file mode 100644 index 0000000000000000000000000000000000000000..4f35be2f8acf3ad2b4c5af93d012f51ddbbefe81 GIT binary patch literal 674 zcmWIWW@Zs#-~d7f2E{HQ0SA0QR!L@VYJ7TXNl9vvUO{Efr2Agd{vyZUKiA~@sK&j^ zrCwn_*S#%YoECm{sP65rRH%-)-9J5S4G zpZ^zDCq6r4dH?;-Ih$R6F!gh;GdRRk*L*E1`?dDk|6x)5SA=$&82`Fpk>0hV+R6T$ zerNnA*0dR)vakGUG&snyz2+dZkD=31XSoxbW0q~1`#xA$E<9-VdBa02a>+k5!n|Aj z9jkO0>#C>w-rjBP-^u<#gh@0nAW`jke3aosg5%a6Hp z7M8rcRe8R%z;=D>EcX4i4z2l)GfpK3Py3~DzrTClUiAqxH~H}S9$+}{EcdCkK{>rk z>E#nww_@k1HlN+)x^A!-_x$k9SnT$7b%2hgLN_&+&nN!YTAq39n%5h&D%YwcW6reY%U(*q zUc~F_@9;Qo5ixVcV&Oo@7?Nk{+rz0uYXN%aQ`^g#BZASpz?ro$d7$KMqDwU zt9>4}{wUlf|AgytRqN8rZ4#L|I%kfV)!*CmUHbbSN0H^JdIrMRd(KS>%52}V%`hX4NqyxBQKo-&GxFflM#Ff%X&cr!AIFe6eFvK%Ny d!N8J65DPVZ1$eWvfkYXB&<02c0L^4z0076NA!z^r literal 0 HcmV?d00001 diff --git a/simple-python/main.tf b/simple-python/main.tf new file mode 100644 index 0000000..4b7c748 --- /dev/null +++ b/simple-python/main.tf @@ -0,0 +1,23 @@ +# Создано: 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. + +terraform { + required_providers { + sless = { + source = "terra.k8c.ru/naeel/sless" + version = "~> 0.1.8" + } + archive = { + source = "hashicorp/archive" + version = "~> 2.0" + } + } +} + +provider "sless" { + endpoint = "https://sless-api.kube5s.ru" + token = "dev-token-change-me" +} diff --git a/simple-python/outputs.tf b/simple-python/outputs.tf new file mode 100644 index 0000000..b4c03b3 --- /dev/null +++ b/simple-python/outputs.tf @@ -0,0 +1,14 @@ +# Создано: 2026-03-09 +# outputs.tf — полезные значения после terraform apply. + +# URL HTTP-триггера для тестирования функции display +output "display_url" { + description = "URL HTTP-триггера функции time_display" + value = sless_trigger.display_http.url +} + +# Результат джоба — JSON строка {"time": "..."} из stdout функции get_time() +output "job_result" { + description = "Stdout джоба (return value функции get_time)" + value = sless_job.run_getter.message +} diff --git a/simple-python/time-display.tf b/simple-python/time-display.tf new file mode 100644 index 0000000..861c938 --- /dev/null +++ b/simple-python/time-display.tf @@ -0,0 +1,40 @@ +# Создано: 2026-03-09 +# time-display.tf — постоянная HTTP-функция, получающая данные от джоба. +# JOB_TIME берётся из sless_job.run_getter.message (stdout джоба) — +# это JSON строка {"time": "..."}, terraform передаёт её в env целиком. +# Функция парсит её через os.environ, а не через event — демонстрирует +# паттерн "данные вычислены один раз при деплое, используются на каждый запрос". + +# Упаковываем код функции в 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 +resource "sless_function" "time_display" { + namespace = "default" + name = "simple-py-time-display" + runtime = "python3.11" + entrypoint = "time_display.show_time" + memory_mb = 64 + + # Значение вычислено джобом при apply и зафиксировано в state + 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") + + depends_on = [sless_job.run_getter] +} + +# HTTP-триггер — публикует функцию по URL +resource "sless_trigger" "display_http" { + namespace = "default" + name = "simple-py-display-http" + type = "http" + function = sless_function.time_display.name +} diff --git a/simple-python/time-getter.tf b/simple-python/time-getter.tf new file mode 100644 index 0000000..b833025 --- /dev/null +++ b/simple-python/time-getter.tf @@ -0,0 +1,38 @@ +# Создано: 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. + +# Упаковываем код функции в 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-триггера +resource "sless_function" "time_getter" { + namespace = "default" + name = "simple-py-time-getter" + runtime = "python3.11" + entrypoint = "time_getter.get_time" + memory_mb = 64 + + code_path = data.archive_file.time_getter_zip.output_path + 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(). +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 = "{}" + + depends_on = [sless_function.time_getter] +}