Skip to content

Commit ce60710

Browse files
Support multiple starting modes
1 parent e486296 commit ce60710

File tree

4 files changed

+171
-23
lines changed

4 files changed

+171
-23
lines changed

cmd/relayproxy/api/server.go

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,61 @@ func (s *Server) initRoutes() {
111111
s.addAdminRoutes(cRetrieverRefresh)
112112
}
113113

114-
// Start launch the API server
114+
115115
func (s *Server) Start() {
116+
// start the OpenTelemetry tracing service
117+
err := s.otelService.Init(context.Background(), s.zapLog, s.config)
118+
if err != nil {
119+
s.zapLog.Error(
120+
"error while initializing OTel, continuing without tracing enabled",
121+
zap.Error(err),
122+
)
123+
// we can continue because otel is not mandatory to start the server
124+
}
125+
126+
switch s.config.GetServerMode(s.zapLog) {
127+
case config.ServerModeLambda:
128+
s.startAwsLambda()
129+
case config.ServerModeUnixSocket:
130+
s.startUnixSocketServer()
131+
default:
132+
s.startAsHTTPServer()
133+
}
134+
}
135+
136+
137+
func (s *Server) startUnixSocketServer() {
138+
socketPath:= s.config.GetUnixSocketPath()
139+
// Clean up the old socket file if it exists (important for graceful restarts)
140+
if _, err := os.Stat(socketPath); err == nil {
141+
os.Remove(socketPath)
142+
}
143+
144+
listener, err := net.Listen("unix", socketPath)
145+
if err != nil {
146+
log.Fatalf("Error creating Unix listener: %v", err)
147+
}
148+
149+
defer listener.Close()
150+
s.apiEcho.Listener = listener
151+
152+
s.zapLog.Info(
153+
"Starting go-feature-flag relay proxy as unix socket...",
154+
zap.String("socket", socketPath),
155+
zap.String("version", s.config.Version))
156+
157+
err = s.apiEcho.StartServer(new(http.Server))
158+
if err != nil && !errors.Is(err, http.ErrServerClosed) {
159+
s.zapLog.Fatal("Error starting relay proxy as unix socket", zap.Error(err))
160+
}
161+
}
162+
163+
// startAsHTTPServer launch the API server
164+
func (s *Server) startAsHTTPServer() {
116165
// starting the monitoring server on a different port if configured
117166
if s.monitoringEcho != nil {
118167
go func() {
119-
addressMonitoring := fmt.Sprintf("%s:%d", s.config.GetServerHost(), s.config.GetMonitoringPort())
168+
addressMonitoring := fmt.Sprintf("%s:%d", s.config.GetServerHost(), s.config.GetMonitoringPort(s.zapLog))
120169
s.zapLog.Info(
121170
"Starting monitoring",
122171
zap.String("address", addressMonitoring))
@@ -128,16 +177,6 @@ func (s *Server) Start() {
128177
defer func() { _ = s.monitoringEcho.Close() }()
129178
}
130179

131-
// start the OpenTelemetry tracing service
132-
err := s.otelService.Init(context.Background(), s.zapLog, s.config)
133-
if err != nil {
134-
s.zapLog.Error(
135-
"error while initializing OTel, continuing without tracing enabled",
136-
zap.Error(err),
137-
)
138-
// we can continue because otel is not mandatory to start the server
139-
}
140-
141180
address := fmt.Sprintf("%s:%d", s.config.GetServerHost(), s.config.GetServerPort(s.zapLog))
142181
s.zapLog.Info(
143182
"Starting go-feature-flag relay proxy ...",
@@ -150,14 +189,10 @@ func (s *Server) Start() {
150189
}
151190
}
152191

153-
// StartAwsLambda is starting the relay proxy as an AWS Lambda
154-
func (s *Server) StartAwsLambda() {
155-
lambda.Start(s.getLambdaHandler())
156-
}
157-
158-
func (s *Server) getLambdaHandler() interface{} {
159-
handlerMngr := newAwsLambdaHandlerManager(s.apiEcho, s.config.AwsApiGatewayBasePath)
160-
return handlerMngr.GetAdapter(s.config.AwsLambdaAdapter)
192+
// startAwsLambda is starting the relay proxy as an AWS Lambda
193+
func (s *Server) startAwsLambda() {
194+
handlerMngr := newAwsLambdaHandlerManager(s.apiEcho, s.config.GetAwsApiGatewayBasePath())
195+
lambda.Start(handlerMngr.GetAdapter(s.config.GetLambdaAdapter()))
161196
}
162197

163198
// Stop shutdown the API server
@@ -179,3 +214,4 @@ func (s *Server) Stop(ctx context.Context) {
179214
s.zapLog.Fatal("impossible to stop go-feature-flag relay proxy", zap.Error(err))
180215
}
181216
}
217+
``

cmd/relayproxy/config/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,24 @@ type Config struct {
110110
AuthorizedKeys APIKeys `mapstructure:"authorizedKeys" koanf:"authorizedkeys"`
111111

112112
// StartAsAwsLambda (optional) if true, the relay proxy will start ready to be launched as AWS Lambda
113+
//
114+
// Deprecated: use `Server.Mode = lambda` instead
113115
StartAsAwsLambda bool `mapstructure:"startAsAwsLambda" koanf:"startasawslambda"`
114116

115117
// AwsLambdaAdapter (optional) is the adapter to use when the relay proxy is started as an AWS Lambda.
116118
// Possible values are "APIGatewayV1", "APIGatewayV2" and "ALB"
117119
// Default: "APIGatewayV2"
120+
//
121+
// Deprecated: use `Server.LambdaAdapter` instead
118122
AwsLambdaAdapter string `mapstructure:"awsLambdaAdapter" koanf:"awslambdaadapter"`
119123

120124
// AwsApiGatewayBasePath (optional) is the base path prefix for AWS API Gateway deployments.
121125
// This is useful when deploying behind a non-root path like "/api" or "/dev/feature-flags".
122126
// The relay proxy will strip this base path from incoming requests before processing.
123127
// Example: if set to "/api/feature-flags", requests to "/api/feature-flags/health" will be processed as "/health"
124128
// Default: ""
129+
//
130+
// Deprecated: use `Server.AwsApiGatewayBasePath` instead
125131
AwsApiGatewayBasePath string `mapstructure:"awsApiGatewayBasePath" koanf:"awsapigatewaybasepath"`
126132

127133
// EvaluationContextEnrichment (optional) will be merged with the evaluation context sent during the evaluation.

cmd/relayproxy/config/config_server.go

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,35 @@
11
package config
22

3-
import "go.uber.org/zap"
3+
import (
4+
"errors"
5+
6+
"go.uber.org/zap"
7+
)
8+
9+
type ServerMode = string
10+
11+
const (
12+
// ServerModeHTTP is the HTTP server mode.
13+
ServerModeHTTP ServerMode = "http"
14+
// ServerModeLambda is the AWS Lambda server mode.
15+
ServerModeLambda ServerMode = "lambda"
16+
// ServerModeUnixSocket is the Unix Socket server mode.
17+
ServerModeUnixSocket ServerMode = "unixsocket"
18+
)
19+
20+
type LambdaAdapter = string
21+
22+
const (
23+
LambdaAdapterAPIGatewayV1 LambdaAdapter = "APIGatewayV1"
24+
LambdaAdapterAPIGatewayV2 LambdaAdapter = "APIGatewayV2"
25+
LambdaAdapterALB LambdaAdapter = "ALB"
26+
)
427

528
type Server struct {
29+
// Mode is the server mode.
30+
// default: http
31+
Mode ServerMode `mapstructure:"mode" koanf:"mode"`
32+
633
// Host is the server host.
734
// default: 0.0.0.0
835
Host string `mapstructure:"host" koanf:"host"`
@@ -11,12 +38,38 @@ type Server struct {
1138
// default: 1031
1239
Port int `mapstructure:"port" koanf:"port"`
1340

14-
// MonitoringPort is the monitoring port.
41+
// MonitoringPort is the monitoring port. It can be specified only if the server mode is HTTP.
1542
// default: none, it will use the same as server port.
1643
MonitoringPort int `mapstructure:"monitoringPort" koanf:"monitoringport"`
1744

1845
// UnixSocket is the server unix socket path.
1946
UnixSocket string `mapstructure:"unixSocket" koanf:"unixsocket"`
47+
48+
// AWS Lambda configuration
49+
// LambdaAdapter is the adapter to use when the relay proxy is started as an AWS Lambda.
50+
// default: APIGatewayV2
51+
LambdaAdapter LambdaAdapter `mapstructure:"awsLambdaAdapter" koanf:"awsLambdaAdapter"`
52+
53+
// AwsApiGatewayBasePath (optional) is the base path prefix for AWS API Gateway deployments.
54+
// This is useful when deploying behind a non-root path like "/api" or "/dev/feature-flags".
55+
// The relay proxy will strip this base path from incoming requests before processing.
56+
// Example: if set to "/api/feature-flags", requests to "/api/feature-flags/health" will be processed as "/health"
57+
// Default: ""
58+
AwsApiGatewayBasePath string `mapstructure:"awsApiGatewayBasePath" koanf:"awsapigatewaybasepath"`
59+
}
60+
61+
func (s *Server) Validate() error {
62+
switch s.Mode {
63+
case ServerModeUnixSocket:
64+
if s.UnixSocket == "" {
65+
return errors.New("unixSocket must be set when server mode is unixsocket")
66+
}
67+
return nil
68+
case ServerModeLambda, ServerModeHTTP:
69+
return nil
70+
default:
71+
return errors.New("invalid server mode: " + s.Mode)
72+
}
2073
}
2174

2275
// GetMonitoringPort returns the monitoring port, checking first the top-level config
@@ -54,3 +107,54 @@ func (c *Config) GetServerPort(logger *zap.Logger) int {
54107
}
55108
return 1031
56109
}
110+
111+
// GetServerMode returns the server mode, checking first the server config
112+
// and then the top-level config, defaulting to HTTP if not set.
113+
func (c *Config) GetServerMode(logger *zap.Logger) ServerMode {
114+
if c.Server.Mode != "" {
115+
return c.Server.Mode
116+
}
117+
118+
if c.StartAsAwsLambda {
119+
if logger != nil {
120+
zap.L().Warn("The server mode is set using `startAsAwsLambda`, this option is deprecated, please migrate to `server.mode`")
121+
}
122+
return ServerModeLambda
123+
}
124+
125+
return ServerModeHTTP
126+
}
127+
128+
// GetLambdaAdapter returns the lambda adapter, checking first the server config
129+
// and then the top-level config, defaulting to APIGatewayV2 if not set.
130+
func (c *Config) GetLambdaAdapter(logger *zap.Logger) LambdaAdapter {
131+
if c.Server.LambdaAdapter != "" {
132+
return c.Server.LambdaAdapter
133+
}
134+
135+
if c.AwsLambdaAdapter != "" {
136+
if logger != nil {
137+
zap.L().Warn("The lambda adapter is set using `awsLambdaAdapter`, this option is deprecated, please migrate to `server.awsLambdaAdapter`")
138+
}
139+
return LambdaAdapter(c.AwsLambdaAdapter)
140+
}
141+
142+
return LambdaAdapterAPIGatewayV2
143+
}
144+
145+
// GetAwsApiGatewayBasePath returns the AWS API Gateway base path, checking first the server config
146+
// and then the top-level config, defaulting to empty string if not set.
147+
func (c *Config) GetAwsApiGatewayBasePath(logger *zap.Logger) string {
148+
if c.Server.AwsApiGatewayBasePath != "" {
149+
return c.Server.AwsApiGatewayBasePath
150+
}
151+
152+
if c.AwsApiGatewayBasePath != "" {
153+
if logger != nil {
154+
zap.L().Warn("The AWS API Gateway base path is set using `awsApiGatewayBasePath`, this option is deprecated, please migrate to `server.awsApiGatewayBasePath`")
155+
}
156+
return c.AwsApiGatewayBasePath
157+
}
158+
159+
return ""
160+
}

cmd/relayproxy/config/config_validator.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ func (c *Config) IsValid() error {
2222
if err := validateLogFormat(c.LogFormat); err != nil {
2323
return err
2424
}
25-
25+
if err := c.Server.Validate(); err != nil {
26+
return err
27+
}
2628
if len(c.FlagSets) > 0 {
2729
return c.validateFlagSets()
2830
}

0 commit comments

Comments
 (0)