diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 4920fbc..acfc6d8 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -89,9 +89,19 @@ jobs: test: { from: 15, to: 17, name: "15 to 17 (multi-step)" } - platform: linux/amd64 test: { from: 14, to: 17, name: "14 to 17 (full upgrade)" } - # ARM64 test - just one to verify it works + - platform: linux/amd64 + test: { from: 17, to: 18, name: "17 to 18" } + - platform: linux/amd64 + test: { from: 16, to: 18, name: "16 to 18 (multi-step)" } + - platform: linux/amd64 + test: { from: 15, to: 18, name: "15 to 18 (multi-step)" } + - platform: linux/amd64 + test: { from: 14, to: 18, name: "14 to 18 (full upgrade)" } + # ARM64 tests - verify latest versions work - platform: linux/arm64 test: { from: 14, to: 17, name: "14 to 17 (full upgrade)" } + - platform: linux/arm64 + test: { from: 14, to: 18, name: "14 to 18 (full upgrade)" } name: Test ${{ matrix.platform }} - ${{ matrix.test.name }} steps: - name: Checkout repository @@ -191,10 +201,28 @@ jobs: # Stop streaming logs kill $LOGS_PID 2>/dev/null || true - # Stop and remove container - docker stop -t 10 $CONTAINER_ID 2>/dev/null || true - docker rm $CONTAINER_ID 2>/dev/null || true - exit 0 + # Check if container exited during the sleep period + CONTAINER_STATE=$(docker inspect $CONTAINER_ID --format='{{.State.Status}}') + if [ "$CONTAINER_STATE" = "exited" ]; then + # Container has exited, check exit code + ACTUAL_EXIT_CODE=$(docker inspect $CONTAINER_ID --format='{{.State.ExitCode}}') + + # Clean up container + docker rm $CONTAINER_ID 2>/dev/null || true + + # Fail if container exited with non-zero code + if [ "$ACTUAL_EXIT_CODE" -ne 0 ]; then + echo "Container exited with code $ACTUAL_EXIT_CODE" + exit $ACTUAL_EXIT_CODE + fi + + echo "Container completed successfully" + else + # Container is still running, stop and remove it + echo "Container still running, stopping gracefully" + docker stop -t 10 $CONTAINER_ID 2>/dev/null || true + docker rm $CONTAINER_ID 2>/dev/null || true + fi - name: Verify upgraded data run: | diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index 8e6c2dd..6558a5b 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -63,8 +63,10 @@ jobs: docker tag ${REGISTRY}/${IMAGE_BASE}:to-15-${IMAGE_TAG} ${REGISTRY}/${IMAGE_BASE}:to-15-latest docker tag ${REGISTRY}/${IMAGE_BASE}:to-16-${IMAGE_TAG} ${REGISTRY}/${IMAGE_BASE}:to-16-latest docker tag ${REGISTRY}/${IMAGE_BASE}:to-17-${IMAGE_TAG} ${REGISTRY}/${IMAGE_BASE}:to-17-latest - + docker tag ${REGISTRY}/${IMAGE_BASE}:to-18-${IMAGE_TAG} ${REGISTRY}/${IMAGE_BASE}:to-18-latest + docker push ${REGISTRY}/${IMAGE_BASE}:to-15-latest docker push ${REGISTRY}/${IMAGE_BASE}:to-16-latest docker push ${REGISTRY}/${IMAGE_BASE}:to-17-latest + docker push ${REGISTRY}/${IMAGE_BASE}:to-18-latest fi \ No newline at end of file diff --git a/.github/workflows/feature-tests.yml b/.github/workflows/feature-tests.yml index d88a32c..8e1496f 100644 --- a/.github/workflows/feature-tests.yml +++ b/.github/workflows/feature-tests.yml @@ -24,7 +24,7 @@ on: env: GO_VERSION: '1.21' IMAGE_NAME: 'postgres-enhanced-test' - POSTGRES_VERSION: '17' + POSTGRES_VERSION: '18' jobs: build-test-image: @@ -339,6 +339,10 @@ jobs: - { from: "14", to: "17", extensions: "pgvector,pgaudit,pg_cron" } - { from: "15", to: "17", extensions: "pgvector,pgsodium,pgjwt" } - { from: "16", to: "17", extensions: "pgvector,pg_stat_monitor,pg_net" } + - { from: "14", to: "18", extensions: "pgvector,pgaudit,pg_cron" } + - { from: "15", to: "18", extensions: "pgvector,pgsodium,pgjwt" } + - { from: "16", to: "18", extensions: "pgvector,pg_stat_monitor,pg_net" } + - { from: "17", to: "18", extensions: "pgvector,pgaudit,pg_cron" } name: Upgrade Tests - PostgreSQL ${{ matrix.upgrade-path.from }} to ${{ matrix.upgrade-path.to }} with extensions steps: diff --git a/.github/workflows/helm.yml b/.github/workflows/helm.yml index 88421c6..7d83078 100644 --- a/.github/workflows/helm.yml +++ b/.github/workflows/helm.yml @@ -4,14 +4,12 @@ on: push: branches: - main - - master paths: - 'chart/**' - '.github/workflows/helm.yml' pull_request: branches: - main - - master paths: - 'chart/**' - '.github/workflows/helm.yml' @@ -147,4 +145,4 @@ jobs: uses: github/codeql-action/upload-sarif@v3 if: always() with: - sarif_file: checkov-results.sarif \ No newline at end of file + sarif_file: checkov-results.sarif diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc8d14d..f223373 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,13 +57,21 @@ jobs: exit 0 fi - # Otherwise, determine next patch version using svu + # Otherwise, determine next patch version using svu (only for branch pushes) CURRENT_VERSION=$(svu current || echo "v0.0.0") NEXT_VERSION=$(svu patch) echo "Current version: $CURRENT_VERSION" echo "Next version: $NEXT_VERSION" + # Validate version is proper semver + if [[ ! "$NEXT_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Warning: Generated version '$NEXT_VERSION' is not valid semver, stopping workflow" + echo "version=" >> $GITHUB_OUTPUT + echo "is_new_version=false" >> $GITHUB_OUTPUT + exit 0 + fi + echo "version=$NEXT_VERSION" >> $GITHUB_OUTPUT echo "is_new_version=true" >> $GITHUB_OUTPUT @@ -81,7 +89,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - pg_version: [16, 17] + pg_version: [16, 17, 18] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -119,6 +127,7 @@ jobs: outputs: pg16_version: ${{ steps.versions.outputs.pg16_version }} pg17_version: ${{ steps.versions.outputs.pg17_version }} + pg18_version: ${{ steps.versions.outputs.pg18_version }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -133,10 +142,16 @@ jobs: with: name: postgres-17 + - name: Download PostgreSQL 18 image + uses: actions/download-artifact@v4 + with: + name: postgres-18 + - name: Load Docker images run: | docker load < postgres-16.tar docker load < postgres-17.tar + docker load < postgres-18.tar - name: Get PostgreSQL versions from built images id: versions @@ -207,6 +222,15 @@ jobs: arch: arm64 platform: linux/arm64 pg_version: 17 + # PostgreSQL 18 builds + - runner: ubuntu-latest + arch: amd64 + platform: linux/amd64 + pg_version: 18 + - runner: ubuntu-24.04-arm + arch: arm64 + platform: linux/arm64 + pg_version: 18 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -226,8 +250,10 @@ jobs: run: | if [ "${{ matrix.pg_version }}" = "16" ]; then echo "minor_version=${{ needs.get-postgres-versions.outputs.pg16_version }}" >> $GITHUB_OUTPUT - else + elif [ "${{ matrix.pg_version }}" = "17" ]; then echo "minor_version=${{ needs.get-postgres-versions.outputs.pg17_version }}" >> $GITHUB_OUTPUT + else + echo "minor_version=${{ needs.get-postgres-versions.outputs.pg18_version }}" >> $GITHUB_OUTPUT fi # Extract git tag (remove 'v' prefix if present) @@ -263,7 +289,7 @@ jobs: packages: write strategy: matrix: - pg_version: [16, 17] + pg_version: [16, 17, 18] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -283,8 +309,10 @@ jobs: run: | if [ "${{ matrix.pg_version }}" = "16" ]; then echo "minor_version=${{ needs.get-postgres-versions.outputs.pg16_version }}" >> $GITHUB_OUTPUT - else + elif [ "${{ matrix.pg_version }}" = "17" ]; then echo "minor_version=${{ needs.get-postgres-versions.outputs.pg17_version }}" >> $GITHUB_OUTPUT + else + echo "minor_version=${{ needs.get-postgres-versions.outputs.pg18_version }}" >> $GITHUB_OUTPUT fi # Extract git tag (remove 'v' prefix if present) @@ -317,13 +345,13 @@ jobs: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.minor_version }}-${{ steps.versions.outputs.sha_short }}-amd64 \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.minor_version }}-${{ steps.versions.outputs.sha_short }}-arm64 - - name: Create and push latest tag for PostgreSQL 17 - if: matrix.pg_version == '17' + - name: Create and push latest tag for PostgreSQL 18 + if: matrix.pg_version == '18' run: | docker buildx imagetools create -t \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \ - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:17-amd64 \ - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:17-arm64 + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:18-amd64 \ + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:18-arm64 create-release: needs: [determine-version, build-postgres-cli, merge-manifests, get-postgres-versions] @@ -345,6 +373,18 @@ jobs: run: | # Use version from determine-version job VERSION="${{ needs.determine-version.outputs.version }}" + + # Validate version is not empty and is proper semver + if [[ -z "$VERSION" ]]; then + echo "Error: VERSION is empty, skipping release creation" + exit 1 + fi + + if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: VERSION '$VERSION' is not valid semver" + exit 1 + fi + # Remove 'v' prefix if present TAG="${VERSION#v}" echo "release_tag=$TAG" >> $GITHUB_OUTPUT @@ -370,7 +410,12 @@ jobs: - \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:17\` - Latest PostgreSQL 17 major version - \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.get-postgres-versions.outputs.pg17_version }}\` - Specific minor version - \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.get-postgres-versions.outputs.pg17_version }}-${{ steps.versions.outputs.sha_short }}\` - Version with commit SHA - - \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest\` - Points to PostgreSQL 17 + + ### PostgreSQL 18 (Current: ${{ needs.get-postgres-versions.outputs.pg18_version }}) + - \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:18\` - Latest PostgreSQL 18 major version + - \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.get-postgres-versions.outputs.pg18_version }}\` - Specific minor version + - \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.get-postgres-versions.outputs.pg18_version }}-${{ steps.versions.outputs.sha_short }}\` - Version with commit SHA + - \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest\` - Points to PostgreSQL 18 ## Usage @@ -378,7 +423,7 @@ jobs: \`\`\`bash docker run --rm \\ -v /path/to/pgdata:/var/lib/postgresql/data \\ - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:17 + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:18 \`\`\` ### Upgrade and Start PostgreSQL @@ -387,20 +432,21 @@ jobs: -v /path/to/pgdata:/var/lib/postgresql/data \\ -e START_POSTGRES=true \\ -p 5432:5432 \\ - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:17 + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:18 \`\`\` ## Supported Upgrade Paths - - PostgreSQL 14 → 15, 16, 17 - - PostgreSQL 15 → 16, 17 - - PostgreSQL 16 → 17 + - PostgreSQL 14 → 15, 16, 17, 18 + - PostgreSQL 15 → 16, 17, 18 + - PostgreSQL 16 → 17, 18 + - PostgreSQL 17 → 18 - Sequential upgrades are performed automatically (e.g., 14→15→16→17). + Sequential upgrades are performed automatically (e.g., 14→15→16→17→18). ## Environment Variables - - \`PG_VERSION\`: Target PostgreSQL version (16 or 17) + - \`PG_VERSION\`: Target PostgreSQL version (16, 17, or 18) - \`START_POSTGRES\`: Start PostgreSQL after upgrade (default: false) - \`AUTO_UPGRADE\`: Enable auto-upgrade (default: true) - \`RESET_PASSWORD\`: Reset password on startup (default: false) @@ -470,8 +516,13 @@ jobs: PG17_TAG="pg17-${{ needs.get-postgres-versions.outputs.pg17_version }}-${SHA_SHORT}" git tag -a "$PG17_TAG" -m "PostgreSQL 17.${{ needs.get-postgres-versions.outputs.pg17_version }} release" + # PostgreSQL 18 tags + PG18_TAG="pg18-${{ needs.get-postgres-versions.outputs.pg18_version }}-${SHA_SHORT}" + git tag -a "$PG18_TAG" -m "PostgreSQL 18.${{ needs.get-postgres-versions.outputs.pg18_version }} release" + # Push tags git push origin "$PG16_TAG" git push origin "$PG17_TAG" + git push origin "$PG18_TAG" - echo "Created tags: $PG16_TAG, $PG17_TAG" + echo "Created tags: $PG16_TAG, $PG17_TAG, $PG18_TAG" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0d29cea..43d9608 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - target_version: ['15', '16', '17'] + target_version: ['15', '16', '17', '18'] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -51,6 +51,13 @@ jobs: # Test all paths to 17 make test ;; + 18) + # Test all paths to 18 + make test-14-to-18 + make test-15-to-18 + make test-16-to-18 + make test-17-to-18 + ;; esac test-complete-suite: @@ -84,4 +91,4 @@ jobs: # Run basic feature verification task test:verify-extensions || echo "Extensions verification not available" task test:verify-services || echo "Services verification not available" - fi \ No newline at end of file + fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c15bf45..bdbb5c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -79,7 +79,7 @@ task dev-setup The upgrade process: 1. **Detection**: Identifies current PostgreSQL version from data directory -2. **Planning**: Determines upgrade path (e.g., 14→15→16→17) +2. **Planning**: Determines upgrade path (e.g., 14→15→16→17→18) 3. **Execution**: Runs pg_upgrade with hard links for efficiency 4. **Verification**: Validates upgraded database 5. **Cleanup**: Preserves old data with `.old` suffix @@ -102,6 +102,7 @@ Extensions are managed through: task build-all # Build specific version +task build:build-18 # PostgreSQL 18 task build:build-17 # PostgreSQL 17 task build:build-16 # PostgreSQL 16 task build:build-15 # PostgreSQL 15 @@ -160,6 +161,7 @@ task build-all # Build all version images task build:build-15 # PostgreSQL 15 image task build:build-16 # PostgreSQL 16 image task build:build-17 # PostgreSQL 17 image +task build:build-18 # PostgreSQL 18 image # Push to registry task build:push-all # Push all images @@ -175,9 +177,10 @@ task test-image # Test Docker image task test:all # Comprehensive test suite # Specific test paths -task test:upgrade-14-to-17 -task test:upgrade-15-to-17 -task test:upgrade-16-to-17 +task test:upgrade-14-to-18 +task test:upgrade-15-to-18 +task test:upgrade-16-to-18 +task test:upgrade-17-to-18 # Test management task test:seed-all # Seed test data @@ -226,6 +229,7 @@ make build # Build default image make build-15 # Build PostgreSQL 15 make build-16 # Build PostgreSQL 16 make build-17 # Build PostgreSQL 17 +make build-18 # Build PostgreSQL 18 make build-all # Build all versions # Custom registry @@ -238,6 +242,7 @@ REGISTRY=myregistry.io IMAGE_TAG=v1.0.0 make build-all make push-15 # Push PostgreSQL 15 make push-16 # Push PostgreSQL 16 make push-17 # Push PostgreSQL 17 +make push-18 # Push PostgreSQL 18 make push-all # Push all images # Custom registry @@ -256,9 +261,13 @@ make test-all # Comprehensive suite make test-14-to-15 make test-14-to-16 make test-14-to-17 +make test-14-to-18 make test-15-to-16 make test-15-to-17 +make test-15-to-18 make test-16-to-17 +make test-16-to-18 +make test-17-to-18 ``` ### CLI Commands @@ -382,8 +391,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - from: [14, 15, 16] - to: [15, 16, 17] + from: [14, 15, 16, 17] + to: [15, 16, 17, 18] steps: - uses: actions/checkout@v3 - run: task test:upgrade-${{ matrix.from }}-to-${{ matrix.to }} @@ -660,7 +669,7 @@ Update documentation for: 1. Add extension to Dockerfile: ```dockerfile -RUN apt-get install -y postgresql-17-newext +RUN apt-get install -y postgresql-18-newext ``` 2. Update extension list: diff --git a/Dockerfile b/Dockerfile index d1f012c..befaab1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,8 +26,8 @@ ARG TARGETOS ENV TARGETARCH=${TARGETARCH} ENV TARGETOS=${TARGETOS} -# Default PostgreSQL version (can be 14, 15, 16, or 17) -ARG PG_VERSION=17 +# Default PostgreSQL version (can be 14, 15, 16, 17 or 18) +ARG PG_VERSION=18 ENV PG_VERSION=${PG_VERSION} # Labels @@ -50,21 +50,24 @@ RUN set -eux; \ wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/postgresql-archive-keyring.gpg && \ echo "deb [signed-by=/usr/share/keyrings/postgresql-archive-keyring.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list -# Install PostgreSQL versions 14-17 and essential tools +# Install PostgreSQL versions 14-18 and essential tools RUN apt-get update && \ apt-get install -y --no-install-recommends \ postgresql-14 \ postgresql-15 \ postgresql-16 \ postgresql-17 \ + postgresql-18 \ postgresql-client-14 \ postgresql-client-15 \ postgresql-client-16 \ postgresql-client-17 \ + postgresql-client-18 \ postgresql-contrib-14 \ postgresql-contrib-15 \ postgresql-contrib-16 \ postgresql-contrib-17 \ + postgresql-contrib-18 \ gosu \ jq \ curl \ @@ -88,6 +91,7 @@ ENV PG14BIN=/usr/lib/postgresql/14/bin ENV PG15BIN=/usr/lib/postgresql/15/bin ENV PG16BIN=/usr/lib/postgresql/16/bin ENV PG17BIN=/usr/lib/postgresql/17/bin +ENV PG18BIN=/usr/lib/postgresql/18/bin # Data directory ENV PGDATA=/var/lib/postgresql/data diff --git a/README.md b/README.md index 52f5421..c8ab29e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ PostgreSQL distribution with automatic version upgrades, password recovery, perf ## Key Features -- **Automatic PostgreSQL upgrades** - Handles upgrade paths from 14→15→16→17 using pg_upgrade with hard links +- **Automatic PostgreSQL upgrades** - Handles upgrade paths from 14→15→16→17→18 using pg_upgrade with hard links - **Password recovery** - Reset passwords without data loss using single-user mode in init containers - **PgTune auto-configuration** - Calculates optimal settings based on container memory/CPU limits - **16 pre-compiled extensions** - pgvector, pgsodium, pgjwt, pgaudit, pg_cron, and more included @@ -144,13 +144,13 @@ The postgres-cli tool provides intelligent PostgreSQL version upgrades with zero ### Upgrade Detection and Planning 1. **Version Detection**: Reads `/var/lib/postgresql/data/PG_VERSION` to identify current version -2. **Upgrade Path Planning**: Determines sequential upgrade steps (e.g., 14→15→16→17) +2. **Upgrade Path Planning**: Determines sequential upgrade steps (e.g., 14→15→16→17→18) 3. **Validation**: Ensures data directory exists and PostgreSQL is stopped ### Multi-Phase Upgrade Process ``` -Current Data (v14) → Backup → Sequential Upgrades → Final Version (v17) +Current Data (v14) → Backup → Sequential Upgrades → Final Version (v18) ↓ /backups/data-14 (preserved) ``` @@ -163,7 +163,7 @@ The `Postgres.Upgrade()` method in `pkg/server/postgres.go` orchestrates: - Excludes recursive backup/upgrade directories 2. **Sequential Version Upgrades** (`upgradeSingle`): - - For each version hop (14→15, 15→16, 16→17): + - For each version hop (14→15, 15→16, 16→17, 17→18): - Validates current cluster with `pg_controldata` - Initializes new cluster in `/var/lib/postgresql/data/upgrades/{version}` - Runs `pg_upgrade --check` for compatibility verification @@ -220,8 +220,10 @@ The Docker container orchestrates upgrades through a layered approach: | Image | Description | |-------|-------------| +| `ghcr.io/flanksource/postgres:18-latest` | PostgreSQL 18 with all extensions | | `ghcr.io/flanksource/postgres:17-latest` | PostgreSQL 17 with all extensions | | `ghcr.io/flanksource/postgres:16-latest` | PostgreSQL 16 with all extensions | +| `ghcr.io/flanksource/postgres-upgrade:to-18` | Upgrade-only image to PostgreSQL 18 | | `ghcr.io/flanksource/postgres-upgrade:to-17` | Upgrade-only image to PostgreSQL 17 | ## Automatic Performance Tuning @@ -297,7 +299,7 @@ docker run --rm \ ```yaml database: - version: "17" + version: "18" password: "change-me" autoUpgrade: true resetPassword: false @@ -343,7 +345,7 @@ helm install my-postgres flanksource/postgres-upgrade -f values.yaml ```bash helm upgrade my-postgres flanksource/postgres-upgrade \ - --set database.version=17 \ + --set database.version=18 \ --reuse-values kubectl rollout restart statefulset/my-postgres @@ -442,9 +444,10 @@ The postgres-cli orchestrates safe, sequential PostgreSQL upgrades with full dat | From | To | Process | Backup Location | |------|-----|---------|-----------------| -| PostgreSQL 14 | PostgreSQL 17 | Sequential: 14→15→16→17 | `/data/backups/data-14` | -| PostgreSQL 15 | PostgreSQL 17 | Sequential: 15→16→17 | `/data/backups/data-15` | -| PostgreSQL 16 | PostgreSQL 17 | Direct: 16→17 | `/data/backups/data-16` | +| PostgreSQL 14 | PostgreSQL 18 | Sequential: 14→15→16→17→18 | `/data/backups/data-14` | +| PostgreSQL 15 | PostgreSQL 18 | Sequential: 15→16→17→18 | `/data/backups/data-15` | +| PostgreSQL 16 | PostgreSQL 18 | Sequential: 16→17→18 | `/data/backups/data-16` | +| PostgreSQL 17 | PostgreSQL 18 | Direct: 17→18 | `/data/backups/data-17` | ### Technical Implementation @@ -526,7 +529,7 @@ If an upgrade fails: | `POSTGRES_USER` | Database user | `postgres` | | `POSTGRES_DB` | Default database | `postgres` | | `POSTGRES_EXTENSIONS` | Comma-separated extensions | None | -| `PG_VERSION` | Target PostgreSQL version | `17` | +| `PG_VERSION` | Target PostgreSQL version | `18` | | `AUTO_UPGRADE` | Enable automatic upgrades | `true` | | `RESET_PASSWORD` | Reset password on startup | `false` | | `PGBOUNCER_ENABLED` | Enable PgBouncer | `false` | diff --git a/Taskfile.build.yaml b/Taskfile.build.yaml index e7b9e7b..06f2891 100644 --- a/Taskfile.build.yaml +++ b/Taskfile.build.yaml @@ -11,7 +11,7 @@ vars: REGISTRY: '{{.REGISTRY | default "ghcr.io"}}' IMAGE_BASE: '{{.IMAGE_BASE | default "flanksource/postgres"}}' IMAGE_TAG: '{{.IMAGE_TAG | default "latest"}}' - TARGET_VERSIONS: [15, 16, 17] + TARGET_VERSIONS: [15, 16, 17, 18] tasks: build: @@ -35,7 +35,7 @@ tasks: cmds: - for: matrix: - VERSION: [15, 16, 17] + VERSION: [15, 16, 17, 18] task: build-version vars: VERSION: "{{.ITEM.VERSION}}" @@ -72,6 +72,12 @@ tasks: - task: build-version vars: { VERSION: "17" } + build-18: + desc: Build image that upgrades to PostgreSQL 18 + cmds: + - task: build-version + vars: { VERSION: "18" } + build-all: desc: Build all target version images cmds: @@ -110,6 +116,12 @@ tasks: - task: push-version vars: { VERSION: "17" } + push-18: + desc: Push PostgreSQL 18 upgrade image + cmds: + - task: push-version + vars: { VERSION: "18" } + push-all: desc: Push all target version images cmds: diff --git a/Taskfile.test.yaml b/Taskfile.test.yaml index 359292f..970f0ed 100644 --- a/Taskfile.test.yaml +++ b/Taskfile.test.yaml @@ -112,6 +112,42 @@ tasks: echo "🧪 Testing upgrade from PostgreSQL 16 to 17..." go test {{.GO_TEST_FLAGS}} -run "TestPostgresUpgrade/Upgrade_16_to_17" + upgrade-14-to-18: + desc: Test upgrade from PostgreSQL 14 to 18 + dir: "{{.TEST_DIR}}" + deps: [build] + cmds: + - | + echo "🧪 Testing upgrade from PostgreSQL 14 to 18..." + go test {{.GO_TEST_FLAGS}} -run "TestPostgresUpgrade/Upgrade_14_to_18" + + upgrade-15-to-18: + desc: Test upgrade from PostgreSQL 15 to 18 + dir: "{{.TEST_DIR}}" + deps: [build] + cmds: + - | + echo "🧪 Testing upgrade from PostgreSQL 15 to 18..." + go test {{.GO_TEST_FLAGS}} -run "TestPostgresUpgrade/Upgrade_15_to_18" + + upgrade-16-to-18: + desc: Test upgrade from PostgreSQL 16 to 18 + dir: "{{.TEST_DIR}}" + deps: [build] + cmds: + - | + echo "🧪 Testing upgrade from PostgreSQL 16 to 18..." + go test {{.GO_TEST_FLAGS}} -run "TestPostgresUpgrade/Upgrade_16_to_18" + + upgrade-17-to-18: + desc: Test upgrade from PostgreSQL 17 to 18 + dir: "{{.TEST_DIR}}" + deps: [build] + cmds: + - | + echo "🧪 Testing upgrade from PostgreSQL 17 to 18..." + go test {{.GO_TEST_FLAGS}} -run "TestPostgresUpgrade/Upgrade_17_to_18" + upgrade-15-to-16: desc: Test upgrade from PostgreSQL 15 to 16 dir: "{{.TEST_DIR}}" diff --git a/chart/README.md b/chart/README.md index df07278..c6ba1fb 100644 --- a/chart/README.md +++ b/chart/README.md @@ -120,9 +120,10 @@ When `postgresql.autoUpgrade` is enabled (default), the container will: ### Supported Upgrade Paths -- PostgreSQL 14 → 15, 16, 17 -- PostgreSQL 15 → 16, 17 -- PostgreSQL 16 → 17 +- PostgreSQL 14 → 15, 16, 17, 18 +- PostgreSQL 15 → 16, 17, 18 +- PostgreSQL 16 → 17, 18 +- PostgreSQL 17 → 18 ## Testing diff --git a/cmd/main.go b/cmd/main.go index 339e7c5..af9da8a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -421,8 +421,8 @@ func runAutoStart(cmd *cobra.Command, args []string) error { targetVersion := upgradeTo if targetVersion == 0 { - // Auto-detect latest available version (default to 17) - targetVersion = 17 + // Auto-detect latest available version (default to 18) + targetVersion = 18 } if currentVersion < targetVersion { diff --git a/pkg/server/postgres.go b/pkg/server/postgres.go index ec3a275..51320c4 100644 --- a/pkg/server/postgres.go +++ b/pkg/server/postgres.go @@ -369,6 +369,11 @@ func (p *Postgres) InitDB() error { "-U", "postgres", // Always use postgres superuser } + if p.GetVersion() == "18" && false { + args = append(args, "--no-data-checksums") + // if pg17 does not have this enabled, pg17->pg18 migration will fail + } + process := clicky.Exec(filepath.Join(p.BinDir, "initdb"), args...).Run() // Generally no password needed for initdb with trust auth @@ -477,8 +482,8 @@ func (p *Postgres) Upgrade(targetVersion int) error { return nil } - if currentVersion < 14 || targetVersion > 17 { - return fmt.Errorf("invalid version range. Current: %d, Target: %d. Supported versions: 14-17", currentVersion, targetVersion) + if currentVersion < 14 || targetVersion > 18 { + return fmt.Errorf("invalid version range. Current: %d, Target: %d. Supported versions: 14-18", currentVersion, targetVersion) } // Check if data exists @@ -589,7 +594,8 @@ func (p *Postgres) upgradeSingle(fromVersion, toVersion int) error { // Run pg_upgrade fmt.Printf("⚡ Performing pg_upgrade from PostgreSQL %d to %d...\n", fromVersion, toVersion) - if err := p.runPgUpgrade(oldBinDir, newBinDir, p.DataDir, newDataDir); err != nil { + link := (fromVersion == 17 && toVersion == 18) + if err := p.runPgUpgrade(oldBinDir, newBinDir, p.DataDir, newDataDir, link); err != nil { return fmt.Errorf("pg_upgrade failed: %w", err) } @@ -700,11 +706,17 @@ func (p *Postgres) initNewCluster(binDir, dataDir string) error { // Initialize with UTF8 encoding and en_US.UTF-8 locale for compatibility // This matches most common PostgreSQL deployments and allows for upgrade compatibility - process := clicky.Exec( - filepath.Join(binDir, "initdb"), + args := []string{ "-D", dataDir, "--encoding=UTF8", "--locale=en_US.UTF-8", + } + if p.GetVersion() == "18" { + args = append(args, "--no-data-checksums") // if pg17 does not have this enabled, pg17->pg18 migration will fail + } + process := clicky.Exec( + filepath.Join(binDir, "initdb"), + args..., ).Run() p.lastStdout = process.Stdout.String() p.lastStderr = process.Stderr.String() @@ -717,7 +729,7 @@ func (p *Postgres) initNewCluster(binDir, dataDir string) error { } // runPgUpgrade executes the pg_upgrade command -func (p *Postgres) runPgUpgrade(oldBinDir, newBinDir, oldDataDir, newDataDir string) error { +func (p *Postgres) runPgUpgrade(oldBinDir, newBinDir, oldDataDir, newDataDir string, link bool) error { // Create socket directory socketDir := "/var/run/postgresql" if err := os.MkdirAll(socketDir, 0755); err != nil { @@ -777,6 +789,10 @@ func (p *Postgres) runPgUpgrade(oldBinDir, newBinDir, oldDataDir, newDataDir str "--new-options=" + localeOpts, } + if link { + upgradeArgs = append(upgradeArgs, "--link") + } + fmt.Println("Performing upgrade...") upgradeProcess := clicky.Exec(filepath.Join(newBinDir, "pg_upgrade"), upgradeArgs...).Run() p.lastStdout = upgradeProcess.Stdout.String()