Skip to content

Commit 43ff23e

Browse files
akalenyuclaude
andauthored
import-populator: update labels before annotations on succeeded PVC (#4174)
When the import populator copies PVC Prime metadata to the target PVC, labels must land before annotations. The AnnPodPhase=Succeeded annotation triggers downstream controllers (DV, DIC) that snapshot the PVC, so instancetype labels need to already be present at that point. Signed-off-by: Alex Kalenyuk <akalenyu@redhat.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7b0ceb9 commit 43ff23e

3 files changed

Lines changed: 32 additions & 4 deletions

File tree

pkg/controller/populators/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ go_test(
8585
"//vendor/k8s.io/utils/ptr:go_default_library",
8686
"//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library",
8787
"//vendor/sigs.k8s.io/controller-runtime/pkg/client/fake:go_default_library",
88+
"//vendor/sigs.k8s.io/controller-runtime/pkg/client/interceptor:go_default_library",
8889
"//vendor/sigs.k8s.io/controller-runtime/pkg/log:go_default_library",
8990
"//vendor/sigs.k8s.io/controller-runtime/pkg/log/zap:go_default_library",
9091
"//vendor/sigs.k8s.io/controller-runtime/pkg/reconcile:go_default_library",

pkg/controller/populators/import-populator.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,12 @@ func (r *ImportPopulatorReconciler) reconcileTargetPVC(pvc, pvcPrime *corev1.Per
195195
}
196196

197197
if cc.IsPVCComplete(pvcPrime) && cc.IsUnbound(pvc) {
198-
// Once the import is succeeded, we copy annotations and labels and rebind the PV from PVC to target PVC
199-
if pvcCopy, err = r.updatePVCWithPVCPrimeAnnotations(pvcCopy, pvcPrime, r.updateImportAnnotations); err != nil {
198+
// Labels must be updated before annotations so that instancetype labels
199+
// are present on the PVC before AnnPodPhase=Succeeded triggers downstream controllers.
200+
if pvcCopy, err = r.updatePVCWithPVCPrimeLabels(pvcCopy, pvcPrime.GetLabels()); err != nil {
200201
return reconcile.Result{}, err
201202
}
202-
if pvcCopy, err = r.updatePVCWithPVCPrimeLabels(pvcCopy, pvcPrime.GetLabels()); err != nil {
203+
if pvcCopy, err = r.updatePVCWithPVCPrimeAnnotations(pvcCopy, pvcPrime, r.updateImportAnnotations); err != nil {
203204
return reconcile.Result{}, err
204205
}
205206
if err := cc.Rebind(context.TODO(), r.client, pvcPrime, pvcCopy); err != nil {

pkg/controller/populators/import-populator_test.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"net/http"
2323
"net/http/httptest"
2424
"net/url"
25+
"reflect"
2526
"strconv"
2627
"strings"
2728
"time"
@@ -42,7 +43,9 @@ import (
4243
"k8s.io/client-go/tools/record"
4344
"k8s.io/utils/ptr"
4445

46+
"sigs.k8s.io/controller-runtime/pkg/client"
4547
"sigs.k8s.io/controller-runtime/pkg/client/fake"
48+
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
4649
logf "sigs.k8s.io/controller-runtime/pkg/log"
4750
"sigs.k8s.io/controller-runtime/pkg/reconcile"
4851

@@ -459,6 +462,27 @@ var _ = Describe("Import populator tests", func() {
459462

460463
By("Reconcile")
461464
reconciler = createImportPopulatorReconciler(targetPvc, pvcPrime, pv, volumeImportSource, sc)
465+
var operations []string
466+
reconciler.client = interceptor.NewClient(reconciler.client.(client.WithWatch), interceptor.Funcs{
467+
Update: func(ctx context.Context, c client.WithWatch, obj client.Object, opts ...client.UpdateOption) error {
468+
pvc, ok := obj.(*corev1.PersistentVolumeClaim)
469+
if !ok {
470+
return c.Update(ctx, obj, opts...)
471+
}
472+
old := &corev1.PersistentVolumeClaim{}
473+
if err := c.Get(ctx, types.NamespacedName{Name: pvc.Name, Namespace: pvc.Namespace}, old); err == nil {
474+
if !reflect.DeepEqual(old.Labels, pvc.Labels) {
475+
operations = append(operations, "labels")
476+
}
477+
oldPhase, currentPhase := old.Annotations[AnnPodPhase], pvc.Annotations[AnnPodPhase]
478+
if currentPhase == string(corev1.PodSucceeded) && oldPhase != currentPhase {
479+
operations = append(operations, "phase")
480+
}
481+
}
482+
return c.Update(ctx, obj, opts...)
483+
},
484+
})
485+
462486
result, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Name: targetPvcName, Namespace: metav1.NamespaceDefault}})
463487
Expect(err).To(Not(HaveOccurred()))
464488
Expect(result).ToNot(BeNil())
@@ -472,6 +496,9 @@ var _ = Describe("Import populator tests", func() {
472496
Expect(updatedPVC.Labels).To(HaveKeyWithValue(testInstancetypeKubevirtIoKey, testInstancetypeKubevirtIoValue))
473497
Expect(updatedPVC.Labels).To(HaveKeyWithValue(testKubevirtIoKeyExisting, testKubevirtIoValueExisting))
474498
Expect(updatedPVC.Labels).ToNot(HaveKey(testUndesiredKey))
499+
500+
By("Verify labels are updated before AnnPodPhase=Succeeded and phase is set once")
501+
Expect(operations).To(Equal([]string{"labels", "phase"}))
475502
})
476503

477504
It("Should set multistage migration annotations on PVC prime", func() {
@@ -707,7 +734,6 @@ func createImportPopulatorReconcilerWithoutConfig(objects ...runtime.Object) *Im
707734
for _, ia := range getIndexArgs() {
708735
builder = builder.WithIndex(ia.obj, ia.field, ia.extractValue)
709736
}
710-
711737
cl := builder.Build()
712738

713739
rec := record.NewFakeRecorder(10)

0 commit comments

Comments
 (0)