feat: REST API (gorilla/mux + slog) + trigger controller + main.go wiring
- controllers/trigger_controller.go: полная реализация, HTTP->Ingress+Service, Cron->CronJob
- internal/config: добавлены IngressHost, APIToken
- internal/api/router.go: gorilla/mux роутер /v1/namespaces/{ns}/...
- internal/api/handler/: functions, triggers, invocations, base handler с slog
- internal/api/middleware/: auth (Bearer token) + logging (slog)
- main.go: запуск operator + HTTP сервера параллельно
- go.sum: добавлен gorilla/mux v1.8.1
This commit is contained in:
@@ -1,23 +1,16 @@
|
||||
/*
|
||||
Copyright 2026.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Изменено: 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.)
|
||||
@@ -33,12 +26,17 @@ import (
|
||||
|
||||
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()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
scheme = runtime.NewScheme()
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -65,6 +63,46 @@ func main() {
|
||||
|
||||
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,
|
||||
@@ -72,51 +110,71 @@ func main() {
|
||||
HealthProbeBindAddress: probeAddr,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: "4b6ba465.kube5s.ru",
|
||||
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
|
||||
// when the Manager ends. This requires the binary to immediately end when the
|
||||
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
|
||||
// speeds up voluntary leader transitions as the new leader don't have to wait
|
||||
// LeaseDuration time first.
|
||||
//
|
||||
// In the default scaffold provided, the program ends immediately after
|
||||
// the manager stops, so would be fine to enable this option. However,
|
||||
// if you are doing or is intended to do any operation such as perform cleanups
|
||||
// after the manager stops then its usage might be unsafe.
|
||||
// LeaderElectionReleaseOnCancel: true,
|
||||
})
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
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,
|
||||
S3Endpoint: cfg.S3Endpoint,
|
||||
S3AccessKey: cfg.S3AccessKey,
|
||||
S3SecretKey: cfg.S3SecretKey,
|
||||
S3Bucket: cfg.S3Bucket,
|
||||
Namespace: "sless",
|
||||
})
|
||||
|
||||
if err = (&controllers.FunctionReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Builder: bldr,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Function")
|
||||
log.Error("unable to create controller", "controller", "Function", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = (&controllers.TriggerReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
IngressHost: cfg.IngressHost,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Trigger")
|
||||
log.Error("unable to create controller", "controller", "Trigger", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
//+kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
log.Error("unable to set up health check", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
log.Error("unable to set up ready check", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
// REST API сервер — запускается параллельно с operator manager
|
||||
apiHandler := slessapi.NewRouter(&handler.Handler{
|
||||
K8s: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
S3: s3Client,
|
||||
PG: pg,
|
||||
Log: log,
|
||||
}, cfg.APIToken, 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 {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
log.Error("problem running manager", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user