Skip to content

Commit 19e9b35

Browse files
authored
chore: retention mechanism for synchronized Kubernetes objects (#2052)
Signed-off-by: Anatolii Bazko <[email protected]>
1 parent 3e68cdf commit 19e9b35

File tree

9 files changed

+678
-91
lines changed

9 files changed

+678
-91
lines changed

controllers/workspaceconfig/configmap2sync.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,17 @@ func (p *configMap2Sync) newDstObject() client.Object {
5656
}
5757

5858
func (p *configMap2Sync) getSrcObjectVersion() string {
59-
if len(p.version) == 0 {
60-
return p.cm.GetResourceVersion()
59+
if p.version != "" {
60+
return p.version
6161
}
62-
return p.version
62+
63+
return p.cm.GetResourceVersion()
6364
}
6465

6566
func (p *configMap2Sync) hasROSpec() bool {
6667
return false
6768
}
69+
70+
func (p *configMap2Sync) defaultRetention() bool {
71+
return false
72+
}

controllers/workspaceconfig/configmap2sync_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,3 +479,128 @@ func TestSyncConfigMapShouldRemoveSomeLabels(t *testing.T) {
479479
assert.Empty(t, cm.Labels["argocd.argoproj.io/instance"])
480480
assert.Empty(t, cm.Labels["argocd.argoproj.io/managed-by"])
481481
}
482+
483+
func TestSyncConfigMapShouldRetainIfAnnotationSetTrue(t *testing.T) {
484+
deployContext := test.NewCtxBuilder().WithObjects(
485+
&corev1.ConfigMap{
486+
TypeMeta: metav1.TypeMeta{
487+
Kind: "ConfigMap",
488+
APIVersion: "v1",
489+
},
490+
ObjectMeta: metav1.ObjectMeta{
491+
Name: objectName,
492+
Namespace: eclipseCheNamespace,
493+
Labels: map[string]string{
494+
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
495+
constants.KubernetesComponentLabelKey: constants.WorkspacesConfig,
496+
},
497+
Annotations: map[string]string{
498+
syncRetainOnDeleteAnnotation: "true",
499+
},
500+
},
501+
}).Build()
502+
503+
workspaceConfigReconciler := NewWorkspacesConfigReconciler(
504+
deployContext.ClusterAPI.Client,
505+
deployContext.ClusterAPI.Client,
506+
deployContext.ClusterAPI.Scheme,
507+
&namespacecache.NamespaceCache{
508+
Client: deployContext.ClusterAPI.Client,
509+
KnownNamespaces: map[string]namespacecache.NamespaceInfo{
510+
userNamespace: {
511+
IsWorkspaceNamespace: true,
512+
Username: "user",
513+
CheCluster: &types.NamespacedName{Name: "eclipse-che", Namespace: "eclipse-che"},
514+
},
515+
},
516+
Lock: sync.Mutex{},
517+
})
518+
519+
// Sync ConfigMap
520+
err := workspaceConfigReconciler.syncNamespace(context.TODO(), eclipseCheNamespace, userNamespace)
521+
assert.Nil(t, err)
522+
assertSyncConfig(t, workspaceConfigReconciler, 2, v1ConfigMapGKV)
523+
524+
// Check ConfigMap in a user namespace is created
525+
cm := &corev1.ConfigMap{}
526+
err = deployContext.ClusterAPI.Client.Get(context.TODO(), objectKeyInUserNs, cm)
527+
assert.Nil(t, err)
528+
assert.Equal(t, "true", cm.Annotations[syncRetainOnDeleteAnnotation])
529+
530+
// Delete src ConfigMap
531+
err = deploy.DeleteIgnoreIfNotFound(context.TODO(), deployContext.ClusterAPI.Client, objectKeyInCheNs, &corev1.ConfigMap{})
532+
assert.Nil(t, err)
533+
534+
// Sync ConfigMap
535+
err = workspaceConfigReconciler.syncNamespace(context.TODO(), eclipseCheNamespace, userNamespace)
536+
assert.Nil(t, err)
537+
assertSyncConfig(t, workspaceConfigReconciler, 0, v1ConfigMapGKV)
538+
539+
// Check that destination ConfigMap in a user namespace NOT is deleted
540+
cm = &corev1.ConfigMap{}
541+
err = deployContext.ClusterAPI.Client.Get(context.TODO(), objectKeyInUserNs, cm)
542+
assert.NoError(t, err)
543+
}
544+
545+
func TestSyncConfigMapShouldNotRetainIfAnnotationSetFalse(t *testing.T) {
546+
deployContext := test.NewCtxBuilder().WithObjects(
547+
&corev1.ConfigMap{
548+
TypeMeta: metav1.TypeMeta{
549+
Kind: "ConfigMap",
550+
APIVersion: "v1",
551+
},
552+
ObjectMeta: metav1.ObjectMeta{
553+
Name: objectName,
554+
Namespace: eclipseCheNamespace,
555+
Labels: map[string]string{
556+
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
557+
constants.KubernetesComponentLabelKey: constants.WorkspacesConfig,
558+
},
559+
Annotations: map[string]string{
560+
syncRetainOnDeleteAnnotation: "false",
561+
},
562+
},
563+
}).Build()
564+
565+
workspaceConfigReconciler := NewWorkspacesConfigReconciler(
566+
deployContext.ClusterAPI.Client,
567+
deployContext.ClusterAPI.Client,
568+
deployContext.ClusterAPI.Scheme,
569+
&namespacecache.NamespaceCache{
570+
Client: deployContext.ClusterAPI.Client,
571+
KnownNamespaces: map[string]namespacecache.NamespaceInfo{
572+
userNamespace: {
573+
IsWorkspaceNamespace: true,
574+
Username: "user",
575+
CheCluster: &types.NamespacedName{Name: "eclipse-che", Namespace: "eclipse-che"},
576+
},
577+
},
578+
Lock: sync.Mutex{},
579+
})
580+
581+
// Sync ConfigMap
582+
err := workspaceConfigReconciler.syncNamespace(context.TODO(), eclipseCheNamespace, userNamespace)
583+
assert.Nil(t, err)
584+
assertSyncConfig(t, workspaceConfigReconciler, 2, v1ConfigMapGKV)
585+
586+
// Check ConfigMap in a user namespace is created
587+
cm := &corev1.ConfigMap{}
588+
err = deployContext.ClusterAPI.Client.Get(context.TODO(), objectKeyInUserNs, cm)
589+
assert.Nil(t, err)
590+
assert.Equal(t, "false", cm.Annotations[syncRetainOnDeleteAnnotation])
591+
592+
// Delete src ConfigMap
593+
err = deploy.DeleteIgnoreIfNotFound(context.TODO(), deployContext.ClusterAPI.Client, objectKeyInCheNs, &corev1.ConfigMap{})
594+
assert.Nil(t, err)
595+
596+
// Sync ConfigMap
597+
err = workspaceConfigReconciler.syncNamespace(context.TODO(), eclipseCheNamespace, userNamespace)
598+
assert.Nil(t, err)
599+
assertSyncConfig(t, workspaceConfigReconciler, 0, v1ConfigMapGKV)
600+
601+
// Check that destination ConfigMap in a user namespace is deleted
602+
cm = &corev1.ConfigMap{}
603+
err = deployContext.ClusterAPI.Client.Get(context.TODO(), objectKeyInUserNs, cm)
604+
assert.NotNil(t, err)
605+
assert.True(t, errors.IsNotFound(err))
606+
}

controllers/workspaceconfig/object2sync_factory.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"strings"
1717

1818
corev1 "k8s.io/api/core/v1"
19-
"k8s.io/apimachinery/pkg/runtime"
19+
"sigs.k8s.io/controller-runtime/pkg/client"
2020

2121
"github.com/eclipse-che/che-operator/pkg/common/utils"
2222
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -35,7 +35,7 @@ var (
3535
v1PvcGKV = corev1.SchemeGroupVersion.WithKind("PersistentVolumeClaim")
3636
)
3737

38-
func createObject2SyncFromRaw(
38+
func createObject2SyncFromRawData(
3939
raw []byte,
4040
userName string,
4141
namespaceName string) (Object2Sync, error) {
@@ -89,12 +89,11 @@ func createObject2SyncFromRaw(
8989

9090
return &unstructured2Sync{
9191
srcObj: srcObj,
92-
dstObj: srcObj,
9392
version: hash,
9493
}, nil
9594
}
9695

97-
func createObject2SyncFromRuntimeObject(obj runtime.Object) Object2Sync {
96+
func createObject2SyncFromObject(obj client.Object) Object2Sync {
9897
gkv := obj.GetObjectKind().GroupVersionKind()
9998
switch gkv {
10099
case v1ConfigMapGKV:
@@ -110,5 +109,7 @@ func createObject2SyncFromRuntimeObject(obj runtime.Object) Object2Sync {
110109
return &pvc2Sync{pvc: pvc}
111110
}
112111

113-
return nil
112+
return &unstructured2Sync{
113+
srcObj: obj,
114+
}
114115
}

controllers/workspaceconfig/pvc2sync.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,17 @@ func (p *pvc2Sync) newDstObject() client.Object {
4949
}
5050

5151
func (p *pvc2Sync) getSrcObjectVersion() string {
52-
if len(p.version) == 0 {
53-
return p.pvc.GetResourceVersion()
52+
if p.version != "" {
53+
return p.version
5454
}
55-
return p.version
55+
56+
return p.pvc.GetResourceVersion()
5657
}
5758

5859
func (p *pvc2Sync) hasROSpec() bool {
5960
return true
6061
}
62+
63+
func (p *pvc2Sync) defaultRetention() bool {
64+
return true
65+
}

controllers/workspaceconfig/pvc2sync_test.go

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ import (
1818
"testing"
1919

2020
"github.com/eclipse-che/che-operator/controllers/namespacecache"
21+
"k8s.io/apimachinery/pkg/api/errors"
2122

2223
"k8s.io/apimachinery/pkg/types"
2324

24-
"github.com/eclipse-che/che-operator/pkg/deploy"
25-
"k8s.io/apimachinery/pkg/api/errors"
26-
2725
"github.com/eclipse-che/che-operator/pkg/common/constants"
2826
"github.com/eclipse-che/che-operator/pkg/common/test"
27+
"github.com/eclipse-che/che-operator/pkg/deploy"
2928
"github.com/stretchr/testify/assert"
3029
corev1 "k8s.io/api/core/v1"
3130
"k8s.io/apimachinery/pkg/api/resource"
@@ -133,9 +132,135 @@ func TestSyncPVC(t *testing.T) {
133132
assert.Nil(t, err)
134133
assertSyncConfig(t, workspaceConfigReconciler, 0, v1PvcGKV)
135134

136-
// Check that destination PersistentVolumeClaim in a user namespace is deleted
135+
// Check that destination PersistentVolumeClaim in a user namespace is NOT deleted
137136
pvc = &corev1.PersistentVolumeClaim{}
138137
err = deployContext.ClusterAPI.Client.Get(context.TODO(), objectKeyInUserNs, pvc)
138+
assert.Nil(t, err)
139+
}
140+
141+
func TestSyncPVCShouldRetainIfAnnotationSetTrue(t *testing.T) {
142+
deployContext := test.NewCtxBuilder().WithObjects(
143+
&corev1.PersistentVolumeClaim{
144+
TypeMeta: metav1.TypeMeta{
145+
Kind: "PersistentVolumeClaim",
146+
APIVersion: "v1",
147+
},
148+
ObjectMeta: metav1.ObjectMeta{
149+
Name: objectName,
150+
Namespace: eclipseCheNamespace,
151+
Labels: map[string]string{
152+
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
153+
constants.KubernetesComponentLabelKey: constants.WorkspacesConfig,
154+
},
155+
Annotations: map[string]string{
156+
syncRetainOnDeleteAnnotation: "true",
157+
},
158+
},
159+
}).Build()
160+
161+
workspaceConfigReconciler := NewWorkspacesConfigReconciler(
162+
deployContext.ClusterAPI.Client,
163+
deployContext.ClusterAPI.Client,
164+
deployContext.ClusterAPI.Scheme,
165+
&namespacecache.NamespaceCache{
166+
Client: deployContext.ClusterAPI.Client,
167+
KnownNamespaces: map[string]namespacecache.NamespaceInfo{
168+
userNamespace: {
169+
IsWorkspaceNamespace: true,
170+
Username: "user",
171+
CheCluster: &types.NamespacedName{Name: "eclipse-che", Namespace: "eclipse-che"},
172+
},
173+
},
174+
Lock: sync.Mutex{},
175+
})
176+
177+
assertSyncConfig(t, workspaceConfigReconciler, 0, v1PvcGKV)
178+
179+
// Sync PVC to a user namespace
180+
err := workspaceConfigReconciler.syncNamespace(context.TODO(), eclipseCheNamespace, userNamespace)
181+
assert.Nil(t, err)
182+
assertSyncConfig(t, workspaceConfigReconciler, 2, v1PvcGKV)
183+
184+
// Check if PVC in a user namespace is created
185+
pvc := &corev1.PersistentVolumeClaim{}
186+
err = deployContext.ClusterAPI.Client.Get(context.TODO(), objectKeyInUserNs, pvc)
187+
assert.Nil(t, err)
188+
assert.Equal(t, "true", pvc.Annotations[syncRetainOnDeleteAnnotation])
189+
190+
// Delete src PVC
191+
err = deploy.DeleteIgnoreIfNotFound(context.TODO(), deployContext.ClusterAPI.Client, objectKeyInCheNs, &corev1.PersistentVolumeClaim{})
192+
assert.Nil(t, err)
193+
194+
// Sync PVC
195+
err = workspaceConfigReconciler.syncNamespace(context.TODO(), eclipseCheNamespace, userNamespace)
196+
assert.Nil(t, err)
197+
assertSyncConfig(t, workspaceConfigReconciler, 0, v1PvcGKV)
198+
199+
// Check that destination PVC in a user namespace is NOT deleted
200+
err = deployContext.ClusterAPI.Client.Get(context.TODO(), objectKeyInUserNs, &corev1.PersistentVolumeClaim{})
201+
assert.NoError(t, err)
202+
}
203+
204+
func TestSyncPVCShouldNotRetainIfAnnotationSetFalse(t *testing.T) {
205+
deployContext := test.NewCtxBuilder().WithObjects(
206+
&corev1.PersistentVolumeClaim{
207+
TypeMeta: metav1.TypeMeta{
208+
Kind: "PersistentVolumeClaim",
209+
APIVersion: "v1",
210+
},
211+
ObjectMeta: metav1.ObjectMeta{
212+
Name: objectName,
213+
Namespace: eclipseCheNamespace,
214+
Labels: map[string]string{
215+
constants.KubernetesPartOfLabelKey: constants.CheEclipseOrg,
216+
constants.KubernetesComponentLabelKey: constants.WorkspacesConfig,
217+
},
218+
Annotations: map[string]string{
219+
syncRetainOnDeleteAnnotation: "false",
220+
},
221+
},
222+
}).Build()
223+
224+
workspaceConfigReconciler := NewWorkspacesConfigReconciler(
225+
deployContext.ClusterAPI.Client,
226+
deployContext.ClusterAPI.Client,
227+
deployContext.ClusterAPI.Scheme,
228+
&namespacecache.NamespaceCache{
229+
Client: deployContext.ClusterAPI.Client,
230+
KnownNamespaces: map[string]namespacecache.NamespaceInfo{
231+
userNamespace: {
232+
IsWorkspaceNamespace: true,
233+
Username: "user",
234+
CheCluster: &types.NamespacedName{Name: "eclipse-che", Namespace: "eclipse-che"},
235+
},
236+
},
237+
Lock: sync.Mutex{},
238+
})
239+
240+
assertSyncConfig(t, workspaceConfigReconciler, 0, v1PvcGKV)
241+
242+
// Sync PVC to a user namespace
243+
err := workspaceConfigReconciler.syncNamespace(context.TODO(), eclipseCheNamespace, userNamespace)
244+
assert.Nil(t, err)
245+
assertSyncConfig(t, workspaceConfigReconciler, 2, v1PvcGKV)
246+
247+
// Check if PVC in a user namespace is created
248+
pvc := &corev1.PersistentVolumeClaim{}
249+
err = deployContext.ClusterAPI.Client.Get(context.TODO(), objectKeyInUserNs, pvc)
250+
assert.Nil(t, err)
251+
assert.Equal(t, "false", pvc.Annotations[syncRetainOnDeleteAnnotation])
252+
253+
// Delete src PVC
254+
err = deploy.DeleteIgnoreIfNotFound(context.TODO(), deployContext.ClusterAPI.Client, objectKeyInCheNs, &corev1.PersistentVolumeClaim{})
255+
assert.Nil(t, err)
256+
257+
// Sync PVC
258+
err = workspaceConfigReconciler.syncNamespace(context.TODO(), eclipseCheNamespace, userNamespace)
259+
assert.Nil(t, err)
260+
assertSyncConfig(t, workspaceConfigReconciler, 0, v1PvcGKV)
261+
262+
// Check that destination PVC in a user namespace is deleted
263+
err = deployContext.ClusterAPI.Client.Get(context.TODO(), objectKeyInUserNs, &corev1.PersistentVolumeClaim{})
139264
assert.NotNil(t, err)
140265
assert.True(t, errors.IsNotFound(err))
141266
}

controllers/workspaceconfig/secret2sync.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,17 @@ func (p *secret2Sync) newDstObject() client.Object {
5656
}
5757

5858
func (p *secret2Sync) getSrcObjectVersion() string {
59-
if len(p.version) == 0 {
60-
return p.secret.GetResourceVersion()
59+
if p.version != "" {
60+
return p.version
6161
}
62-
return p.version
62+
63+
return p.secret.GetResourceVersion()
6364
}
6465

6566
func (p *secret2Sync) hasROSpec() bool {
6667
return false
6768
}
69+
70+
func (p *secret2Sync) defaultRetention() bool {
71+
return false
72+
}

0 commit comments

Comments
 (0)