Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6bde30f
Change format to configure server listening port
thomaspoignant Nov 5, 2025
1f7fd51
Adding comments
thomaspoignant Nov 5, 2025
cfb5980
adding a log to mention the deprecation
thomaspoignant Nov 5, 2025
e486296
remove duplicated package
thomaspoignant Nov 5, 2025
f7e568e
Support multiple starting modes
thomaspoignant Nov 6, 2025
6459397
Adding test for server accessors
thomaspoignant Nov 6, 2025
15e436f
Merge branch 'main' into unix-socket-support
thomaspoignant Nov 6, 2025
0f0da52
Change the way to start the API
thomaspoignant Nov 6, 2025
e82c05d
Use logger pass in function
thomaspoignant Nov 6, 2025
41fc636
Add test unix socket
thomaspoignant Nov 6, 2025
8a12f6f
Replace LISTEN
thomaspoignant Nov 6, 2025
071af6c
Use zap fatal instead of log
thomaspoignant Nov 6, 2025
054e89b
set logger
thomaspoignant Nov 6, 2025
bc91b32
GetServerPort never return 0
thomaspoignant Nov 6, 2025
b4158fb
Use background instead of TODO for context
thomaspoignant Nov 6, 2025
bf19e60
Log and exit if impossible to remove existing socket
thomaspoignant Nov 6, 2025
bb3dece
log error when closing the unix socket
thomaspoignant Nov 6, 2025
197f16f
better way for the socket to be created
thomaspoignant Nov 6, 2025
93d0007
checking errors in tests
thomaspoignant Nov 6, 2025
4a4308b
use directly the logger passed in the function
thomaspoignant Nov 6, 2025
908c1e7
better waiting mechanism
thomaspoignant Nov 6, 2025
539c42b
move server validation logic to Config object
thomaspoignant Nov 6, 2025
7f26aba
add test to validate error socket
thomaspoignant Nov 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ This flag split the usage of this flag, 20% will use the variation `my-new-featu
Create a new `YAML` file containing the configuration of your relay proxy.

```yaml title="goff-proxy.yaml"
listen: 1031
server:
mode: http
port: 1031
pollingInterval: 1000
startWithRetrieverError: false
retriever:
Expand Down
2 changes: 1 addition & 1 deletion cmd/relayproxy/api/routes_monitoring.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

func (s *Server) addMonitoringRoutes() {
if s.config.MonitoringPort != 0 {
if s.config.GetMonitoringPort(s.zapLog) != 0 {
s.monitoringEcho = echo.New()
s.monitoringEcho.HideBanner = true
s.monitoringEcho.HidePort = true
Expand Down
9 changes: 6 additions & 3 deletions cmd/relayproxy/api/routes_monitoring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ func TestPprofEndpointsStarts(t *testing.T) {
},
},
},
Server: config.Server{
Mode: config.ServerModeHTTP,
Port: 1031,
},
MonitoringPort: tt.MonitoringPort,
ListenPort: 1031,
EnablePprof: tt.EnablePprof,
}

Expand All @@ -65,12 +68,12 @@ func TestPprofEndpointsStarts(t *testing.T) {
Metrics: metric.Metrics{},
}, z)

portToCheck := c.ListenPort
portToCheck := c.GetServerPort(z)
if tt.MonitoringPort != 0 {
portToCheck = tt.MonitoringPort
}

go apiServer.Start()
go apiServer.StartWithContext(context.Background())
defer apiServer.Stop(context.Background())
time.Sleep(1 * time.Second) // waiting for the apiServer to start
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/debug/pprof/heap", portToCheck))
Expand Down
101 changes: 75 additions & 26 deletions cmd/relayproxy/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"context"
"errors"
"fmt"
"net"
"net/http"
"os"
"strings"

"github.com/aws/aws-lambda-go/lambda"
Expand Down Expand Up @@ -111,12 +113,68 @@ func (s *Server) initRoutes() {
s.addAdminRoutes(cRetrieverRefresh)
}

