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
83 changes: 26 additions & 57 deletions internal/link/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,19 @@ import (
"os"
"strconv"
"strings"
"sync"

"github.com/go-errors/errors"
"github.com/jackc/pgconn"
"github.com/jackc/pgx/v4"
"github.com/spf13/afero"
"github.com/spf13/viper"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/internal/utils/flags"
"github.com/supabase/cli/internal/utils/tenant"
"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"
)

func Run(ctx context.Context, projectRef string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
Expand Down Expand Up @@ -60,59 +59,20 @@ major_version = %d
}

func LinkServices(ctx context.Context, projectRef, serviceKey string, fsys afero.Fs) {
// Ignore non-fatal errors linking services
var wg sync.WaitGroup
wg.Add(8)
go func() {
defer wg.Done()
if err := linkDatabaseSettings(ctx, projectRef); err != nil && viper.GetBool("DEBUG") {
fmt.Fprintln(os.Stderr, err)
}
}()
go func() {
defer wg.Done()
if err := linkNetworkRestrictions(ctx, projectRef); err != nil && viper.GetBool("DEBUG") {
fmt.Fprintln(os.Stderr, err)
}
}()
go func() {
defer wg.Done()
if err := linkPostgrest(ctx, projectRef); err != nil && viper.GetBool("DEBUG") {
fmt.Fprintln(os.Stderr, err)
}
}()
go func() {
defer wg.Done()
if err := linkGotrue(ctx, projectRef); err != nil && viper.GetBool("DEBUG") {
fmt.Fprintln(os.Stderr, err)
}
}()
go func() {
defer wg.Done()
if err := linkStorage(ctx, projectRef); err != nil && viper.GetBool("DEBUG") {
fmt.Fprintln(os.Stderr, err)
}
}()
go func() {
defer wg.Done()
if err := linkPooler(ctx, projectRef, fsys); err != nil && viper.GetBool("DEBUG") {
fmt.Fprintln(os.Stderr, err)
}
}()
jq := queue.NewJobQueue(5)
logger := utils.GetDebugLogger()
fmt.Fprintln(logger, jq.Put(func() error { return linkDatabaseSettings(ctx, projectRef) }))
fmt.Fprintln(logger, jq.Put(func() error { return linkNetworkRestrictions(ctx, projectRef) }))
fmt.Fprintln(logger, jq.Put(func() error { return linkPostgrest(ctx, projectRef) }))
fmt.Fprintln(logger, jq.Put(func() error { return linkGotrue(ctx, projectRef) }))
fmt.Fprintln(logger, jq.Put(func() error { return linkStorage(ctx, projectRef) }))
fmt.Fprintln(logger, jq.Put(func() error { return linkPooler(ctx, projectRef, fsys) }))
api := tenant.NewTenantAPI(ctx, projectRef, serviceKey)
go func() {
defer wg.Done()
if err := linkPostgrestVersion(ctx, api, fsys); err != nil && viper.GetBool("DEBUG") {
fmt.Fprintln(os.Stderr, err)
}
}()
go func() {
defer wg.Done()
if err := linkGotrueVersion(ctx, api, fsys); err != nil && viper.GetBool("DEBUG") {
fmt.Fprintln(os.Stderr, err)
}
}()
wg.Wait()
fmt.Fprintln(logger, jq.Put(func() error { return linkPostgrestVersion(ctx, api, fsys) }))
fmt.Fprintln(logger, jq.Put(func() error { return linkGotrueVersion(ctx, api, fsys) }))
fmt.Fprintln(logger, jq.Put(func() error { return linkStorageVersion(ctx, api, fsys) }))
// Ignore non-fatal errors linking services
fmt.Fprintln(logger, jq.Collect())
}

