Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 4 additions & 28 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,22 +239,8 @@ func run() int {
)
}

srvOpts := []server.Option{
server.WithEnableServices(cfg.EnableServices),
server.WithLogger(logger),
server.WithGRPCServerOptions(extraOpts...),
server.WithMaxRecvMsgBytes(cfg.GRPCMaxRecvMsgBytes),
server.WithMaxSendMsgBytes(cfg.GRPCMaxSendMsgBytes),
}
if cfg.InsecureListen {
srvOpts = append(srvOpts, server.WithInsecure())
} else {
srvOpts = append(srvOpts, server.WithTLS(serverTLS))
}
if rlInterceptor != nil {
srvOpts = append(srvOpts, server.WithRateLimiter(rlInterceptor))
}
srv, err := server.New(cfg.GRPCPort, authInterceptor, srvOpts...)
srvBuild := buildServerOptions(cfg, logger, extraOpts, serverTLS, rlInterceptor)
srv, err := server.New(cfg.GRPCPort, authInterceptor, srvBuild.Opts...)
if err != nil {
logger.ErrorContext(ctx, "failed to create server", "error", err)
return 1
Expand Down Expand Up @@ -346,18 +332,8 @@ func run() int {
}
}

gwOpts := []server.GatewayOption{
server.WithGatewayLogger(logger),
server.WithOpenAPISpec(openAPISpec),
server.WithGatewayMaxRecvMsgBytes(cfg.GRPCMaxRecvMsgBytes),
server.WithGatewayMaxSendMsgBytes(cfg.GRPCMaxSendMsgBytes),
}
if cfg.InsecureListen {
gwOpts = append(gwOpts, server.WithGatewayInsecure())
} else {
gwOpts = append(gwOpts, server.WithGatewayTLS(gwTLS))
}
gw, err = server.NewGateway(ctx, cfg.HTTPPort, fmt.Sprintf("localhost:%s", cfg.GRPCPort), gwOpts...)
gwBuild := buildGatewayOptions(cfg, logger, openAPISpec, gwTLS)
gw, err = server.NewGateway(ctx, cfg.HTTPPort, fmt.Sprintf("localhost:%s", cfg.GRPCPort), gwBuild.Opts...)
if err != nil {
logger.ErrorContext(ctx, "failed to create HTTP gateway", "error", err)
return 1
Expand Down
81 changes: 81 additions & 0 deletions cmd/server/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package main

import (
"log/slog"

"google.golang.org/grpc"

"github.com/opendecree/decree/internal/ratelimit"
"github.com/opendecree/decree/internal/server"
)

// serverOptionsBuild captures the option slice handed to server.New plus the
// boolean decisions that drove it. Tests assert on the decisions so a flag
// silently dropped (read but never wired into an option) is caught.
type serverOptionsBuild struct {
Opts []server.Option
UseTLS bool
UseInsecure bool
HasRateLimiter bool
}

func buildServerOptions(
cfg serverConfig,
logger *slog.Logger,
extraGRPCOpts []grpc.ServerOption,
serverTLS *server.TLSConfig,
rl *ratelimit.Interceptor,
) serverOptionsBuild {
out := serverOptionsBuild{
Opts: []server.Option{
server.WithEnableServices(cfg.EnableServices),
server.WithLogger(logger),
server.WithGRPCServerOptions(extraGRPCOpts...),
server.WithMaxRecvMsgBytes(cfg.GRPCMaxRecvMsgBytes),
server.WithMaxSendMsgBytes(cfg.GRPCMaxSendMsgBytes),
},
}
if cfg.InsecureListen {
out.Opts = append(out.Opts, server.WithInsecure())
out.UseInsecure = true
} else {
out.Opts = append(out.Opts, server.WithTLS(serverTLS))
out.UseTLS = true
}
if rl != nil {
out.Opts = append(out.Opts, server.WithRateLimiter(rl))
out.HasRateLimiter = true
}
return out
}

// gatewayOptionsBuild mirrors serverOptionsBuild for the HTTP gateway.
type gatewayOptionsBuild struct {
Opts []server.GatewayOption
UseTLS bool
UseInsecure bool
}

func buildGatewayOptions(
cfg serverConfig,
logger *slog.Logger,
openAPISpec []byte,
gwTLS *server.GatewayTLSConfig,
) gatewayOptionsBuild {
out := gatewayOptionsBuild{
Opts: []server.GatewayOption{
server.WithGatewayLogger(logger),
server.WithOpenAPISpec(openAPISpec),
server.WithGatewayMaxRecvMsgBytes(cfg.GRPCMaxRecvMsgBytes),
server.WithGatewayMaxSendMsgBytes(cfg.GRPCMaxSendMsgBytes),
},
}
if cfg.InsecureListen {
out.Opts = append(out.Opts, server.WithGatewayInsecure())
out.UseInsecure = true
} else {
out.Opts = append(out.Opts, server.WithGatewayTLS(gwTLS))
out.UseTLS = true
}
return out
}
108 changes: 108 additions & 0 deletions cmd/server/options_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package main

import (
"io"
"log/slog"
"testing"

"github.com/stretchr/testify/assert"
"golang.org/x/time/rate"
"google.golang.org/grpc"

"github.com/opendecree/decree/internal/ratelimit"
"github.com/opendecree/decree/internal/server"
)

func discardLogger() *slog.Logger {
return slog.New(slog.NewTextHandler(io.Discard, nil))
}

func baseServerCfg() serverConfig {
return serverConfig{
EnableServices: []string{"schema", "config", "audit"},
GRPCMaxRecvMsgBytes: 4 << 20,
GRPCMaxSendMsgBytes: 4 << 20,
}
}

func newTestRateLimiter() *ratelimit.Interceptor {
lim := ratelimit.NewInProcess(rate.Limit(1), 1)
return ratelimit.New(ratelimit.Config{Authenticated: lim})
}

func TestBuildServerOptions_TLS(t *testing.T) {
cfg := baseServerCfg()
cfg.InsecureListen = false
tlsCfg := &server.TLSConfig{CertFile: "cert.pem", KeyFile: "key.pem"}

got := buildServerOptions(cfg, discardLogger(), nil, tlsCfg, nil)

assert.True(t, got.UseTLS, "expected TLS branch")
assert.False(t, got.UseInsecure, "expected insecure branch off")
assert.False(t, got.HasRateLimiter, "rate limiter should not be wired when nil")
assert.Len(t, got.Opts, 6, "5 base options + TLS option")
}

func TestBuildServerOptions_Insecure(t *testing.T) {
cfg := baseServerCfg()
cfg.InsecureListen = true

got := buildServerOptions(cfg, discardLogger(), nil, nil, nil)

assert.False(t, got.UseTLS)
assert.True(t, got.UseInsecure)
assert.Len(t, got.Opts, 6, "5 base options + Insecure option")
}

func TestBuildServerOptions_RateLimiterWired(t *testing.T) {
cfg := baseServerCfg()
cfg.InsecureListen = true

got := buildServerOptions(cfg, discardLogger(), nil, nil, newTestRateLimiter())

assert.True(t, got.HasRateLimiter, "non-nil rate limiter must be wired into the option slice")
assert.Len(t, got.Opts, 7, "5 base options + Insecure + RateLimiter")
}

func TestBuildServerOptions_RateLimiterAbsent(t *testing.T) {
cfg := baseServerCfg()
cfg.InsecureListen = true

got := buildServerOptions(cfg, discardLogger(), nil, nil, nil)

assert.False(t, got.HasRateLimiter, "nil rate limiter must not produce a WithRateLimiter option")
assert.Len(t, got.Opts, 6)
}

func TestBuildServerOptions_ExtraGRPCOpts(t *testing.T) {
cfg := baseServerCfg()
cfg.InsecureListen = true
extra := []grpc.ServerOption{grpc.MaxConcurrentStreams(42)}

got := buildServerOptions(cfg, discardLogger(), extra, nil, nil)

assert.Len(t, got.Opts, 6, "extra grpc opts go inside WithGRPCServerOptions, not as separate options")
}

func TestBuildGatewayOptions_TLS(t *testing.T) {
cfg := baseServerCfg()
cfg.InsecureListen = false
gwTLS := &server.GatewayTLSConfig{CAFile: "ca.pem"}

got := buildGatewayOptions(cfg, discardLogger(), []byte(`{}`), gwTLS)

assert.True(t, got.UseTLS)
assert.False(t, got.UseInsecure)
assert.Len(t, got.Opts, 5, "4 base options + TLS option")
}

func TestBuildGatewayOptions_Insecure(t *testing.T) {
cfg := baseServerCfg()
cfg.InsecureListen = true

got := buildGatewayOptions(cfg, discardLogger(), []byte(`{}`), nil)

assert.False(t, got.UseTLS)
assert.True(t, got.UseInsecure)
assert.Len(t, got.Opts, 5, "4 base options + Insecure option")
}
Loading