// Start launch the API server
func (s *Server) Start() {
func (s *Server) StartWithContext(ctx context.Context) {
// start the OpenTelemetry tracing service
err := s.otelService.Init(ctx, s.zapLog, s.config)
if err != nil {
s.zapLog.Error(
"error while initializing OTel, continuing without tracing enabled",
zap.Error(err),
)
// we can continue because otel is not mandatory to start the server
}

switch s.config.GetServerMode(s.zapLog) {
case config.ServerModeLambda:
s.startAwsLambda()
case config.ServerModeUnixSocket:
s.startUnixSocketServer(ctx)
default:
s.startAsHTTPServer()
}
}

// startUnixSocketServer launch the API server as a unix socket.
func (s *Server) startUnixSocketServer(ctx context.Context) {
socketPath := s.config.GetUnixSocketPath()

// Clean up the old socket file if it exists (important for graceful restarts)
if _, err := os.Stat(socketPath); err == nil {
if err := os.Remove(socketPath); err != nil {
s.zapLog.Fatal("Could not remove old socket file", zap.String("path", socketPath), zap.Error(err))
}
}

lc := net.ListenConfig{}
listener, err := lc.Listen(ctx, "unix", socketPath)
if err != nil {
s.zapLog.Fatal("Error creating Unix listener", zap.Error(err))
}

defer func() {
if err := listener.Close(); err != nil {
s.zapLog.Error("error closing unix socket listener", zap.Error(err))
}
}()
s.apiEcho.Listener = listener

s.zapLog.Info(
"Starting go-feature-flag relay proxy as unix socket...",
zap.String("socket", socketPath),
zap.String("version", s.config.Version))

err = s.apiEcho.StartServer(new(http.Server))
if err != nil && !errors.Is(err, http.ErrServerClosed) {
s.zapLog.Fatal("Error starting relay proxy as unix socket", zap.Error(err))
}
}

// startAsHTTPServer launch the API server
func (s *Server) startAsHTTPServer() {
// starting the monitoring server on a different port if configured
if s.monitoringEcho != nil {
go func() {
addressMonitoring := fmt.Sprintf("0.0.0.0:%d", s.config.MonitoringPort)
addressMonitoring := fmt.Sprintf("%s:%d", s.config.GetServerHost(), s.config.GetMonitoringPort(s.zapLog))
s.zapLog.Info(
"Starting monitoring",
zap.String("address", addressMonitoring))
Expand All @@ -128,40 +186,29 @@ func (s *Server) Start() {
defer func() { _ = s.monitoringEcho.Close() }()
}

// start the OpenTelemetry tracing service
err := s.otelService.Init(context.Background(), s.zapLog, s.config)
if err != nil {
s.zapLog.Error(
"error while initializing OTel, continuing without tracing enabled",
zap.Error(err),
)
// we can continue because otel is not mandatory to start the server
}

// starting the main application
if s.config.ListenPort == 0 {
s.config.ListenPort = 1031
}
address := fmt.Sprintf("0.0.0.0:%d", s.config.ListenPort)
address := fmt.Sprintf("%s:%d", s.config.GetServerHost(), s.config.GetServerPort(s.zapLog))
s.zapLog.Info(
"Starting go-feature-flag relay proxy ...",
zap.String("address", address),
zap.String("version", s.config.Version))

err = s.apiEcho.Start(address)
err := s.apiEcho.Start(address)
if err != nil && !errors.Is(err, http.ErrServerClosed) {
s.zapLog.Fatal("Error starting relay proxy", zap.Error(err))
}
}

// StartAwsLambda is starting the relay proxy as an AWS Lambda
func (s *Server) StartAwsLambda() {
// startAwsLambda is starting the relay proxy as an AWS Lambda
func (s *Server) startAwsLambda() {
lambda.Start(s.getLambdaHandler())
}

// getLambdaHandler returns the appropriate lambda handler based on the configuration.
// We need a dedicated function because it is called from tests as well, this is the
// reason why we can't merged it in startAwsLambda.
func (s *Server) getLambdaHandler() interface{} {
handlerMngr := newAwsLambdaHandlerManager(s.apiEcho, s.config.AwsApiGatewayBasePath)
return handlerMngr.GetAdapter(s.config.AwsLambdaAdapter)
handlerMngr := newAwsLambdaHandlerManager(s.apiEcho, s.config.GetAwsApiGatewayBasePath(s.zapLog))
return handlerMngr.GetAdapter(s.config.GetLambdaAdapter(s.zapLog))
}

// Stop shutdown the API server
Expand All @@ -178,8 +225,10 @@ func (s *Server) Stop(ctx context.Context) {
}
}

err = s.apiEcho.Close()
if err != nil {
s.zapLog.Fatal("impossible to stop go-feature-flag relay proxy", zap.Error(err))
if s.apiEcho != nil {
err = s.apiEcho.Close()
if err != nil {
s.zapLog.Fatal("impossible to stop go-feature-flag relay proxy", zap.Error(err))
}
}
}
Loading
Loading