Skip to content

Commit 6a7de83

Browse files
authored
feat: Allow serving all edge functions using edge runtime (#828)
2 parents 03f65df + 4aa793a commit 6a7de83

File tree

6 files changed

+119
-16
lines changed

6 files changed

+119
-16
lines changed

cmd/functions.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,23 @@ var (
9595
}
9696

9797
envFilePath string
98+
serveAll bool
9899

99100
functionsServeCmd = &cobra.Command{
100101
Use: "serve <Function name>",
101102
Short: "Serve a Function locally",
102-
Args: cobra.ExactArgs(1),
103+
Args: cobra.RangeArgs(0, 1),
103104
RunE: func(cmd *cobra.Command, args []string) error {
104105
ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
105106
// Fallback to config if user did not set the flag.
106107
if !cmd.Flags().Changed("no-verify-jwt") {
107108
noVerifyJWT = nil
108109
}
109-
return serve.Run(ctx, args[0], envFilePath, noVerifyJWT, importMapPath, afero.NewOsFs())
110+
slug := ""
111+
if len(args) > 1 {
112+
slug = args[0]
113+
}
114+
return serve.Run(ctx, slug, envFilePath, noVerifyJWT, importMapPath, serveAll, afero.NewOsFs())
110115
},
111116
}
112117
)
@@ -120,6 +125,7 @@ func init() {
120125
functionsServeCmd.Flags().BoolVar(noVerifyJWT, "no-verify-jwt", false, "Disable JWT verification for the Function.")
121126
functionsServeCmd.Flags().StringVar(&envFilePath, "env-file", "", "Path to an env file to be populated to the Function environment.")
122127
functionsServeCmd.Flags().StringVar(&importMapPath, "import-map", "", "Path to import map file.")
128+
functionsServeCmd.Flags().BoolVar(&serveAll, "all", false, "Serve all functions (caution: Experimental feature)")
123129
functionsDownloadCmd.Flags().StringVar(&projectRef, "project-ref", "", "Project ref of the Supabase project.")
124130
functionsCmd.AddCommand(functionsDeleteCmd)
125131
functionsCmd.AddCommand(functionsDeployCmd)

internal/db/diff/diff.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ func DiffSchema(ctx context.Context, source, target string, schema []string, p u
8282
utils.DifferImage,
8383
nil,
8484
args,
85+
nil,
8586
stream.Stdout(),
8687
stream.Stderr(),
8788
); err != nil {
@@ -95,6 +96,7 @@ func DiffSchema(ctx context.Context, source, target string, schema []string, p u
9596
utils.DifferImage,
9697
nil,
9798
append([]string{"--schema", s}, args...),
99+
nil,
98100
stream.Stdout(),
99101
stream.Stderr(),
100102
); err != nil {

internal/functions/serve/serve.go

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ func ParseEnvFile(envFilePath string) ([]string, error) {
4040
return env, nil
4141
}
4242

43-
func Run(ctx context.Context, slug string, envFilePath string, noVerifyJWT *bool, importMapPath string, fsys afero.Fs) error {
43+
func Run(ctx context.Context, slug string, envFilePath string, noVerifyJWT *bool, importMapPath string, serveAll bool, fsys afero.Fs) error {
44+
if serveAll {
45+
return runServeAll(ctx, envFilePath, noVerifyJWT, importMapPath, fsys)
46+
}
47+
4448
// 1. Sanity checks.
4549
{
4650
if err := utils.LoadConfigFS(fsys); err != nil {
@@ -216,3 +220,89 @@ func Run(ctx context.Context, slug string, envFilePath string, noVerifyJWT *bool
216220
fmt.Println("Stopped serving " + utils.Bold(localFuncDir))
217221
return nil
218222
}
223+
224+
func runServeAll(ctx context.Context, envFilePath string, noVerifyJWT *bool, importMapPath string, fsys afero.Fs) error {
225+
// 1. Sanity checks.
226+
{
227+
if err := utils.LoadConfigFS(fsys); err != nil {
228+
return err
229+
}
230+
if err := utils.AssertSupabaseDbIsRunning(); err != nil {
231+
return err
232+
}
233+
if envFilePath != "" {
234+
if _, err := fsys.Stat(envFilePath); err != nil {
235+
return fmt.Errorf("Failed to read env file: %w", err)
236+
}
237+
}
238+
if importMapPath != "" {
239+
// skip
240+
} else if f, err := fsys.Stat(utils.FallbackImportMapPath); err == nil && !f.IsDir() {
241+
importMapPath = utils.FallbackImportMapPath
242+
}
243+
if importMapPath != "" {
244+
if _, err := fsys.Stat(importMapPath); err != nil {
245+
return fmt.Errorf("Failed to read import map: %w", err)
246+
}
247+
}
248+
}
249+
250+
// 2. Parse user defined env
251+
userEnv, err := ParseEnvFile(envFilePath)
252+
if err != nil {
253+
return err
254+
}
255+
256+
// 3. Start container
257+
{
258+
_ = utils.Docker.ContainerRemove(ctx, utils.DenoRelayId, types.ContainerRemoveOptions{
259+
RemoveVolumes: true,
260+
Force: true,
261+
})
262+
263+
env := []string{
264+
"JWT_SECRET=" + utils.JWTSecret,
265+
"SUPABASE_URL=http://" + utils.KongId + ":8000",
266+
"SUPABASE_ANON_KEY=" + utils.AnonKey,
267+
"SUPABASE_SERVICE_ROLE_KEY=" + utils.ServiceRoleKey,
268+
"SUPABASE_DB_URL=postgresql://postgres:postgres@localhost:" + strconv.FormatUint(uint64(utils.Config.Db.Port), 10) + "/postgres",
269+
}
270+
verifyJWTEnv := "VERIFY_JWT=true"
271+
if noVerifyJWT != nil {
272+
verifyJWTEnv = "VERIFY_JWT=false"
273+
}
274+
env = append(env, verifyJWTEnv)
275+
276+
cwd, err := os.Getwd()
277+
if err != nil {
278+
return err
279+
}
280+
281+
binds := []string{
282+
filepath.Join(cwd, utils.FunctionsDir) + ":" + relayFuncDir + ":ro,z",
283+
utils.DenoRelayId + ":/root/.cache/deno:rw,z",
284+
}
285+
// If a import map path is explcitly provided, mount it as a separate file
286+
if importMapPath != "" {
287+
binds = append(binds, filepath.Join(cwd, importMapPath)+":"+customDockerImportMapPath+":ro,z")
288+
}
289+
290+
fmt.Println("Serving " + utils.Bold(utils.FunctionsDir))
291+
292+
if err := utils.DockerRunOnceWithStream(
293+
ctx,
294+
utils.EdgeRuntimeImage,
295+
append(env, userEnv...),
296+
[]string{"start", "--dir", relayFuncDir, "-p", "8081"},
297+
binds,
298+
os.Stdout,
299+
os.Stderr,
300+
); err != nil {
301+
return err
302+
}
303+
}
304+
305+
fmt.Println("Stopped serving " + utils.Bold(utils.FunctionsDir))
306+
return nil
307+
308+
}

internal/functions/serve/serve_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func TestServeCommand(t *testing.T) {
3535
Post("/v" + utils.Docker.ClientVersion() + "/containers").
3636
Reply(http.StatusServiceUnavailable)
3737
// Run test
38-
err := Run(context.Background(), "test-func", "", nil, "", fsys)
38+
err := Run(context.Background(), "test-func", "", nil, "", false, fsys)
3939
// Check error
4040
assert.ErrorContains(t, err, "request returned Service Unavailable for API route and version http://localhost/v1.41/containers/supabase_deno_relay_serve/exec")
4141
assert.Empty(t, apitest.ListUnmatchedRequests())

internal/utils/docker.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,19 +302,23 @@ func DockerRunOnce(ctx context.Context, image string, env []string, cmd []string
302302
stderr = os.Stderr
303303
}
304304
var out bytes.Buffer
305-
err := DockerRunOnceWithStream(ctx, image, env, cmd, &out, stderr)
305+
err := DockerRunOnceWithStream(ctx, image, env, cmd, nil, &out, stderr)
306306
return out.String(), err
307307
}
308308

309-
func DockerRunOnceWithStream(ctx context.Context, image string, env []string, cmd []string, stdout, stderr io.Writer) error {
309+
func DockerRunOnceWithStream(ctx context.Context, image string, env, cmd, binds []string, stdout, stderr io.Writer) error {
310310
// Cannot rely on docker's auto remove because
311311
// 1. We must inspect exit code after container stops
312312
// 2. Context cancellation may happen after start
313313
container, err := DockerStart(ctx, container.Config{
314314
Image: image,
315315
Env: env,
316316
Cmd: cmd,
317-
}, container.HostConfig{}, "")
317+
}, container.HostConfig{
318+
Binds: binds,
319+
// Allows containerized functions on Linux to reach host OS
320+
ExtraHosts: []string{"host.docker.internal:host-gateway"},
321+
}, "")
318322
if err != nil {
319323
return err
320324
}

internal/utils/misc.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,16 @@ const (
2828
Pg14Image = "supabase/postgres:14.1.0.89"
2929
Pg15Image = "supabase/postgres:15.1.0.33"
3030
// Append to ServiceImages when adding new dependencies below
31-
KongImage = "library/kong:2.8.1"
32-
InbucketImage = "inbucket/inbucket:3.0.3"
33-
PostgrestImage = "postgrest/postgrest:v10.1.1.20221215"
34-
DifferImage = "supabase/pgadmin-schema-diff:cli-0.0.5"
35-
MigraImage = "djrobstep/migra:3.0.1621480950"
36-
PgmetaImage = "supabase/postgres-meta:v0.60.3"
37-
StudioImage = "supabase/studio:20230127-6bfd87b"
38-
DenoRelayImage = "supabase/deno-relay:v1.5.0"
39-
ImageProxyImage = "darthsim/imgproxy:v3.8.0"
31+
KongImage = "library/kong:2.8.1"
32+
InbucketImage = "inbucket/inbucket:3.0.3"
33+
PostgrestImage = "postgrest/postgrest:v10.1.1.20221215"
34+
DifferImage = "supabase/pgadmin-schema-diff:cli-0.0.5"
35+
MigraImage = "djrobstep/migra:3.0.1621480950"
36+
PgmetaImage = "supabase/postgres-meta:v0.60.3"
37+
StudioImage = "supabase/studio:20230127-6bfd87b"
38+
DenoRelayImage = "supabase/deno-relay:v1.5.0"
39+
ImageProxyImage = "darthsim/imgproxy:v3.8.0"
40+
EdgeRuntimeImage = "supabase/edge-runtime:v1.0.7"
4041
// Update initial schemas in internal/utils/templates/initial_schemas when
4142
// updating any one of these.
4243
GotrueImage = "supabase/gotrue:v2.40.1"

0 commit comments

Comments
 (0)