Repository: supabase/cli
Pull request #2731 (2024-10-06 08:09:42)

fix: inspect storage container before seeding buckets
## What kind of change does this PR introduce?

Bug fix

## What is the current behavior?

`db start && db reset` throws an error because storage container is missing.

## What is the new behavior?

Only seeds storage if local container is started.

## Additional context

Add any other context or screenshots.


coveralls: ## Pull Request Test Coverage Report for [Build 11200220452](https://coveralls.io/builds/70184687)


### Details

* **4** of **7**   **(57.14%)**  changed or added relevant lines in **2** files are covered.
* **5** unchanged lines in **1** file lost coverage.
* Overall coverage increased (+**0.2%**) to **60.253%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [internal/db/reset/reset.go](https://coveralls.io/builds/70184687/source?filename=internal%2Fdb%2Freset%2Freset.go#L60) | 3 | 6 | 50.0%
<!-- | **Total:** | **4** | **7** | **57.14%** | -->

|  Files with Coverage Reduction | New Missed Lines | % |
| :-----|--------------|--: |
| [internal/gen/keys/keys.go](https://coveralls.io/builds/70184687/source?filename=internal%2Fgen%2Fkeys%2Fkeys.go#L87) | 5 | 12.9% |
<!-- | **Total:** | **5** |  | -->

|  Totals | [![Coverage Status](https://coveralls.io/builds/70184687/badge)](https://coveralls.io/builds/70184687) |
| :-- | --: |
| Change from base [Build 11183330771](https://coveralls.io/builds/70171759): |  0.2% |
| Covered Lines: | 6476 |
| Relevant Lines: | 10748 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/internal/db/reset/reset.go b/internal/db/reset/reset.go
index d53392ae..88e59504 100644
--- a/internal/db/reset/reset.go
+++ b/internal/db/reset/reset.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/errdefs"
@@ -54,9 +55,11 @@ func Run(ctx context.Context, version string, config pgconn.Config, fsys afero.F
 		return err
 	}
 	// Seed objects from supabase/buckets directory
-	if utils.Config.Storage.Enabled {
-		if err := start.WaitForHealthyService(ctx, 30*time.Second, utils.StorageId); err != nil {
-			return err
+	if resp, err := utils.Docker.ContainerInspect(ctx, utils.StorageId); err == nil {
+		if resp.State.Health == nil || resp.State.Health.Status != types.Healthy {
+			if err := start.WaitForHealthyService(ctx, 30*time.Second, utils.StorageId); err != nil {
+				return err
+			}
 		}
 		if err := buckets.Run(ctx, "", false, fsys); err != nil {
 			return err
@@ -112,10 +115,6 @@ func resetDatabase15(ctx context.Context, version string, fsys afero.Fs, options
 	if err := utils.Docker.VolumeRemove(ctx, utils.DbId, true); err != nil {
 		return errors.Errorf("failed to remove volume: %w", err)
 	}
-	// Skip syslog if vector container is not started
-	if _, err := utils.Docker.ContainerInspect(ctx, utils.VectorId); err != nil {
-		utils.Config.Analytics.Enabled = false
-	}
 	config := start.NewContainerConfig()
 	hostConfig := start.NewHostConfig()
 	networkingConfig := network.NetworkingConfig{
@@ -212,7 +211,7 @@ func restartServices(ctx context.Context) error {
 	services := listServicesToRestart()
 	result := utils.WaitAll(services, func(id string) error {
 		if err := utils.Docker.ContainerRestart(ctx, id, container.StopOptions{}); err != nil && !errdefs.IsNotFound(err) {
-			return errors.Errorf("Failed to restart %s: %w", id, err)
+			return errors.Errorf("failed to restart %s: %w", id, err)
 		}
 		return nil
 	})
diff --git a/internal/db/start/start.go b/internal/db/start/start.go
index 74e40554..912f845d 100644
--- a/internal/db/start/start.go
+++ b/internal/db/start/start.go
@@ -43,8 +43,6 @@ func Run(ctx context.Context, fsys afero.Fs) error {
 	} else if !errors.Is(err, utils.ErrNotRunning) {
 		return err
 	}
-	// Skip logflare container in db start
-	utils.Config.Analytics.Enabled = false
 	err := StartDatabase(ctx, fsys, os.Stderr)
 	if err != nil {
 		if err := utils.DockerRemoveAll(context.Background(), os.Stderr, utils.Config.ProjectId); err != nil {
diff --git a/internal/status/status.go b/internal/status/status.go
index e78af5cd..1b9d02f9 100644
--- a/internal/status/status.go
+++ b/internal/status/status.go
@@ -14,6 +14,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/go-errors/errors"
 	"github.com/spf13/afero"
@@ -114,7 +115,7 @@ func assertContainerHealthy(ctx context.Context, container string) error {
 		return errors.Errorf("failed to inspect container health: %w", err)
 	} else if !resp.State.Running {
 		return errors.Errorf("%s container is not running: %s", container, resp.State.Status)
-	} else if resp.State.Health != nil && resp.State.Health.Status != "healthy" {
+	} else if resp.State.Health != nil && resp.State.Health.Status != types.Healthy {
 		return errors.Errorf("%s container is not ready: %s", container, resp.State.Health.Status)
 	}
 	return nil


Test patch:
diff --git a/internal/db/diff/diff_test.go b/internal/db/diff/diff_test.go
index d5e8c6a4..6ada103c 100644
--- a/internal/db/diff/diff_test.go
+++ b/internal/db/diff/diff_test.go
@@ -55,7 +55,7 @@ func TestRun(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Realtime.Image), "test-shadow-realtime")
@@ -267,7 +267,7 @@ func TestDiffDatabase(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
@@ -303,7 +303,7 @@ At statement 0: create schema public`)
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
diff --git a/internal/db/reset/reset_test.go b/internal/db/reset/reset_test.go
index ace03c9b..c65aa0ef 100644
--- a/internal/db/reset/reset_test.go
+++ b/internal/db/reset/reset_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/migration"
 	"github.com/supabase/cli/pkg/pgtest"
+	"github.com/supabase/cli/pkg/storage"
 )
 
 func TestResetCommand(t *testing.T) {
@@ -38,6 +39,69 @@ func TestResetCommand(t *testing.T) {
 		Database: "postgres",
 	}
 
+	t.Run("seeds storage after reset", func(t *testing.T) {
+		utils.DbId = "test-reset"
+		utils.ConfigId = "test-config"
+		utils.Config.Db.MajorVersion = 15
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock docker
+		require.NoError(t, apitest.MockDocker(utils.Docker))
+		defer gock.OffAll()
+		gock.New(utils.Docker.DaemonHost()).
+			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId).
+			Reply(http.StatusOK).
+			JSON(types.ContainerJSON{})
+		gock.New(utils.Docker.DaemonHost()).
+			Delete("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId).
+			Reply(http.StatusOK)
+		gock.New(utils.Docker.DaemonHost()).
+			Delete("/v" + utils.Docker.ClientVersion() + "/volumes/" + utils.DbId).
+			Reply(http.StatusOK)
+		apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), utils.DbId)
+		gock.New(utils.Docker.DaemonHost()).
+			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
+			Reply(http.StatusOK).
+			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
+				State: &types.ContainerState{
+					Running: true,
+					Health:  &types.Health{Status: types.Healthy},
+				},
+			}})
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		// Restarts services
+		utils.StorageId = "test-storage"
+		utils.GotrueId = "test-auth"
+		utils.RealtimeId = "test-realtime"
+		utils.PoolerId = "test-pooler"
+		for _, container := range listServicesToRestart() {
+			gock.New(utils.Docker.DaemonHost()).
+				Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart").
+				Reply(http.StatusOK)
+		}
+		// Seeds storage
+		gock.New(utils.Docker.DaemonHost()).
+			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.StorageId + "/json").
+			Reply(http.StatusOK).
+			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
+				State: &types.ContainerState{
+					Running: true,
+					Health:  &types.Health{Status: types.Healthy},
+				},
+			}})
+		gock.New(utils.Config.Api.ExternalUrl).
+			Get("/storage/v1/bucket").
+			Reply(http.StatusOK).
+			JSON([]storage.BucketResponse{})
+		// Run test
+		err := Run(context.Background(), "", dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+		assert.Empty(t, apitest.ListUnmatchedRequests())
+	})
+
 	t.Run("throws error on context canceled", func(t *testing.T) {
 		// Setup in-memory fs
 		fsys := afero.NewMemMapFs()
@@ -225,7 +289,7 @@ func TestRestartDatabase(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		// Restarts services
@@ -260,7 +324,7 @@ func TestRestartDatabase(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		// Restarts services
@@ -279,9 +343,9 @@ func TestRestartDatabase(t *testing.T) {
 		// Run test
 		err := RestartDatabase(context.Background(), io.Discard)
 		// Check error
-		assert.ErrorContains(t, err, "Failed to restart "+utils.StorageId)
-		assert.ErrorContains(t, err, "Failed to restart "+utils.GotrueId)
-		assert.ErrorContains(t, err, "Failed to restart "+utils.RealtimeId)
+		assert.ErrorContains(t, err, "failed to restart "+utils.StorageId)
+		assert.ErrorContains(t, err, "failed to restart "+utils.GotrueId)
+		assert.ErrorContains(t, err, "failed to restart "+utils.RealtimeId)
 		assert.Empty(t, apitest.ListUnmatchedRequests())
 	})
 
diff --git a/internal/db/start/start_test.go b/internal/db/start/start_test.go
index 55514119..8d1816b0 100644
--- a/internal/db/start/start_test.go
+++ b/internal/db/start/start_test.go
@@ -78,7 +78,7 @@ func TestStartDatabase(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Realtime.Image), "test-realtime")
@@ -126,7 +126,7 @@ func TestStartDatabase(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		// Run test
diff --git a/internal/migration/squash/squash_test.go b/internal/migration/squash/squash_test.go
index c85f014c..cc0461f3 100644
--- a/internal/migration/squash/squash_test.go
+++ b/internal/migration/squash/squash_test.go
@@ -61,7 +61,7 @@ func TestSquashCommand(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
@@ -251,7 +251,7 @@ func TestSquashMigrations(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
@@ -286,7 +286,7 @@ func TestSquashMigrations(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
diff --git a/internal/start/start_test.go b/internal/start/start_test.go
index 654831cc..079eb3c7 100644
--- a/internal/start/start_test.go
+++ b/internal/start/start_test.go
@@ -160,7 +160,7 @@ func TestDatabaseStart(t *testing.T) {
 				JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 					State: &types.ContainerState{
 						Running: true,
-						Health:  &types.Health{Status: "healthy"},
+						Health:  &types.Health{Status: types.Healthy},
 					},
 				}})
 		}
@@ -177,7 +177,7 @@ func TestDatabaseStart(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Config.Api.ExternalUrl).
@@ -225,7 +225,7 @@ func TestDatabaseStart(t *testing.T) {
 			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
 				State: &types.ContainerState{
 					Running: true,
-					Health:  &types.Health{Status: "healthy"},
+					Health:  &types.Health{Status: types.Healthy},
 				},
 			}})
 		// Run test