Skip to content
Open
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
40 changes: 26 additions & 14 deletions internal/utils/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,23 +167,35 @@ func ConnectByUrl(ctx context.Context, url string, options ...func(*pgx.ConnConf
cc.Fallbacks = fallbacks
})
conn, err := pgxv5.Connect(ctx, url, options...)
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
if strings.Contains(pgErr.Message, "connect: connection refused") {
CmdSuggestion = fmt.Sprintf("Make sure your local IP is allowed in Network Restrictions and Network Bans.\n%s/project/_/database/settings", CurrentProfile.DashboardURL)
} else if strings.Contains(pgErr.Message, "SSL connection is required") && viper.GetBool("DEBUG") {
CmdSuggestion = "SSL connection is not supported with --debug flag"
} else if strings.Contains(pgErr.Message, "SCRAM exchange: Wrong password") || strings.Contains(pgErr.Message, "failed SASL auth") {
// password authentication failed for user / invalid SCRAM server-final-message received from server
CmdSuggestion = "Try setting the SUPABASE_DB_PASSWORD environment variable"
} else if strings.Contains(pgErr.Message, "connect: no route to host") || strings.Contains(pgErr.Message, "Tenant or user not found") {
// Assumes IPv6 check has been performed before this
CmdSuggestion = "Make sure your project exists on profile: " + CurrentProfile.Name
}
}
SetConnectSuggestion(err)
return conn, err
}

const SuggestEnvVar = "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD"

// Sets CmdSuggestion to an actionable hint based on the given pg connection error.
func SetConnectSuggestion(err error) {
if err == nil {
return
}
msg := err.Error()
if strings.Contains(msg, "connect: connection refused") ||
strings.Contains(msg, "Address not in tenant allow_list") {
CmdSuggestion = fmt.Sprintf(
"Make sure your local IP is allowed in Network Restrictions and Network Bans.\n%s/project/_/database/settings",
CurrentProfile.DashboardURL,
)
} else if strings.Contains(msg, "SSL connection is required") && viper.GetBool("DEBUG") {
CmdSuggestion = "SSL connection is not supported with --debug flag"
} else if strings.Contains(msg, "SCRAM exchange: Wrong password") || strings.Contains(msg, "failed SASL auth") {
// password authentication failed for user / invalid SCRAM server-final-message received from server
CmdSuggestion = SuggestEnvVar
} else if strings.Contains(msg, "connect: no route to host") || strings.Contains(msg, "Tenant or user not found") {
// Assumes IPv6 check has been performed before this
CmdSuggestion = "Make sure your project exists on profile: " + CurrentProfile.Name
}
}

const (
SUPERUSER_ROLE = "supabase_admin"
CLI_LOGIN_PREFIX = "cli_login_"
Expand Down
77 changes: 77 additions & 0 deletions internal/utils/connect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,83 @@ func TestPoolerConfig(t *testing.T) {
})
}

func TestSetConnectSuggestion(t *testing.T) {
oldProfile := CurrentProfile
CurrentProfile = allProfiles[0]
defer t.Cleanup(func() { CurrentProfile = oldProfile })

cases := []struct {
name string
err error
suggestion string
debug bool
}{
{
name: "no-op on nil error",
err: nil,
suggestion: "",
},
{
name: "no-op on unrecognised error",
err: errors.New("some unknown error"),
suggestion: "",
},
{
name: "connection refused",
err: errors.New("connect: connection refused"),
suggestion: "Make sure your local IP is allowed in Network Restrictions and Network Bans",
},
{
name: "address not in allow list",
err: errors.New("server error (FATAL: Address not in tenant allow_list: {1,2,3} (SQLSTATE XX000))"),
suggestion: "Make sure your local IP is allowed in Network Restrictions and Network Bans",
},
{
name: "ssl required without debug flag",
err: errors.New("SSL connection is required"),
suggestion: "",
},
{
name: "ssl required with debug flag",
err: errors.New("SSL connection is required"),
debug: true,
suggestion: "SSL connection is not supported with --debug flag",
},
{
name: "wrong password via SCRAM",
err: errors.New("SCRAM exchange: Wrong password"),
suggestion: "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD",
},
{
name: "failed SASL auth",
err: errors.New("failed SASL auth"),
suggestion: "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD",
},
{
name: "no route to host",
err: errors.New("connect: no route to host"),
suggestion: "Make sure your project exists on profile: " + CurrentProfile.Name,
},
{
name: "tenant or user not found",
err: errors.New("Tenant or user not found"),
suggestion: "Make sure your project exists on profile: " + CurrentProfile.Name,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
CmdSuggestion = ""
viper.Set("DEBUG", tc.debug)
SetConnectSuggestion(tc.err)
if tc.suggestion == "" {
assert.Empty(t, CmdSuggestion)
} else {
assert.Contains(t, CmdSuggestion, tc.suggestion)
}
})
}
}

func TestPostgresURL(t *testing.T) {
url := ToPostgresURL(pgconn.Config{
Host: "2406:da18:4fd:9b0d:80ec:9812:3e65:450b",
Expand Down
9 changes: 5 additions & 4 deletions internal/utils/flags/db_url.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ func RandomString(size int) (string, error) {
return string(data), nil
}

const suggestEnvVar = "Connect to your database by setting the env var: SUPABASE_DB_PASSWORD"

func NewDbConfigWithPassword(ctx context.Context, projectRef string) (pgconn.Config, error) {
config := pgconn.Config{
Host: utils.GetSupabaseDbHost(projectRef),
Expand All @@ -144,7 +142,10 @@ func NewDbConfigWithPassword(ctx context.Context, projectRef string) (pgconn.Con
fmt.Fprintln(logger, "Using database password from env var...")
poolerConfig.Password = config.Password
} else if err := initPoolerLogin(ctx, projectRef, poolerConfig); err != nil {
utils.CmdSuggestion = suggestEnvVar
utils.SetConnectSuggestion(err)
if utils.CmdSuggestion == "" {
utils.CmdSuggestion = utils.SuggestEnvVar
}
return *poolerConfig, err
}
return *poolerConfig, nil
Expand All @@ -157,7 +158,7 @@ func NewDbConfigWithPassword(ctx context.Context, projectRef string) (pgconn.Con
fmt.Fprintln(logger, "Using database password from env var...")
} else if err := initLoginRole(ctx, projectRef, &config); err != nil {
// Do not prompt because reading masked input is buggy on windows
utils.CmdSuggestion = suggestEnvVar
utils.CmdSuggestion = utils.SuggestEnvVar
return config, err
}
return config, nil
Expand Down
Loading