feat: polish & windviz & deploy
This commit is contained in:
parent
81b8e763bd
commit
465ad00f7b
78 changed files with 20622 additions and 2154 deletions
|
|
@ -19,7 +19,6 @@ import (
|
|||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"predictor-refactored/internal/api"
|
||||
"predictor-refactored/internal/api/async"
|
||||
"predictor-refactored/internal/config"
|
||||
"predictor-refactored/internal/datasets"
|
||||
"predictor-refactored/internal/datasets/gefs"
|
||||
|
|
@ -27,15 +26,65 @@ import (
|
|||
"predictor-refactored/internal/elevation"
|
||||
"predictor-refactored/internal/metrics"
|
||||
wgfs "predictor-refactored/internal/weather/gfs"
|
||||
"predictor-refactored/internal/windviz"
|
||||
)
|
||||
|
||||
// Build metadata, injected via -ldflags at build time (see Dockerfile).
|
||||
var (
|
||||
version = "dev"
|
||||
revision = "unknown"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// `predictor -healthcheck` probes the local /health endpoint and exits
|
||||
// 0/1. The container HEALTHCHECK uses it so the (distroless) image needs
|
||||
// no shell or curl.
|
||||
for _, a := range os.Args[1:] {
|
||||
if a == "-healthcheck" || a == "--healthcheck" {
|
||||
os.Exit(healthcheck())
|
||||
}
|
||||
}
|
||||
if err := run(os.Args[1:]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "fatal:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// healthcheck performs a liveness probe against the local server. It resolves
|
||||
// the port through the same config loader as the server, so the probe always
|
||||
// matches the bind port regardless of how it was set (flag, env, or file).
|
||||
func healthcheck() int {
|
||||
port := 8080
|
||||
if cfg, err := config.Load(withoutHealthcheckFlag(os.Args[1:])); err == nil {
|
||||
port = cfg.HTTP.Port
|
||||
}
|
||||
client := &http.Client{Timeout: 3 * time.Second}
|
||||
resp, err := client.Get(fmt.Sprintf("http://127.0.0.1:%d/health", port))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "healthcheck:", err)
|
||||
return 1
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Fprintln(os.Stderr, "healthcheck: status", resp.StatusCode)
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// withoutHealthcheckFlag drops the -healthcheck flag so the remaining args
|
||||
// parse cleanly through config.Load (which does not define it).
|
||||
func withoutHealthcheckFlag(args []string) []string {
|
||||
out := make([]string, 0, len(args))
|
||||
for _, a := range args {
|
||||
if a == "-healthcheck" || a == "--healthcheck" {
|
||||
continue
|
||||
}
|
||||
out = append(out, a)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func run(args []string) error {
|
||||
cfg, err := config.Load(args)
|
||||
if err != nil {
|
||||
|
|
@ -48,6 +97,10 @@ func run(args []string) error {
|
|||
}
|
||||
defer log.Sync()
|
||||
|
||||
log.Info("starting stratoflights-predictor",
|
||||
zap.String("version", version),
|
||||
zap.String("revision", revision))
|
||||
|
||||
log.Info("configuration loaded",
|
||||
zap.Int("port", cfg.HTTP.Port),
|
||||
zap.String("data_dir", cfg.Data.Dir),
|
||||
|
|
@ -141,12 +194,10 @@ func run(args []string) error {
|
|||
scheduler.StartAsync()
|
||||
defer scheduler.Stop()
|
||||
|
||||
asyncMgr := async.New(async.Config{
|
||||
Workers: cfg.HTTP.AsyncWorkers,
|
||||
QueueSize: cfg.HTTP.AsyncQueueSize,
|
||||
ResultTTL: cfg.HTTP.AsyncResultTTL,
|
||||
}, mgr, elev, sink, log)
|
||||
defer asyncMgr.Close()
|
||||
var windCache *windviz.Cache
|
||||
if cfg.Wind.Enabled {
|
||||
windCache = windviz.NewCache(cfg.Wind.CacheSize, cfg.Wind.CacheTTL)
|
||||
}
|
||||
|
||||
server, err := api.New(cfg.HTTP.Port, api.Deps{
|
||||
Manager: mgr,
|
||||
|
|
@ -154,12 +205,17 @@ func run(args []string) error {
|
|||
Metrics: sink,
|
||||
MetricsHandler: metricsHandler,
|
||||
MetricsPath: cfg.Metrics.Path,
|
||||
AsyncManager: asyncMgr,
|
||||
EnableWind: cfg.Wind.Enabled,
|
||||
WindCache: windCache,
|
||||
AsyncWorkers: cfg.HTTP.AsyncWorkers,
|
||||
AsyncQueueSize: cfg.HTTP.AsyncQueueSize,
|
||||
AsyncResultTTL: cfg.HTTP.AsyncResultTTL,
|
||||
Log: log,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("init server: %w", err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
// Graceful shutdown
|
||||
ctx, cancel := signalContext()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue