Skip to content

Commit 5edab61

Browse files
fix: add a migration logic for wipe refactor
Signed-off-by: Suleyman Akbas <[email protected]>
1 parent 9ea56e1 commit 5edab61

File tree

3 files changed

+374
-0
lines changed

3 files changed

+374
-0
lines changed

cmd/operator/operator.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
persistent_volume_claim "github.com/openshift/lvm-operator/v4/internal/controllers/persistent-volume-claim"
3838
internalCSI "github.com/openshift/lvm-operator/v4/internal/csi"
3939
"github.com/openshift/lvm-operator/v4/internal/migration/microlvms"
40+
wipe_refactor "github.com/openshift/lvm-operator/v4/internal/migration/wipe-refactor"
4041
"github.com/spf13/cobra"
4142
topolvmcontrollers "github.com/topolvm/topolvm/pkg/controller"
4243
"github.com/topolvm/topolvm/pkg/driver"
@@ -181,6 +182,10 @@ func run(cmd *cobra.Command, _ []string, opts *Options) error {
181182
return fmt.Errorf("failed to run pre 4.16 MicroLVMS cleanup: %w", err)
182183
}
183184

185+
if err := wipe_refactor.NewWipeRefactor(setupClient, operatorNamespace).AnnotateExistingLVMVolumeGroupsIfWipingEnabled(ctx); err != nil {
186+
return fmt.Errorf("failed to run wipe migration logic: %w", err)
187+
}
188+
184189
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
185190
Scheme: opts.Scheme,
186191
Metrics: metricsserver.Options{
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package wipe_refactor
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
lvmv1alpha1 "github.com/openshift/lvm-operator/v4/api/v1alpha1"
9+
"github.com/openshift/lvm-operator/v4/internal/controllers/constants"
10+
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
)
13+
14+
type WipeRefactor struct {
15+
namespace string
16+
client client.Client
17+
}
18+
19+
func NewWipeRefactor(client client.Client, namespace string) *WipeRefactor {
20+
return &WipeRefactor{
21+
namespace: namespace,
22+
client: client,
23+
}
24+
}
25+
26+
func (w *WipeRefactor) AnnotateExistingLVMVolumeGroupsIfWipingEnabled(ctx context.Context) error {
27+
nodeStatusList := &lvmv1alpha1.LVMVolumeGroupNodeStatusList{}
28+
if err := w.client.List(context.TODO(), nodeStatusList, &client.ListOptions{Namespace: w.namespace}); err != nil {
29+
return fmt.Errorf("failed to list LVMVolumeGroupNodeStatus instances: %w", err)
30+
}
31+
if len(nodeStatusList.Items) < 1 {
32+
return nil
33+
}
34+
35+
lvmVolumeGroupList := &lvmv1alpha1.LVMVolumeGroupList{}
36+
if err := w.client.List(context.TODO(), lvmVolumeGroupList, &client.ListOptions{Namespace: w.namespace}); err != nil {
37+
return fmt.Errorf("failed to list LVMVolumeGroup instances: %w", err)
38+
}
39+
if len(lvmVolumeGroupList.Items) < 1 {
40+
return nil
41+
}
42+
43+
for _, volumeGroup := range lvmVolumeGroupList.Items {
44+
updated := false
45+
for _, nodeStatus := range nodeStatusList.Items {
46+
for _, vgStatus := range nodeStatus.Spec.LVMVGStatus {
47+
if vgStatus.Name == volumeGroup.Name {
48+
if volumeGroup.Spec.DeviceSelector == nil || volumeGroup.Spec.DeviceSelector.ForceWipeDevicesAndDestroyAllData == nil || !*volumeGroup.Spec.DeviceSelector.ForceWipeDevicesAndDestroyAllData {
49+
continue
50+
}
51+
if volumeGroup.Annotations == nil {
52+
volumeGroup.Annotations = make(map[string]string)
53+
}
54+
volumeGroup.Annotations[constants.DevicesWipedAnnotationPrefix+nodeStatus.GetName()] = fmt.Sprintf(
55+
"the devices of this volume group have been wiped at %s by lvms according to policy. This marker"+
56+
"serves as indicator that the devices have been wiped before and should not be wiped again."+
57+
"removal of this annotation is unsupported and may lead to data loss due to additional wiping.",
58+
time.Now().Format(time.RFC3339))
59+
updated = true
60+
}
61+
}
62+
}
63+
if updated {
64+
if err := w.client.Update(ctx, &volumeGroup); err != nil {
65+
return fmt.Errorf("failed to add wiped annotation to LVMVolumeGroup instance: %w", err)
66+
}
67+
}
68+
}
69+
70+
return nil
71+
}
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
package wipe_refactor
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
lvmv1alpha1 "github.com/openshift/lvm-operator/v4/api/v1alpha1"
8+
"github.com/openshift/lvm-operator/v4/internal/controllers/constants"
9+
"github.com/stretchr/testify/assert"
10+
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/runtime"
13+
"sigs.k8s.io/controller-runtime/pkg/client"
14+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
15+
)
16+
17+
const namespace = "openshift-storage"
18+
19+
func TestAnnotateExistingLVMVolumeGroupsIfWipingEnabled(t *testing.T) {
20+
truePtr := true
21+
tests := []struct {
22+
name string
23+
inObjs []client.Object
24+
expectedAnnotationKeys map[string][]string
25+
}{
26+
{
27+
name: "nodeStatus does not exist, return nil",
28+
},
29+
{
30+
name: "nodeStatus exist but wipe flag not set, return nil",
31+
inObjs: []client.Object{
32+
&lvmv1alpha1.LVMVolumeGroupNodeStatus{
33+
Spec: lvmv1alpha1.LVMVolumeGroupNodeStatusSpec{
34+
LVMVGStatus: []lvmv1alpha1.VGStatus{
35+
{
36+
Name: "vg1",
37+
},
38+
},
39+
},
40+
},
41+
&lvmv1alpha1.LVMVolumeGroup{
42+
ObjectMeta: metav1.ObjectMeta{
43+
Name: "vg1",
44+
},
45+
},
46+
},
47+
},
48+
{
49+
name: "nodeStatus exist and wipe flag set, annotate vg",
50+
inObjs: []client.Object{
51+
&lvmv1alpha1.LVMVolumeGroupNodeStatus{
52+
ObjectMeta: metav1.ObjectMeta{
53+
Name: "node1",
54+
Namespace: namespace,
55+
},
56+
Spec: lvmv1alpha1.LVMVolumeGroupNodeStatusSpec{
57+
LVMVGStatus: []lvmv1alpha1.VGStatus{
58+
{
59+
Name: "vg1",
60+
},
61+
},
62+
},
63+
},
64+
&lvmv1alpha1.LVMVolumeGroup{
65+
ObjectMeta: metav1.ObjectMeta{
66+
Name: "vg1",
67+
Namespace: namespace,
68+
},
69+
Spec: lvmv1alpha1.LVMVolumeGroupSpec{
70+
DeviceSelector: &lvmv1alpha1.DeviceSelector{
71+
ForceWipeDevicesAndDestroyAllData: &truePtr,
72+
},
73+
},
74+
},
75+
},
76+
expectedAnnotationKeys: map[string][]string{"vg1": {constants.DevicesWipedAnnotationPrefix + "node1"}},
77+
},
78+
{
79+
name: "nodeStatus exist and wipe flag set for vg1, annotate vg1",
80+
inObjs: []client.Object{
81+
&lvmv1alpha1.LVMVolumeGroupNodeStatus{
82+
ObjectMeta: metav1.ObjectMeta{
83+
Name: "node1",
84+
Namespace: namespace,
85+
},
86+
Spec: lvmv1alpha1.LVMVolumeGroupNodeStatusSpec{
87+
LVMVGStatus: []lvmv1alpha1.VGStatus{
88+
{
89+
Name: "vg1",
90+
},
91+
{
92+
Name: "vg2",
93+
},
94+
},
95+
},
96+
},
97+
&lvmv1alpha1.LVMVolumeGroup{
98+
ObjectMeta: metav1.ObjectMeta{
99+
Name: "vg1",
100+
Namespace: namespace,
101+
},
102+
Spec: lvmv1alpha1.LVMVolumeGroupSpec{
103+
DeviceSelector: &lvmv1alpha1.DeviceSelector{
104+
ForceWipeDevicesAndDestroyAllData: &truePtr,
105+
},
106+
},
107+
},
108+
&lvmv1alpha1.LVMVolumeGroup{
109+
ObjectMeta: metav1.ObjectMeta{
110+
Name: "vg2",
111+
Namespace: namespace,
112+
},
113+
},
114+
},
115+
expectedAnnotationKeys: map[string][]string{"vg1": {constants.DevicesWipedAnnotationPrefix + "node1"}},
116+
},
117+
{
118+
name: "nodeStatus exist and wipe flag set for vg1 and vg2, annotate both",
119+
inObjs: []client.Object{
120+
&lvmv1alpha1.LVMVolumeGroupNodeStatus{
121+
ObjectMeta: metav1.ObjectMeta{
122+
Name: "node1",
123+
Namespace: namespace,
124+
},
125+
Spec: lvmv1alpha1.LVMVolumeGroupNodeStatusSpec{
126+
LVMVGStatus: []lvmv1alpha1.VGStatus{
127+
{
128+
Name: "vg1",
129+
},
130+
{
131+
Name: "vg2",
132+
},
133+
},
134+
},
135+
},
136+
&lvmv1alpha1.LVMVolumeGroup{
137+
ObjectMeta: metav1.ObjectMeta{
138+
Name: "vg1",
139+
Namespace: namespace,
140+
},
141+
Spec: lvmv1alpha1.LVMVolumeGroupSpec{
142+
DeviceSelector: &lvmv1alpha1.DeviceSelector{
143+
ForceWipeDevicesAndDestroyAllData: &truePtr,
144+
},
145+
},
146+
},
147+
&lvmv1alpha1.LVMVolumeGroup{
148+
ObjectMeta: metav1.ObjectMeta{
149+
Name: "vg2",
150+
Namespace: namespace,
151+
},
152+
Spec: lvmv1alpha1.LVMVolumeGroupSpec{
153+
DeviceSelector: &lvmv1alpha1.DeviceSelector{
154+
ForceWipeDevicesAndDestroyAllData: &truePtr,
155+
},
156+
},
157+
},
158+
},
159+
expectedAnnotationKeys: map[string][]string{"vg1": {constants.DevicesWipedAnnotationPrefix + "node1"}, "vg2": {constants.DevicesWipedAnnotationPrefix + "node1"}},
160+
},
161+
{
162+
name: "nodeStatus exist and wipe flag set, annotate vg on multiple nodes",
163+
inObjs: []client.Object{
164+
&lvmv1alpha1.LVMVolumeGroupNodeStatus{
165+
ObjectMeta: metav1.ObjectMeta{
166+
Name: "node1",
167+
Namespace: namespace,
168+
},
169+
Spec: lvmv1alpha1.LVMVolumeGroupNodeStatusSpec{
170+
LVMVGStatus: []lvmv1alpha1.VGStatus{
171+
{
172+
Name: "vg1",
173+
},
174+
},
175+
},
176+
},
177+
&lvmv1alpha1.LVMVolumeGroupNodeStatus{
178+
ObjectMeta: metav1.ObjectMeta{
179+
Name: "node2",
180+
Namespace: namespace,
181+
},
182+
Spec: lvmv1alpha1.LVMVolumeGroupNodeStatusSpec{
183+
LVMVGStatus: []lvmv1alpha1.VGStatus{
184+
{
185+
Name: "vg1",
186+
},
187+
},
188+
},
189+
},
190+
&lvmv1alpha1.LVMVolumeGroup{
191+
ObjectMeta: metav1.ObjectMeta{
192+
Name: "vg1",
193+
Namespace: namespace,
194+
},
195+
Spec: lvmv1alpha1.LVMVolumeGroupSpec{
196+
DeviceSelector: &lvmv1alpha1.DeviceSelector{
197+
ForceWipeDevicesAndDestroyAllData: &truePtr,
198+
},
199+
},
200+
},
201+
},
202+
expectedAnnotationKeys: map[string][]string{"vg1": {constants.DevicesWipedAnnotationPrefix + "node1", constants.DevicesWipedAnnotationPrefix + "node2"}},
203+
},
204+
{
205+
name: "nodeStatus exist and wipe flag set for vg1 but not for vg2, annotate vg1 on multiple nodes",
206+
inObjs: []client.Object{
207+
&lvmv1alpha1.LVMVolumeGroupNodeStatus{
208+
ObjectMeta: metav1.ObjectMeta{
209+
Name: "node1",
210+
Namespace: namespace,
211+
},
212+
Spec: lvmv1alpha1.LVMVolumeGroupNodeStatusSpec{
213+
LVMVGStatus: []lvmv1alpha1.VGStatus{
214+
{
215+
Name: "vg1",
216+
},
217+
{
218+
Name: "vg2",
219+
},
220+
},
221+
},
222+
},
223+
&lvmv1alpha1.LVMVolumeGroupNodeStatus{
224+
ObjectMeta: metav1.ObjectMeta{
225+
Name: "node2",
226+
Namespace: namespace,
227+
},
228+
Spec: lvmv1alpha1.LVMVolumeGroupNodeStatusSpec{
229+
LVMVGStatus: []lvmv1alpha1.VGStatus{
230+
{
231+
Name: "vg1",
232+
},
233+
{
234+
Name: "vg2",
235+
},
236+
},
237+
},
238+
},
239+
&lvmv1alpha1.LVMVolumeGroup{
240+
ObjectMeta: metav1.ObjectMeta{
241+
Name: "vg1",
242+
Namespace: namespace,
243+
},
244+
Spec: lvmv1alpha1.LVMVolumeGroupSpec{
245+
DeviceSelector: &lvmv1alpha1.DeviceSelector{
246+
ForceWipeDevicesAndDestroyAllData: &truePtr,
247+
},
248+
},
249+
},
250+
&lvmv1alpha1.LVMVolumeGroup{
251+
ObjectMeta: metav1.ObjectMeta{
252+
Name: "vg2",
253+
Namespace: namespace,
254+
},
255+
},
256+
},
257+
expectedAnnotationKeys: map[string][]string{"vg1": {constants.DevicesWipedAnnotationPrefix + "node1", constants.DevicesWipedAnnotationPrefix + "node2"}},
258+
},
259+
}
260+
261+
for _, tt := range tests {
262+
t.Run(tt.name, func(t *testing.T) {
263+
fakeClient := fake.NewClientBuilder().
264+
WithScheme(setUpScheme()).
265+
WithObjects(tt.inObjs...).
266+
Build()
267+
268+
wipeRefactor := NewWipeRefactor(fakeClient, namespace)
269+
assert.NoError(t, wipeRefactor.AnnotateExistingLVMVolumeGroupsIfWipingEnabled(context.Background()))
270+
271+
lvmVolumeGroups := &lvmv1alpha1.LVMVolumeGroupList{}
272+
assert.NoError(t, fakeClient.List(context.Background(), lvmVolumeGroups))
273+
274+
for vgName, expectedAnnotationKeys := range tt.expectedAnnotationKeys {
275+
vgExists := false
276+
for _, volumeGroup := range lvmVolumeGroups.Items {
277+
if volumeGroup.Name == vgName {
278+
vgExists = true
279+
for _, key := range expectedAnnotationKeys {
280+
if _, ok := volumeGroup.Annotations[key]; !ok {
281+
t.Errorf("Annotation %s does not exist in LVMVolumeGroup %s", key, vgName)
282+
}
283+
}
284+
}
285+
}
286+
if !vgExists {
287+
t.Errorf("LVMVolumeGroup %s does not exist", vgName)
288+
}
289+
}
290+
})
291+
}
292+
}
293+
294+
func setUpScheme() *runtime.Scheme {
295+
scheme := runtime.NewScheme()
296+
_ = lvmv1alpha1.AddToScheme(scheme)
297+
return scheme
298+
}

0 commit comments

Comments
 (0)