Skip to content

Commit 2a04d16

Browse files
fix: clarify db connection error for Address not in tenant allow_list (#4873)
Co-authored-by: Andrew Valleteau <avallete@users.noreply.github.com>
1 parent 8ff4b5e commit 2a04d16

File tree

3 files changed

+108
-18
lines changed

3 files changed

+108
-18
lines changed

internal/utils/connect.go

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -167,23 +167,35 @@ func ConnectByUrl(ctx context.Context, url string, options ...func(*pgx.ConnConf
167167
cc.Fallbacks = fallbacks
168168
})
169169
conn, err := pgxv5.Connect(ctx, url, options...)
170-
var pgErr *pgconn.PgError
171-
if errors.As(err, &pgErr) {
172-
if strings.Contains(pgErr.Message, "connect: connection refused") {
173-
CmdSuggestion = fmt.Sprintf("Make sure your local IP is allowed in Network Restrictions and Network Bans.\n%s/project/_/database/settings", CurrentProfile.DashboardURL)
174-
} else if strings.Contains(pgErr.Message, "SSL connection is required") && viper.GetBool("DEBUG") {
175-
CmdSuggestion = "SSL connection is not supported with --debug flag"
176-
} else if strings.Contains(pgErr.Message, "SCRAM exchange: Wrong password") || strings.Contains(pgErr.Message, "failed SASL auth") {
177-
// password authentication failed for user / invalid SCRAM server-final-message received from server
178-
CmdSuggestion = "Try setting the SUPABASE_DB_PASSWORD environment variable"
179-
} else if strings.Contains(pgErr.Message, "connect: no route to host") || strings.Contains(pgErr.Message, "Tenant or user not found") {
180-
// Assumes IPv6 check has been performed before this
181-
CmdSuggestion = "Make sure your project exists on profile: " + CurrentProfile.Name
182-
}
183-
}
170+
SetConnectSuggestion(err)
184171
return conn, err
185172
}
186173

174+
const SuggestEnvVar = "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD"
175+
176+
// Sets CmdSuggestion to an actionable hint based on the given pg connection error.
177+
func SetConnectSuggestion(err error) {
178+
if err == nil {
179+
return
180+
}
181+
msg := err.Error()
182+
if strings.Contains(msg, "connect: connection refused") ||
183+
strings.Contains(msg, "Address not in tenant allow_list") {
184+
CmdSuggestion = fmt.Sprintf(
185+
"Make sure your local IP is allowed in Network Restrictions and Network Bans.\n%s/project/_/database/settings",
186+
CurrentProfile.DashboardURL,
187+
)
188+
} else if strings.Contains(msg, "SSL connection is required") && viper.GetBool("DEBUG") {
189+
CmdSuggestion = "SSL connection is not supported with --debug flag"
190+
} else if strings.Contains(msg, "SCRAM exchange: Wrong password") || strings.Contains(msg, "failed SASL auth") {
191+
// password authentication failed for user / invalid SCRAM server-final-message received from server
192+
CmdSuggestion = SuggestEnvVar
193+
} else if strings.Contains(msg, "connect: no route to host") || strings.Contains(msg, "Tenant or user not found") {
194+
// Assumes IPv6 check has been performed before this
195+
CmdSuggestion = "Make sure your project exists on profile: " + CurrentProfile.Name
196+
}
197+
}
198+
187199
const (
188200
SUPERUSER_ROLE = "supabase_admin"
189201
CLI_LOGIN_PREFIX = "cli_login_"

internal/utils/connect_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,83 @@ func TestPoolerConfig(t *testing.T) {
168168
})
169169
}
170170

