-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
145 lines (121 loc) · 3.21 KB
/
main.go
File metadata and controls
145 lines (121 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package main
import (
"context"
"fmt"
"log/slog"
"net/http"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"time"
"github.com/libops/ppb/pkg/config"
"github.com/libops/ppb/pkg/proxy"
)
func init() {
level := slog.LevelInfo
ll := os.Getenv("LOG_LEVEL")
switch strings.ToUpper(ll) {
case "DEBUG":
level = slog.LevelDebug
case "WARN":
level = slog.LevelWarn
case "ERROR":
level = slog.LevelError
}
opts := &slog.HandlerOptions{
Level: level,
}
handler := slog.New(slog.NewTextHandler(os.Stdout, opts))
slog.SetDefault(handler)
}
func startPingRoutine(ctx context.Context, wg *sync.WaitGroup, c *config.Config, interval time.Duration) {
defer wg.Done()
ticker := time.NewTicker(interval)
defer ticker.Stop()
slog.Info("Starting ping routine to GCE instance", "interval", interval)
for {
select {
case <-ctx.Done():
slog.Info("Ping routine shutting down")
return
case <-ticker.C:
host := c.Machine.Host()
if host == "" {
slog.Debug("No GCE host IP available for ping")
continue
}
pingURL := fmt.Sprintf("http://%s:8808/ping", host)
slog.Debug("Pinging GCE instance", "url", pingURL)
client := &http.Client{
Timeout: 5 * time.Second,
}
resp, err := client.Get(pingURL)
if err != nil {
slog.Debug("Ping failed", "url", pingURL, "error", err)
continue
}
resp.Body.Close()
slog.Debug("Ping successful", "url", pingURL, "status", resp.StatusCode)
}
}
}
func main() {
c, err := config.LoadConfig()
if err != nil {
slog.Error("Unable to load config", "err", err)
os.Exit(1)
}
slog.Debug("Loaded config", "config", c)
// Set default cooldown interval if not specified
if c.PowerOnCooldown <= 0 {
c.PowerOnCooldown = 30
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
var wg sync.WaitGroup
wg.Add(1)
go startPingRoutine(ctx, &wg, c, 30*time.Second)
http.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = fmt.Fprintln(w, "OK")
})
p := proxy.New(c)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if !c.IpIsAllowed(r) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
// Attempt to power on machine with cooldown protection
reqCtx := context.Background()
err := c.Machine.PowerOnWithCooldown(reqCtx, c.PowerOnCooldown)
if err != nil {
slog.Error("Power-on attempt failed", "err", err)
http.Error(w, "Backend not available", http.StatusServiceUnavailable)
return
}
p.SetRequestHeaders(r)
p.SetHost()
p.ServeHTTP(w, r)
})
server := &http.Server{Addr: ":8080"}
go func() {
slog.Info("Server listening on :8080")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
slog.Error("Server error", "err", err)
}
}()
<-sigChan
slog.Info("Received shutdown signal, gracefully shutting down...")
cancel()
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer shutdownCancel()
if err := server.Shutdown(shutdownCtx); err != nil {
slog.Error("Server shutdown error", "err", err)
}
wg.Wait()
slog.Info("Shutdown complete")
}