Skip to content

Commit 7434eeb

Browse files
Merge pull request #1611 from tesshuflower/restic_cache_volume_name_conflict
fix restic cache pvc name collisions for src & dst
2 parents acdf3a1 + f5c4b19 commit 7434eeb

6 files changed

+152
-7
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Restic updated to v0.18.0
1313
- Syncthing updated to v1.29.3
1414

15+
### Fixed
16+
17+
- Fix restic cache PVC name collision if replicationsource and
18+
replicationdestination have the same name and are in the same
19+
namespace
20+
1521
## [v0.12.1]
1622

1723
### Security

controllers/mover/restic/mover.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"k8s.io/utils/ptr"
3838
ctrl "sigs.k8s.io/controller-runtime"
3939
"sigs.k8s.io/controller-runtime/pkg/client"
40+
ctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
4041

4142
volsyncv1alpha1 "github.com/backube/volsync/api/v1alpha1"
4243
vserrors "github.com/backube/volsync/controllers/errors"
@@ -214,12 +215,51 @@ func (m *Mover) ensureCache(ctx context.Context,
214215
return nil, err
215216
}
216217

218+
err = m.migrationCleanupObsoleteCachePVC(ctx)
219+
if err != nil {
220+
return nil, err
221+
}
222+
217223
// Allocate cache volume
218-
cacheName := mover.VolSyncPrefix + m.owner.GetName() + "-cache"
224+
dir := "src"
225+
if !m.isSource {
226+
dir = "dst"
227+
}
228+
cacheName := mover.VolSyncPrefix + dir + "-" + m.owner.GetName() + "-cache"
219229
m.logger.Info("allocating cache volume", "PVC", cacheName, "isTemporary", isTemporary)
220230
return cacheVh.EnsureNewPVC(ctx, m.logger, cacheName, isTemporary)
221231
}
222232