func linkPostgrest(ctx context.Context, projectRef string) error {
Expand Down Expand Up @@ -164,14 +124,23 @@ func linkStorage(ctx context.Context, projectRef string) error {
return nil
}

func linkStorageVersion(ctx context.Context, api tenant.TenantAPI, fsys afero.Fs) error {
version, err := api.GetStorageVersion(ctx)
if err != nil {
return err
}
fmt.Fprintln(os.Stderr, version)
return utils.WriteFile(utils.StorageVersionPath, []byte(version), fsys)
}

const GET_LATEST_STORAGE_MIGRATION = "SELECT name FROM storage.migrations ORDER BY id DESC LIMIT 1"

func linkStorageVersion(ctx context.Context, conn *pgx.Conn, fsys afero.Fs) error {
func linkStorageMigration(ctx context.Context, conn *pgx.Conn, fsys afero.Fs) error {
var name string
if err := conn.QueryRow(ctx, GET_LATEST_STORAGE_MIGRATION).Scan(&name); err != nil {
return errors.Errorf("failed to fetch storage migration: %w", err)
}
return utils.WriteFile(utils.StorageVersionPath, []byte(name), fsys)
return utils.WriteFile(utils.StorageMigrationPath, []byte(name), fsys)
}

func linkDatabaseSettings(ctx context.Context, projectRef string) error {
Expand Down Expand Up @@ -203,7 +172,7 @@ func linkDatabase(ctx context.Context, config pgconn.Config, fsys afero.Fs, opti
}
defer conn.Close(context.Background())
updatePostgresConfig(conn)
if err := linkStorageVersion(ctx, conn, fsys); err != nil {
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.
Expand Down
20 changes: 17 additions & 3 deletions internal/link/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ func TestLinkCommand(t *testing.T) {
Get("/rest/v1/").
Reply(200).
JSON(rest)
storage := "1.28.0"
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/storage/v1/version").
Reply(200).
BodyString(storage)
// Run test
err := Run(context.Background(), project, fsys, conn.Intercept)
// Check error
Expand All @@ -128,6 +133,9 @@ func TestLinkCommand(t *testing.T) {
authVersion, err := afero.ReadFile(fsys, utils.GotrueVersionPath)
assert.NoError(t, err)
assert.Equal(t, []byte(auth.Version), authVersion)
storageVersion, err := afero.ReadFile(fsys, utils.StorageVersionPath)
assert.NoError(t, err)
assert.Equal(t, []byte("v"+storage), storageVersion)
postgresVersion, err := afero.ReadFile(fsys, utils.PostgresVersionPath)
assert.NoError(t, err)
assert.Equal(t, []byte(mockPostgres.Database.Version), postgresVersion)
Expand Down Expand Up @@ -180,6 +188,9 @@ func TestLinkCommand(t *testing.T) {
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/rest/v1/").
ReplyError(errors.New("network error"))
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/storage/v1/version").
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 Down Expand Up @@ -251,6 +262,9 @@ func TestLinkCommand(t *testing.T) {
gock.New("https://" + utils.GetSupabaseHost(project)).
Get("/rest/v1/").
ReplyError(errors.New("network error"))
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"))
Expand Down Expand Up @@ -430,7 +444,7 @@ func TestLinkDatabase(t *testing.T) {
err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept)
// Check error
assert.NoError(t, err)
version, err := afero.ReadFile(fsys, utils.StorageVersionPath)
version, err := afero.ReadFile(fsys, utils.StorageMigrationPath)
assert.NoError(t, err)
assert.Equal(t, "custom-metadata", string(version))
})
Expand All @@ -454,7 +468,7 @@ func TestLinkDatabase(t *testing.T) {
// Check error
assert.NoError(t, err)
assert.Equal(t, uint(15), utils.Config.Db.MajorVersion)
version, err := afero.ReadFile(fsys, utils.StorageVersionPath)
version, err := afero.ReadFile(fsys, utils.StorageMigrationPath)
assert.NoError(t, err)
assert.Equal(t, "custom-metadata", string(version))
})
Expand All @@ -479,7 +493,7 @@ func TestLinkDatabase(t *testing.T) {
err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept)
// Check error
assert.ErrorContains(t, err, "ERROR: permission denied for relation supabase_migrations (SQLSTATE 42501)")
exists, err := afero.Exists(fsys, utils.StorageVersionPath)
exists, err := afero.Exists(fsys, utils.StorageMigrationPath)
assert.NoError(t, err)
assert.False(t, exists)
})
Expand Down
1 change: 1 addition & 0 deletions internal/utils/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ var (
GotrueVersionPath = filepath.Join(TempDir, "gotrue-version")
RestVersionPath = filepath.Join(TempDir, "rest-version")
StorageVersionPath = filepath.Join(TempDir, "storage-version")
StorageMigrationPath = filepath.Join(TempDir, "storage-migration")
StudioVersionPath = filepath.Join(TempDir, "studio-version")
PgmetaVersionPath = filepath.Join(TempDir, "pgmeta-version")
PoolerVersionPath = filepath.Join(TempDir, "pooler-version")
Expand Down
9 changes: 6 additions & 3 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,11 +607,14 @@ func (c *config) Load(path string, fsys fs.FS) error {
}
}
if version, err := fs.ReadFile(fsys, builder.StorageVersionPath); err == nil && len(version) > 0 {
// For backwards compatibility, exclude all strings that look like semver
if v := strings.TrimSpace(string(version)); !semver.IsValid(v) {
c.Storage.TargetMigration = v
// Only replace image if local storage version is newer
if i := strings.IndexByte(Images.Storage, ':'); VersionCompare(string(version), Images.Storage[i+1:]) > 0 {
c.Storage.Image = replaceImageTag(Images.Storage, string(version))
}
}
if version, err := fs.ReadFile(fsys, builder.StorageMigrationPath); err == nil && len(version) > 0 {
c.Storage.TargetMigration = string(version)
}
if version, err := fs.ReadFile(fsys, builder.EdgeRuntimeVersionPath); err == nil && len(version) > 0 {
c.EdgeRuntime.Image = replaceImageTag(Images.EdgeRuntime, string(version))
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type pathBuilder struct {
GotrueVersionPath string
RestVersionPath string
StorageVersionPath string
StorageMigrationPath string
StudioVersionPath string
PgmetaVersionPath string
PoolerVersionPath string
Expand Down Expand Up @@ -55,6 +56,7 @@ func NewPathBuilder(configPath string) pathBuilder {
GotrueVersionPath: filepath.Join(base, ".temp", "gotrue-version"),
RestVersionPath: filepath.Join(base, ".temp", "rest-version"),
StorageVersionPath: filepath.Join(base, ".temp", "storage-version"),
StorageMigrationPath: filepath.Join(base, ".temp", "storage-migration"),
EdgeRuntimeVersionPath: filepath.Join(base, ".temp", "edge-runtime-version"),
StudioVersionPath: filepath.Join(base, ".temp", "studio-version"),
PgmetaVersionPath: filepath.Join(base, ".temp", "pgmeta-version"),
Expand Down