diff --git a/build/crd/percona/generated/pgv2.percona.com_perconapgclusters.yaml b/build/crd/percona/generated/pgv2.percona.com_perconapgclusters.yaml index fd4317efe..5ed1d26b4 100644 --- a/build/crd/percona/generated/pgv2.percona.com_perconapgclusters.yaml +++ b/build/crd/percona/generated/pgv2.percona.com_perconapgclusters.yaml @@ -5237,6 +5237,9 @@ spec: Version of the operator. Update this to new version after operator upgrade to apply changes to Kubernetes objects. Default is the latest version. + Version is the application version in the format X.Y.Z (e.g., "2.7.0"). + example: 2.7.0 + pattern: ^$|^\d+\.\d+\.\d+$ type: string dataSource: description: Specifies a data source for bootstrapping the PostgreSQL diff --git a/config/crd/bases/pgv2.percona.com_perconapgclusters.yaml b/config/crd/bases/pgv2.percona.com_perconapgclusters.yaml index a0dccc91b..e851a61a6 100644 --- a/config/crd/bases/pgv2.percona.com_perconapgclusters.yaml +++ b/config/crd/bases/pgv2.percona.com_perconapgclusters.yaml @@ -5644,6 +5644,9 @@ spec: Version of the operator. Update this to new version after operator upgrade to apply changes to Kubernetes objects. Default is the latest version. + Version is the application version in the format X.Y.Z (e.g., "2.7.0"). + example: 2.7.0 + pattern: ^$|^\d+\.\d+\.\d+$ type: string dataSource: description: Specifies a data source for bootstrapping the PostgreSQL diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index 5ec0a7101..9e1328949 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -5941,6 +5941,9 @@ spec: Version of the operator. Update this to new version after operator upgrade to apply changes to Kubernetes objects. Default is the latest version. + Version is the application version in the format X.Y.Z (e.g., "2.7.0"). + example: 2.7.0 + pattern: ^$|^\d+\.\d+\.\d+$ type: string dataSource: description: Specifies a data source for bootstrapping the PostgreSQL diff --git a/deploy/cr.yaml b/deploy/cr.yaml index 5f4cffe8b..dbb411f05 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -9,7 +9,7 @@ metadata: # - percona.com/delete-ssl # - percona.com/delete-backups spec: - crVersion: 2.7.0 + crVersion: "2.7.0" # initContainer: # image: perconalab/percona-postgresql-operator:main # resources: diff --git a/deploy/crd.yaml b/deploy/crd.yaml index 407ac4d2d..099dc2526 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -5941,6 +5941,9 @@ spec: Version of the operator. Update this to new version after operator upgrade to apply changes to Kubernetes objects. Default is the latest version. + Version is the application version in the format X.Y.Z (e.g., "2.7.0"). + example: 2.7.0 + pattern: ^$|^\d+\.\d+\.\d+$ type: string dataSource: description: Specifies a data source for bootstrapping the PostgreSQL diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index 0e0dc91ef..bd0d15c93 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -5941,6 +5941,9 @@ spec: Version of the operator. Update this to new version after operator upgrade to apply changes to Kubernetes objects. Default is the latest version. + Version is the application version in the format X.Y.Z (e.g., "2.7.0"). + example: 2.7.0 + pattern: ^$|^\d+\.\d+\.\d+$ type: string dataSource: description: Specifies a data source for bootstrapping the PostgreSQL diff --git a/percona/controller/pgcluster/controller.go b/percona/controller/pgcluster/controller.go index f3e0d0392..4c6637ba0 100644 --- a/percona/controller/pgcluster/controller.go +++ b/percona/controller/pgcluster/controller.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "reflect" + logf "sigs.k8s.io/controller-runtime/pkg/log" "slices" "strings" "time" @@ -49,6 +50,7 @@ import ( "github.com/percona/percona-postgresql-operator/percona/pmm" perconaPG "github.com/percona/percona-postgresql-operator/percona/postgres" "github.com/percona/percona-postgresql-operator/percona/utils/registry" + "github.com/percona/percona-postgresql-operator/percona/version" "github.com/percona/percona-postgresql-operator/percona/watcher" v2 "github.com/percona/percona-postgresql-operator/pkg/apis/pgv2.percona.com/v2" "github.com/percona/percona-postgresql-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" @@ -201,6 +203,9 @@ func (r *PGClusterReconciler) Reconcile(ctx context.Context, request reconcile.R return ctrl.Result{}, errors.Wrap(err, "get PerconaPGCluster") } + if err := r.setCRVersion(ctx, cr); err != nil { + return reconcile.Result{}, errors.Wrap(err, "set CR version") + } cr.Default() if cr.Spec.OpenShift == nil { @@ -964,3 +969,20 @@ func (r *PGClusterReconciler) ensureFinalizers(ctx context.Context, cr *v2.Perco return nil } + +func (r *PGClusterReconciler) setCRVersion(ctx context.Context, cr *v2.PerconaPGCluster) error { + if len(cr.Spec.CRVersion) > 0 { + return nil + } + + orig := cr.DeepCopy() + cr.Spec.CRVersion = version.Version() + + if err := r.Client.Patch(ctx, cr, client.MergeFrom(orig)); err != nil { + return errors.Wrap(err, "patch CR") + } + + logf.FromContext(ctx).Info("Set CR version", "version", cr.Spec.CRVersion) + + return nil +} diff --git a/percona/controller/pgcluster/controller_test.go b/percona/controller/pgcluster/controller_test.go index 8ba1ee9ef..86b8a35ab 100644 --- a/percona/controller/pgcluster/controller_test.go +++ b/percona/controller/pgcluster/controller_test.go @@ -27,6 +27,7 @@ import ( "github.com/percona/percona-postgresql-operator/internal/feature" "github.com/percona/percona-postgresql-operator/internal/naming" pNaming "github.com/percona/percona-postgresql-operator/percona/naming" + "github.com/percona/percona-postgresql-operator/percona/version" v2 "github.com/percona/percona-postgresql-operator/pkg/apis/pgv2.percona.com/v2" "github.com/percona/percona-postgresql-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" ) @@ -686,7 +687,7 @@ var _ = Describe("Version labels", Ordered, func() { }) cr, err := readDefaultCR(crName, ns) - It("should read defautl cr.yaml", func() { + It("should read default cr.yaml", func() { Expect(err).NotTo(HaveOccurred()) }) @@ -2265,3 +2266,77 @@ var _ = Describe("Init Container", Ordered, func() { }) }) }) + +var _ = Describe("CR Version Management", Ordered, func() { + ctx := context.Background() + const crName = "cr-version" + const ns = crName + + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: crName, + Namespace: ns, + }, + } + + BeforeAll(func() { + By("Creating the Namespace for CR version tests") + err := k8sClient.Create(ctx, namespace) + Expect(err).To(Not(HaveOccurred())) + }) + + AfterAll(func() { + By("Deleting the Namespace after CR version tests") + _ = k8sClient.Delete(ctx, namespace) + }) + + Context("setCRVersion logic", Ordered, func() { + When("the CRVersion is already set", func() { + It("should not change the CRVersion", func() { + cr, err := readDefaultCR("cr-version-1", ns) + Expect(err).NotTo(HaveOccurred()) + + cr.Spec.CRVersion = "2.7.0" + Expect(k8sClient.Create(ctx, cr)).Should(Succeed()) + + reconciler := &PGClusterReconciler{Client: k8sClient} + err = reconciler.setCRVersion(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + Expect(cr.Spec.CRVersion).To(Equal("2.7.0")) + }) + }) + + When("the CRVersion is empty", func() { + It("should set CRVersion and patch the resource", func() { + cr, err := readDefaultCR("cr-version-2", ns) + Expect(err).NotTo(HaveOccurred()) + + cr.Spec.CRVersion = "" + Expect(k8sClient.Create(ctx, cr)).Should(Succeed()) + + reconciler := &PGClusterReconciler{Client: k8sClient} + err = reconciler.setCRVersion(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + + // Fetch the CR again to verify the patch was applied in the cluster + updated := &v2.PerconaPGCluster{} + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: cr.Name, Namespace: cr.Namespace}, updated)).Should(Succeed()) + Expect(updated.Spec.CRVersion).To(Equal(version.Version())) + }) + }) + + When("the patch operation fails", func() { + It("should return an error", func() { + cr, err := readDefaultCR("cr-version-3", ns) + Expect(err).NotTo(HaveOccurred()) + cr.Spec.CRVersion = "" + + // Do NOT create the CR in k8s, so Patch will fail (object does not exist) + reconciler := &PGClusterReconciler{Client: k8sClient} + err = reconciler.setCRVersion(ctx, cr) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("patch CR")) + }) + }) + }) +}) diff --git a/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go b/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go index dd579c5d4..ff1385ae5 100644 --- a/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go +++ b/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go @@ -52,6 +52,9 @@ type PerconaPGClusterSpec struct { // upgrade to apply changes to Kubernetes objects. Default is the latest // version. // +optional + // Version is the application version in the format X.Y.Z (e.g., "2.7.0"). + // +kubebuilder:validation:Pattern=`^$|^\d+\.\d+\.\d+$` + // +kubebuilder:example="2.7.0" CRVersion string `json:"crVersion,omitempty"` InitContainer *crunchyv1beta1.InitContainerSpec `json:"initContainer,omitempty"`