Skip to content

Commit

Permalink
feat: configurable Access-Control-Allow-Origin (#406)
Browse files Browse the repository at this point in the history
  • Loading branch information
alecthomas authored Sep 19, 2023
1 parent 7c8d654 commit a742a08
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 20 deletions.
1 change: 1 addition & 0 deletions Dockerfile.controller
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ EXPOSE 8892

ENV FTL_CONTROLLER_BIND="http://0.0.0.0:8899"
ENV FTL_CONTROLLER_ADVERTISE="http://127.0.0.1:8899"
ENV FTL_CONTROLLER_ALLOW_ORIGIN="*"
ENV FTL_CONTROLLER_DSN="postgres://host.docker.internal/ftl?sslmode=disable&user=postgres&password=secret"

CMD ["/root/ftl-controller"]
17 changes: 12 additions & 5 deletions backend/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,29 @@ import (

type Config struct {
Bind *url.URL `help:"Socket to bind to." default:"http://localhost:8892" env:"FTL_CONTROLLER_BIND"`
Advertise *url.URL `help:"Endpoint the Controller should advertise (use --bind if omitted)." default:"" env:"FTL_CONTROLLER_ADVERTISE"`
Advertise *url.URL `help:"Endpoint the Controller should advertise (must be unique across the cluster, defaults to --bind if omitted)." env:"FTL_CONTROLLER_ADVERTISE"`
AllowOrigin string `help:"Allow CORS requests from this origin." default:"*" env:"FTL_CONTROLLER_ALLOW_ORIGIN"`
Key model.ControllerKey `help:"Controller key (auto)." placeholder:"C<ULID>" default:"C00000000000000000000000000"`
DSN string `help:"DAL DSN." default:"postgres://localhost/ftl?sslmode=disable&user=postgres&password=secret" env:"FTL_CONTROLLER_DSN"`
RunnerTimeout time.Duration `help:"Runner heartbeat timeout." default:"10s"`
DeploymentReservationTimeout time.Duration `help:"Deployment reservation timeout." default:"120s"`
ArtefactChunkSize int `help:"Size of each chunk streamed to the client." default:"1048576"`
}

func (c *Config) SetDefaults() {
if c.Advertise == nil {
c.Advertise = c.Bind
}
}

// Start the Controller. Blocks until the context is cancelled.
func Start(ctx context.Context, config Config) error {
config.SetDefaults()

logger := log.FromContext(ctx)
logger.Infof("Starting FTL controller")

c, err := console.Server(ctx)
c, err := console.Server(ctx, config.AllowOrigin)
if err != nil {
return errors.WithStack(err)
}
Expand Down Expand Up @@ -116,6 +125,7 @@ func New(ctx context.Context, db *dal.DAL, config Config) (*Service, error) {
if config.Key.ULID() == (ulid.ULID{}) {
key = model.NewControllerKey()
}
config.SetDefaults()
svc := &Service{
dal: db,
key: key,
Expand All @@ -124,9 +134,6 @@ func New(ctx context.Context, db *dal.DAL, config Config) (*Service, error) {
routes: map[string][]dal.Route{},
config: config,
}
if config.Advertise.String() == "" {
config.Advertise = config.Bind
}

go runWithRetries(ctx, time.Second*1, time.Second*2, svc.syncRoutes)
go runWithRetries(ctx, time.Second*3, time.Second*5, svc.heartbeatController)
Expand Down
6 changes: 3 additions & 3 deletions cmd/ftl-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import (
"github.com/alecthomas/kong"

_ "github.com/TBD54566975/ftl/backend/common/automaxprocs" // Set GOMAXPROCS to match Linux container CPU quota.
log2 "github.com/TBD54566975/ftl/backend/common/log"
log "github.com/TBD54566975/ftl/backend/common/log"
"github.com/TBD54566975/ftl/backend/controller"
)

var version = "dev"

var cli struct {
Version kong.VersionFlag `help:"Show version."`
LogConfig log2.Config `embed:"" prefix:"log-"`
LogConfig log.Config `embed:"" prefix:"log-"`
ControllerConfig controller.Config `embed:""`
}

Expand All @@ -25,7 +25,7 @@ func main() {
kong.UsageOnError(),
kong.Vars{"version": version},
)
ctx := log2.ContextWithLogger(context.Background(), log2.Configure(os.Stderr, cli.LogConfig))
ctx := log.ContextWithLogger(context.Background(), log.Configure(os.Stderr, cli.LogConfig))
err := controller.Start(ctx, cli.ControllerConfig)
kctx.FatalIfErrorf(err)
}
11 changes: 11 additions & 0 deletions console/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package console

import (
"net/http"
)

func writeCORSHeaders(w http.ResponseWriter, allowOrigin string) {
w.Header().Set("Access-Control-Allow-Origin", allowOrigin)
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
21 changes: 10 additions & 11 deletions console/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
var consoleURL, _ = url.Parse("http://localhost:5173")
var proxy = httputil.NewSingleHostReverseProxy(consoleURL)

func Server(ctx context.Context) (http.Handler, error) {
func Server(ctx context.Context, allowOrigin string) (http.Handler, error) {
logger := log.FromContext(ctx)
logger.Infof("Building console...")

Expand All @@ -32,17 +32,16 @@ func Server(ctx context.Context) (http.Handler, error) {
}
logger.Infof("Console started")

return http.HandlerFunc(handler), nil
return http.HandlerFunc(handler(allowOrigin)), nil
}

func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")

if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
func handler(allowOrigin string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
writeCORSHeaders(w, allowOrigin)
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
proxy.ServeHTTP(w, r)
}
proxy.ServeHTTP(w, r)
}
3 changes: 2 additions & 1 deletion console/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import (
//go:embed all:client/dist
var build embed.FS

func Server(ctx context.Context) (http.Handler, error) {
func Server(ctx context.Context, allowOrigin string) (http.Handler, error) {
dir, err := fs.Sub(build, "client/dist")
if err != nil {
return nil, errors.WithStack(err)
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
writeCORSHeaders(w, allowOrigin)
var f fs.File
var err error
filePath := strings.TrimPrefix(r.URL.Path, "/")
Expand Down

0 comments on commit a742a08

Please sign in to comment.