Skip to content

Commit

Permalink
fix: update postgres image if linked project is healthy
Browse files Browse the repository at this point in the history
  • Loading branch information
sweatybridge committed Nov 19, 2024
1 parent f4b5ad4 commit 652c0cf
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 28 deletions.
8 changes: 6 additions & 2 deletions internal/link/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func Run(ctx context.Context, projectRef string, fsys afero.Fs, options ...func(
fmt.Fprintln(utils.GetDebugLogger(), err)
}

if err := checkRemoteProjectStatus(ctx, projectRef); err != nil {
if err := checkRemoteProjectStatus(ctx, projectRef, fsys); err != nil {
return err
}

Expand Down Expand Up @@ -254,7 +254,7 @@ func updatePoolerConfig(config api.SupavisorConfigResponse) {

var errProjectPaused = errors.New("project is paused")

func checkRemoteProjectStatus(ctx context.Context, projectRef string) error {
func checkRemoteProjectStatus(ctx context.Context, projectRef string, fsys afero.Fs) error {
resp, err := utils.GetSupabase().V1GetProjectWithResponse(ctx, projectRef)
if err != nil {
return errors.Errorf("failed to retrieve remote project status: %w", err)
Expand All @@ -279,5 +279,9 @@ func checkRemoteProjectStatus(ctx context.Context, projectRef string) error {
fmt.Fprintf(os.Stderr, "%s: Project status is %s instead of Active Healthy. Some operations might fail.\n", utils.Yellow("WARNING"), resp.JSON200.Status)
}

// Update postgres image version to match the remote project
if version := resp.JSON200.Database.Version; len(version) > 0 {
return utils.WriteFile(utils.PostgresVersionPath, []byte(version), fsys)
}
return nil
}
108 changes: 82 additions & 26 deletions internal/link/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,38 @@ func TestLinkCommand(t *testing.T) {
// Flush pending mocks after test execution
defer gock.OffAll()
// Mock project status
postgres := api.V1DatabaseResponse{
Host: utils.GetSupabaseDbHost(project),
Version: "15.1.0.117",
}
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project).
Reply(200).
JSON(api.V1ProjectResponse{Status: api.V1ProjectResponseStatusACTIVEHEALTHY})
JSON(api.V1ProjectResponse{
Status: api.V1ProjectResponseStatusACTIVEHEALTHY,
Database: &postgres,
})
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/api-keys").
Reply(200).
JSON([]api.ApiKeyResponse{{Name: "anon", ApiKey: "anon-key"}})
// Link configs
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/database/postgres").
Reply(200).
JSON(api.PostgresConfigResponse{})
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/postgrest").
Reply(200).
JSON(api.V1PostgrestConfigResponse{})
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/auth").
Reply(200).
JSON(api.AuthConfigResponse{})
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/storage").
Reply(200).
JSON(api.StorageConfigResponse{})
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/database/pooler").
Reply(200).
Expand All @@ -83,23 +102,6 @@ func TestLinkCommand(t *testing.T) {
Get("/storage/v1/version").
Reply(200).
BodyString("0.40.4")
postgres := api.V1DatabaseResponse{
Host: utils.GetSupabaseDbHost(project),
Version: "15.1.0.117",
}
gock.New(utils.DefaultApiHost).
Get("/v1/projects").
Reply(200).
JSON([]api.V1ProjectResponse{
{
Id: project,
Database: &postgres,
OrganizationId: "combined-fuchsia-lion",
Name: "Test Project",
Region: "us-west-1",
CreatedAt: "2022-04-25T02:14:55.906498Z",
},
})
// Run test
err := Run(context.Background(), project, fsys, conn.Intercept)
// Check error
Expand Down Expand Up @@ -130,15 +132,27 @@ func TestLinkCommand(t *testing.T) {
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project).
Reply(200).
JSON(api.V1ProjectResponse{Status: api.V1ProjectResponseStatusACTIVEHEALTHY})
JSON(api.V1ProjectResponse{
Status: api.V1ProjectResponseStatusACTIVEHEALTHY,
Database: &api.V1DatabaseResponse{},
})
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/api-keys").
Reply(200).
JSON([]api.ApiKeyResponse{{Name: "anon", ApiKey: "anon-key"}})
// Link configs
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/database/postgres").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/postgrest").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/auth").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/storage").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/database/pooler").
ReplyError(errors.New("network error"))
Expand All @@ -152,9 +166,6 @@ func TestLinkCommand(t *testing.T) {
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/storage/v1/version").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects").
ReplyError(errors.New("network error"))
// Run test
err := Run(context.Background(), project, fsys, func(cc *pgx.ConnConfig) {
cc.LookupFunc = func(ctx context.Context, host string) (addrs []string, err error) {
Expand All @@ -175,15 +186,27 @@ func TestLinkCommand(t *testing.T) {
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project).
Reply(200).
JSON(api.V1ProjectResponse{Status: api.V1ProjectResponseStatusACTIVEHEALTHY})
JSON(api.V1ProjectResponse{
Status: api.V1ProjectResponseStatusACTIVEHEALTHY,
Database: &api.V1DatabaseResponse{},
})
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/api-keys").
Reply(200).
JSON([]api.ApiKeyResponse{{Name: "anon", ApiKey: "anon-key"}})
// Link configs
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/database/postgres").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/postgrest").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/auth").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/storage").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/config/database/pooler").
ReplyError(errors.New("network error"))
Expand Down Expand Up @@ -215,21 +238,51 @@ func TestLinkCommand(t *testing.T) {
func TestStatusCheck(t *testing.T) {
project := "test-project"

t.Run("updates postgres version when healthy", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Flush pending mocks after test execution
defer gock.OffAll()
// Mock project status
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project).
Reply(http.StatusOK).
JSON(api.V1ProjectResponse{
Status: api.V1ProjectResponseStatusACTIVEHEALTHY,
Database: &api.V1DatabaseResponse{Version: "15.6.1.139"},
})
// Run test
err := checkRemoteProjectStatus(context.Background(), project, fsys)
// Check error
assert.NoError(t, err)
version, err := afero.ReadFile(fsys, utils.PostgresVersionPath)
assert.NoError(t, err)
assert.Equal(t, "15.6.1.139", string(version))
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("ignores project not found", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Flush pending mocks after test execution
defer gock.OffAll()
// Mock project status
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project).
Reply(http.StatusNotFound)
// Run test
err := checkRemoteProjectStatus(context.Background(), project)
err := checkRemoteProjectStatus(context.Background(), project, fsys)
// Check error
assert.NoError(t, err)
exists, err := afero.Exists(fsys, utils.PostgresVersionPath)
assert.NoError(t, err)
assert.False(t, exists)
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("throws error on project inactive", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Flush pending mocks after test execution
defer gock.OffAll()
// Mock project status
Expand All @@ -238,9 +291,12 @@ func TestStatusCheck(t *testing.T) {
Reply(http.StatusOK).
JSON(api.V1ProjectResponse{Status: api.V1ProjectResponseStatusINACTIVE})
// Run test
err := checkRemoteProjectStatus(context.Background(), project)
err := checkRemoteProjectStatus(context.Background(), project, fsys)
// Check error
assert.ErrorIs(t, err, errProjectPaused)
exists, err := afero.Exists(fsys, utils.PostgresVersionPath)
assert.NoError(t, err)
assert.False(t, exists)
assert.Empty(t, apitest.ListUnmatchedRequests())
})
}
Expand Down Expand Up @@ -309,7 +365,7 @@ func TestLinkPostgrest(t *testing.T) {
// Run test
err := linkPostgrest(context.Background(), project)
// Validate api
assert.ErrorIs(t, err, tenant.ErrAuthToken)
assert.ErrorContains(t, err, `unexpected API config status 500: {"message":"unavailable"}`)
assert.Empty(t, apitest.ListUnmatchedRequests())
})
}
Expand Down

0 comments on commit 652c0cf

Please sign in to comment.