@@ -21,6 +21,38 @@ import (
21
21
)
22
22
23
23
const (
24
+ // bashDataDirectory is a Bash function that ensures a directory has the correct permissions for PostgreSQL data.
25
+ //
26
+ // Postgres requires its data directories be writable by only itself.
27
+ // Pod "securityContext.fsGroup" sets g+w on directories for *some* storage providers.
28
+ // Ensure the current user owns the directory, and remove group-write permission.
29
+ //
30
+ // - https://www.postgresql.org/docs/current/creating-cluster.html
31
+ // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/postmaster/postmaster.c;hb=REL_10_0#l1522
32
+ // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/utils/init/miscinit.c;hb=REL_11_0#l142
33
+ // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/utils/init/miscinit.c;hb=REL_17_0#l386
34
+ // - https://issue.k8s.io/93802#issuecomment-717646167
35
+ //
36
+ // During CREATE TABLESPACE, Postgres sets the permissions of a tablespace directory to match the data directory.
37
+ //
38
+ // - https://www.postgresql.org/docs/current/manage-ag-tablespaces.html
39
+ // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/commands/tablespace.c;hb=REL_14_0#l600
40
+ // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/common/file_perm.c;hb=REL_14_0#l27
41
+ //
42
+ bashDataDirectory = `dataDirectory() {` +
43
+ // When the directory does not exist, create it with the correct permissions.
44
+ // When the directory has the correct owner, set the correct permissions.
45
+ ` if [[ ! -e "$1" || -O "$1" ]]; then install --directory --mode=0750 "$1";` +
46
+ //
47
+ // The directory exists but its owner is wrong.
48
+ // When it is writable, the set-group-ID bit indicates that "fsGroup" probably ran on its contents making them safe to use.
49
+ // In this case, we can make a new directory (owned by this user) and refill it.
50
+ ` elif [[ -w "$1" && -g "$1" ]]; then recreate "$1" '0750';` +
51
+ //
52
+ // The directory exists, its owner is wrong, and it is not writable.
53
+ // This is probably fatal, but indicate failure to let the caller decide.
54
+ ` else false; fi; }`
55
+
24
56
// bashHalt is a Bash function that prints its arguments to stderr then
25
57
// exits with a non-zero status. It uses the exit status of the prior
26
58
// command if that was not zero.
@@ -327,47 +359,31 @@ func startupCommand(
327
359
version := fmt .Sprint (cluster .Spec .PostgresVersion )
328
360
walDir := WALDirectory (cluster , instance )
329
361
330
- // If the user requests tablespaces, we want to make sure the directories exist with the
331
- // correct owner and permissions.
332
- tablespaceCmd := ""
333
- if feature .Enabled (ctx , feature .TablespaceVolumes ) {
334
- // This command checks if a dir exists and if not, creates it;
335
- // if the dir does exist, then we `recreate` it to make sure the owner is correct;
336
- // if the dir exists with the wrong owner and is not writeable, we error.
337
- // This is the same behavior we use for the main PGDATA directory.
338
- // Note: Postgres requires the tablespace directory to be "an existing, empty directory
339
- // that is owned by the PostgreSQL operating system user."
340
- // - https://www.postgresql.org/docs/current/manage-ag-tablespaces.html
341
- // However, unlike the PGDATA directory, Postgres will _not_ error out
342
- // if the permissions are wrong on the tablespace directory.
343
- // Instead, when a tablespace is created in Postgres, Postgres will `chmod` the
344
- // tablespace directory to match permissions on the PGDATA directory (either 700 or 750).
345
- // Postgres setting the tablespace directory permissions:
346
- // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/commands/tablespace.c;hb=REL_14_0#l600
347
- // Postgres choosing directory permissions:
348
- // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/common/file_perm.c;hb=REL_14_0#l27
349
- // Note: This permission change seems to happen only when the tablespace is created in Postgres.
350
- // If the user manually `chmod`'ed the directory after the creation of the tablespace, Postgres
351
- // would not attempt to change the directory permissions.
352
- // Note: as noted below, we mount the tablespace directory to the mountpoint `/tablespaces/NAME`,
353
- // and so we add the subdirectory `data` in order to set the permissions.
354
- checkInstallRecreateCmd := strings .Join ([]string {
355
- `if [[ ! -e "${tablespace_dir}" || -O "${tablespace_dir}" ]]; then` ,
356
- `install --directory --mode=0750 "${tablespace_dir}"` ,
357
- `elif [[ -w "${tablespace_dir}" && -g "${tablespace_dir}" ]]; then` ,
358
- `recreate "${tablespace_dir}" '0750'` ,
359
- `else (halt Permissions!); fi ||` ,
360
- `halt "$(permissions "${tablespace_dir}" ||:)"` ,
361
- }, "\n " )
362
+ mkdirs := make ([]string , 0 , 7 + len (instance .TablespaceVolumes ))
363
+ mkdirs = append (mkdirs , `dataDirectory "${postgres_data_directory}" || halt "$(permissions "${postgres_data_directory}" ||:)"` )
362
364
365
+ // If the user requests tablespaces, we want to make sure the directories exist with the correct owner and permissions.
366
+ //
367
+ // The path for tablespaces volumes is /tablespaces/NAME/data -- the `data` directory is so we can arrange the permissions.
368
+ if feature .Enabled (ctx , feature .TablespaceVolumes ) {
363
369
for _ , tablespace := range instance .TablespaceVolumes {
364
- // The path for tablespaces volumes is /tablespaces/NAME/data
365
- // -- the `data` path is added so that we can arrange the permissions.
366
- tablespaceCmd = tablespaceCmd + "\n tablespace_dir=/tablespaces/" + tablespace .Name + "/data" + "\n " +
367
- checkInstallRecreateCmd
370
+ dir := shell .QuoteWord ("/tablespaces/" + tablespace .Name + "/data" )
371
+ mkdirs = append (mkdirs , `dataDirectory ` + dir + ` || halt "$(permissions ` + dir + ` ||:)"` )
368
372
}
369
373
}
370
374
375
+ // These directories are outside "data_directory" and can be created.
376
+ mkdirs = append (mkdirs ,
377
+ `(` + shell .MakeDirectories (dataMountPath , LogDirectory ())+ `) ||` ,
378
+ `halt "$(permissions ` + shell .QuoteWord (LogDirectory ())+ ` ||:)"` ,
379
+
380
+ `(` + shell .MakeDirectories (dataMountPath , naming .PatroniPGDataLogPath )+ `) ||` ,
381
+ `halt "$(permissions ` + shell .QuoteWord (naming .PatroniPGDataLogPath )+ ` ||:)"` ,
382
+
383
+ `(` + shell .MakeDirectories (dataMountPath , naming .PGBackRestPGDataLogPath )+ `) ||` ,
384
+ `halt "$(permissions ` + shell .QuoteWord (naming .PGBackRestPGDataLogPath )+ ` ||:)"` ,
385
+ )
386
+
371
387
pg_rewind_override := ""
372
388
if config .FetchKeyCommand (& cluster .Spec ) != "" {
373
389
// Quoting "EOF" disables parameter substitution during write.
@@ -384,6 +400,9 @@ chmod +x /tmp/pg_rewind_tde.sh
384
400
script := strings .Join ([]string {
385
401
`declare -r expected_major_version="$1" pgwal_directory="$2"` ,
386
402
403
+ // Function to create a Postgres data directory.
404
+ bashDataDirectory ,
405
+
387
406
// Function to print the permissions of a file or directory and its parents.
388
407
bashPermissions ,
389
408
@@ -425,42 +444,10 @@ chmod +x /tmp/pg_rewind_tde.sh
425
444
426
445
// Determine if the data directory has been prepared for bootstrapping the cluster
427
446
`bootstrap_dir="${postgres_data_directory}_bootstrap"` ,
428
- `[[ -d "${bootstrap_dir}" ]] && results 'bootstrap directory' "${bootstrap_dir}"` ,
429
- `[[ -d "${bootstrap_dir}" ]] && postgres_data_directory="${bootstrap_dir}"` ,
430
-
431
- // PostgreSQL requires its directory to be writable by only itself.
432
- // Pod "securityContext.fsGroup" sets g+w on directories for *some*
433
- // storage providers. Ensure the current user owns the directory, and
434
- // remove group-write permission.
435
- // - https://www.postgresql.org/docs/current/creating-cluster.html
436
- // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/postmaster/postmaster.c;hb=REL_10_0#l1522
437
- // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/utils/init/miscinit.c;hb=REL_11_0#l142
438
- // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/utils/init/miscinit.c;hb=REL_17_0#l386
439
- // - https://issue.k8s.io/93802#issuecomment-717646167
440
- //
441
- // When the directory does not exist, create it with the correct permissions.
442
- // When the directory has the correct owner, set the correct permissions.
443
- `if [[ ! -e "${postgres_data_directory}" || -O "${postgres_data_directory}" ]]; then` ,
444
- `install --directory --mode=0750 "${postgres_data_directory}"` ,
445
- //
446
- // The directory exists but its owner is wrong. When it is writable,
447
- // the set-group-ID bit indicates that "fsGroup" probably ran on its
448
- // contents making them safe to use. In this case, we can make a new
449
- // directory (owned by this user) and refill it.
450
- `elif [[ -w "${postgres_data_directory}" && -g "${postgres_data_directory}" ]]; then` ,
451
- `recreate "${postgres_data_directory}" '0750'` ,
452
- //
453
- // The directory exists, its owner is wrong, and it is not writable.
454
- `else (halt Permissions!); fi ||` ,
455
- `halt "$(permissions "${postgres_data_directory}" ||:)"` ,
447
+ `[[ -d "${bootstrap_dir}" ]] && postgres_data_directory="${bootstrap_dir}" && results 'bootstrap directory' "${bootstrap_dir}"` ,
456
448
457
- // Create log directories.
458
- `(` + shell .MakeDirectories (dataMountPath , naming .PGBackRestPGDataLogPath ) + `) ||` ,
459
- `halt "$(permissions ` + naming .PGBackRestPGDataLogPath + ` ||:)"` ,
460
- `(` + shell .MakeDirectories (dataMountPath , naming .PatroniPGDataLogPath ) + `) ||` ,
461
- `halt "$(permissions ` + naming .PatroniPGDataLogPath + ` ||:)"` ,
462
- `(` + shell .MakeDirectories (dataMountPath , LogDirectory ()) + `) ||` ,
463
- `halt "$(permissions ` + LogDirectory () + ` ||:)"` ,
449
+ // Create directories for and related to the data directory.
450
+ strings .Join (mkdirs , "\n " ),
464
451
465
452
// Copy replication client certificate files
466
453
// from the /pgconf/tls/replication directory to the /tmp/replication directory in order
@@ -478,7 +465,6 @@ chmod +x /tmp/pg_rewind_tde.sh
478
465
// Add the pg_rewind wrapper script, if TDE is enabled.
479
466
pg_rewind_override ,
480
467
481
- tablespaceCmd ,
482
468
// When the data directory is empty, there's nothing more to do.
483
469
`[[ -f "${postgres_data_directory}/PG_VERSION" ]] || exit 0` ,
484
470
0 commit comments