Skip to content

Commit

Permalink
feat: use separate bind for http ingress server (#1615)
Browse files Browse the repository at this point in the history
Fixes #1609 

@alecthomas I took a stab at this one. Lemme know if there's a better
approach here. :)
  • Loading branch information
wesbillman authored Jun 1, 2024
1 parent 3351d23 commit f445e18
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ integration-tests *test:
#!/bin/bash
set -euo pipefail
testName=${1:-}
go test -fullpath -count 1 -v -tags integration -run "$testName" ./...
go test -fullpath -count 1 -v -tags integration -run "$testName" -p 1 ./...

# Run README doc tests
test-readme *args:
Expand Down
27 changes: 19 additions & 8 deletions backend/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/jellydator/ttlcache/v3"
"github.com/jpillora/backoff"
"golang.org/x/exp/maps"
"golang.org/x/sync/errgroup"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
Expand All @@ -45,6 +46,7 @@ import (
cf "github.com/TBD54566975/ftl/common/configuration"
frontend "github.com/TBD54566975/ftl/frontend"
"github.com/TBD54566975/ftl/internal/cors"
ftlhttp "github.com/TBD54566975/ftl/internal/http"
"github.com/TBD54566975/ftl/internal/log"
ftlmaps "github.com/TBD54566975/ftl/internal/maps"
"github.com/TBD54566975/ftl/internal/model"
Expand All @@ -67,6 +69,7 @@ type CommonConfig struct {

type Config struct {
Bind *url.URL `help:"Socket to bind to." default:"http://localhost:8892" env:"FTL_CONTROLLER_BIND"`
IngressBind *url.URL `help:"Socket to bind to for ingress." default:"http://localhost:8891" env:"FTL_CONTROLLER_INGRESS_BIND"`
Key model.ControllerKey `help:"Controller key (auto)."`
DSN string `help:"DAL DSN." default:"postgres://localhost:54320/ftl?sslmode=disable&user=postgres&password=secret" env:"FTL_CONTROLLER_DSN"`
Advertise *url.URL `help:"Endpoint the Controller should advertise (must be unique across the cluster, defaults to --bind if omitted)." env:"FTL_CONTROLLER_ADVERTISE"`
Expand Down Expand Up @@ -128,18 +131,26 @@ func Start(ctx context.Context, config Config, runnerScaling scaling.RunnerScali

console := NewConsoleService(dal)

ingressHandler := http.StripPrefix("/ingress", svc)
ingressHandler := http.Handler(svc)
if len(config.AllowOrigins) > 0 {
ingressHandler = cors.Middleware(slices.Map(config.AllowOrigins, func(u *url.URL) string { return u.String() }), ingressHandler)
}

return rpc.Serve(ctx, config.Bind,
rpc.GRPC(ftlv1connect.NewVerbServiceHandler, svc),
rpc.GRPC(ftlv1connect.NewControllerServiceHandler, svc),
rpc.GRPC(pbconsoleconnect.NewConsoleServiceHandler, console),
rpc.HTTP("/ingress/", ingressHandler),
rpc.HTTP("/", consoleHandler),
)
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
return ftlhttp.Serve(ctx, config.IngressBind, ingressHandler)
})

g.Go(func() error {
return rpc.Serve(ctx, config.Bind,
rpc.GRPC(ftlv1connect.NewVerbServiceHandler, svc),
rpc.GRPC(ftlv1connect.NewControllerServiceHandler, svc),
rpc.GRPC(pbconsoleconnect.NewConsoleServiceHandler, console),
rpc.HTTP("/", consoleHandler),
)
})

return g.Wait()
}

var _ ftlv1connect.ControllerServiceHandler = (*Service)(nil)
Expand Down
2 changes: 2 additions & 0 deletions cmd/ftl/cmd_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

type serveCmd struct {
Bind *url.URL `help:"Starting endpoint to bind to and advertise to. Each controller and runner will increment the port by 1" default:"http://localhost:8892"`
IngressBind *url.URL `help:"Starting endpoint to bind to for http ingress" default:"http://localhost:8891"`
DBPort int `help:"Port to use for the database." default:"54320"`
Recreate bool `help:"Recreate the database even if it already exists." default:"false"`
Controllers int `short:"c" help:"Number of controllers to start." default:"1"`
Expand Down Expand Up @@ -101,6 +102,7 @@ func (s *serveCmd) Run(ctx context.Context) error {
config := controller.Config{
CommonConfig: s.CommonConfig,
Bind: controllerAddresses[i],
IngressBind: s.IngressBind,
Key: model.NewLocalControllerKey(i),
DSN: dsn,
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/verbs/verb.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Module, Verb } from '../../protos/xyz/block/ftl/v1/console/console_pb'
import { MetadataCalls, MetadataCronJob, MetadataIngress, Ref } from '../../protos/xyz/block/ftl/v1/schema/schema_pb'
import { JSONSchemaFaker } from 'json-schema-faker'

const basePath = 'http://localhost:8892/ingress/'
const basePath = 'http://localhost:8891/'

export const verbRefString = (verb: Ref): string => {
return `${verb.module}.${verb.name}`
Expand Down
2 changes: 1 addition & 1 deletion integration/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ func JsonData(t testing.TB, body interface{}) []byte {
func HttpCall(method string, path string, body []byte, onResponse func(t testing.TB, resp *HTTPResponse)) Action {
return func(t testing.TB, ic TestContext) {
Infof("HTTP %s %s", method, path)
baseURL, err := url.Parse(fmt.Sprintf("http://localhost:8892/ingress"))
baseURL, err := url.Parse(fmt.Sprintf("http://localhost:8891"))
assert.NoError(t, err)

r, err := http.NewRequestWithContext(ic, method, baseURL.JoinPath(path).String(), bytes.NewReader(body))
Expand Down
45 changes: 45 additions & 0 deletions internal/http/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package http

import (
"context"
"errors"
"net"
"net/http"
"net/url"
"time"

"github.com/TBD54566975/ftl/internal/log"
)

const ShutdownGracePeriod = 5 * time.Second

func Serve(ctx context.Context, listen *url.URL, handler http.Handler) error {
httpServer := &http.Server{
Addr: listen.Host,
Handler: handler,
ReadHeaderTimeout: 30 * time.Second,
BaseContext: func(_ net.Listener) context.Context {
return ctx
},
}

go func() {
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), ShutdownGracePeriod)
defer cancel()
err := httpServer.Shutdown(shutdownCtx)
if err != nil {
if errors.Is(err, context.Canceled) {
_ = httpServer.Close()
return
}
log.FromContext(ctx).Errorf(err, "server shutdown error")
}
}()

err := httpServer.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
}

0 comments on commit f445e18

Please sign in to comment.