diff --git a/cmd/functions.go b/cmd/functions.go index f7294267f..772392d55 100644 --- a/cmd/functions.go +++ b/cmd/functions.go @@ -95,18 +95,23 @@ var ( } envFilePath string + serveAll bool functionsServeCmd = &cobra.Command{ Use: "serve ", Short: "Serve a Function locally", - Args: cobra.ExactArgs(1), + Args: cobra.RangeArgs(0, 1), RunE: func(cmd *cobra.Command, args []string) error { ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt) // Fallback to config if user did not set the flag. if !cmd.Flags().Changed("no-verify-jwt") { noVerifyJWT = nil } - return serve.Run(ctx, args[0], envFilePath, noVerifyJWT, importMapPath, afero.NewOsFs()) + slug := "" + if len(args) > 1 { + slug = args[0] + } + return serve.Run(ctx, slug, envFilePath, noVerifyJWT, importMapPath, serveAll, afero.NewOsFs()) }, } ) @@ -120,6 +125,7 @@ func init() { functionsServeCmd.Flags().BoolVar(noVerifyJWT, "no-verify-jwt", false, "Disable JWT verification for the Function.") functionsServeCmd.Flags().StringVar(&envFilePath, "env-file", "", "Path to an env file to be populated to the Function environment.") functionsServeCmd.Flags().StringVar(&importMapPath, "import-map", "", "Path to import map file.") + functionsServeCmd.Flags().BoolVar(&serveAll, "all", false, "Serve all functions (caution: Experimental feature)") functionsDownloadCmd.Flags().StringVar(&projectRef, "project-ref", "", "Project ref of the Supabase project.") functionsCmd.AddCommand(functionsDeleteCmd) functionsCmd.AddCommand(functionsDeployCmd) diff --git a/internal/db/diff/diff.go b/internal/db/diff/diff.go index 655f80337..7c1691c04 100644 --- a/internal/db/diff/diff.go +++ b/internal/db/diff/diff.go @@ -82,6 +82,7 @@ func DiffSchema(ctx context.Context, source, target string, schema []string, p u utils.DifferImage, nil, args, + nil, stream.Stdout(), stream.Stderr(), ); err != nil { @@ -95,6 +96,7 @@ func DiffSchema(ctx context.Context, source, target string, schema []string, p u utils.DifferImage, nil, append([]string{"--schema", s}, args...), + nil, stream.Stdout(), stream.Stderr(), ); err != nil { diff --git a/internal/functions/serve/serve.go b/internal/functions/serve/serve.go index 687952206..88f30e394 100644 --- a/internal/functions/serve/serve.go +++ b/internal/functions/serve/serve.go @@ -40,7 +40,11 @@ func ParseEnvFile(envFilePath string) ([]string, error) { return env, nil } -func Run(ctx context.Context, slug string, envFilePath string, noVerifyJWT *bool, importMapPath string, fsys afero.Fs) error { +func Run(ctx context.Context, slug string, envFilePath string, noVerifyJWT *bool, importMapPath string, serveAll bool, fsys afero.Fs) error { + if serveAll { + return runServeAll(ctx, envFilePath, noVerifyJWT, importMapPath, fsys) + } + // 1. Sanity checks. { if err := utils.LoadConfigFS(fsys); err != nil { @@ -216,3 +220,89 @@ func Run(ctx context.Context, slug string, envFilePath string, noVerifyJWT *bool fmt.Println("Stopped serving " + utils.Bold(localFuncDir)) return nil } + +func runServeAll(ctx context.Context, envFilePath string, noVerifyJWT *bool, importMapPath string, fsys afero.Fs) error { + // 1. Sanity checks. + { + if err := utils.LoadConfigFS(fsys); err != nil { + return err + } + if err := utils.AssertSupabaseDbIsRunning(); err != nil { + return err + } + if envFilePath != "" { + if _, err := fsys.Stat(envFilePath); err != nil { + return fmt.Errorf("Failed to read env file: %w", err) + } + } + if importMapPath != "" { + // skip + } else if f, err := fsys.Stat(utils.FallbackImportMapPath); err == nil && !f.IsDir() { + importMapPath = utils.FallbackImportMapPath + } + if importMapPath != "" { + if _, err := fsys.Stat(importMapPath); err != nil { + return fmt.Errorf("Failed to read import map: %w", err) + } + } + } + + // 2. Parse user defined env + userEnv, err := ParseEnvFile(envFilePath) + if err != nil { + return err + } + + // 3. Start container + { + _ = utils.Docker.ContainerRemove(ctx, utils.DenoRelayId, types.ContainerRemoveOptions{ + RemoveVolumes: true, + Force: true, + }) + + env := []string{ + "JWT_SECRET=" + utils.JWTSecret, + "SUPABASE_URL=http://" + utils.KongId + ":8000", + "SUPABASE_ANON_KEY=" + utils.AnonKey, + "SUPABASE_SERVICE_ROLE_KEY=" + utils.ServiceRoleKey, + "SUPABASE_DB_URL=postgresql://postgres:postgres@localhost:" + strconv.FormatUint(uint64(utils.Config.Db.Port), 10) + "/postgres", + } + verifyJWTEnv := "VERIFY_JWT=true" + if noVerifyJWT != nil { + verifyJWTEnv = "VERIFY_JWT=false" + } + env = append(env, verifyJWTEnv) + + cwd, err := os.Getwd() + if err != nil { + return err + } + + binds := []string{ + filepath.Join(cwd, utils.FunctionsDir) + ":" + relayFuncDir + ":ro,z", + utils.DenoRelayId + ":/root/.cache/deno:rw,z", + } + // If a import map path is explcitly provided, mount it as a separate file + if importMapPath != "" { + binds = append(binds, filepath.Join(cwd, importMapPath)+":"+customDockerImportMapPath+":ro,z") + } + + fmt.Println("Serving " + utils.Bold(utils.FunctionsDir)) + + if err := utils.DockerRunOnceWithStream( + ctx, + utils.EdgeRuntimeImage, + append(env, userEnv...), + []string{"start", "--dir", relayFuncDir, "-p", "8081"}, + binds, + os.Stdout, + os.Stderr, + ); err != nil { + return err + } + } + + fmt.Println("Stopped serving " + utils.Bold(utils.FunctionsDir)) + return nil + +} diff --git a/internal/functions/serve/serve_test.go b/internal/functions/serve/serve_test.go index 50cba2fbc..845bbea02 100644 --- a/internal/functions/serve/serve_test.go +++ b/internal/functions/serve/serve_test.go @@ -35,7 +35,7 @@ func TestServeCommand(t *testing.T) { Post("/v" + utils.Docker.ClientVersion() + "/containers"). Reply(http.StatusServiceUnavailable) // Run test - err := Run(context.Background(), "test-func", "", nil, "", fsys) + err := Run(context.Background(), "test-func", "", nil, "", false, fsys) // Check error assert.ErrorContains(t, err, "request returned Service Unavailable for API route and version http://localhost/v1.41/containers/supabase_deno_relay_serve/exec") assert.Empty(t, apitest.ListUnmatchedRequests()) diff --git a/internal/utils/docker.go b/internal/utils/docker.go index 3a35151ae..428d3adbf 100644 --- a/internal/utils/docker.go +++ b/internal/utils/docker.go @@ -302,11 +302,11 @@ func DockerRunOnce(ctx context.Context, image string, env []string, cmd []string stderr = os.Stderr } var out bytes.Buffer - err := DockerRunOnceWithStream(ctx, image, env, cmd, &out, stderr) + err := DockerRunOnceWithStream(ctx, image, env, cmd, nil, &out, stderr) return out.String(), err } -func DockerRunOnceWithStream(ctx context.Context, image string, env []string, cmd []string, stdout, stderr io.Writer) error { +func DockerRunOnceWithStream(ctx context.Context, image string, env, cmd, binds []string, stdout, stderr io.Writer) error { // Cannot rely on docker's auto remove because // 1. We must inspect exit code after container stops // 2. Context cancellation may happen after start @@ -314,7 +314,11 @@ func DockerRunOnceWithStream(ctx context.Context, image string, env []string, cm Image: image, Env: env, Cmd: cmd, - }, container.HostConfig{}, "") + }, container.HostConfig{ + Binds: binds, + // Allows containerized functions on Linux to reach host OS + ExtraHosts: []string{"host.docker.internal:host-gateway"}, + }, "") if err != nil { return err } diff --git a/internal/utils/misc.go b/internal/utils/misc.go index 4d6941f7b..9bd756123 100644 --- a/internal/utils/misc.go +++ b/internal/utils/misc.go @@ -28,15 +28,16 @@ const ( Pg14Image = "supabase/postgres:14.1.0.89" Pg15Image = "supabase/postgres:15.1.0.33" // Append to ServiceImages when adding new dependencies below - KongImage = "library/kong:2.8.1" - InbucketImage = "inbucket/inbucket:3.0.3" - PostgrestImage = "postgrest/postgrest:v10.1.1.20221215" - DifferImage = "supabase/pgadmin-schema-diff:cli-0.0.5" - MigraImage = "djrobstep/migra:3.0.1621480950" - PgmetaImage = "supabase/postgres-meta:v0.60.3" - StudioImage = "supabase/studio:20230127-6bfd87b" - DenoRelayImage = "supabase/deno-relay:v1.5.0" - ImageProxyImage = "darthsim/imgproxy:v3.8.0" + KongImage = "library/kong:2.8.1" + InbucketImage = "inbucket/inbucket:3.0.3" + PostgrestImage = "postgrest/postgrest:v10.1.1.20221215" + DifferImage = "supabase/pgadmin-schema-diff:cli-0.0.5" + MigraImage = "djrobstep/migra:3.0.1621480950" + PgmetaImage = "supabase/postgres-meta:v0.60.3" + StudioImage = "supabase/studio:20230127-6bfd87b" + DenoRelayImage = "supabase/deno-relay:v1.5.0" + ImageProxyImage = "darthsim/imgproxy:v3.8.0" + EdgeRuntimeImage = "supabase/edge-runtime:v1.0.7" // Update initial schemas in internal/utils/templates/initial_schemas when // updating any one of these. GotrueImage = "supabase/gotrue:v2.40.1"