Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions internal/controller/postgrescluster/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ func (r *Reconciler) Reconcile(
return runtime.ErrorWithBackoff(err)
}
}
// Issue Warning Event if postgres version is EOL according to PostgreSQL:
// https://www.postgresql.org/support/versioning/
currentTime := time.Now()
if postgres.ReleaseIsFinal(cluster.Spec.PostgresVersion, currentTime) {
r.Recorder.Eventf(cluster, corev1.EventTypeWarning, "EndOfLifePostgresVersion",
"The last minor version of Postgres %[1]v has been released."+
" PG %[1]v will no longer receive updates. We recommend upgrading."+
" See https://www.postgresql.org/support/versioning",
cluster.Spec.PostgresVersion)
}

if cluster.Spec.Standby != nil &&
cluster.Spec.Standby.Enabled &&
Expand Down
62 changes: 62 additions & 0 deletions internal/controller/postgrescluster/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,4 +556,66 @@ spec:
Expect(instance.Spec.Replicas).To(PointTo(BeEquivalentTo(1)))
})
})

Context("Postgres version EOL", func() {
var cluster *v1beta1.PostgresCluster

BeforeEach(func() {
cluster = create(`
metadata:
name: old-postgres
spec:
postgresVersion: 11
image: postgres
instances:
- name: instance1
dataVolumeClaimSpec:
accessModes:
- "ReadWriteMany"
resources:
requests:
storage: 1Gi
backups:
pgbackrest:
image: pgbackrest
repos:
- name: repo1
volume:
volumeClaimSpec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 1Gi
`)
Expect(reconcile(cluster)).To(BeZero())
})

AfterEach(func() {
ctx := context.Background()

if cluster != nil {
Expect(client.IgnoreNotFound(
suite.Client.Delete(ctx, cluster),
)).To(Succeed())

// Remove finalizers, if any, so the namespace can terminate.
Expect(client.IgnoreNotFound(
suite.Client.Patch(ctx, cluster, client.RawPatch(
client.Merge.Type(), []byte(`{"metadata":{"finalizers":[]}}`))),
)).To(Succeed())
}
})

Specify("Postgres EOL Warning Event", func() {
existing := &v1beta1.PostgresCluster{}
Expect(suite.Client.Get(
context.Background(), client.ObjectKeyFromObject(cluster), existing,
)).To(Succeed())

event, ok := <-test.Recorder.Events
Expect(ok).To(BeTrue())
Expect(event).To(ContainSubstring("PG 11 will no longer receive updates. We recommend upgrading."))
})
})
})
26 changes: 26 additions & 0 deletions internal/postgres/versions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2021 - 2024 Crunchy Data Solutions, Inc.
//
// SPDX-License-Identifier: Apache-2.0

package postgres

import "time"

// https://www.postgresql.org/support/versioning
var finalReleaseDates = map[int]time.Time{
10: time.Date(2022, time.November+1, 10, 0, 0, 0, 0, time.UTC),
11: time.Date(2023, time.November+1, +9, 0, 0, 0, 0, time.UTC),
12: time.Date(2024, time.November+1, 14, 0, 0, 0, 0, time.UTC),
13: time.Date(2025, time.November+1, 13, 0, 0, 0, 0, time.UTC),
14: time.Date(2026, time.November+1, 12, 0, 0, 0, 0, time.UTC),
15: time.Date(2027, time.November+1, 11, 0, 0, 0, 0, time.UTC),
16: time.Date(2028, time.November+1, +9, 0, 0, 0, 0, time.UTC),
17: time.Date(2029, time.November+1, +8, 0, 0, 0, 0, time.UTC),
}

// ReleaseIsFinal returns whether or not t is definitively past the final
// scheduled release of a Postgres version.
func ReleaseIsFinal(majorVersion int, t time.Time) bool {
known, ok := finalReleaseDates[majorVersion]
return ok && t.After(known)
}
34 changes: 34 additions & 0 deletions internal/postgres/versions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2021 - 2024 Crunchy Data Solutions, Inc.
//
// SPDX-License-Identifier: Apache-2.0

package postgres

import (
"testing"
"time"

"gotest.tools/v3/assert"
)

func TestReleaseIsFinal(t *testing.T) {
// On November 4th, 2024, PG 10 and 11 were EOL and 12-17 were supported.
testDate, err := time.Parse("2006-Jan-02", "2024-Nov-04")
assert.NilError(t, err)
assert.Check(t, ReleaseIsFinal(10, testDate))
assert.Check(t, ReleaseIsFinal(11, testDate))
assert.Check(t, !ReleaseIsFinal(12, testDate))
assert.Check(t, !ReleaseIsFinal(13, testDate))
assert.Check(t, !ReleaseIsFinal(14, testDate))
assert.Check(t, !ReleaseIsFinal(15, testDate))
assert.Check(t, !ReleaseIsFinal(16, testDate))
assert.Check(t, !ReleaseIsFinal(17, testDate))

// On December 15th, 2024 we alert that PG 12 is EOL
testDate = testDate.AddDate(0, 1, 11)
assert.Check(t, ReleaseIsFinal(12, testDate))

// ReleaseIsFinal covers PG versions 10 and greater. Any version not covered
// by the case statement in ReleaseIsFinal returns false
assert.Check(t, !ReleaseIsFinal(1, testDate))
}