Skip to content

Commit 4eae062

Browse files
committed
Look in more directories for upgrade binaries
This makes major upgrades compatible with images from other distros. Issue: PGO-864
1 parent 07ac87a commit 4eae062

File tree

4 files changed

+57
-9
lines changed

4 files changed

+57
-9
lines changed

internal/controller/pgupgrade/jobs.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/crunchydata/postgres-operator/internal/feature"
2222
"github.com/crunchydata/postgres-operator/internal/initialize"
2323
"github.com/crunchydata/postgres-operator/internal/naming"
24+
"github.com/crunchydata/postgres-operator/internal/postgres"
2425
"github.com/crunchydata/postgres-operator/internal/shell"
2526
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
2627
)
@@ -87,13 +88,16 @@ func upgradeCommand(spec *v1beta1.PGUpgradeSettings, fetchKeyCommand string) []s
8788

8889
`section 'Step 2 of 7: Finding data and tools...'`,
8990

90-
// Expect Postgres executables at the Red Hat paths.
91+
// Find directories that contain the desired Postgres executables.
92+
`old_bin=$(` + postgres.ShellPath(oldVersion) + ` && command -v postgres)`,
93+
`old_bin="${old_bin%/postgres}"`,
94+
`new_bin=$(` + postgres.ShellPath(newVersion) + ` && command -v initdb)`,
95+
`new_bin="${new_bin%/initdb}"`,
96+
9197
`old_data="${data_volume}/pg${old_version}"`,
9298
`new_data="${data_volume}/pg${new_version}"`,
93-
`old_bin="/usr/pgsql-${old_version}/bin"`,
94-
`new_bin="/usr/pgsql-${new_version}/bin"`,
9599

96-
`[[ -x "${old_bin}/postgres" && -x "${new_bin}/initdb" && -d "${old_data}" ]]`,
100+
`[[ -d "${old_bin}" && -d "${old_data}" && -d "${new_bin}" ]]`,
97101

98102
// pg_upgrade writes its files in "${new_data}/pg_upgrade_output.d" since PostgreSQL v15.
99103
// Change to a writable working directory to be compatible with PostgreSQL v14 and earlier.
@@ -255,7 +259,7 @@ func removeDataCommand(upgrade *v1beta1.PGUpgrade) []string {
255259
`delete() (set -x && rm -rf -- "$@")`,
256260

257261
`old_data="${data_volume}/pg${old_version}"`,
258-
`control=$(LC_ALL=C /usr/pgsql-${old_version}/bin/pg_controldata "${old_data}")`,
262+
`control=$(` + postgres.ShellPath(oldVersion) + ` && LC_ALL=C pg_controldata "${old_data}")`,
259263
`read -r state <<< "${control##*cluster state:}"`,
260264

261265
// We expect exactly one state for a replica that has been stopped.

internal/controller/pgupgrade/jobs_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,13 @@ spec:
213213
export LD_PRELOAD='libnss_wrapper.so' NSS_WRAPPER_GROUP NSS_WRAPPER_PASSWD
214214
id; [[ "$(id -nu)" == 'postgres' && "$(id -ng)" == 'postgres' ]]
215215
section 'Step 2 of 7: Finding data and tools...'
216+
old_bin=$(PATH="/usr/lib/postgresql/19/bin:/usr/libexec/postgresql19:/usr/pgsql-19/bin${PATH+:${PATH}}" && command -v postgres)
217+
old_bin="${old_bin%/postgres}"
218+
new_bin=$(PATH="/usr/lib/postgresql/25/bin:/usr/libexec/postgresql25:/usr/pgsql-25/bin${PATH+:${PATH}}" && command -v initdb)
219+
new_bin="${new_bin%/initdb}"
216220
old_data="${data_volume}/pg${old_version}"
217221
new_data="${data_volume}/pg${new_version}"
218-
old_bin="/usr/pgsql-${old_version}/bin"
219-
new_bin="/usr/pgsql-${new_version}/bin"
220-
[[ -x "${old_bin}/postgres" && -x "${new_bin}/initdb" && -d "${old_data}" ]]
222+
[[ -d "${old_bin}" && -d "${old_data}" && -d "${new_bin}" ]]
221223
cd "${data_volume}"
222224
section 'Step 3 of 7: Initializing new data directory...'
223225
PGDATA="${new_data}" "${new_bin}/initdb" --allow-group-access --data-checksums
@@ -353,7 +355,7 @@ spec:
353355
printf 'Removing PostgreSQL %s data...\n\n' "$@"
354356
delete() (set -x && rm -rf -- "$@")
355357
old_data="${data_volume}/pg${old_version}"
356-
control=$(LC_ALL=C /usr/pgsql-${old_version}/bin/pg_controldata "${old_data}")
358+
control=$(PATH="/usr/lib/postgresql/19/bin:/usr/libexec/postgresql19:/usr/pgsql-19/bin${PATH+:${PATH}}" && LC_ALL=C pg_controldata "${old_data}")
357359
read -r state <<< "${control##*cluster state:}"
358360
[[ "${state}" == 'shut down in recovery' ]] || { printf >&2 'Unexpected state! %q\n' "${state}"; exit 1; }
359361
delete "${old_data}/pg_wal/"

internal/postgres/config.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,17 @@ func Environment(cluster *v1beta1.PostgresCluster) []corev1.EnvVar {
264264
}
265265
}
266266

