check / check (push) Successful in 5m14s
fx alone handles SIGINT and SIGTERM; the server's own handler, which only cancelled a context that fx's stop did not wait for, is gone. runApp starts the fx app, waits for a signal or a shutdown request, stops the app and returns the exit code, which main passes to os.Exit. A listen error asks fx to shut down with exit code 1. A Sentry DSN that cannot be used fails the server's start hook, so fx stops what had already started. The server's stop hook stops the HTTP server, then waits for the images still being processed, both within ShutdownTimeout; images still being processed after that are logged and fail the stop, so the exit code is 1. Model: opus-5-5
102 lines
2.2 KiB
Go
102 lines
2.2 KiB
Go
// Package main is the entry point for the pixad image proxy server.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/spf13/cobra"
|
|
"go.uber.org/fx"
|
|
"sneak.berlin/go/pixa/internal/config"
|
|
"sneak.berlin/go/pixa/internal/database"
|
|
"sneak.berlin/go/pixa/internal/globals"
|
|
"sneak.berlin/go/pixa/internal/handlers"
|
|
"sneak.berlin/go/pixa/internal/healthcheck"
|
|
"sneak.berlin/go/pixa/internal/logger"
|
|
"sneak.berlin/go/pixa/internal/middleware"
|
|
"sneak.berlin/go/pixa/internal/server"
|
|
)
|
|
|
|
var Version string //nolint:gochecknoglobals // set by ldflags
|
|
|
|
var configPath string //nolint:gochecknoglobals // cobra flag
|
|
|
|
func main() {
|
|
rootCmd := &cobra.Command{
|
|
Use: "pixad",
|
|
Short: "Pixa image caching proxy server",
|
|
Run: run,
|
|
}
|
|
|
|
rootCmd.Flags().StringVarP(&configPath, "config", "c", "", "path to config file")
|
|
|
|
err := rootCmd.Execute()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func run(_ *cobra.Command, _ []string) {
|
|
globals.Version = Version
|
|
|
|
// Set config path in environment if specified via flag
|
|
if configPath != "" {
|
|
_ = os.Setenv("PIXA_CONFIG_PATH", configPath)
|
|
}
|
|
|
|
// A write to a closed stdout or stderr must not end the process.
|
|
signal.Ignore(syscall.SIGPIPE)
|
|
|
|
app := fx.New(
|
|
fx.Provide(
|
|
config.New,
|
|
database.New,
|
|
globals.New,
|
|
handlers.New,
|
|
logger.New,
|
|
server.New,
|
|
middleware.New,
|
|
healthcheck.New,
|
|
),
|
|
fx.Invoke(
|
|
func(log *logger.Logger) { log.Identify() },
|
|
func(*server.Server) {},
|
|
),
|
|
)
|
|
|
|
os.Exit(runApp(app))
|
|
}
|
|
|
|
// runApp starts app, waits for SIGINT, SIGTERM or a shutdown request, then
|
|
// stops app. It returns the exit code: the one the shutdown request carries,
|
|
// 0 for a signal, or 1 when app fails to start or to stop; fx logs that
|
|
// error itself. It does what fx's App.Run does, but returns the exit code
|
|
// instead of exiting.
|
|
func runApp(app *fx.App) int {
|
|
startCtx, cancelStart := context.WithTimeout(
|
|
context.Background(), app.StartTimeout())
|
|
defer cancelStart()
|
|
|
|
err := app.Start(startCtx)
|
|
if err != nil {
|
|
return 1
|
|
}
|
|
|
|
shutdown := <-app.Wait()
|
|
|
|
stopCtx, cancelStop := context.WithTimeout(
|
|
context.Background(), app.StopTimeout())
|
|
defer cancelStop()
|
|
|
|
err = app.Stop(stopCtx)
|
|
if err != nil {
|
|
return 1
|
|
}
|
|
|
|
return shutdown.ExitCode
|
|
}
|