diff --git a/pkg/config/config.go b/pkg/config/config.go index bb709b7..860c924 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -20,9 +20,21 @@ type Config struct { PowerOnCooldown int `yaml:"powerOnCooldown"` // seconds ProxyTimeouts ProxyTimeouts `yaml:"proxyTimeouts"` MachineMetadata map[string]any `yaml:"machineMetadata"` + ProxyTarget *ProxyTarget `yaml:"proxyTarget"` Machine *machine.GoogleComputeEngine } +// ProxyTarget optionally overrides where requests are proxied to. +// When set, the machine referenced by MachineMetadata is still powered on and +// pinged, but HTTP traffic is forwarded to ProxyTarget instead. This supports +// topologies where a sidecar (e.g. a Cloud Run frontend container) serves +// requests while still depending on the remote machine being up. +type ProxyTarget struct { + Scheme string `yaml:"scheme"` + Host string `yaml:"host"` + Port int `yaml:"port"` +} + type ProxyTimeouts struct { DialTimeout int `yaml:"dialTimeout"` // seconds, default: 120 KeepAlive int `yaml:"keepAlive"` // seconds, default: 120 diff --git a/pkg/proxy/proxy_test.go b/pkg/proxy/proxy_test.go index ac9c664..7af21a7 100644 --- a/pkg/proxy/proxy_test.go +++ b/pkg/proxy/proxy_test.go @@ -156,3 +156,26 @@ func TestReverseProxy_SetHost(t *testing.T) { t.Error("Target is nil after SetHost()") } } + +func TestReverseProxy_SetHost_ProxyTargetOverride(t *testing.T) { + config := &config.Config{ + Scheme: "http", + Port: 8080, + ProxyTarget: &config.ProxyTarget{ + Scheme: "http", + Host: "localhost", + Port: 9000, + }, + Machine: machine.NewGceMachine(), + } + + proxy := New(config) + proxy.SetHost() + + if proxy.Target.Host != "localhost:9000" { + t.Errorf("Target.Host = %q, want localhost:9000", proxy.Target.Host) + } + if proxy.Target.Scheme != "http" { + t.Errorf("Target.Scheme = %q, want http", proxy.Target.Scheme) + } +} diff --git a/pkg/proxy/tls.go b/pkg/proxy/tls.go index e8c5f04..4d2b05f 100644 --- a/pkg/proxy/tls.go +++ b/pkg/proxy/tls.go @@ -31,9 +31,13 @@ func New(c *config.Config) *ReverseProxy { tlsHandshakeTimeout := time.Duration(c.ProxyTimeouts.TLSHandshakeTimeout) * time.Second expectContinueTimeout := time.Duration(c.ProxyTimeouts.ExpectContinueTimeout) * time.Second + scheme := c.Scheme + if c.ProxyTarget != nil && c.ProxyTarget.Scheme != "" { + scheme = c.ProxyTarget.Scheme + } return &ReverseProxy{ Target: &url.URL{ - Scheme: c.Scheme, + Scheme: scheme, }, Config: c, Transport: &http.Transport{ @@ -59,6 +63,15 @@ func New(c *config.Config) *ReverseProxy { } func (p *ReverseProxy) SetHost() { + if p.Config.ProxyTarget != nil && p.Config.ProxyTarget.Host != "" { + port := p.Config.ProxyTarget.Port + if port == 0 { + port = p.Config.Port + } + p.Target.Host = net.JoinHostPort(p.Config.ProxyTarget.Host, strconv.Itoa(port)) + slog.Debug("Set proxy target host", "host", p.Target.Host) + return + } p.Target.Host = net.JoinHostPort( p.Config.Machine.Host(), strconv.Itoa(p.Config.Port),