diff --git a/cmd/ftl/cmd_dev.go b/cmd/ftl/cmd_dev.go index e596d84fa3..79427253b6 100644 --- a/cmd/ftl/cmd_dev.go +++ b/cmd/ftl/cmd_dev.go @@ -47,7 +47,7 @@ func (d *devCmd) Run(ctx context.Context, projConfig projectconfig.Config) error } if d.InitDB { - dsn, err := d.ServeCmd.setupDB(ctx) + dsn, err := d.ServeCmd.setupDB(ctx, d.ServeCmd.DatabaseImage) if err != nil { return fmt.Errorf("failed to setup database: %w", err) } diff --git a/cmd/ftl/cmd_schema_import.go b/cmd/ftl/cmd_schema_import.go index 3ce698fc36..c798b97062 100644 --- a/cmd/ftl/cmd_schema_import.go +++ b/cmd/ftl/cmd_schema_import.go @@ -136,7 +136,7 @@ func query(ctx context.Context, prompt string) error { func (s *schemaImportCmd) setup(ctx context.Context) error { logger := log.FromContext(ctx) - exists, err := container.DoesExist(ctx, ollamaContainerName) + exists, err := container.DoesExist(ctx, ollamaContainerName, "") if err != nil { return err } diff --git a/cmd/ftl/cmd_serve.go b/cmd/ftl/cmd_serve.go index 001de80f35..e33e82bb4c 100644 --- a/cmd/ftl/cmd_serve.go +++ b/cmd/ftl/cmd_serve.go @@ -44,6 +44,7 @@ type serveCmd struct { Stop bool `help:"Stop the running FTL instance. Can be used with --background to restart the server" default:"false"` StartupTimeout time.Duration `help:"Timeout for the server to start up." default:"1m"` ObservabilityConfig observability.Config `embed:"" prefix:"o11y-"` + DatabaseImage string `help:"The container image to start for the database" default:"postgres:15.4" env:"FTL_DATABASE_IMAGE"` controller.CommonConfig } @@ -87,7 +88,7 @@ func (s *serveCmd) run(ctx context.Context, projConfig projectconfig.Config, ini logger.Infof("Starting FTL with %d controller(s)", s.Controllers) // Bring up the DB and DAL. - dsn, err := s.setupDB(ctx) + dsn, err := s.setupDB(ctx, s.DatabaseImage) if err != nil { return err } @@ -299,13 +300,13 @@ func isServeRunning(logger *log.Logger) (bool, error) { return true, nil } -func (s *serveCmd) setupDB(ctx context.Context) (string, error) { +func (s *serveCmd) setupDB(ctx context.Context, image string) (string, error) { logger := log.FromContext(ctx) recreate := s.Recreate port := s.DBPort - exists, err := container.DoesExist(ctx, ftlContainerName) + exists, err := container.DoesExist(ctx, ftlContainerName, image) if err != nil { return "", err } @@ -320,7 +321,7 @@ func (s *serveCmd) setupDB(ctx context.Context) (string, error) { return "", fmt.Errorf("failed to close listener: %w", err) } - err = container.RunDB(ctx, ftlContainerName, s.DBPort) + err = container.RunDB(ctx, ftlContainerName, s.DBPort, image) if err != nil { return "", err } diff --git a/internal/container/container.go b/internal/container/container.go index 1956cd4a69..8895027d38 100644 --- a/internal/container/container.go +++ b/internal/container/container.go @@ -24,7 +24,7 @@ var dockerClient = once.Once(func(ctx context.Context) (*client.Client, error) { return client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) }) -func DoesExist(ctx context.Context, name string) (bool, error) { +func DoesExist(ctx context.Context, name string, image string) (bool, error) { cli, err := dockerClient.Get(ctx) if err != nil { return false, err @@ -37,8 +37,18 @@ func DoesExist(ctx context.Context, name string) (bool, error) { if err != nil { return false, fmt.Errorf("failed to list containers: %w", err) } - - return len(containers) > 0, nil + if len(containers) > 0 { + if image == "" { + return true, nil + } + for _, container := range containers { + if container.Image != image { + return false, fmt.Errorf("expecting to use container image %s for container with name %s, bit it was already running with image %s. please delete the container or select a different database image", image, name, container.Image) + } + } + return true, nil + } + return false, nil } // Pull pulls the given image. @@ -105,28 +115,26 @@ func Run(ctx context.Context, image, name string, hostPort, containerPort int, v } // RunDB runs a new detached postgres container with the given name and exposed port. -func RunDB(ctx context.Context, name string, port int) error { +func RunDB(ctx context.Context, name string, port int, image string) error { cli, err := dockerClient.Get(ctx) if err != nil { return err } - const containerName = "postgres" - - exists, err := DoesExist(ctx, containerName) + exists, err := DoesExist(ctx, name, image) if err != nil { return err } if !exists { - err = Pull(ctx, "postgres:latest") + err = Pull(ctx, image) if err != nil { return err } } config := container.Config{ - Image: "postgres:latest", + Image: image, Env: []string{"POSTGRES_PASSWORD=secret"}, User: "postgres", Cmd: []string{"postgres"},