diff --git a/engine/internal/embeddedui/embedded_ui.go b/engine/internal/embeddedui/embedded_ui.go index 7d2896e952035ad165ac3b627c4d804133289f54..994a9c4783520bc704f31f1334c487ecd6674544 100644 --- a/engine/internal/embeddedui/embedded_ui.go +++ b/engine/internal/embeddedui/embedded_ui.go @@ -13,7 +13,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" @@ -95,54 +94,37 @@ func (ui *UIManager) Run(ctx context.Context) error { return fmt.Errorf("failed to prepare Docker image: %w", err) } - var containerID = "" - - // try to fetch an existing UI container - containerData, err := ui.docker.ContainerInspect(ctx, getEmbeddedUIName(ui.engProps.InstanceID)) - - if err == nil { - containerID = containerData.ID - } - - if containerID == "" { - embeddedUI, err := ui.docker.ContainerCreate(ctx, - &container.Config{ - Labels: map[string]string{ - cont.DBLabSatelliteLabel: cont.DBLabEmbeddedUILabel, - cont.DBLabInstanceIDLabel: ui.engProps.InstanceID, - cont.DBLabEngineNameLabel: ui.engProps.ContainerName, - }, - Image: ui.cfg.DockerImage, - Env: []string{ - EnvEngineName + "=" + ui.engProps.ContainerName, - EnvEnginePort + "=" + strconv.FormatUint(uint64(ui.engProps.EnginePort), 10), - }, - Healthcheck: &container.HealthConfig{ - Interval: healthCheckInterval, - Timeout: healthCheckTimeout, - Retries: healthCheckRetries, - }, + containerID, err := tools.CreateContainerIfMissing(ctx, ui.docker, getEmbeddedUIName(ui.engProps.InstanceID), + &container.Config{ + Labels: map[string]string{ + cont.DBLabSatelliteLabel: cont.DBLabEmbeddedUILabel, + cont.DBLabInstanceIDLabel: ui.engProps.InstanceID, + cont.DBLabEngineNameLabel: ui.engProps.ContainerName, + }, + Image: ui.cfg.DockerImage, + Env: []string{ + EnvEngineName + "=" + ui.engProps.ContainerName, + EnvEnginePort + "=" + strconv.FormatUint(uint64(ui.engProps.EnginePort), 10), }, - &container.HostConfig{ - PortBindings: map[nat.Port][]nat.PortBinding{ - "80/tcp": { - { - HostIP: ui.cfg.Host, - HostPort: strconv.Itoa(ui.cfg.Port), - }, + Healthcheck: &container.HealthConfig{ + Interval: healthCheckInterval, + Timeout: healthCheckTimeout, + Retries: healthCheckRetries, + }, + }, + &container.HostConfig{ + PortBindings: map[nat.Port][]nat.PortBinding{ + "80/tcp": { + { + HostIP: ui.cfg.Host, + HostPort: strconv.Itoa(ui.cfg.Port), }, }, }, - &network.NetworkingConfig{}, - nil, - getEmbeddedUIName(ui.engProps.InstanceID), - ) - - if err != nil { - return fmt.Errorf("failed to prepare Docker image for embedded UI: %w", err) - } + }) - containerID = embeddedUI.ID + if err != nil { + return fmt.Errorf("failed to prepare Docker image for embedded UI: %w", err) } if err := networks.Connect(ctx, ui.docker, ui.engProps.InstanceID, containerID); err != nil { diff --git a/engine/internal/retrieval/engine/postgres/logical/dump.go b/engine/internal/retrieval/engine/postgres/logical/dump.go index afa1fcec7c66fb7cef485ac85dca6f84f593b3c2..a6bd1b4615cf61e837df0e4ce2f8cedf7bc3ae31 100644 --- a/engine/internal/retrieval/engine/postgres/logical/dump.go +++ b/engine/internal/retrieval/engine/postgres/logical/dump.go @@ -15,7 +15,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/jackc/pgx/v4" "github.com/pkg/errors" @@ -30,7 +29,6 @@ import ( "gitlab.com/postgres-ai/database-lab/v3/internal/retrieval/engine/postgres/tools/defaults" "gitlab.com/postgres-ai/database-lab/v3/internal/retrieval/engine/postgres/tools/health" "gitlab.com/postgres-ai/database-lab/v3/internal/retrieval/options" - "gitlab.com/postgres-ai/database-lab/v3/pkg/config/global" "gitlab.com/postgres-ai/database-lab/v3/pkg/log" ) @@ -270,16 +268,13 @@ func (d *DumpJob) Run(ctx context.Context) (err error) { return errors.Wrap(err, "failed to generate PostgreSQL password") } - dumpCont, err := d.dockerClient.ContainerCreate(ctx, d.buildContainerConfig(pwd), hostConfig, &network.NetworkingConfig{}, - nil, d.dumpContainerName(), - ) - if err != nil { - log.Err(err) + containerID, err := tools.CreateContainerIfMissing(ctx, d.dockerClient, d.dumpContainerName(), d.buildContainerConfig(pwd), hostConfig) - return errors.Wrapf(err, "failed to create container %q", d.dumpContainerName()) + if err != nil { + return fmt.Errorf("failed to create container %q %w", d.dumpContainerName(), err) } - defer tools.RemoveContainer(ctx, d.dockerClient, dumpCont.ID, cont.StopTimeout) + defer tools.RemoveContainer(ctx, d.dockerClient, containerID, cont.StopTimeout) defer func() { if err != nil { @@ -287,9 +282,9 @@ func (d *DumpJob) Run(ctx context.Context) (err error) { } }() - log.Msg(fmt.Sprintf("Running container: %s. ID: %v", d.dumpContainerName(), dumpCont.ID)) + log.Msg(fmt.Sprintf("Running container: %s. ID: %v", d.dumpContainerName(), containerID)) - if err := d.dockerClient.ContainerStart(ctx, dumpCont.ID, types.ContainerStartOptions{}); err != nil { + if err := d.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { return errors.Wrapf(err, "failed to start container %q", d.dumpContainerName()) } @@ -299,13 +294,13 @@ func (d *DumpJob) Run(ctx context.Context) (err error) { log.Msg("Waiting for container readiness") - if err := tools.MakeDir(ctx, d.dockerClient, dumpCont.ID, tmpDBLabPGDataDir); err != nil { + if err := tools.MakeDir(ctx, d.dockerClient, containerID, tmpDBLabPGDataDir); err != nil { return err } dataDir := d.fsPool.DataDir() - if err := tools.CheckContainerReadiness(ctx, d.dockerClient, dumpCont.ID); err != nil { + if err := tools.CheckContainerReadiness(ctx, d.dockerClient, containerID); err != nil { var errHealthCheck *tools.ErrHealthCheck if !errors.As(err, &errHealthCheck) { return errors.Wrap(err, "failed to readiness check") @@ -316,13 +311,13 @@ func (d *DumpJob) Run(ctx context.Context) (err error) { pgDataDir = dataDir } - if err := setupPGData(ctx, d.dockerClient, pgDataDir, dumpCont.ID); err != nil { + if err := setupPGData(ctx, d.dockerClient, pgDataDir, containerID); err != nil { return errors.Wrap(err, "failed to set up Postgres data") } } if d.DumpOptions.Restore.Enabled && len(d.DumpOptions.Restore.Configs) > 0 { - if err := updateConfigs(ctx, d.dockerClient, dataDir, dumpCont.ID, d.DumpOptions.Restore.Configs); err != nil { + if err := updateConfigs(ctx, d.dockerClient, dataDir, containerID, d.DumpOptions.Restore.Configs); err != nil { return errors.Wrap(err, "failed to update configs") } } @@ -336,12 +331,12 @@ func (d *DumpJob) Run(ctx context.Context) (err error) { } } - if err := d.cleanupDumpLocation(ctx, dumpCont.ID, dbList); err != nil { + if err := d.cleanupDumpLocation(ctx, containerID, dbList); err != nil { return err } for dbName, dbDetails := range dbList { - if err := d.dumpDatabase(ctx, dumpCont.ID, dbName, dbDetails); err != nil { + if err := d.dumpDatabase(ctx, containerID, dbName, dbDetails); err != nil { return errors.Wrapf(err, "failed to dump the database %s", dbName) } } @@ -358,11 +353,11 @@ func (d *DumpJob) Run(ctx context.Context) (err error) { log.Msg("Running analyze command: ", analyzeCmd) - if err := tools.ExecCommand(ctx, d.dockerClient, dumpCont.ID, types.ExecConfig{Cmd: analyzeCmd}); err != nil { + if err := tools.ExecCommand(ctx, d.dockerClient, containerID, types.ExecConfig{Cmd: analyzeCmd}); err != nil { return errors.Wrap(err, "failed to recalculate statistics after restore") } - if err := tools.StopPostgres(ctx, d.dockerClient, dumpCont.ID, dataDir, tools.DefaultStopTimeout); err != nil { + if err := tools.StopPostgres(ctx, d.dockerClient, containerID, dataDir, tools.DefaultStopTimeout); err != nil { return errors.Wrap(err, "failed to stop Postgres instance") } } diff --git a/engine/internal/retrieval/engine/postgres/logical/dump_integration_test.go b/engine/internal/retrieval/engine/postgres/logical/dump_integration_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7306f03a5c4dbdb4002c95b727bbeade8f33efad --- /dev/null +++ b/engine/internal/retrieval/engine/postgres/logical/dump_integration_test.go @@ -0,0 +1,91 @@ +//go:build integration +// +build integration + +/* +2021 © Postgres.ai +*/ + +package logical + +import ( + "context" + "fmt" + "math/rand" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/client" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + dockerutils "gitlab.com/postgres-ai/database-lab/v3/internal/provision/docker" + "gitlab.com/postgres-ai/database-lab/v3/internal/provision/resources" + "gitlab.com/postgres-ai/database-lab/v3/internal/retrieval/config" + "gitlab.com/postgres-ai/database-lab/v3/internal/retrieval/engine/postgres/tools" + "gitlab.com/postgres-ai/database-lab/v3/pkg/config/global" +) + +func TestStartExisingDumpContainer(t *testing.T) { + t.Parallel() + ctx := context.Background() + + docker, err := client.NewClientWithOpts(client.FromEnv) + require.NoError(t, err) + + // create dump job + + source := rand.NewSource(time.Now().UnixNano()) + random := rand.New(source) + + engProps := global.EngineProps{ + InstanceID: fmt.Sprintf("dumpjob-%d", random.Intn(10000)), + } + + job, err := NewDumpJob( + config.JobConfig{ + Spec: config.JobSpec{Name: "test"}, + FSPool: &resources.Pool{ + DataSubDir: t.TempDir(), + }, + Docker: docker, + }, + &global.Config{}, + engProps, + ) + assert.NoError(t, err) + job.DockerImage = "postgresai/extended-postgres:14" + job.DumpOptions.DumpLocation = t.TempDir() + + err = dockerutils.PrepareImage(ctx, docker, job.DockerImage) + assert.NoError(t, err) + + // create dump container and stop it + container, err := docker.ContainerCreate(ctx, job.buildContainerConfig(""), nil, &network.NetworkingConfig{}, + nil, job.dumpContainerName(), + ) + assert.NoError(t, err) + + // clean container in case of any error + defer tools.RemoveContainer(ctx, docker, container.ID, 10*time.Second) + + job.Run(ctx) + + // list containers and check that container job container got processed + filterArgs := filters.NewArgs() + filterArgs.Add("name", job.dumpContainerName()) + + list, err := docker.ContainerList( + ctx, + types.ContainerListOptions{ + All: false, + Filters: filterArgs, + }, + ) + + require.NoError(t, err) + assert.Empty(t, list) + +} diff --git a/engine/internal/retrieval/engine/postgres/logical/restore.go b/engine/internal/retrieval/engine/postgres/logical/restore.go index 3caa4ab808dd7a13ad69c00a0546a7f8dc6a295c..8a6953cf8a7470072d2dad932850fc390afc2323 100644 --- a/engine/internal/retrieval/engine/postgres/logical/restore.go +++ b/engine/internal/retrieval/engine/postgres/logical/restore.go @@ -20,7 +20,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/docker/docker/pkg/archive" "github.com/pkg/errors" @@ -195,18 +194,13 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) { return errors.Wrap(err, "failed to generate PostgreSQL password") } - restoreCont, err := r.dockerClient.ContainerCreate(ctx, - r.buildContainerConfig(pwd), - hostConfig, - &network.NetworkingConfig{}, - nil, - r.restoreContainerName(), - ) + containerID, err := tools.CreateContainerIfMissing(ctx, r.dockerClient, r.restoreContainerName(), r.buildContainerConfig(pwd), hostConfig) + if err != nil { - return errors.Wrapf(err, "failed to create container %q", r.restoreContainerName()) + return fmt.Errorf("failed to create container %q %w", r.restoreContainerName(), err) } - defer tools.RemoveContainer(ctx, r.dockerClient, restoreCont.ID, cont.StopTimeout) + defer tools.RemoveContainer(ctx, r.dockerClient, containerID, cont.StopTimeout) defer func() { if err != nil { @@ -214,9 +208,9 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) { } }() - log.Msg(fmt.Sprintf("Running container: %s. ID: %v", r.restoreContainerName(), restoreCont.ID)) + log.Msg(fmt.Sprintf("Running container: %s. ID: %v", r.restoreContainerName(), containerID)) - if err := r.dockerClient.ContainerStart(ctx, restoreCont.ID, types.ContainerStartOptions{}); err != nil { + if err := r.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { return errors.Wrapf(err, "failed to start container %q", r.restoreContainerName()) } @@ -224,24 +218,24 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) { log.Msg("Waiting for container readiness") - if err := tools.CheckContainerReadiness(ctx, r.dockerClient, restoreCont.ID); err != nil { + if err := tools.CheckContainerReadiness(ctx, r.dockerClient, containerID); err != nil { var errHealthCheck *tools.ErrHealthCheck if !errors.As(err, &errHealthCheck) { return errors.Wrap(err, "failed to readiness check") } - if err := setupPGData(ctx, r.dockerClient, dataDir, restoreCont.ID); err != nil { + if err := setupPGData(ctx, r.dockerClient, dataDir, containerID); err != nil { return errors.Wrap(err, "failed to set up Postgres data") } } if len(r.RestoreOptions.Configs) > 0 { - if err := updateConfigs(ctx, r.dockerClient, dataDir, restoreCont.ID, r.RestoreOptions.Configs); err != nil { + if err := updateConfigs(ctx, r.dockerClient, dataDir, containerID, r.RestoreOptions.Configs); err != nil { return errors.Wrap(err, "failed to update configs") } } - dbList, err := r.getDBList(ctx, restoreCont.ID) + dbList, err := r.getDBList(ctx, containerID) if err != nil { return err } @@ -249,7 +243,7 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) { log.Dbg("Database List to restore: ", dbList) for dbName, dbDefinition := range dbList { - if err := r.restoreDB(ctx, restoreCont.ID, dbName, dbDefinition); err != nil { + if err := r.restoreDB(ctx, containerID, dbName, dbDefinition); err != nil { return errors.Wrap(err, "failed to restore a database") } } @@ -261,11 +255,11 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) { log.Msg("Running analyze command: ", analyzeCmd) - if err := tools.ExecCommand(ctx, r.dockerClient, restoreCont.ID, types.ExecConfig{Cmd: analyzeCmd}); err != nil { + if err := tools.ExecCommand(ctx, r.dockerClient, containerID, types.ExecConfig{Cmd: analyzeCmd}); err != nil { return errors.Wrap(err, "failed to recalculate statistics after restore") } - if err := tools.StopPostgres(ctx, r.dockerClient, restoreCont.ID, dataDir, tools.DefaultStopTimeout); err != nil { + if err := tools.StopPostgres(ctx, r.dockerClient, containerID, dataDir, tools.DefaultStopTimeout); err != nil { return errors.Wrap(err, "failed to stop Postgres instance") } diff --git a/engine/internal/retrieval/engine/postgres/physical/physical.go b/engine/internal/retrieval/engine/postgres/physical/physical.go index 0a70093bdcd9ba8d5f1ca88eb75e9cf2f4dcb721..d10f6f526f8b0d52cbb66922d947127682ef90e3 100644 --- a/engine/internal/retrieval/engine/postgres/physical/physical.go +++ b/engine/internal/retrieval/engine/postgres/physical/physical.go @@ -16,7 +16,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/pkg/errors" @@ -291,17 +290,17 @@ func (r *RestoreJob) startContainer(ctx context.Context, containerName string, c return "", err } - newContainer, err := r.dockerClient.ContainerCreate(ctx, containerConfig, hostConfig, &network.NetworkingConfig{}, nil, - containerName) + containerID, err := tools.CreateContainerIfMissing(ctx, r.dockerClient, containerName, containerConfig, hostConfig) + if err != nil { - return "", errors.Wrapf(err, "failed to create container %s", containerName) + return "", fmt.Errorf("failed to create container %q %w", containerName, err) } - if err = r.dockerClient.ContainerStart(ctx, newContainer.ID, types.ContainerStartOptions{}); err != nil { + if err = r.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { return "", errors.Wrapf(err, "failed to start container %s", containerName) } - return newContainer.ID, nil + return containerID, nil } func (r *RestoreJob) syncInstanceName() string { diff --git a/engine/internal/retrieval/engine/postgres/snapshot/logical.go b/engine/internal/retrieval/engine/postgres/snapshot/logical.go index 8f812429b7a66b3fb7adbaee67afa7b9947900a2..88e7a909a5506e38d4bca9343ec4e5d62f518685 100644 --- a/engine/internal/retrieval/engine/postgres/snapshot/logical.go +++ b/engine/internal/retrieval/engine/postgres/snapshot/logical.go @@ -13,7 +13,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/pkg/errors" @@ -214,18 +213,14 @@ func (s *LogicalInitial) runPreprocessingQueries(ctx context.Context, dataDir st } // Run patch container. - patchCont, err := s.dockerClient.ContainerCreate(ctx, - s.buildContainerConfig(dataDir, patchImage, pwd), - hostConfig, - &network.NetworkingConfig{}, - nil, - s.patchContainerName(), - ) + containerID, err := tools.CreateContainerIfMissing(ctx, s.dockerClient, s.patchContainerName(), + s.buildContainerConfig(dataDir, patchImage, pwd), hostConfig) + if err != nil { - return errors.Wrap(err, "failed to create container") + return fmt.Errorf("failed to create container %w", err) } - defer tools.RemoveContainer(ctx, s.dockerClient, patchCont.ID, cont.StopPhysicalTimeout) + defer tools.RemoveContainer(ctx, s.dockerClient, containerID, cont.StopPhysicalTimeout) defer func() { if err != nil { @@ -234,20 +229,20 @@ func (s *LogicalInitial) runPreprocessingQueries(ctx context.Context, dataDir st } }() - log.Msg(fmt.Sprintf("Running container: %s. ID: %v", s.patchContainerName(), patchCont.ID)) + log.Msg(fmt.Sprintf("Running container: %s. ID: %v", s.patchContainerName(), containerID)) - if err := s.dockerClient.ContainerStart(ctx, patchCont.ID, types.ContainerStartOptions{}); err != nil { + if err := s.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { return errors.Wrap(err, "failed to start container") } log.Msg("Starting PostgreSQL and waiting for readiness") log.Msg(fmt.Sprintf("View logs using the command: %s %s", tools.ViewLogsCmd, s.patchContainerName())) - if err := tools.CheckContainerReadiness(ctx, s.dockerClient, patchCont.ID); err != nil { + if err := tools.CheckContainerReadiness(ctx, s.dockerClient, containerID); err != nil { return errors.Wrap(err, "failed to readiness check") } - if err := s.queryProcessor.applyPreprocessingQueries(ctx, patchCont.ID); err != nil { + if err := s.queryProcessor.applyPreprocessingQueries(ctx, containerID); err != nil { return errors.Wrap(err, "failed to run preprocessing queries") } diff --git a/engine/internal/retrieval/engine/postgres/snapshot/physical.go b/engine/internal/retrieval/engine/postgres/snapshot/physical.go index e93a9a7d4ec020f30b3a1d671b81719e19f50267..a9f43bf41e8bfb6cb575af916c86b0b5a19bc9a6 100644 --- a/engine/internal/retrieval/engine/postgres/snapshot/physical.go +++ b/engine/internal/retrieval/engine/postgres/snapshot/physical.go @@ -19,7 +19,6 @@ import ( "github.com/araddon/dateparse" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/pkg/errors" "github.com/robfig/cron/v3" @@ -539,19 +538,14 @@ func (p *PhysicalInitial) promoteInstance(ctx context.Context, clonePath string, } // Run promotion container. - promoteCont, err := p.dockerClient.ContainerCreate(ctx, - p.buildContainerConfig(clonePath, promoteImage, pwd, recoveryConfig[targetActionOption]), - hostConfig, - &network.NetworkingConfig{}, - nil, - p.promoteContainerName(), - ) + containerID, err := tools.CreateContainerIfMissing(ctx, p.dockerClient, p.promoteContainerName(), + p.buildContainerConfig(clonePath, promoteImage, pwd, recoveryConfig[targetActionOption]), hostConfig) if err != nil { - return errors.Wrap(err, "failed to create container") + return fmt.Errorf("failed to create container %w", err) } - defer tools.RemoveContainer(ctx, p.dockerClient, promoteCont.ID, cont.StopPhysicalTimeout) + defer tools.RemoveContainer(ctx, p.dockerClient, containerID, cont.StopPhysicalTimeout) defer func() { if err != nil { @@ -560,14 +554,14 @@ func (p *PhysicalInitial) promoteInstance(ctx context.Context, clonePath string, } }() - log.Msg(fmt.Sprintf("Running container: %s. ID: %v", p.promoteContainerName(), promoteCont.ID)) + log.Msg(fmt.Sprintf("Running container: %s. ID: %v", p.promoteContainerName(), containerID)) - if err := p.dockerClient.ContainerStart(ctx, promoteCont.ID, types.ContainerStartOptions{}); err != nil { + if err := p.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { return errors.Wrap(err, "failed to start container") } if syState.DSA == "" { - dsa, err := p.getDSAFromWAL(ctx, cfgManager.GetPgVersion(), promoteCont.ID, clonePath) + dsa, err := p.getDSAFromWAL(ctx, cfgManager.GetPgVersion(), containerID, clonePath) if err != nil { log.Dbg("cannot extract DSA form WAL files: ", err) } @@ -582,11 +576,11 @@ func (p *PhysicalInitial) promoteInstance(ctx context.Context, clonePath string, log.Msg("Starting PostgreSQL and waiting for readiness") log.Msg(fmt.Sprintf("View logs using the command: %s %s", tools.ViewLogsCmd, p.promoteContainerName())) - if err := tools.CheckContainerReadiness(ctx, p.dockerClient, promoteCont.ID); err != nil { + if err := tools.CheckContainerReadiness(ctx, p.dockerClient, containerID); err != nil { return errors.Wrap(err, "failed to readiness check") } - shouldBePromoted, err := p.checkRecovery(ctx, promoteCont.ID) + shouldBePromoted, err := p.checkRecovery(ctx, containerID) if err != nil { return errors.Wrap(err, "failed to check recovery mode") } @@ -596,11 +590,11 @@ func (p *PhysicalInitial) promoteInstance(ctx context.Context, clonePath string, // Detect dataStateAt. if shouldBePromoted == "t" { // Promote PGDATA. - if err := p.runPromoteCommand(ctx, promoteCont.ID, clonePath); err != nil { + if err := p.runPromoteCommand(ctx, containerID, clonePath); err != nil { return errors.Wrapf(err, "failed to promote PGDATA: %s", clonePath) } - isInRecovery, err := p.checkRecovery(ctx, promoteCont.ID) + isInRecovery, err := p.checkRecovery(ctx, containerID) if err != nil { return errors.Wrap(err, "failed to check recovery mode after promotion") } @@ -610,18 +604,18 @@ func (p *PhysicalInitial) promoteInstance(ctx context.Context, clonePath string, } } - if err := p.markDSA(ctx, syState.DSA, promoteCont.ID, clonePath, cfgManager.GetPgVersion()); err != nil { + if err := p.markDSA(ctx, syState.DSA, containerID, clonePath, cfgManager.GetPgVersion()); err != nil { return errors.Wrap(err, "failed to mark dataStateAt") } if p.queryProcessor != nil { - if err := p.queryProcessor.applyPreprocessingQueries(ctx, promoteCont.ID); err != nil { + if err := p.queryProcessor.applyPreprocessingQueries(ctx, containerID); err != nil { return errors.Wrap(err, "failed to run preprocessing queries") } } // Checkpoint. - if err := p.checkpoint(ctx, promoteCont.ID); err != nil { + if err := p.checkpoint(ctx, containerID); err != nil { return err } @@ -642,9 +636,9 @@ func (p *PhysicalInitial) promoteInstance(ctx context.Context, clonePath string, return errors.Wrap(err, "failed to store prepared configuration") } - if err := tools.StopPostgres(ctx, p.dockerClient, promoteCont.ID, clonePath, tools.DefaultStopTimeout); err != nil { + if err := tools.StopPostgres(ctx, p.dockerClient, containerID, clonePath, tools.DefaultStopTimeout); err != nil { log.Msg("Failed to stop Postgres", err) - tools.PrintContainerLogs(ctx, p.dockerClient, promoteCont.ID) + tools.PrintContainerLogs(ctx, p.dockerClient, containerID) } return nil diff --git a/engine/internal/retrieval/engine/postgres/tools/tools.go b/engine/internal/retrieval/engine/postgres/tools/tools.go index a02bc43eb49f3339b5a7031ee7afaf4247e7e289..ac23a2957dd35c743f05fad8017d985f8f7c2bba 100644 --- a/engine/internal/retrieval/engine/postgres/tools/tools.go +++ b/engine/internal/retrieval/engine/postgres/tools/tools.go @@ -24,6 +24,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/jsonmessage" @@ -521,3 +522,23 @@ func processAttachResponse(ctx context.Context, reader io.Reader) ([]byte, error return bytes.TrimSpace(outBuf.Bytes()), nil } + +// CreateContainerIfMissing create a new container if there is no other container with the same name, if the container +// exits returns existing container id. +func CreateContainerIfMissing(ctx context.Context, docker *client.Client, containerName string, + config *container.Config, hostConfig *container.HostConfig) (string, error) { + containerData, err := docker.ContainerInspect(ctx, containerName) + + if err == nil { + return containerData.ID, nil + } + + createdContainer, err := docker.ContainerCreate(ctx, config, hostConfig, &network.NetworkingConfig{}, + nil, containerName, + ) + if err != nil { + return "", err + } + + return createdContainer.ID, nil +}