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
20 changes: 6 additions & 14 deletions internal/bans/get/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,15 @@ import (
"context"
"fmt"

"github.com/go-errors/errors"
"github.com/spf13/afero"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/internal/utils/flags"
)

func Run(ctx context.Context, projectRef string, fsys afero.Fs) error {
// 1. Sanity checks.
// 2. get network bans
{
resp, err := utils.GetSupabase().V1ListAllNetworkBansWithResponse(ctx, projectRef)
if err != nil {
return errors.Errorf("failed to retrieve network bans: %w", err)
}
if resp.JSON201 == nil {
return errors.New("Unexpected error retrieving network bans: " + string(resp.Body))
}
fmt.Printf("DB banned IPs: %+v\n", resp.JSON201.BannedIpv4Addresses)
return nil
ips, err := flags.ListNetworkBans(ctx, projectRef)
if err != nil {
return err
}
fmt.Printf("DB banned IPs: %+v\n", ips)
return nil
}
5 changes: 4 additions & 1 deletion internal/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ func Run(ctx context.Context, starter StarterTemplate, fsys afero.Fs, options ..
return err
}
// 6. Push migrations
config := flags.NewDbConfigWithPassword(ctx, flags.ProjectRef)
config, err := flags.NewDbConfigWithPassword(ctx, flags.ProjectRef)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
if err := writeDotEnv(keys, config, fsys); err != nil {
fmt.Fprintln(os.Stderr, "Failed to create .env file:", err)
}
Expand Down
18 changes: 1 addition & 17 deletions internal/branches/get/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func Run(ctx context.Context, branchId string, fsys afero.Fs) error {
if err != nil {
return err
}
pooler, err := getPoolerConfig(ctx, detail.Ref)
pooler, err := utils.GetPoolerConfigPrimary(ctx, detail.Ref)
if err != nil {
return err
}
Expand Down Expand Up @@ -81,22 +81,6 @@ func getBranchDetail(ctx context.Context, branchId string) (api.BranchDetailResp
return *resp.JSON200, nil
}

func getPoolerConfig(ctx context.Context, ref string) (api.SupavisorConfigResponse, error) {
var result api.SupavisorConfigResponse
resp, err := utils.GetSupabase().V1GetPoolerConfigWithResponse(ctx, ref)
if err != nil {
return result, errors.Errorf("failed to get pooler: %w", err)
} else if resp.JSON200 == nil {
return result, errors.Errorf("unexpected get pooler status %d: %s", resp.StatusCode(), string(resp.Body))
}
for _, config := range *resp.JSON200 {
if config.DatabaseType == api.SupavisorConfigResponseDatabaseTypePRIMARY {
return config, nil
}
}
return result, errors.Errorf("primary database not found: %s", ref)
}

func toStandardEnvs(detail api.BranchDetailResponse, pooler api.SupavisorConfigResponse, keys []api.ApiKeyResponse) map[string]string {
direct := pgconn.Config{
Host: detail.DbHost,
Expand Down
28 changes: 7 additions & 21 deletions internal/link/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/supabase/cli/pkg/api"
"github.com/supabase/cli/pkg/cast"
cliConfig "github.com/supabase/cli/pkg/config"
"github.com/supabase/cli/pkg/migration"
"github.com/supabase/cli/pkg/queue"
)

Expand All @@ -36,8 +35,9 @@ func Run(ctx context.Context, projectRef string, skipPooler bool, fsys afero.Fs,
LinkServices(ctx, projectRef, keys.ServiceRole, skipPooler, fsys)

// 2. Check database connection
config := flags.NewDbConfigWithPassword(ctx, projectRef)
if err := linkDatabase(ctx, config, fsys, options...); err != nil {
if config, err := flags.NewDbConfigWithPassword(ctx, projectRef); err != nil {
fmt.Fprintln(os.Stderr, utils.Yellow("WARN:"), err)
} else if err := linkDatabase(ctx, config, fsys, options...); err != nil {
return err
}

Expand Down Expand Up @@ -186,14 +186,7 @@ func linkDatabase(ctx context.Context, config pgconn.Config, fsys afero.Fs, opti
}
defer conn.Close(context.Background())
updatePostgresConfig(conn)
if err := linkStorageMigration(ctx, conn, fsys); err != nil {
fmt.Fprintln(os.Stderr, err)
}
// If `schema_migrations` doesn't exist on the remote database, create it.
if err := migration.CreateMigrationTable(ctx, conn); err != nil {
return err
}
return migration.CreateSeedTable(ctx, conn)
return linkStorageMigration(ctx, conn, fsys)
}

func updatePostgresConfig(conn *pgx.Conn) {
Expand All @@ -207,18 +200,11 @@ func updatePostgresConfig(conn *pgx.Conn) {
}

func linkPooler(ctx context.Context, projectRef string, fsys afero.Fs) error {
resp, err := utils.GetSupabase().V1GetPoolerConfigWithResponse(ctx, projectRef)
primary, err := utils.GetPoolerConfigPrimary(ctx, projectRef)
if err != nil {
return errors.Errorf("failed to get pooler config: %w", err)
}
if resp.JSON200 == nil {
return errors.Errorf("%w: %s", tenant.ErrAuthToken, string(resp.Body))
}
for _, config := range *resp.JSON200 {
if config.DatabaseType == api.SupavisorConfigResponseDatabaseTypePRIMARY {
updatePoolerConfig(config)
}
return err
}
updatePoolerConfig(primary)
return utils.WriteFile(utils.PoolerUrlPath, []byte(utils.Config.Db.Pooler.ConnectionString), fsys)
}

Expand Down
58 changes: 22 additions & 36 deletions internal/link/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/supabase/cli/internal/testing/apitest"
"github.com/supabase/cli/internal/testing/fstest"
"github.com/supabase/cli/internal/testing/helper"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/internal/utils/tenant"
"github.com/supabase/cli/pkg/api"
"github.com/supabase/cli/pkg/migration"
"github.com/supabase/cli/pkg/pgtest"
"github.com/zalando/go-keyring"
)
Expand All @@ -44,15 +42,6 @@ func TestLinkCommand(t *testing.T) {
t.Cleanup(fstest.MockStdin(t, "\n"))
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(utils.SET_SESSION_ROLE).
Reply("SET ROLE").
Query(GET_LATEST_STORAGE_MIGRATION).
Reply("SELECT 1", []any{"custom-metadata"})
helper.MockMigrationHistory(conn)
helper.MockSeedHistory(conn)
// Flush pending mocks after test execution
defer gock.OffAll()
// Mock project status
Expand Down Expand Up @@ -119,7 +108,7 @@ func TestLinkCommand(t *testing.T) {
Reply(200).
BodyString(storage)
// Run test
err := Run(context.Background(), project, false, fsys, conn.Intercept)
err := Run(context.Background(), project, false, fsys)
// Check error
assert.NoError(t, err)
assert.Empty(t, apitest.ListUnmatchedRequests())
Expand Down Expand Up @@ -198,22 +187,13 @@ func TestLinkCommand(t *testing.T) {
}
})
// Check error
assert.ErrorContains(t, err, "hostname resolving error")
assert.NoError(t, err)
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("throws error on write failure", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewReadOnlyFs(afero.NewMemMapFs())
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(utils.SET_SESSION_ROLE).
Reply("SET ROLE").
Query(GET_LATEST_STORAGE_MIGRATION).
Reply("SELECT 1", []any{"custom-metadata"})
helper.MockMigrationHistory(conn)
helper.MockSeedHistory(conn)
// Flush pending mocks after test execution
defer gock.OffAll()
// Mock project status
Expand Down Expand Up @@ -269,7 +249,7 @@ func TestLinkCommand(t *testing.T) {
Get("/v1/projects").
ReplyError(errors.New("network error"))
// Run test
err := Run(context.Background(), project, false, fsys, conn.Intercept)
err := Run(context.Background(), project, false, fsys)
// Check error
assert.ErrorContains(t, err, "operation not permitted")
assert.Empty(t, apitest.ListUnmatchedRequests())
Expand Down Expand Up @@ -419,6 +399,23 @@ func TestLinkPostgrest(t *testing.T) {
}

func TestLinkDatabase(t *testing.T) {
t.Run("syncs storage migration", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(GET_LATEST_STORAGE_MIGRATION).
Reply("SELECT 1", []any{"custom-metadata"})
// Run test
err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept)
// Check error
assert.NoError(t, err)
storage, err := afero.ReadFile(fsys, utils.StorageMigrationPath)
assert.NoError(t, err)
assert.Equal(t, []byte("custom-metadata"), storage)
})

t.Run("throws error on connect failure", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
Expand All @@ -438,8 +435,6 @@ func TestLinkDatabase(t *testing.T) {
defer conn.Close(t)
conn.Query(GET_LATEST_STORAGE_MIGRATION).
Reply("SELECT 1", []any{"custom-metadata"})
helper.MockMigrationHistory(conn)
helper.MockSeedHistory(conn)
// Run test
err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept)
// Check error
Expand All @@ -461,8 +456,6 @@ func TestLinkDatabase(t *testing.T) {
defer conn.Close(t)
conn.Query(GET_LATEST_STORAGE_MIGRATION).
Reply("SELECT 1", []any{"custom-metadata"})
helper.MockMigrationHistory(conn)
helper.MockSeedHistory(conn)
// Run test
err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept)
// Check error
Expand All @@ -481,18 +474,11 @@ func TestLinkDatabase(t *testing.T) {
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(GET_LATEST_STORAGE_MIGRATION).
ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation migrations").
Query(migration.SET_LOCK_TIMEOUT).
Query(migration.CREATE_VERSION_SCHEMA).
Reply("CREATE SCHEMA").
Query(migration.CREATE_VERSION_TABLE).
ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation supabase_migrations").
Query(migration.ADD_STATEMENTS_COLUMN).
Query(migration.ADD_NAME_COLUMN)
ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation migrations")
// Run test
err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept)
// Check error
assert.ErrorContains(t, err, "ERROR: permission denied for relation supabase_migrations (SQLSTATE 42501)")
assert.ErrorContains(t, err, "ERROR: permission denied for relation migrations (SQLSTATE 42501)")
exists, err := afero.Exists(fsys, utils.StorageMigrationPath)
assert.NoError(t, err)
assert.False(t, exists)
Expand Down
4 changes: 0 additions & 4 deletions internal/projects/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/spf13/afero"
"github.com/spf13/viper"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/internal/utils/credentials"
"github.com/supabase/cli/internal/utils/flags"
"github.com/supabase/cli/pkg/api"
)
Expand All @@ -30,9 +29,6 @@ func Run(ctx context.Context, params api.V1CreateProjectBody, fsys afero.Fs) err

flags.ProjectRef = resp.JSON201.Id
viper.Set("DB_PASSWORD", params.DbPass)
if err := credentials.StoreProvider.Set(flags.ProjectRef, params.DbPass); err != nil {
fmt.Fprintln(os.Stderr, "Failed to save database password:", err)
}

projectUrl := fmt.Sprintf("%s/project/%s", utils.GetSupabaseDashboardURL(), resp.JSON201.Id)
fmt.Fprintf(os.Stderr, "Created a new project at %s\n", utils.Bold(projectUrl))
Expand Down
17 changes: 17 additions & 0 deletions internal/utils/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/jackc/pgx/v4"
"github.com/spf13/viper"
"github.com/supabase/cli/internal/debug"
"github.com/supabase/cli/pkg/api"
"github.com/supabase/cli/pkg/pgxv5"
"golang.org/x/net/publicsuffix"
)
Expand Down Expand Up @@ -43,6 +44,22 @@ func ToPostgresURL(config pgconn.Config) string {
)
}

func GetPoolerConfigPrimary(ctx context.Context, ref string) (api.SupavisorConfigResponse, error) {
var result api.SupavisorConfigResponse
resp, err := GetSupabase().V1GetPoolerConfigWithResponse(ctx, ref)
if err != nil {
return result, errors.Errorf("failed to get pooler: %w", err)
} else if resp.JSON200 == nil {
return result, errors.Errorf("unexpected get pooler status %d: %s", resp.StatusCode(), string(resp.Body))
}
for _, config := range *resp.JSON200 {
if config.DatabaseType == api.SupavisorConfigResponseDatabaseTypePRIMARY {
return config, nil
}
}
return result, errors.Errorf("primary database not found: %s", ref)
}

func GetPoolerConfig(projectRef string) *pgconn.Config {
logger := GetDebugLogger()
if len(Config.Db.Pooler.ConnectionString) == 0 {
Expand Down
Loading