233+
// Old names for the cache PVC were common between source & dest - our new name will include -src or -dst
234+
// to avoid collisions - migrate by cleanup old cache PVC if it exists
235+
// (see issue: https://github.com/backube/volsync/issues/1575)
236+
func (m *Mover) migrationCleanupObsoleteCachePVC(ctx context.Context) error {
237+
obsCachePVC := &corev1.PersistentVolumeClaim{
238+
ObjectMeta: metav1.ObjectMeta{
239+
Name: mover.VolSyncPrefix + m.owner.GetName() + "-cache",
240+
Namespace: m.owner.GetNamespace(),
241+
},
242+
}
243+
err := m.client.Get(ctx, client.ObjectKeyFromObject(obsCachePVC), obsCachePVC)
244+
if err == nil {
245+
// the cache PVC (with old obsolete name) still exists, remove it but only if it is owned by this CR
246+
isOwnedByUs, err := ctrlutil.HasOwnerReference(obsCachePVC.GetOwnerReferences(), m.owner, m.client.Scheme())
247+
if err != nil {
248+
return err
249+
}
250+
if !isOwnedByUs {
251+
// Do not delete, not owned by this VolSync CR
252+
return nil
253+
}
254+
255+
m.logger.Info("deleting old cache volume, a new one will be created", "old cachePVC", obsCachePVC.GetName())
256+
_ = m.client.Delete(ctx, obsCachePVC, client.PropagationPolicy(metav1.DeletePropagationBackground))
257+
return nil
258+
}
259+
260+
return client.IgnoreNotFound(err)
261+
}
262+
223263
func (m *Mover) ensureSourcePVC(ctx context.Context) (*corev1.PersistentVolumeClaim, error) {
224264
srcPVC := &corev1.PersistentVolumeClaim{
225265
ObjectMeta: metav1.ObjectMeta{

controllers/mover/restic/restic_test.go

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ import (
4040
"k8s.io/apimachinery/pkg/types"
4141
"k8s.io/client-go/tools/events"
4242
"k8s.io/utils/ptr"
43+
ctrl "sigs.k8s.io/controller-runtime"
4344
"sigs.k8s.io/controller-runtime/pkg/client"
4445
"sigs.k8s.io/controller-runtime/pkg/log/zap"
4546

4647
volsyncv1alpha1 "github.com/backube/volsync/api/v1alpha1"
47-
"github.com/backube/volsync/controllers/mover"
48+
vsmover "github.com/backube/volsync/controllers/mover"
4849
"github.com/backube/volsync/controllers/utils"
4950
)
5051

@@ -239,7 +240,7 @@ var _ = Describe("Restic properly registers", func() {
239240

240241
It("is added to the mover catalog", func() {
241242
found := false
242-
for _, v := range mover.Catalog {
243+
for _, v := range vsmover.Catalog {
243244
if _, ok := v.(*Builder); ok {
244245
found = true
245246
}
@@ -624,6 +625,78 @@ var _ = Describe("Restic as a source", func() {
624625
Expect(*cache.Spec.StorageClassName).To(Equal(cachesc))
625626
})
626627
})
628+
629+
Context("old cache name migration", func() {
630+
var oldCache *corev1.PersistentVolumeClaim
631+
When("The cache PVC with the old name exists", func() {
632+
BeforeEach(func() {
633+
sc := "some-storage"
634+
oldCache = &corev1.PersistentVolumeClaim{
635+
ObjectMeta: metav1.ObjectMeta{
636+
Name: vsmover.VolSyncPrefix + rs.GetName() + "-cache",
637+
Namespace: rs.GetNamespace(),
638+
},
639+
Spec: corev1.PersistentVolumeClaimSpec{
640+
AccessModes: []corev1.PersistentVolumeAccessMode{
641+
corev1.ReadWriteOnce,
642+
},
643+
Resources: corev1.VolumeResourceRequirements{
644+
Requests: corev1.ResourceList{
645+
"storage": resource.MustParse("1Gi"),
646+
},
647+
},
648+
StorageClassName: &sc,
649+
},
650+
}
651+
})
652+
653+
JustBeforeEach(func() {
654+
Expect(k8sClient.Create(ctx, oldCache)).To(Succeed())
655+
})
656+
657+
When("The old cache pvc is not owned by this VolSync RS", func() {
658+
It("Should not delete the old cache PVC and still create the new one", func() {
659+
cache, err := mover.ensureCache(ctx, dataPVC, false)
660+
Expect(err).ToNot(HaveOccurred())
661+
Expect(cache.GetName()).To(ContainSubstring("-cache"))
662+
Expect(cache.GetName()).To(ContainSubstring("-src-"))
663+
664+
// Check that old cache pvc is still there
665+
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(oldCache), oldCache)).To(Succeed())
666+
Expect(oldCache.GetDeletionTimestamp().IsZero()).To(BeTrue()) // Not marked for deletion
667+
})
668+
})
669+
670+
When("The old cache PVC was owned by this VolSync RS", func() {
671+
JustBeforeEach(func() {
672+
// Need to do this after the RS has been created
673+
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(oldCache), oldCache)).To(Succeed())
674+
Expect(ctrl.SetControllerReference(rs, oldCache, k8sClient.Scheme())).To(Succeed())
675+
Expect(k8sClient.Update(ctx, oldCache)).To(Succeed())
676+
})
677+
678+
It("Should delete the old cache pvc and still create the new one", func() {
679+
cache, err := mover.ensureCache(ctx, dataPVC, false)
680+
Expect(err).ToNot(HaveOccurred())
681+
Expect(cache.GetName()).To(ContainSubstring("-cache"))
682+
Expect(cache.GetName()).To(ContainSubstring("-src-"))
683+
684+
// Check that old cache pvc was deleted
685+
err = k8sClient.Get(ctx, client.ObjectKeyFromObject(oldCache), oldCache)
686+
if err == nil {
687+
// Should be marked for deletion
688+
Expect(oldCache.GetDeletionTimestamp().IsZero()).To(BeFalse())
689+
} else {
690+
Expect(kerrors.IsNotFound(err)).To(BeTrue())
691+
}
692+
693+
// Should be able to reconcile again
694+
_, err = mover.ensureCache(ctx, dataPVC, false)
695+
Expect(err).ToNot(HaveOccurred())
696+
})
697+
})
698+
})
699+
})
627700
})
628701

629702
Context("Source volume is handled properly", func() {
@@ -1829,6 +1902,32 @@ var _ = Describe("Restic as a destination", func() {
18291902
mover, _ = m.(*Mover)
18301903
Expect(mover).NotTo(BeNil())
18311904
})
1905+
1906+
Context("Restic cache is created correctly", func() {
1907+
var dataPVC *corev1.PersistentVolumeClaim
1908+
BeforeEach(func() {
1909+
am := corev1.ReadWriteMany
1910+
rd.Spec.Restic.AccessModes = []corev1.PersistentVolumeAccessMode{
1911+
am,
1912+
}
1913+
destVolCap := resource.MustParse("6Gi")
1914+
rd.Spec.Restic.Capacity = &destVolCap
1915+
})
1916+
1917+
JustBeforeEach(func() {
1918+
var err error
1919+
dataPVC, err = mover.ensureDestinationPVC(ctx)
1920+
Expect(err).NotTo(HaveOccurred())
1921+
})
1922+
1923+
It("Should create old cache pvc", func() {
1924+
cache, err := mover.ensureCache(ctx, dataPVC, false)
1925+
Expect(err).ToNot(HaveOccurred())
1926+
Expect(cache.GetName()).To(ContainSubstring("-cache"))
1927+
Expect(cache.GetName()).To(ContainSubstring("-dst-"))
1928+
})
1929+
})
1930+
18321931
When("no destination volume is supplied", func() {
18331932
var destVolCap resource.Quantity
18341933
var am corev1.PersistentVolumeAccessMode

test-e2e/test_restic_manual_normal.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@
224224
kubernetes.core.k8s_info:
225225
api_version: v1
226226
kind: PersistentVolumeClaim
227-
name: volsync-restore-cache
227+
name: volsync-dst-restore-cache
228228
namespace: "{{ namespace }}"
229229
register: cachepvcreloaded
230230

test-e2e/test_restic_manual_normal_cleanup_pvcs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@
207207
kubernetes.core.k8s_info:
208208
api_version: v1
209209
kind: PersistentVolumeClaim
210-
name: volsync-restore-cache
210+
name: volsync-dst-restore-cache
211211
namespace: "{{ namespace }}"
212212
register: cachepvc
213213
until: >
@@ -300,7 +300,7 @@
300300
kubernetes.core.k8s_info:
301301
api_version: v1
302302
kind: PersistentVolumeClaim
303-
name: volsync-restore-cache
303+
name: volsync-dst-restore-cache
304304
namespace: "{{ namespace }}"
305305
register: cachepvcreloaded
306306

test-e2e/test_restic_manual_normal_longname.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@
225225
kubernetes.core.k8s_info:
226226
api_version: v1
227227
kind: PersistentVolumeClaim
228-
name: volsync-restore-thisisavery-very-very-very-longnameevenlongerthan63chars2-cache
228+
name: volsync-dst-restore-thisisavery-very-very-very-longnameevenlongerthan63chars2-cache
229229
namespace: "{{ namespace }}"
230230
register: cachepvcreloaded
231231

0 commit comments

Comments
 (0)