// ⛔⛔⛔ ЛЕГАСИ (2026-08-16) — СТАРЫЙ IoT (оператор, CRD-контроллер). В новой архитектуре не используется. // Актуальное: HISTORY/2026-08-16-session-log.md // Создано: 2026-04-12 // main.go — точка входа IoT managed service. // Запускает controller-manager (IoTDevice CRD) и REST API сервер (порт 9090) в одном процессе. // Перенесено из sless/main.go — только IoT-специфичная часть. // // Компоненты: // 1. Controller-Manager (controller-runtime) — reconcile IoTDevice CRD // 2. REST API Server (gorilla/mux) — CRUD устройств, телеметрия, MQTT auth, admin // // Конфигурация через env vars: // IOT_PG_DSN — DSN для IoT Postgres (опционально) package main import ( "context" "fmt" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" iotv1alpha1 "gitea.services.ngcloud.ru/Nail/IoT/api/v1alpha1" iotcontrollers "gitea.services.ngcloud.ru/Nail/IoT/controllers" iotapi "gitea.services.ngcloud.ru/Nail/IoT/internal/api" "gitea.services.ngcloud.ru/Nail/IoT/internal/api/handler" "gitea.services.ngcloud.ru/Nail/IoT/internal/storage/iotpg" ) var scheme = runtime.NewScheme() func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) // IoT API group iot.kube5s.ru/v1alpha1 utilruntime.Must(iotv1alpha1.AddToScheme(scheme)) } func main() { log := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) ctrl.SetLogger(zap.New(zap.UseDevMode(true))) log.Info("starting IoT managed service operator") // Controller Manager — управляет reconcile loop для IoTDevice CRD mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, HealthProbeBindAddress: ":8081", LeaderElection: false, }) if err != nil { log.Error("unable to create controller manager", "err", err) os.Exit(1) } // Регистрируем IoTDevice controller if err = (&iotcontrollers.IoTDeviceReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { log.Error("unable to create IoTDevice controller", "err", err) os.Exit(1) } // Health/ready пробы для k8s if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { log.Error("unable to set up health check", "err", err) os.Exit(1) } if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { log.Error("unable to set up ready check", "err", err) os.Exit(1) } // IoT Postgres — опционален (если IOT_PG_DSN не задан — телеметрия отключена) iotPGStore, err := iotpg.NewFromEnv(log) if err != nil { log.Error("failed to init IoT Postgres", "err", err) os.Exit(1) } if iotPGStore != nil { defer iotPGStore.Close() } // REST API handler h := &handler.Handler{ K8s: mgr.GetClient(), Scheme: mgr.GetScheme(), IoTPG: iotPGStore, Log: log, BridgeUsername: os.Getenv("MQTT_BRIDGE_USERNAME"), BridgePassword: os.Getenv("MQTT_BRIDGE_PASSWORD"), } router := iotapi.NewRouter(h, log) // HTTP API сервер на порту 9090 apiServer := &http.Server{ Addr: ":9090", Handler: router, ReadTimeout: 30 * time.Second, WriteTimeout: 60 * time.Second, } // Запускаем API сервер в горутине go func() { log.Info("starting REST API server", "addr", ":9090") if err := apiServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Error("API server failed", "err", err) os.Exit(1) } }() // Graceful shutdown ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) defer cancel() // Запускаем controller-manager (блокирует до ctx.Done) go func() { log.Info("starting controller manager") if err := mgr.Start(ctx); err != nil { log.Error("controller manager failed", "err", err) os.Exit(1) } }() <-ctx.Done() log.Info("shutting down IoT operator") shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) defer shutdownCancel() if err := apiServer.Shutdown(shutdownCtx); err != nil { log.Error("API server shutdown error", "err", err) } fmt.Println("IoT operator stopped") }