Skip to content

Commit b71131c

Browse files
committed
add status condition for migration status
1 parent f15c14a commit b71131c

4 files changed

Lines changed: 179 additions & 1 deletion

File tree

e2e-tests/tests/upgrade-minor/05-assert.yaml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
apiVersion: kuttl.dev/v1beta1
22
kind: TestAssert
3-
timeout: 120
3+
timeout: 180
44
---
55
kind: StatefulSet
66
apiVersion: apps/v1
@@ -125,6 +125,20 @@ metadata:
125125
name: upgrade-minor
126126
generation: 1
127127
status:
128+
conditions:
129+
- type: ReadyForBackup
130+
status: "True"
131+
- type: PGBackRestRepoHostReady
132+
status: "True"
133+
- type: PGBackRestReplicaRepoReady
134+
status: "True"
135+
- type: PGBackRestReplicaCreate
136+
status: "True"
137+
- type: ProxyAvailable
138+
status: "True"
139+
- type: APIGroupMigration
140+
reason: APIGroupMigrationCompleted
141+
status: "True"
128142
pgbouncer:
129143
ready: 3
130144
size: 3

e2e-tests/tests/upgrade-minor/99-remove-cluster-gracefully.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ commands:
1616
1717
source ../../functions
1818
19+
kubectl -n ${NAMESPACE} patch postgrescluster upgrade-minor --type=json -p='[{"op": "remove", "path": "/metadata/finalizers"}]'
20+
kubectl -n ${NAMESPACE} delete postgrescluster upgrade-minor || true
1921
remove_all_finalizers
2022
check_operator_panic
2123
destroy_operator

percona/controller/pgcluster/controller.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,15 @@ func (r *PGClusterReconciler) Reconcile(ctx context.Context, request reconcile.R
385385
return reconcile.Result{}, errors.Wrap(err, "check patroni version from instance pods")
386386
}
387387

