@@ -8,16 +8,17 @@ import (
8
8
"context"
9
9
"fmt"
10
10
"os"
11
+ "path"
11
12
"strconv"
12
13
"strings"
13
14
"time"
14
15
15
16
"github.com/docker/docker/api/types"
16
17
"github.com/docker/docker/api/types/container"
18
+ "github.com/docker/docker/api/types/mount"
17
19
"github.com/docker/docker/api/types/network"
18
20
"github.com/docker/docker/client"
19
21
"github.com/pkg/errors"
20
-
21
22
"gitlab.com/postgres-ai/database-lab/v2/pkg/config/global"
22
23
"gitlab.com/postgres-ai/database-lab/v2/pkg/log"
23
24
"gitlab.com/postgres-ai/database-lab/v2/pkg/retrieval/config"
@@ -27,6 +28,7 @@ import (
27
28
"gitlab.com/postgres-ai/database-lab/v2/pkg/retrieval/engine/postgres/tools/defaults"
28
29
"gitlab.com/postgres-ai/database-lab/v2/pkg/retrieval/engine/postgres/tools/health"
29
30
"gitlab.com/postgres-ai/database-lab/v2/pkg/retrieval/options"
31
+ "gitlab.com/postgres-ai/database-lab/v2/pkg/services/provision/databases/postgres/pgconfig"
30
32
"gitlab.com/postgres-ai/database-lab/v2/pkg/services/provision/resources"
31
33
)
32
34
@@ -109,7 +111,8 @@ type Connection struct {
109
111
110
112
// ImmediateRestore contains options for direct data restore without saving the dump file on disk.
111
113
type ImmediateRestore struct {
112
- ForceInit bool `yaml:"forceInit"`
114
+ ForceInit bool `yaml:"forceInit"`
115
+ Configs map [string ]string `yaml:"configs"`
113
116
}
114
117
115
118
// NewDumpJob creates a new DumpJob.
@@ -230,6 +233,10 @@ func (d *DumpJob) Run(ctx context.Context) (err error) {
230
233
return errors .Wrap (err , "failed to scan pulling image response" )
231
234
}
232
235
236
+ if err := os .MkdirAll (d .DumpOptions .DumpLocation , 0666 ); err != nil {
237
+ return errors .Wrap (err , "failed to create a location directory" )
238
+ }
239
+
233
240
hostConfig , err := d .buildHostConfig (ctx )
234
241
if err != nil {
235
242
return errors .Wrap (err , "failed to build container host config" )
@@ -263,14 +270,29 @@ func (d *DumpJob) Run(ctx context.Context) (err error) {
263
270
return errors .Wrapf (err , "failed to start container %q" , d .dumpContainerName ())
264
271
}
265
272
273
+ if err := d .setupConnectionOptions (ctx ); err != nil {
274
+ return errors .Wrap (err , "failed to setup connection options" )
275
+ }
276
+
266
277
log .Msg ("Waiting for container readiness" )
267
278
279
+ dataDir := d .fsPool .DataDir ()
280
+
268
281
if err := tools .CheckContainerReadiness (ctx , d .dockerClient , dumpCont .ID ); err != nil {
269
- return errors .Wrap (err , "failed to readiness check" )
282
+ var errHealthCheck * tools.ErrHealthCheck
283
+ if ! errors .As (err , & errHealthCheck ) {
284
+ return errors .Wrap (err , "failed to readiness check" )
285
+ }
286
+
287
+ if err := setupPGData (ctx , d .dockerClient , dataDir , dumpCont .ID ); err != nil {
288
+ return errors .Wrap (err , "failed to set up Postgres data" )
289
+ }
270
290
}
271
291
272
- if err := d .setupConnectionOptions (ctx ); err != nil {
273
- return errors .Wrap (err , "failed to setup connection options" )
292
+ if d .DumpOptions .Restore != nil && len (d .DumpOptions .Restore .Configs ) > 0 {
293
+ if err := updateConfigs (ctx , d .dockerClient , dataDir , dumpCont .ID , d .DumpOptions .Restore .Configs ); err != nil {
294
+ return errors .Wrap (err , "failed to update configs" )
295
+ }
274
296
}
275
297
276
298
dumpCommand := d .buildLogicalDumpCommand ()
@@ -281,9 +303,10 @@ func (d *DumpJob) Run(ctx context.Context) (err error) {
281
303
}
282
304
283
305
if d .DumpOptions .DumpLocation != "" && d .DumpOptions .Restore == nil {
284
- if err := tools .ExecCommand (ctx , d .dockerClient , dumpCont .ID , types.ExecConfig {
285
- Cmd : []string {"rm" , "-rf" , d .DumpOptions .DumpLocation },
306
+ if out , err := tools .ExecCommandWithOutput (ctx , d .dockerClient , dumpCont .ID , types.ExecConfig {
307
+ Cmd : []string {"rm" , "-rf" , path . Join ( d .DumpOptions .DumpLocation , "*" ) },
286
308
}); err != nil {
309
+ log .Dbg (out )
287
310
return errors .Wrap (err , "failed to clean up dump location" )
288
311
}
289
312
}
@@ -317,6 +340,65 @@ func (d *DumpJob) Run(ctx context.Context) (err error) {
317
340
return nil
318
341
}
319
342
343
+ func setupPGData (ctx context.Context , dockerClient * client.Client , dataDir string , dumpContID string ) error {
344
+ isEmpty , err := tools .IsEmptyDirectory (dataDir )
345
+ if err != nil {
346
+ return errors .Wrap (err , "failed to explore the data directory" )
347
+ }
348
+
349
+ if ! isEmpty {
350
+ return nil
351
+ }
352
+
353
+ if err := tools .ExecCommand (ctx , dockerClient , dumpContID , types.ExecConfig {
354
+ Cmd : []string {"chown" , "-R" , "postgres" , dataDir },
355
+ }); err != nil {
356
+ return errors .Wrap (err , "failed to set permissions" )
357
+ }
358
+
359
+ if err := tools .InitDB (ctx , dockerClient , dumpContID , dataDir ); err != nil {
360
+ return errors .Wrap (err , "failed to init Postgres" )
361
+ }
362
+
363
+ log .Dbg ("Database has been initialized" )
364
+
365
+ if err := tools .StartPostgres (ctx , dockerClient , dumpContID , dataDir , tools .DefaultStopTimeout ); err != nil {
366
+ return errors .Wrap (err , "failed to init Postgres" )
367
+ }
368
+
369
+ log .Dbg ("Postgres has been started" )
370
+
371
+ return nil
372
+ }
373
+
374
+ func updateConfigs (ctx context.Context , dockerClient * client.Client , dataDir , contID string , configs map [string ]string ) error {
375
+ log .Dbg ("Stopping container to update configuration" )
376
+
377
+ tools .StopContainer (ctx , dockerClient , contID , cont .StopTimeout )
378
+
379
+ // Run basic PostgreSQL configuration.
380
+ cfgManager , err := pgconfig .NewCorrector (dataDir )
381
+ if err != nil {
382
+ return errors .Wrap (err , "failed to create a config manager" )
383
+ }
384
+
385
+ if err := cfgManager .AppendGeneralConfig (configs ); err != nil {
386
+ return errors .Wrap (err , "failed to append general configuration" )
387
+ }
388
+
389
+ if err := dockerClient .ContainerStart (ctx , contID , types.ContainerStartOptions {}); err != nil {
390
+ return errors .Wrapf (err , "failed to start container %q" , contID )
391
+ }
392
+
393
+ log .Dbg ("Waiting for container readiness" )
394
+
395
+ if err := tools .CheckContainerReadiness (ctx , dockerClient , contID ); err != nil {
396
+ return errors .Wrap (err , "failed to readiness check" )
397
+ }
398
+
399
+ return nil
400
+ }
401
+
320
402
// setupConnectionOptions prepares connection options to perform a logical dump.
321
403
func (d * DumpJob ) setupConnectionOptions (ctx context.Context ) error {
322
404
d .config .db = d .DumpOptions .Source .Connection
@@ -343,10 +425,7 @@ func (d *DumpJob) getEnvironmentVariables(password string) []string {
343
425
"POSTGRES_PASSWORD=" + password ,
344
426
}
345
427
346
- // Avoid initialization of PostgreSQL directory in case of preparing of a dump.
347
- if d .DumpOptions .Restore != nil {
348
- envs = append (envs , "PGDATA=" + d .fsPool .DataDir ())
349
- }
428
+ envs = append (envs , "PGDATA=" + d .fsPool .DataDir ())
350
429
351
430
if d .DumpOptions .Source .Type == sourceTypeLocal && d .DumpOptions .Source .Connection .Port == defaults .Port {
352
431
log .Msg (fmt .Sprintf ("The default PostgreSQL port is busy, trying to use an alternative one: %d" , reservePort ))
@@ -377,6 +456,12 @@ func (d *DumpJob) buildHostConfig(ctx context.Context) (*container.HostConfig, e
377
456
return nil , err
378
457
}
379
458
459
+ hostConfig .Mounts = append (hostConfig .Mounts , mount.Mount {
460
+ Type : mount .TypeBind ,
461
+ Source : d .DumpOptions .DumpLocation ,
462
+ Target : d .DumpOptions .DumpLocation ,
463
+ })
464
+
380
465
return hostConfig , nil
381
466
}
382
467
@@ -418,7 +503,7 @@ func (d *DumpJob) buildLogicalDumpCommand() []string {
418
503
// Define if restore directly or export to dump location.
419
504
if d .DumpOptions .Restore != nil {
420
505
dumpCmd = append (dumpCmd , "--format" , customFormat )
421
- dumpCmd = append (dumpCmd , d .buildLogicalRestoreCommand ()... )
506
+ dumpCmd = append (dumpCmd , d .buildLogicalRestoreCommand (d . DumpOptions . Source . Connection . DBName )... )
422
507
cmd := strings .Join (dumpCmd , " " )
423
508
424
509
log .Dbg (cmd )
@@ -431,10 +516,15 @@ func (d *DumpJob) buildLogicalDumpCommand() []string {
431
516
return dumpCmd
432
517
}
433
518
434
- func (d * DumpJob ) buildLogicalRestoreCommand () []string {
435
- restoreCmd := []string {"|" , "pg_restore" , "--username" , d .globalCfg .Database .User (), "--create" , "-- dbname" , defaults .DBName ,
519
+ func (d * DumpJob ) buildLogicalRestoreCommand (dbName string ) []string {
520
+ restoreCmd := []string {"|" , "pg_restore" , "--username" , d .globalCfg .Database .User (), "--dbname" , defaults .DBName ,
436
521
"--no-privileges" , "--no-owner" }
437
522
523
+ if dbName != defaults .DBName {
524
+ // To avoid recreating of the default database.
525
+ restoreCmd = append (restoreCmd , "--create" )
526
+ }
527
+
438
528
if d .Restore .ForceInit {
439
529
restoreCmd = append (restoreCmd , "--clean" , "--if-exists" )
440
530
}
0 commit comments