Files
sless/main.go
T
“Naeel” 5ae2ee7f85 feat: JWT auth in operator + hello-node example updated
- operator: auth middleware теперь валидирует JWT (sub+exp), не статический токен
- operator: ensureNamespace идемпотентен при race condition (IsAlreadyExists)
- operator: NewRouter убран параметр apiToken — больше не нужен
- examples/hello-node: prod.token + nubes_endpoint + версия провайдера 0.1.12
- протестировано: namespace sless-cdd874dfa31ba6ca создан автоматически
2026-03-11 07:51:06 +04:00

195 lines
6.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Изменено: 2026-03-07
// main.go — точка входа. Запускает operator manager и REST API сервер параллельно.
// Operator manager управляет Function/Trigger CRD через reconcile loop.
// REST API (gorilla/mux) принимает запросы от Terraform provider.
package main
import (
"context"
"flag"
"fmt"
"log/slog"
"net/http"
"os"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
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"
slessv1alpha1 "gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/api/v1alpha1"
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/controllers"
slessapi "gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/api"
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/api/handler"
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/builder"
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/config"
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/storage/postgres"
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/storage/s3"
//+kubebuilder:scaffold:imports
)
var (
scheme = runtime.NewScheme()
)
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(slessv1alpha1.AddToScheme(scheme))
//+kubebuilder:scaffold:scheme
}
func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
opts := zap.Options{
Development: true,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
// slog для REST API (JSON-формат для prod, text для dev)
log := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
// Загружаем конфиг из env vars
cfg, err := config.Load()
if err != nil {
log.Error("load config", "err", err)
os.Exit(1)
}
// Подключаемся к PostgreSQL
pg, err := postgres.New(cfg.PostgresDSN)
if err != nil {
log.Error("connect postgres", "err", err)
os.Exit(1)
}
defer pg.Close()
// Применяем миграции БД — читаем SQL и передаём строкой
migrationSQL, err := os.ReadFile("migrations/001_initial.sql")
if err != nil {
log.Error("read migration file", "err", err)
os.Exit(1)
}
if err := pg.RunMigrations(context.Background(), string(migrationSQL)); err != nil {
log.Error("run migrations", "err", err)
os.Exit(1)
}
// S3 клиент
s3Client, err := s3.New(cfg.S3Endpoint, cfg.S3AccessKey, cfg.S3SecretKey, cfg.S3Bucket, cfg.S3UseSSL)
if err != nil {
log.Error("connect s3", "err", err)
os.Exit(1)
}
if err := s3Client.EnsureBucket(context.Background()); err != nil {
log.Error("ensure s3 bucket", "err", err)
os.Exit(1)
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "4b6ba465.kube5s.ru",
})
if err != nil {
log.Error("unable to start manager", "err", err)
os.Exit(1)
}
// Builder использует k8s client из manager'а
bldr := builder.New(mgr.GetClient(), builder.Config{
BuilderImage: cfg.BuilderImage,
RegistryHost: cfg.RegistryHost,
RegistrySecret: cfg.RegistrySecret,
S3Endpoint: cfg.S3Endpoint,
S3AccessKey: cfg.S3AccessKey,
S3SecretKey: cfg.S3SecretKey,
S3Bucket: cfg.S3Bucket,
Namespace: "sless",
})
if err = (&controllers.FunctionReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Builder: bldr,
RegistrySecret: cfg.RegistrySecret,
OperatorNamespace: "sless",
}).SetupWithManager(mgr); err != nil {
log.Error("unable to create controller", "controller", "Function", "err", err)
os.Exit(1)
}
if err = (&controllers.TriggerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
IngressHost: cfg.IngressHost,
ExternalURL: cfg.ExternalURL,
}).SetupWithManager(mgr); err != nil {
log.Error("unable to create controller", "controller", "Trigger", "err", err)
os.Exit(1)
}
if err = (&controllers.FunctionJobReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
RegistrySecret: cfg.RegistrySecret,
KubeClient: kubernetes.NewForConfigOrDie(mgr.GetConfig()),
}).SetupWithManager(mgr); err != nil {
log.Error("unable to create controller", "controller", "FunctionJob", "err", err)
os.Exit(1)
}
//+kubebuilder:scaffold:builder
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)
}
// REST API сервер — запускается параллельно с operator manager
apiHandler := slessapi.NewRouter(&handler.Handler{
K8s: mgr.GetClient(),
Scheme: mgr.GetScheme(),
S3: s3Client,
PG: pg,
Log: log,
}, log)
go func() {
addr := fmt.Sprintf(":%d", cfg.APIPort)
log.Info("starting REST API", "addr", addr)
if err := http.ListenAndServe(addr, apiHandler); err != nil {
log.Error("REST API server failed", "err", err)
os.Exit(1)
}
}()
log.Info("starting operator manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
log.Error("problem running manager", "err", err)
os.Exit(1)
}
}