388+
if err := r.reconcileOwnerRefMigrationStatus(ctx, cr); err != nil {
389+
if errors.Is(err, errOwnerRefMigrationInProgress) {
390+
return reconcile.Result{
391+
RequeueAfter: 5 * time.Second,
392+
}, nil
393+
}
394+
return reconcile.Result{}, errors.Wrap(err, "reconcile owner ref migration status")
395+
}
396+
388397
return ctrl.Result{}, nil
389398
}
390399

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package pgcluster
2+
3+
import (
4+
"context"
5+
6+
apierrors "k8s.io/apimachinery/pkg/api/errors"
7+
"k8s.io/apimachinery/pkg/api/meta"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
10+
"k8s.io/apimachinery/pkg/runtime/schema"
11+
"k8s.io/apimachinery/pkg/types"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
13+
14+
"github.com/percona/percona-postgresql-operator/v2/internal/logging"
15+
"github.com/percona/percona-postgresql-operator/v2/internal/naming"
16+
v2 "github.com/percona/percona-postgresql-operator/v2/pkg/apis/pgv2.percona.com/v2"
17+
"github.com/percona/percona-postgresql-operator/v2/pkg/apis/upstream.pgv2.percona.com/v1beta1"
18+
"github.com/pkg/errors"
19+
)
20+
21+
var childKinds = []schema.GroupVersionKind{
22+
{Group: "apps", Version: "v1", Kind: "StatefulSet"},
23+
{Group: "apps", Version: "v1", Kind: "Deployment"},
24+
{Group: "", Version: "v1", Kind: "Service"},
25+
{Group: "", Version: "v1", Kind: "ConfigMap"},
26+
{Group: "", Version: "v1", Kind: "ServiceAccount"},
27+
{Group: "", Version: "v1", Kind: "Endpoints"},
28+
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "Role"},
29+
{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"},
30+
{Group: "batch", Version: "v1", Kind: "Job"},
31+
{Group: "batch", Version: "v1", Kind: "CronJob"},
32+
{Group: "policy", Version: "v1", Kind: "PodDisruptionBudget"},
33+
{Group: "cert-manager.io", Version: "v1", Kind: "Certificate"},
34+
{Group: "cert-manager.io", Version: "v1", Kind: "Issuer"},
35+
}
36+
37+
// LegacyGVK is the GroupVersionKind of the pre-rename PostgresCluster.
38+
var LegacyGVK = schema.GroupVersionKind{
39+
Group: "postgres-operator.crunchydata.com",
40+
Version: "v1beta1",
41+
Kind: "PostgresCluster",
42+
}
43+
44+
var errOwnerRefMigrationInProgress = errors.New("owner ref migration is in progress")
45+
46+
func (r *PGClusterReconciler) reconcileOwnerRefMigrationStatus(ctx context.Context, cr *v2.PerconaPGCluster) error {
47+
log := logging.FromContext(ctx).WithName("APIGroupMigration")
48+
49+
if c := meta.FindStatusCondition(cr.Status.Conditions, "APIGroupMigration"); c != nil && c.Status == metav1.ConditionTrue {
50+
return nil
51+
}
52+
53+
mapper := r.Client.RESTMapper()
54+
if _, err := mapper.RESTMapping(LegacyGVK.GroupKind(), LegacyGVK.Version); err != nil {
55+
if meta.IsNoMatchError(err) {
56+
log.Info("legacy PostgresCluster CRD not found; no need to check references", "gvk", LegacyGVK)
57+
return nil
58+
}
59+
return errors.Wrap(err, "discover legacy PostgresCluster GVK")
60+
}
61+
62+
err := setStatusCondition(ctx, r.Client, cr, metav1.Condition{
63+
Type: "APIGroupMigration",
64+
Status: metav1.ConditionFalse,
65+
Reason: "APIGroupMigrationInProgress",
66+
Message: "Waiting for owner references to be updated",
67+
ObservedGeneration: cr.GetGeneration(),
68+
})
69+
if err != nil {
70+
return errors.Wrap(err, "set status condition to false")
71+
}
72+
73+
legacy := &unstructured.Unstructured{}
74+
legacy.SetGroupVersionKind(LegacyGVK)
75+
76+
if err := r.Client.Get(ctx, types.NamespacedName{Name: cr.Name, Namespace: cr.Namespace}, legacy); err != nil {
77+
return client.IgnoreNotFound(err)
78+
}
79+
80+
newPC := &v1beta1.PostgresCluster{}
81+
err = r.Client.Get(ctx, types.NamespacedName{Name: cr.Name, Namespace: cr.Namespace}, newPC)
82+
switch {
83+
case apierrors.IsNotFound(err):
84+
log.Info("new-group PostgresCluster not present yet; waiting for PerconaPGCluster reconciler to create it")
85+
return errOwnerRefMigrationInProgress
86+
case err != nil:
87+
return errors.Wrap(err, "get new PostgresCluster")
88+
}
89+
90+
legacyUID := legacy.GetUID()
91+
92+
selector := client.MatchingLabels{naming.LabelCluster: legacy.GetName()}
93+
namespace := client.InNamespace(legacy.GetNamespace())
94+
95+
for _, gvk := range childKinds {
96+
if _, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version); err != nil {
97+
if meta.IsNoMatchError(err) {
98+
log.V(1).Info("Skipping kind: NoMatchError", "gvk", gvk)
99+
continue
100+
}
101+
return errors.Wrapf(err, "discover %s", gvk)
102+
}
103+
104+
list := &unstructured.UnstructuredList{}
105+
list.SetGroupVersionKind(gvk)
106+
if err := r.Client.List(ctx, list, namespace, selector); err != nil {
107+
return errors.Wrapf(err, "list %s", gvk.Kind)
108+
}
109+
110+
for i := range list.Items {
111+
child := &list.Items[i]
112+
113+
if ownedBy(child, legacyUID) {
114+
log.Info("Child is still owned by legacy cluster", "child", child.GetName(), "kind", child.GetKind())
115+
return errOwnerRefMigrationInProgress
116+
}
117+
118+
log.Info("Child is updated successfully", "child", child.GetName(), "kind", child.GetKind())
119+
}
120+
}
121+
122+
err = setStatusCondition(ctx, r.Client, cr, metav1.Condition{
123+
Type: "APIGroupMigration",
124+
Status: metav1.ConditionTrue,
125+
Reason: "APIGroupMigrationCompleted",
126+
Message: "All owner references are updated",
127+
ObservedGeneration: cr.GetGeneration(),
128+
})
129+
if err != nil {
130+
return errors.Wrap(err, "set status condition to false")
131+
}
132+
133+
return nil
134+
}
135+
136+
// ownedBy reports whether the object has an ownerReference with the given UID.
137+
func ownedBy(obj *unstructured.Unstructured, uid types.UID) bool {
138+
for _, ref := range obj.GetOwnerReferences() {
139+
if ref.UID == uid {
140+
return true
141+
}
142+
}
143+
return false
144+
}
145+
146+
// setStatusCondition immediately sets condition and immediately updates object in K8s.
147+
func setStatusCondition(ctx context.Context, cl client.Client, cr *v2.PerconaPGCluster, cond metav1.Condition) error {
148+
orig := cr.DeepCopy()
149+
150+
meta.SetStatusCondition(&cr.Status.Conditions, cond)
151+
152+
return cl.Status().Patch(ctx, cr, client.MergeFrom(orig))
153+
}

0 commit comments

Comments
 (0)