Skip to content

Commit

Permalink
feat: sync database version with remote pg15 project
Browse files Browse the repository at this point in the history
  • Loading branch information
sweatybridge committed Sep 29, 2023
1 parent 752ff37 commit 7422659
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 18 deletions.
25 changes: 21 additions & 4 deletions internal/link/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package link

import (
"context"
"errors"
"fmt"
"io"
"os"
Expand All @@ -16,6 +15,7 @@ import (
"github.com/jackc/pgx/v4"
"github.com/spf13/afero"
"github.com/supabase/cli/internal/migration/repair"
"github.com/supabase/cli/internal/services"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/internal/utils/credentials"
"github.com/supabase/cli/internal/utils/tenant"
Expand Down Expand Up @@ -87,7 +87,13 @@ func PostRun(projectRef string, stdout io.Writer, fsys afero.Fs) error {
func linkServices(ctx context.Context, projectRef string, fsys afero.Fs) {
// Ignore non-fatal errors linking services
var wg sync.WaitGroup
wg.Add(4)
wg.Add(5)
go func() {
defer wg.Done()
if err := linkDatabaseVersion(ctx, projectRef, fsys); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}()
go func() {
defer wg.Done()
if err := linkPostgrest(ctx, projectRef); err != nil {
Expand Down Expand Up @@ -121,7 +127,7 @@ func linkPostgrest(ctx context.Context, projectRef string) error {
return err
}
if resp.JSON200 == nil {
return errors.New("Authorization failed for the access token and project ref pair: " + string(resp.Body))
return fmt.Errorf("%w: %s", tenant.ErrAuthToken, string(resp.Body))
}
updateApiConfig(*resp.JSON200)
return nil
Expand Down Expand Up @@ -185,6 +191,17 @@ func linkDatabase(ctx context.Context, config pgconn.Config, options ...func(*pg
return repair.CreateMigrationTable(ctx, conn)
}

func linkDatabaseVersion(ctx context.Context, projectRef string, fsys afero.Fs) error {
version, err := services.GetDatabaseVersion(ctx, projectRef)
if err != nil {
return err
}
if err := utils.MkdirIfNotExistFS(fsys, filepath.Dir(utils.PostgresVersionPath)); err != nil {
return err
}
return afero.WriteFile(fsys, utils.PostgresVersionPath, []byte(version), 0644)
}

func updatePostgresConfig(conn *pgx.Conn) {
serverVersion := conn.PgConn().ParameterStatus("server_version")
// Safe to assume that supported Postgres version is 10.0 <= n < 100.0
Expand All @@ -207,7 +224,7 @@ func linkPooler(ctx context.Context, projectRef string) error {
return err
}
if resp.JSON200 == nil {
return errors.New("Authorization failed for the access token and project ref pair: " + string(resp.Body))
return fmt.Errorf("%w: %s", tenant.ErrAuthToken, string(resp.Body))
}
updatePoolerConfig(*resp.JSON200)
return nil
Expand Down
51 changes: 39 additions & 12 deletions internal/link/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package link
import (
"context"
"errors"
"fmt"
"os"
"strings"
"testing"
Expand Down Expand Up @@ -134,6 +133,11 @@ func TestLinkCommand(t *testing.T) {
Reply("ALTER TABLE")
// Flush pending mocks after test execution
defer gock.OffAll()
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 + "/postgrest").
Reply(200).
Expand All @@ -142,20 +146,34 @@ func TestLinkCommand(t *testing.T) {
Get("/v1/projects/" + project + "/config/database/pgbouncer").
Reply(200).
JSON(api.V1PgbouncerConfigResponse{})
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + project + "/api-keys").
Reply(200).
JSON([]api.ApiKeyResponse{{Name: "anon", ApiKey: "anon-key"}})
// Link versions
rest := tenant.SwaggerResponse{Info: tenant.SwaggerInfo{Version: "11.1.0"}}
gock.New(fmt.Sprintf("https://%s.supabase.co", project)).
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/rest/v1/").
Reply(200).
JSON(rest)
auth := tenant.HealthResponse{Version: "v2.74.2"}
gock.New(fmt.Sprintf("https://%s.supabase.co", project)).
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/auth/v1/health").
Reply(200).
JSON(auth)
postgres := api.DatabaseResponse{
Host: utils.GetSupabaseDbHost(project),
Version: "15.1.0.117",
}
gock.New(utils.DefaultApiHost).
Get("/v1/projects").
Reply(200).
JSON([]api.ProjectResponse{
{
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, dbConfig.Password, fsys, conn.Intercept)
// Check error
Expand All @@ -171,6 +189,9 @@ func TestLinkCommand(t *testing.T) {
authVersion, err := afero.ReadFile(fsys, utils.GotrueVersionPath)
assert.NoError(t, err)
assert.Equal(t, []byte(auth.Version), authVersion)
postgresVersion, err := afero.ReadFile(fsys, utils.PostgresVersionPath)
assert.NoError(t, err)
assert.Equal(t, []byte(postgres.Version), postgresVersion)
})

t.Run("throws error on network failure", func(t *testing.T) {
Expand Down Expand Up @@ -206,12 +227,15 @@ func TestLinkCommand(t *testing.T) {
Get("/v1/projects/" + project + "/config/database/pgbouncer").
ReplyError(errors.New("network error"))
// Link versions
gock.New(fmt.Sprintf("https://%s.supabase.co", project)).
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/auth/v1/health").
ReplyError(errors.New("network error"))
gock.New(fmt.Sprintf("https://%s.supabase.co", project)).
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/rest/v1/").
ReplyError(errors.New("network error"))
gock.New(utils.DefaultApiHost).
Get("/v1/projects").
ReplyError(errors.New("network error"))
// Run test
err := Run(context.Background(), project, dbConfig.Password, fsys, func(cc *pgx.ConnConfig) {
cc.LookupFunc = func(ctx context.Context, host string) (addrs []string, err error) {
Expand Down Expand Up @@ -241,12 +265,15 @@ func TestLinkCommand(t *testing.T) {
Get("/v1/projects/" + project + "/config/database/pgbouncer").
ReplyError(errors.New("network error"))
// Link versions
gock.New(fmt.Sprintf("https://%s.supabase.co", project)).
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/auth/v1/health").
ReplyError(errors.New("network error"))
gock.New(fmt.Sprintf("https://%s.supabase.co", project)).
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/rest/v1/").
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)
// Check error
Expand Down Expand Up @@ -331,7 +358,7 @@ func TestLinkPostgrest(t *testing.T) {
// Run test
err := linkPostgrest(context.Background(), project)
// Validate api
assert.ErrorContains(t, err, `Authorization failed for the access token and project ref pair: {"message":"unavailable"}`)
assert.ErrorIs(t, err, tenant.ErrAuthToken)
assert.Empty(t, apitest.ListUnmatchedRequests())
})
}
Expand Down
4 changes: 4 additions & 0 deletions internal/utils/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,10 @@ func LoadConfigFS(fsys afero.Fs) error {
Config.Db.Image = Pg14Image
InitialSchemaSql = InitialSchemaPg14Sql
case 15:
if version, err := afero.ReadFile(fsys, PostgresVersionPath); err == nil && len(version) > 0 {
index := strings.IndexByte(Pg15Image, ':')
Config.Db.Image = Pg15Image[:index+1] + string(version)
}
default:
return fmt.Errorf("Failed reading config: Invalid %s: %v.", Aqua("db.major_version"), Config.Db.MajorVersion)
}
Expand Down
1 change: 1 addition & 0 deletions internal/utils/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ var (
ImportMapsDir = filepath.Join(SupabaseDirPath, TempDir, "import_maps")
ProjectRefPath = filepath.Join(SupabaseDirPath, TempDir, "project-ref")
RemoteDbPath = filepath.Join(SupabaseDirPath, TempDir, "remote-db-url")
PostgresVersionPath = filepath.Join(SupabaseDirPath, TempDir, "postgres-version")
GotrueVersionPath = filepath.Join(SupabaseDirPath, TempDir, "gotrue-version")
RestVersionPath = filepath.Join(SupabaseDirPath, TempDir, "rest-version")
CurrBranchPath = filepath.Join(SupabaseDirPath, ".branches", "_current_branch")
Expand Down
4 changes: 2 additions & 2 deletions internal/utils/tenant/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var (
apiKey ApiKey
keyOnce sync.Once

errAuthToken = errors.New("Authorization failed for the access token and project ref pair")
ErrAuthToken = errors.New("Authorization failed for the access token and project ref pair")
errMissingKey = errors.New("Anon key not found.")
)

Expand All @@ -38,7 +38,7 @@ func GetApiKeys(ctx context.Context, projectRef string) (ApiKey, error) {
return
}
if resp.JSON200 == nil {
errKey = fmt.Errorf("%w: %s", errAuthToken, string(resp.Body))
errKey = fmt.Errorf("%w: %s", ErrAuthToken, string(resp.Body))
return
}
for _, key := range *resp.JSON200 {
Expand Down

0 comments on commit 7422659

Please sign in to comment.