171+
func TestSetConnectSuggestion(t *testing.T) {
172+
oldProfile := CurrentProfile
173+
CurrentProfile = allProfiles[0]
174+
defer t.Cleanup(func() { CurrentProfile = oldProfile })
175+
176+
cases := []struct {
177+
name string
178+
err error
179+
suggestion string
180+
debug bool
181+
}{
182+
{
183+
name: "no-op on nil error",
184+
err: nil,
185+
suggestion: "",
186+
},
187+
{
188+
name: "no-op on unrecognised error",
189+
err: errors.New("some unknown error"),
190+
suggestion: "",
191+
},
192+
{
193+
name: "connection refused",
194+
err: errors.New("connect: connection refused"),
195+
suggestion: "Make sure your local IP is allowed in Network Restrictions and Network Bans",
196+
},
197+
{
198+
name: "address not in allow list",
199+
err: errors.New("server error (FATAL: Address not in tenant allow_list: {1,2,3} (SQLSTATE XX000))"),
200+
suggestion: "Make sure your local IP is allowed in Network Restrictions and Network Bans",
201+
},
202+
{
203+
name: "ssl required without debug flag",
204+
err: errors.New("SSL connection is required"),
205+
suggestion: "",
206+
},
207+
{
208+
name: "ssl required with debug flag",
209+
err: errors.New("SSL connection is required"),
210+
debug: true,
211+
suggestion: "SSL connection is not supported with --debug flag",
212+
},
213+
{
214+
name: "wrong password via SCRAM",
215+
err: errors.New("SCRAM exchange: Wrong password"),
216+
suggestion: "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD",
217+
},
218+
{
219+
name: "failed SASL auth",
220+
err: errors.New("failed SASL auth"),
221+
suggestion: "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD",
222+
},
223+
{
224+
name: "no route to host",
225+
err: errors.New("connect: no route to host"),
226+
suggestion: "Make sure your project exists on profile: " + CurrentProfile.Name,
227+
},
228+
{
229+
name: "tenant or user not found",
230+
err: errors.New("Tenant or user not found"),
231+
suggestion: "Make sure your project exists on profile: " + CurrentProfile.Name,
232+
},
233+
}
234+
for _, tc := range cases {
235+
t.Run(tc.name, func(t *testing.T) {
236+
CmdSuggestion = ""
237+
viper.Set("DEBUG", tc.debug)
238+
SetConnectSuggestion(tc.err)
239+
if tc.suggestion == "" {
240+
assert.Empty(t, CmdSuggestion)
241+
} else {
242+
assert.Contains(t, CmdSuggestion, tc.suggestion)
243+
}
244+
})
245+
}
246+
}
247+
171248
func TestPostgresURL(t *testing.T) {
172249
url := ToPostgresURL(pgconn.Config{
173250
Host: "2406:da18:4fd:9b0d:80ec:9812:3e65:450b",

internal/utils/flags/db_url.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,6 @@ func RandomString(size int) (string, error) {
120120
return string(data), nil
121121
}
122122

123-
const suggestEnvVar = "Connect to your database by setting the env var: SUPABASE_DB_PASSWORD"
124-
125123
func NewDbConfigWithPassword(ctx context.Context, projectRef string) (pgconn.Config, error) {
126124
config := pgconn.Config{
127125
Host: utils.GetSupabaseDbHost(projectRef),
@@ -144,7 +142,10 @@ func NewDbConfigWithPassword(ctx context.Context, projectRef string) (pgconn.Con
144142
fmt.Fprintln(logger, "Using database password from env var...")
145143
poolerConfig.Password = config.Password
146144
} else if err := initPoolerLogin(ctx, projectRef, poolerConfig); err != nil {
147-
utils.CmdSuggestion = suggestEnvVar
145+
utils.SetConnectSuggestion(err)
146+
if utils.CmdSuggestion == "" {
147+
utils.CmdSuggestion = utils.SuggestEnvVar
148+
}
148149
return *poolerConfig, err
149150
}
150151
return *poolerConfig, nil
@@ -157,7 +158,7 @@ func NewDbConfigWithPassword(ctx context.Context, projectRef string) (pgconn.Con
157158
fmt.Fprintln(logger, "Using database password from env var...")
158159
} else if err := initLoginRole(ctx, projectRef, &config); err != nil {
159160
// Do not prompt because reading masked input is buggy on windows
160-
utils.CmdSuggestion = suggestEnvVar
161+
utils.CmdSuggestion = utils.SuggestEnvVar
161162
return config, err
162163
}
163164
return config, nil

0 commit comments

Comments
 (0)