Files
sless/main.go

210 lines
6.9 KiB
Go
Raw Permalink 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/harbor"
"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'а
// harborClient — nil если HARBOR_USER/HARBOR_PASS не заданы (EnsureProject пропускается).
var harborClient *harbor.Client
var harborProjecter builder.Projecter
if cfg.HarborUser != "" && cfg.HarborPass != "" {
harborClient = harbor.New(cfg.RegistryHost, cfg.HarborUser, cfg.HarborPass)
harborProjecter = harborClient
log.Info("harbor client configured", "host", cfg.RegistryHost)
} else {
log.Info("harbor client disabled (HARBOR_USER/HARBOR_PASS not set)")
}
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",
HarborClient: harborProjecter,
})
if err = (&controllers.FunctionReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Builder: bldr,
KubeClient: kubernetes.NewForConfigOrDie(mgr.GetConfig()),
RegistrySecret: cfg.RegistrySecret,
OperatorNamespace: "sless",
HarborClient: harborClient,
}).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)
}
}