feat(provider): add fission_mq_trigger, fission_cron_trigger, fission_iot_device + weather-demo example

This commit is contained in:
“Naeel”
2026-05-11 09:37:20 +04:00
parent df0afa9894
commit 44deb9f537
10 changed files with 1125 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
import os
import json
import psycopg2
def main(event, context):
pg_dsn = os.environ["PG_DSN"]
# Извлекаем тело сообщения из SQS (POST от sqs-consumer)
body = getattr(event, "body", event)
if isinstance(body, (bytes, bytearray)):
body = body.decode("utf-8")
if isinstance(body, str):
data = json.loads(body)
else:
data = body
conn = psycopg2.connect(pg_dsn)
try:
cur = conn.cursor()
cur.execute(
"""
INSERT INTO weather_metrics
(city, country, temperature, feels_like, humidity,
pressure, wind_speed, description, recorded_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, to_timestamp(%s))
""",
(
data["city"],
data["country"],
data["temperature"],
data["feels_like"],
data["humidity"],
data["pressure"],
data["wind_speed"],
data["description"],
data["owm_timestamp"],
),
)
conn.commit()
cur.close()
finally:
conn.close()
return {"status": "ok", "city": data["city"], "temp": data["temperature"]}
@@ -0,0 +1 @@
psycopg2-binary==2.9.9
@@ -0,0 +1,2 @@
requests==2.31.0
boto3==1.34.0
+149
View File
@@ -0,0 +1,149 @@
terraform {
required_providers {
fission = {
source = "nail/fission"
version = "~> 0.2.0"
}
}
}
provider "fission" {
kubeconfig_path = var.kubeconfig_path
namespace = var.namespace
}
# ── Переменные ───────────────────────────────────────────────────────
variable "kubeconfig_path" {
default = "/home/naeel/.kube/config"
description = "Путь к kubeconfig."
}
variable "namespace" {
default = "fission-weather"
description = "Namespace для функций и триггеров."
}
variable "owm_api_key" {
sensitive = true
description = "OpenWeatherMap API key."
}
variable "sqs_access_key" {
sensitive = true
description = "SQS Access Key."
}
variable "sqs_secret_key" {
sensitive = true
description = "SQS Secret Key."
}
variable "pg_dsn" {
sensitive = true
description = "PostgreSQL DSN для записи метрик. Пример: postgresql://user:pass@host:5432/db?sslmode=disable"
}
# ── IoT-устройство — виртуальная метеостанция ────────────────────────
resource "fission_iot_device" "weather_station" {
name = "weather-station"
device_id = "weather-station-01"
namespace = "sless"
metadata = {
type = "weather-station"
location = "multi-city"
cities = "Moscow,London,Paphos,Ulyanovsk,Santiago"
}
}
# ── Python environment ───────────────────────────────────────────────
resource "fission_environment" "python" {
name = "weather-python"
image = "naeel/fission-python-env:v1.0"
version = 2
namespace = var.namespace
}
# ── Пакет: fetcher ───────────────────────────────────────────────────
resource "fission_package" "fetcher" {
name = "weather-fetcher-pkg"
environment = fission_environment.python.name
namespace = var.namespace
source_dir = "${path.module}/fetcher"
deploy_type = "source"
}
# ── Пакет: consumer ──────────────────────────────────────────────────
resource "fission_package" "consumer" {
name = "weather-consumer-pkg"
environment = fission_environment.python.name
namespace = var.namespace
source_dir = "${path.module}/consumer"
deploy_type = "source"
}
# ── Функция: fetcher (читает OWM, пишет в SQS) ───────────────────────
resource "fission_function" "fetcher" {
name = "weather-fetcher"
environment = fission_environment.python.name
namespace = var.namespace
package_name = fission_package.fetcher.name
entrypoint = "main"
# Env vars с секретами задаются через K8s Secret вне Terraform.
# Имя секрета: weather-fetcher-env (namespace: var.namespace)
# Ключи: OWM_API_KEY, SQS_ACCESS_KEY, SQS_SECRET_KEY
}
# ── CRON trigger: каждые 10 минут ────────────────────────────────────
resource "fission_cron_trigger" "fetcher" {
name = "weather-cron"
function = fission_function.fetcher.name
namespace = var.namespace
cron = "*/10 * * * *"
}
# ── Функция: consumer (читает из SQS, пишет в PG) ────────────────────
resource "fission_function" "consumer" {
name = "weather-consumer"
environment = fission_environment.python.name
namespace = var.namespace
package_name = fission_package.consumer.name
entrypoint = "main"
# Env vars: PG_DSN задаётся через K8s Secret weather-consumer-env
}
# ── MQ trigger: SQS queue → consumer function ────────────────────────
resource "fission_mq_trigger" "weather" {
name = "weather-mq"
function = fission_function.consumer.name
namespace = var.namespace
queue = "weather-data"
access_key = var.sqs_access_key
secret_key = var.sqs_secret_key
sqs_endpoint = "http://shared-sqs.shared-sqs.svc.cluster.local:4100"
}
# ── Outputs ──────────────────────────────────────────────────────────
output "iot_device_phase" {
value = fission_iot_device.weather_station.phase
description = "Статус IoT-устройства weather-station."
}
output "iot_mqtt_username" {
value = fission_iot_device.weather_station.mqtt_username
description = "MQTT username для weather-station."
}
output "iot_topic_prefix" {
value = fission_iot_device.weather_station.topic_prefix
description = "MQTT topic prefix для weather-station."
}
+18
View File
@@ -0,0 +1,18 @@
-- Миграция: таблица метрик погоды для weather-demo
-- Применять: psql $PG_DSN -f migration.sql
CREATE TABLE IF NOT EXISTS weather_metrics (
id BIGSERIAL PRIMARY KEY,
city TEXT NOT NULL,
country TEXT NOT NULL,
temperature NUMERIC(5,2),
feels_like NUMERIC(5,2),
humidity INTEGER,
pressure INTEGER,
wind_speed NUMERIC(6,2),
description TEXT,
recorded_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_weather_city_time
ON weather_metrics (city, recorded_at DESC);