267+
// ShellPath returns a POSIX shell command that prepends typical Postgres executable paths to the PATH variable.
268+
func ShellPath(postgresVersion int32) string {
269+
return fmt.Sprintf(`PATH="`+
270+
strings.Join([]string{
271+
`/usr/lib/postgresql/%[1]d/bin`, // Debian
272+
`/usr/libexec/postgresql%[1]d`, // Alpine
273+
`/usr/pgsql-%[1]d/bin`, // Red Hat
274+
}, ":")+
275+
`${PATH+:${PATH}}"`, postgresVersion)
276+
}
277+
267278
// reloadCommand returns an entrypoint that convinces PostgreSQL to reload
268279
// certificate files when they change. The process will appear as name in `ps`
269280
// and `top`.

internal/postgres/config_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,37 @@ func TestBashSafeLink(t *testing.T) {
547547
})
548548
}
549549

550+
func TestShellPath(t *testing.T) {
551+
t.Parallel()
552+
553+
script := ShellPath(11)
554+
555+
assert.Assert(t, cmp.Contains(script, `/usr/lib/postgresql/11/bin`))
556+
assert.Assert(t, cmp.Contains(script, `/usr/libexec/postgresql11`))
557+
assert.Assert(t, cmp.Contains(script, `/usr/pgsql-11/bin`))
558+
559+
t.Run("ShellCheckPOSIX", func(t *testing.T) {
560+
shellcheck := require.ShellCheck(t)
561+
562+
dir := t.TempDir()
563+
file := filepath.Join(dir, "script.sh")
564+
assert.NilError(t, os.WriteFile(file, []byte(script), 0o600))
565+
566+
// Expect ShellCheck for "sh" to be happy.
567+
// - https://www.shellcheck.net/wiki/SC2148
568+
cmd := exec.CommandContext(t.Context(), shellcheck, "--enable=all", "--shell=sh", file)
569+
output, err := cmd.CombinedOutput()
570+
assert.NilError(t, err, "%q\n%s", cmd.Args, output)
571+
})
572+
573+
t.Run("PrettyYAML", func(t *testing.T) {
574+
b, err := yaml.Marshal(script)
575+
assert.NilError(t, err)
576+
assert.Assert(t, !strings.Contains(string(b), `\n`), "expected literal flow scalar, got:\n%s", b)
577+
assert.Equal(t, 1, strings.Count(string(b), "\n"), "expected one trailing newline, got:\n%s", b)
578+
})
579+
}
580+
550581
func TestStartupCommand(t *testing.T) {
551582
shellcheck := require.ShellCheck(t)
552583
t.Parallel()

0 commit comments

Comments
 (0)