@@ -6,7 +6,11 @@ import (
66 "log/slog"
77 "net/http"
88 "os"
9+ "os/signal"
910 "strings"
11+ "sync"
12+ "syscall"
13+ "time"
1014
1115 "github.com/libops/ppb/pkg/config"
1216 "github.com/libops/ppb/pkg/proxy"
@@ -31,6 +35,45 @@ func init() {
3135 slog .SetDefault (handler )
3236}
3337
38+ func startPingRoutine (ctx context.Context , wg * sync.WaitGroup , c * config.Config , interval time.Duration ) {
39+ defer wg .Done ()
40+
41+ ticker := time .NewTicker (interval )
42+ defer ticker .Stop ()
43+
44+ slog .Info ("Starting ping routine to GCE instance" , "interval" , interval )
45+
46+ for {
47+ select {
48+ case <- ctx .Done ():
49+ slog .Info ("Ping routine shutting down" )
50+ return
51+ case <- ticker .C :
52+ host := c .Machine .Host ()
53+ if host == "" {
54+ slog .Debug ("No GCE host IP available for ping" )
55+ continue
56+ }
57+
58+ pingURL := fmt .Sprintf ("http://%s:8808/ping" , host )
59+ slog .Debug ("Pinging GCE instance" , "url" , pingURL )
60+
61+ client := & http.Client {
62+ Timeout : 5 * time .Second ,
63+ }
64+
65+ resp , err := client .Get (pingURL )
66+ if err != nil {
67+ slog .Debug ("Ping failed" , "url" , pingURL , "error" , err )
68+ continue
69+ }
70+ resp .Body .Close ()
71+
72+ slog .Debug ("Ping successful" , "url" , pingURL , "status" , resp .StatusCode )
73+ }
74+ }
75+ }
76+
3477func main () {
3578 c , err := config .LoadConfig ()
3679 if err != nil {
@@ -45,6 +88,14 @@ func main() {
4588 c .PowerOnCooldown = 30
4689 }
4790
91+ ctx , cancel := context .WithCancel (context .Background ())
92+ defer cancel ()
93+ sigChan := make (chan os.Signal , 1 )
94+ signal .Notify (sigChan , syscall .SIGTERM , syscall .SIGINT )
95+ var wg sync.WaitGroup
96+ wg .Add (1 )
97+ go startPingRoutine (ctx , & wg , c , 30 * time .Second )
98+
4899 http .HandleFunc ("/healthcheck" , func (w http.ResponseWriter , r * http.Request ) {
49100 w .WriteHeader (http .StatusOK )
50101 _ , _ = fmt .Fprintln (w , "OK" )
@@ -58,8 +109,8 @@ func main() {
58109 }
59110
60111 // Attempt to power on machine with cooldown protection
61- ctx := context .Background ()
62- err := c .Machine .PowerOnWithCooldown (ctx , c .PowerOnCooldown )
112+ reqCtx := context .Background ()
113+ err := c .Machine .PowerOnWithCooldown (reqCtx , c .PowerOnCooldown )
63114 if err != nil {
64115 slog .Error ("Power-on attempt failed" , "err" , err )
65116 http .Error (w , "Backend not available" , http .StatusServiceUnavailable )
@@ -71,8 +122,24 @@ func main() {
71122 p .ServeHTTP (w , r )
72123 })
73124
74- slog .Info ("Server listening on :8080" )
75- if err := http .ListenAndServe (":8080" , nil ); err != nil {
76- panic (err )
125+ server := & http.Server {Addr : ":8080" }
126+ go func () {
127+ slog .Info ("Server listening on :8080" )
128+ if err := server .ListenAndServe (); err != nil && err != http .ErrServerClosed {
129+ slog .Error ("Server error" , "err" , err )
130+ }
131+ }()
132+
133+ <- sigChan
134+ slog .Info ("Received shutdown signal, gracefully shutting down..." )
135+ cancel ()
136+
137+ shutdownCtx , shutdownCancel := context .WithTimeout (context .Background (), 10 * time .Second )
138+ defer shutdownCancel ()
139+ if err := server .Shutdown (shutdownCtx ); err != nil {
140+ slog .Error ("Server shutdown error" , "err" , err )
77141 }
142+
143+ wg .Wait ()
144+ slog .Info ("Shutdown complete" )
78145}
0 commit comments