Skip to content

Commit c28d13f

Browse files
Merge pull request #194 from cybozu-go/5672-lock-volume-2
change api CreateOrUpdateMantleBackup to CreateMantleBackup
2 parents d9b5acf + 61821f3 commit c28d13f

16 files changed

Lines changed: 194 additions & 171 deletions

docs/controller-protocol.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
## Table of Contents
55

66
- [pkg/controller/proto/controller.proto](#pkg_controller_proto_controller-proto)
7-
- [CreateOrUpdateMantleBackupRequest](#proto-CreateOrUpdateMantleBackupRequest)
8-
- [CreateOrUpdateMantleBackupResponse](#proto-CreateOrUpdateMantleBackupResponse)
7+
- [CreateMantleBackupRequest](#proto-CreateMantleBackupRequest)
8+
- [CreateMantleBackupResponse](#proto-CreateMantleBackupResponse)
99
- [CreateOrUpdatePVCRequest](#proto-CreateOrUpdatePVCRequest)
1010
- [CreateOrUpdatePVCResponse](#proto-CreateOrUpdatePVCResponse)
1111
- [ListMantleBackupRequest](#proto-ListMantleBackupRequest)
@@ -26,10 +26,10 @@
2626

2727

2828

29-
<a name="proto-CreateOrUpdateMantleBackupRequest"></a>
29+
<a name="proto-CreateMantleBackupRequest"></a>
3030

31-
### CreateOrUpdateMantleBackupRequest
32-
CreateOrUpdateMantleBackupRequest is a request message for CreateOrUpdateMantleBackup RPC.
31+
### CreateMantleBackupRequest
32+
CreateMantleBackupRequest is a request message for CreateMantleBackup RPC.
3333

3434

3535
| Field | Type | Label | Description |
@@ -41,10 +41,10 @@ CreateOrUpdateMantleBackupRequest is a request message for CreateOrUpdateMantleB
4141

4242

4343

44-
<a name="proto-CreateOrUpdateMantleBackupResponse"></a>
44+
<a name="proto-CreateMantleBackupResponse"></a>
4545

46-
### CreateOrUpdateMantleBackupResponse
47-
CreateOrUpdateMantleBackupResponse is a response message for CreateOrUpdateMantleBackup RPC.
46+
### CreateMantleBackupResponse
47+
CreateMantleBackupResponse is a response message for CreateMantleBackup RPC.
4848

4949
nothing.
5050

@@ -155,7 +155,7 @@ SetSynchronizingResponse is a response message for SetSynchronize RPC.
155155
| Method Name | Request Type | Response Type | Description |
156156
| ----------- | ------------ | ------------- | ------------|
157157
| CreateOrUpdatePVC | [CreateOrUpdatePVCRequest](#proto-CreateOrUpdatePVCRequest) | [CreateOrUpdatePVCResponse](#proto-CreateOrUpdatePVCResponse) | |
158-
| CreateOrUpdateMantleBackup | [CreateOrUpdateMantleBackupRequest](#proto-CreateOrUpdateMantleBackupRequest) | [CreateOrUpdateMantleBackupResponse](#proto-CreateOrUpdateMantleBackupResponse) | |
158+
| CreateMantleBackup | [CreateMantleBackupRequest](#proto-CreateMantleBackupRequest) | [CreateMantleBackupResponse](#proto-CreateMantleBackupResponse) | |
159159
| ListMantleBackup | [ListMantleBackupRequest](#proto-ListMantleBackupRequest) | [ListMantleBackupResponse](#proto-ListMantleBackupResponse) | |
160160
| SetSynchronizing | [SetSynchronizingRequest](#proto-SetSynchronizingRequest) | [SetSynchronizingResponse](#proto-SetSynchronizingResponse) | |
161161

internal/controller/mantlebackup_controller.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -617,10 +617,10 @@ func (r *MantleBackupReconciler) replicateManifests(
617617
return ctrl.Result{}, err
618618
}
619619

620-
// Call CreateOrUpdateMantleBackup.
621-
if _, err := client.CreateOrUpdateMantleBackup(
620+
// Call CreateMantleBackup.
621+
if _, err := client.CreateMantleBackup(
622622
ctx,
623-
&proto.CreateOrUpdateMantleBackupRequest{
623+
&proto.CreateMantleBackupRequest{
624624
MantleBackup: backupSentJson,
625625
},
626626
); err != nil {

internal/controller/mantlebackup_controller_test.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -494,21 +494,21 @@ var _ = Describe("MantleBackup controller", func() {
494494
Uid: "a7c9d5e2-4b8f-4e2a-9d3f-1b6a7c8e9f2b",
495495
}, nil)
496496
secondaryBackups := []*mantlev1.MantleBackup{}
497-
grpcClient.EXPECT().CreateOrUpdateMantleBackup(gomock.Any(), gomock.Any()).
497+
grpcClient.EXPECT().CreateMantleBackup(gomock.Any(), gomock.Any()).
498498
MinTimes(1).
499499
DoAndReturn(
500500
func(
501501
ctx context.Context,
502-
req *proto.CreateOrUpdateMantleBackupRequest,
502+
req *proto.CreateMantleBackupRequest,
503503
opts ...grpc.CallOption,
504-
) (*proto.CreateOrUpdateMantleBackupResponse, error) {
504+
) (*proto.CreateMantleBackupResponse, error) {
505505
var secondaryBackup mantlev1.MantleBackup
506506
err := json.Unmarshal(req.GetMantleBackup(), &secondaryBackup)
507507
if err != nil {
508508
panic(err)
509509
}
510510
secondaryBackups = append(secondaryBackups, &secondaryBackup)
511-
return &proto.CreateOrUpdateMantleBackupResponse{}, nil
511+
return &proto.CreateMantleBackupResponse{}, nil
512512
})
513513
grpcClient.EXPECT().ListMantleBackup(gomock.Any(), gomock.Any()).
514514
MinTimes(1).
@@ -1380,7 +1380,7 @@ var _ = Describe("export and upload", func() {
13801380
diffFrom: diffFrom,
13811381
})
13821382
Expect(err).NotTo(HaveOccurred())
1383-
Expect(ret.Requeue).To(BeTrue())
1383+
Expect(ret.RequeueAfter).NotTo(BeZero())
13841384

13851385
return target
13861386
}
@@ -1394,7 +1394,7 @@ var _ = Describe("export and upload", func() {
13941394
diffFrom: nil,
13951395
})
13961396
Expect(err).NotTo(HaveOccurred())
1397-
Expect(ret.Requeue).To(BeTrue())
1397+
Expect(ret.RequeueAfter).NotTo(BeZero())
13981398
}
13991399

14001400
completeJob := func(ctx SpecContext, jobName string) {
@@ -1739,7 +1739,7 @@ var _ = Describe("import", func() {
17391739
// The first call to reconcileImportJob should create an import Job
17401740
res, err := mbr.reconcileImportJob(ctx, backup, snapshotTarget, -1)
17411741
Expect(err).NotTo(HaveOccurred())
1742-
Expect(res.Requeue).To(BeTrue())
1742+
Expect(res.RequeueAfter).NotTo(BeZero())
17431743

17441744
var importJob batchv1.Job
17451745
err = k8sClient.Get(ctx, types.NamespacedName{
@@ -1751,7 +1751,7 @@ var _ = Describe("import", func() {
17511751
// The successive calls should return ctrl.Result{Requeue: true} until the import Job is completed.
17521752
res, err = mbr.reconcileImportJob(ctx, backup, snapshotTarget, -1)
17531753
Expect(err).NotTo(HaveOccurred())
1754-
Expect(res.Requeue).To(BeTrue())
1754+
Expect(res.RequeueAfter).NotTo(BeZero())
17551755

17561756
// Make the import Job completed.
17571757
completeJob(ctx, importJob.GetNamespace(), importJob.GetName())
@@ -2177,7 +2177,7 @@ var _ = Describe("import", func() {
21772177

21782178
result, err := mbr.reconcileZeroOutJob(ctx, backup, &snapshotTarget{})
21792179
Expect(err).NotTo(HaveOccurred())
2180-
Expect(result.Requeue).To(BeFalse())
2180+
Expect(result.RequeueAfter).To(BeZero())
21812181

21822182
var pv corev1.PersistentVolume
21832183
err = k8sClient.Get(ctx, types.NamespacedName{Name: MakeZeroOutPVName(backup)}, &pv)
@@ -2258,14 +2258,14 @@ var _ = Describe("import", func() {
22582258
// The first call to reconcileZeroOutJob should create a PV, PVC, and Job, and requeue.
22592259
result, err := mbr.reconcileZeroOutJob(ctx, backup, snapshotTarget)
22602260
Expect(err).NotTo(HaveOccurred())
2261-
Expect(result.Requeue).To(BeTrue())
2261+
Expect(result.RequeueAfter).NotTo(BeZero())
22622262

22632263
var pv corev1.PersistentVolume
22642264
err = k8sClient.Get(ctx, types.NamespacedName{Name: MakeZeroOutPVName(backup), Namespace: nsController}, &pv)
22652265
Expect(err).NotTo(HaveOccurred())
22662266
Expect(pv.GetLabels()["app.kubernetes.io/name"]).To(Equal(labelAppNameValue))
22672267
Expect(pv.GetLabels()["app.kubernetes.io/component"]).To(Equal(labelComponentZeroOutVolume))
2268-
Expect(len(pv.Spec.AccessModes)).To(Equal(1))
2268+
Expect(pv.Spec.AccessModes).To(HaveLen(1))
22692269
Expect(pv.Spec.AccessModes[0]).To(Equal(corev1.ReadWriteOnce))
22702270
Expect(pv.Spec.Capacity).To(Equal(pvCapacity))
22712271
Expect(pv.Spec.CSI.Driver).To(Equal(pvDriver))
@@ -2287,7 +2287,7 @@ var _ = Describe("import", func() {
22872287
Expect(pvc.GetLabels()["app.kubernetes.io/name"]).To(Equal(labelAppNameValue))
22882288
Expect(pvc.GetLabels()["app.kubernetes.io/component"]).To(Equal(labelComponentZeroOutVolume))
22892289
Expect(*pvc.Spec.StorageClassName).To(Equal(""))
2290-
Expect(len(pvc.Spec.AccessModes)).To(Equal(1))
2290+
Expect(pvc.Spec.AccessModes).To(HaveLen(1))
22912291
Expect(pvc.Spec.AccessModes[0]).To(Equal(corev1.ReadWriteOnce))
22922292
Expect(pvc.Spec.Resources).To(Equal(pvcResources))
22932293
Expect(*pvc.Spec.VolumeMode).To(Equal(corev1.PersistentVolumeBlock))
@@ -2299,17 +2299,17 @@ var _ = Describe("import", func() {
22992299
Expect(job.GetLabels()["app.kubernetes.io/name"]).To(Equal(labelAppNameValue))
23002300
Expect(job.GetLabels()["app.kubernetes.io/component"]).To(Equal(labelComponentZeroOutJob))
23012301
Expect(*job.Spec.BackoffLimit).To(Equal(int32(65535)))
2302-
Expect(len(job.Spec.Template.Spec.Containers)).To(Equal(1))
2302+
Expect(job.Spec.Template.Spec.Containers).To(HaveLen(1))
23032303
Expect(job.Spec.Template.Spec.Containers[0].Name).To(Equal("zeroout"))
23042304
Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext.Privileged).To(BeTrue())
23052305
Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup).To(Equal(int64(0)))
23062306
Expect(*job.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser).To(Equal(int64(0)))
23072307
Expect(job.Spec.Template.Spec.Containers[0].Image).To(Equal(mbr.podImage))
2308-
Expect(len(job.Spec.Template.Spec.Containers[0].VolumeDevices)).To(Equal(1))
2308+
Expect(job.Spec.Template.Spec.Containers[0].VolumeDevices).To(HaveLen(1))
23092309
Expect(job.Spec.Template.Spec.Containers[0].VolumeDevices[0].Name).To(Equal("zeroout-rbd"))
23102310
Expect(job.Spec.Template.Spec.Containers[0].VolumeDevices[0].DevicePath).To(Equal("/dev/zeroout-rbd"))
23112311
Expect(job.Spec.Template.Spec.RestartPolicy).To(Equal(corev1.RestartPolicyOnFailure))
2312-
Expect(len(job.Spec.Template.Spec.Volumes)).To(Equal(1))
2312+
Expect(job.Spec.Template.Spec.Volumes).To(HaveLen(1))
23132313
Expect(job.Spec.Template.Spec.Volumes[0]).To(Equal(corev1.Volume{
23142314
Name: "zeroout-rbd",
23152315
VolumeSource: corev1.VolumeSource{
@@ -2325,7 +2325,7 @@ var _ = Describe("import", func() {
23252325
// A call to reconcileZeroOutJob should NOT requeue after the Job completed
23262326
result, err = mbr.reconcileZeroOutJob(ctx, backup, snapshotTarget)
23272327
Expect(err).NotTo(HaveOccurred())
2328-
Expect(result.Requeue).To(BeFalse())
2328+
Expect(result.RequeueAfter).To(BeZero())
23292329
})
23302330
})
23312331

internal/controller/replication.go

Lines changed: 51 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/cybozu-go/mantle/pkg/controller/proto"
1111
"google.golang.org/grpc"
1212
corev1 "k8s.io/api/core/v1"
13+
aerrors "k8s.io/apimachinery/pkg/api/errors"
1314
"k8s.io/apimachinery/pkg/labels"
1415
"k8s.io/apimachinery/pkg/types"
1516
ctrl "sigs.k8s.io/controller-runtime"
@@ -109,10 +110,10 @@ func (s *SecondaryServer) CreateOrUpdatePVC(
109110
return &proto.CreateOrUpdatePVCResponse{Uid: string(pvc.GetUID())}, nil
110111
}
111112

112-
func (s *SecondaryServer) CreateOrUpdateMantleBackup(
113+
func (s *SecondaryServer) CreateMantleBackup(
113114
ctx context.Context,
114-
req *proto.CreateOrUpdateMantleBackupRequest,
115-
) (*proto.CreateOrUpdateMantleBackupResponse, error) {
115+
req *proto.CreateMantleBackupRequest,
116+
) (*proto.CreateMantleBackupResponse, error) {
116117
var backupReceived mantlev1.MantleBackup
117118
if err := json.Unmarshal(req.MantleBackup, &backupReceived); err != nil {
118119
return nil, err
@@ -139,62 +140,59 @@ func (s *SecondaryServer) CreateOrUpdateMantleBackup(
139140
annotRemoteUID, backupReceived.GetName(), backupReceived.GetNamespace())
140141
}
141142

142-
var backup mantlev1.MantleBackup
143-
backup.SetName(backupReceived.GetName())
144-
backup.SetNamespace(backupReceived.GetNamespace())
145-
if _, err := ctrl.CreateOrUpdate(ctx, s.client, &backup, func() error {
146-
if !backup.CreationTimestamp.IsZero() {
147-
errMsg := ""
148-
if backup.Labels == nil {
149-
errMsg = "labels field is nil in backup"
150-
} else {
151-
pvcUID, ok := backup.Labels[labelLocalBackupTargetPVCUID]
152-
if !ok {
153-
errMsg = "label not found"
154-
} else if pvcUID != pvcUIDReceived {
155-
errMsg = "label not matched"
156-
}
157-
}
158-
if errMsg != "" {
159-
return fmt.Errorf("%s: %s: %s: %s",
160-
errMsg, labelLocalBackupTargetPVCUID, backupReceived.GetName(), backupReceived.GetNamespace())
161-
}
162-
163-
if backup.Annotations == nil {
164-
errMsg = "annotation field is nil in backup"
165-
} else {
166-
remoteUID, ok := backup.Annotations[annotRemoteUID]
167-
if !ok {
168-
errMsg = "annotation not found in backup"
169-
} else if remoteUID != remoteUIDReceived {
170-
errMsg = "annotation not matched"
171-
}
172-
}
173-
if errMsg != "" {
174-
return fmt.Errorf("%s: %s: %s: %s",
175-
errMsg, annotRemoteUID, backupReceived.GetName(), backupReceived.GetNamespace())
176-
}
143+
var backupExists mantlev1.MantleBackup
144+
if err := s.client.Get(ctx, types.NamespacedName{
145+
Namespace: backupReceived.GetNamespace(),
146+
Name: backupReceived.GetName(),
147+
}, &backupExists); err != nil {
148+
if !aerrors.IsNotFound(err) {
149+
return nil, fmt.Errorf("failed to get MantleBackup: %w", err)
150+
}
151+
// Not found, create a new one.
152+
if err := s.client.Create(ctx, &backupReceived); err != nil {
153+
return nil, fmt.Errorf("failed to create MantleBackup: %w", err)
177154
}
155+
if err := s.client.Status().Update(ctx, &backupReceived); err != nil {
156+
return nil, fmt.Errorf("failed to update status of MantleBackup: %w", err)
157+
}
158+
return &proto.CreateMantleBackupResponse{}, nil
159+
}
178160

179-
backup.Finalizers = backupReceived.Finalizers
180-
backup.Annotations = backupReceived.Annotations
181-
backup.Labels = backupReceived.Labels
182-
backup.Spec = backupReceived.Spec
183-
return nil
184-
}); err != nil {
185-
return nil, fmt.Errorf("CreateOrUpdate failed: %w", err)
161+
// Found, check it.
162+
if backupExists.Labels == nil || backupExists.Annotations == nil {
163+
return nil, fmt.Errorf("both labels and annotations must not be nil in the existing MantleBackup: %s/%s",
164+
backupReceived.GetNamespace(), backupReceived.GetName())
165+
}
166+
167+
if pvcUID, ok := backupExists.Labels[labelLocalBackupTargetPVCUID]; !ok {
168+
return nil, fmt.Errorf("label %s not found in the existing MantleBackup: %s/%s",
169+
labelLocalBackupTargetPVCUID, backupReceived.GetNamespace(), backupReceived.GetName())
170+
} else if pvcUID != pvcUIDReceived {
171+
return nil, fmt.Errorf("label %s not matched in the existing MantleBackup: %s/%s",
172+
labelLocalBackupTargetPVCUID, backupReceived.GetNamespace(), backupReceived.GetName())
186173
}
187174

188-
// Use Patch here because updateStatus is likely to fail due to "the object has been modified" error.
189-
newBackup := backup.DeepCopy()
190-
newBackup.Status.CreatedAt = backupReceived.Status.CreatedAt
191-
newBackup.Status.SnapSize = backupReceived.Status.SnapSize
192-
newBackup.Status.TransferPartSize = backupReceived.Status.TransferPartSize
193-
if err := s.client.Status().Patch(ctx, newBackup, client.MergeFrom(&backup)); err != nil {
194-
return nil, fmt.Errorf("status patch failed: %w", err)
175+
if remoteUID, ok := backupExists.Annotations[annotRemoteUID]; !ok {
176+
return nil, fmt.Errorf("annotation %s not found in the existing MantleBackup: %s/%s",
177+
annotRemoteUID, backupReceived.GetNamespace(), backupReceived.GetName())
178+
} else if remoteUID != remoteUIDReceived {
179+
return nil, fmt.Errorf("annotation %s not matched in the existing MantleBackup: %s/%s",
180+
annotRemoteUID, backupReceived.GetNamespace(), backupReceived.GetName())
181+
}
182+
183+
// Update status if not set.
184+
if backupExists.Status.CreatedAt.IsZero() {
185+
newBackup := backupExists.DeepCopy()
186+
newBackup.Status.CreatedAt = backupReceived.Status.CreatedAt
187+
newBackup.Status.SnapSize = backupReceived.Status.SnapSize
188+
newBackup.Status.TransferPartSize = backupReceived.Status.TransferPartSize
189+
if err := s.client.Status().Patch(ctx, newBackup, client.MergeFrom(&backupExists)); err != nil {
190+
return nil, fmt.Errorf("status patch failed: %w", err)
191+
}
195192
}
196193

197-
return &proto.CreateOrUpdateMantleBackupResponse{}, nil
194+
// Already exists with matching pvc-uid and remote-uid, do nothing.
195+
return &proto.CreateMantleBackupResponse{}, nil
198196
}
199197

200198
func (s *SecondaryServer) ListMantleBackup(

internal/controller/suite_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ var _ = BeforeSuite(func(ctx SpecContext) {
7171
Expect(err).NotTo(HaveOccurred())
7272
Expect(k8sClient).NotTo(BeNil())
7373

74+
err = os.Setenv("REQUEUE_RECONCILIATION_AFTER", "10s")
75+
Expect(err).NotTo(HaveOccurred())
76+
7477
By("Setup common resources")
7578
resMgr, err = testutil.NewResourceManager(k8sClient)
7679
Expect(err).NotTo(HaveOccurred())

internal/controller/util.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,12 @@ func IsJobConditionTrue(conditions []batchv1.JobCondition, conditionType batchv1
151151

152152
func requeueReconciliation() ctrl.Result {
153153
requeueAfter := os.Getenv("REQUEUE_RECONCILIATION_AFTER")
154-
if requeueAfter != "" {
155-
duration, err := time.ParseDuration(requeueAfter)
156-
if err != nil {
157-
panic(fmt.Sprintf("set REQUEUE_RECONCILIATION_AFTER properly: %v", err))
158-
}
159-
return ctrl.Result{RequeueAfter: duration}
154+
if len(requeueAfter) == 0 {
155+
panic("You should set REQUEUE_RECONCILIATION_AFTER env var.")
156+
}
157+
duration, err := time.ParseDuration(requeueAfter)
158+
if err != nil {
159+
panic(fmt.Sprintf("Set REQUEUE_RECONCILIATION_AFTER properly: %v", err))
160160
}
161-
return ctrl.Result{Requeue: true}
161+
return ctrl.Result{RequeueAfter: duration}
162162
}

internal/webhook/v1/suite_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ var _ = BeforeSuite(func(ctx SpecContext) {
7373
Expect(err).NotTo(HaveOccurred())
7474
Expect(k8sClient).NotTo(BeNil())
7575

76+
err = os.Setenv("REQUEUE_RECONCILIATION_AFTER", "10s")
77+
Expect(err).NotTo(HaveOccurred())
78+
7679
By("Setup common resources")
7780
resMgr, err = testutil.NewResourceManager(k8sClient)
7881
Expect(err).NotTo(HaveOccurred())

0 commit comments

Comments
 (0)