@@ -17,13 +17,60 @@ limitations under the License.
1717package volumesnapshot
1818
1919import (
20+ "context"
21+ "errors"
22+ "iter"
23+ "strings"
2024 "testing"
2125
2226 "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots"
23- orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1"
27+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+ "k8s.io/apimachinery/pkg/runtime"
2429 "k8s.io/utils/ptr"
30+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
31+
32+ orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1"
2533)
2634
35+ var errNotImplemented = errors .New ("not implemented" )
36+
37+ type testVolumeSnapshotClient struct {
38+ listOpts snapshots.ListOptsBuilder
39+ listCalled bool
40+ }
41+
42+ func (c * testVolumeSnapshotClient ) ListVolumeSnapshots (_ context.Context , listOpts snapshots.ListOptsBuilder ) iter.Seq2 [* snapshots.Snapshot , error ] {
43+ c .listCalled = true
44+ c .listOpts = listOpts
45+ return func (yield func (* snapshots.Snapshot , error ) bool ) {}
46+ }
47+
48+ func (c * testVolumeSnapshotClient ) CreateVolumeSnapshot (_ context.Context , _ snapshots.CreateOptsBuilder ) (* snapshots.Snapshot , error ) {
49+ return nil , errNotImplemented
50+ }
51+
52+ func (c * testVolumeSnapshotClient ) DeleteVolumeSnapshot (_ context.Context , _ string ) error {
53+ return errNotImplemented
54+ }
55+
56+ func (c * testVolumeSnapshotClient ) GetVolumeSnapshot (_ context.Context , _ string ) (* snapshots.Snapshot , error ) {
57+ return nil , errNotImplemented
58+ }
59+
60+ func (c * testVolumeSnapshotClient ) UpdateVolumeSnapshot (_ context.Context , _ string , _ snapshots.UpdateOptsBuilder ) (* snapshots.Snapshot , error ) {
61+ return nil , errNotImplemented
62+ }
63+
64+ func newTestScheme (t * testing.T ) * runtime.Scheme {
65+ t .Helper ()
66+
67+ scheme := runtime .NewScheme ()
68+ if err := orcv1alpha1 .AddToScheme (scheme ); err != nil {
69+ t .Fatalf ("failed adding API scheme: %v" , err )
70+ }
71+ return scheme
72+ }
73+
2774func TestNeedsUpdate (t * testing.T ) {
2875 testCases := []struct {
2976 name string
@@ -44,7 +91,10 @@ func TestNeedsUpdate(t *testing.T) {
4491
4592 for _ , tt := range testCases {
4693 t .Run (tt .name , func (t * testing.T ) {
47- got , _ := needsUpdate (tt .updateOpts )
94+ got , err := needsUpdate (tt .updateOpts )
95+ if err != nil {
96+ t .Fatalf ("Expected no error, got: %v" , err )
97+ }
4898 if got != tt .expectChange {
4999 t .Errorf ("Expected change: %v, got: %v" , tt .expectChange , got )
50100 }
@@ -78,7 +128,10 @@ func TestHandleNameUpdate(t *testing.T) {
78128 updateOpts := snapshots.UpdateOpts {}
79129 handleNameUpdate (& updateOpts , resource , osResource )
80130
81- got , _ := needsUpdate (updateOpts )
131+ got , err := needsUpdate (updateOpts )
132+ if err != nil {
133+ t .Fatalf ("Expected no error, got: %v" , err )
134+ }
82135 if got != tt .expectChange {
83136 t .Errorf ("Expected change: %v, got: %v" , tt .expectChange , got )
84137 }
@@ -109,11 +162,184 @@ func TestHandleDescriptionUpdate(t *testing.T) {
109162 updateOpts := snapshots.UpdateOpts {}
110163 handleDescriptionUpdate (& updateOpts , resource , osResource )
111164
112- got , _ := needsUpdate (updateOpts )
165+ got , err := needsUpdate (updateOpts )
166+ if err != nil {
167+ t .Fatalf ("Expected no error, got: %v" , err )
168+ }
113169 if got != tt .expectChange {
114170 t .Errorf ("Expected change: %v, got: %v" , tt .expectChange , got )
115171 }
116172 })
117173
118174 }
119175}
176+
177+ func TestGetVolumeIDForImport (t * testing.T ) {
178+ ctx := context .Background ()
179+ namespace := "default"
180+ volumeRef := orcv1alpha1 .KubernetesNameRef ("import-volume" )
181+ orcObject := & orcv1alpha1.VolumeSnapshot {
182+ ObjectMeta : v1.ObjectMeta {
183+ Name : "import-snapshot" ,
184+ Namespace : namespace ,
185+ },
186+ }
187+
188+ t .Run ("waits for missing volume" , func (t * testing.T ) {
189+ actuator := volumesnapshotActuator {
190+ k8sClient : fake .NewClientBuilder ().
191+ WithScheme (newTestScheme (t )).
192+ Build (),
193+ }
194+
195+ volumeID , reconcileStatus := actuator .getVolumeIDForImport (ctx , orcObject , orcv1alpha1.VolumeSnapshotFilter {
196+ VolumeRef : & volumeRef ,
197+ })
198+ if volumeID != "" {
199+ t .Fatalf ("expected empty volume ID, got %q" , volumeID )
200+ }
201+ if reconcileStatus == nil {
202+ t .Fatalf ("expected reconcile status, got nil" )
203+ }
204+ needsReschedule , err := reconcileStatus .NeedsReschedule ()
205+ if err != nil {
206+ t .Fatalf ("expected no error, got %v" , err )
207+ }
208+ if ! needsReschedule {
209+ t .Fatalf ("expected reconcile status to require reschedule" )
210+ }
211+ if ! strings .Contains (strings .Join (reconcileStatus .GetProgressMessages (), "\n " ), "Waiting for Volume/import-volume to be created" ) {
212+ t .Fatalf ("expected waiting for volume creation message, got %v" , reconcileStatus .GetProgressMessages ())
213+ }
214+ })
215+
216+ t .Run ("waits for not-ready volume" , func (t * testing.T ) {
217+ volume := & orcv1alpha1.Volume {
218+ ObjectMeta : v1.ObjectMeta {
219+ Name : "import-volume" ,
220+ Namespace : namespace ,
221+ },
222+ }
223+
224+ actuator := volumesnapshotActuator {
225+ k8sClient : fake .NewClientBuilder ().
226+ WithScheme (newTestScheme (t )).
227+ WithObjects (volume ).
228+ Build (),
229+ }
230+
231+ volumeID , reconcileStatus := actuator .getVolumeIDForImport (ctx , orcObject , orcv1alpha1.VolumeSnapshotFilter {
232+ VolumeRef : & volumeRef ,
233+ })
234+ if volumeID != "" {
235+ t .Fatalf ("expected empty volume ID, got %q" , volumeID )
236+ }
237+ if reconcileStatus == nil {
238+ t .Fatalf ("expected reconcile status, got nil" )
239+ }
240+ needsReschedule , err := reconcileStatus .NeedsReschedule ()
241+ if err != nil {
242+ t .Fatalf ("expected no error, got %v" , err )
243+ }
244+ if ! needsReschedule {
245+ t .Fatalf ("expected reconcile status to require reschedule" )
246+ }
247+ if ! strings .Contains (strings .Join (reconcileStatus .GetProgressMessages (), "\n " ), "Waiting for Volume/import-volume to be ready" ) {
248+ t .Fatalf ("expected waiting for volume ready message, got %v" , reconcileStatus .GetProgressMessages ())
249+ }
250+ })
251+
252+ t .Run ("returns volume ID when volume is ready" , func (t * testing.T ) {
253+ volume := & orcv1alpha1.Volume {
254+ ObjectMeta : v1.ObjectMeta {
255+ Name : "import-volume" ,
256+ Namespace : namespace ,
257+ },
258+ Status : orcv1alpha1.VolumeStatus {
259+ ID : ptr .To ("volume-id" ),
260+ Conditions : []v1.Condition {
261+ {
262+ Type : orcv1alpha1 .ConditionAvailable ,
263+ Status : v1 .ConditionTrue ,
264+ },
265+ },
266+ },
267+ }
268+
269+ actuator := volumesnapshotActuator {
270+ k8sClient : fake .NewClientBuilder ().
271+ WithScheme (newTestScheme (t )).
272+ WithObjects (volume ).
273+ Build (),
274+ }
275+
276+ volumeID , reconcileStatus := actuator .getVolumeIDForImport (ctx , orcObject , orcv1alpha1.VolumeSnapshotFilter {
277+ VolumeRef : & volumeRef ,
278+ })
279+ if reconcileStatus != nil {
280+ t .Fatalf ("expected nil reconcile status, got %v" , reconcileStatus )
281+ }
282+ if volumeID != "volume-id" {
283+ t .Fatalf ("expected volume-id, got %q" , volumeID )
284+ }
285+ })
286+ }
287+
288+ func TestListOSResourcesForImport_ResolvesVolumeRefToVolumeID (t * testing.T ) {
289+ ctx := context .Background ()
290+ namespace := "default"
291+ volumeRef := orcv1alpha1 .KubernetesNameRef ("import-volume" )
292+
293+ volume := & orcv1alpha1.Volume {
294+ ObjectMeta : v1.ObjectMeta {
295+ Name : "import-volume" ,
296+ Namespace : namespace ,
297+ },
298+ Status : orcv1alpha1.VolumeStatus {
299+ ID : ptr .To ("volume-id" ),
300+ Conditions : []v1.Condition {
301+ {
302+ Type : orcv1alpha1 .ConditionAvailable ,
303+ Status : v1 .ConditionTrue ,
304+ },
305+ },
306+ },
307+ }
308+
309+ osClient := & testVolumeSnapshotClient {}
310+ actuator := volumesnapshotActuator {
311+ osClient : osClient ,
312+ k8sClient : fake .NewClientBuilder ().
313+ WithScheme (newTestScheme (t )).
314+ WithObjects (volume ).
315+ Build (),
316+ }
317+
318+ orcObject := & orcv1alpha1.VolumeSnapshot {
319+ ObjectMeta : v1.ObjectMeta {
320+ Name : "import-snapshot" ,
321+ Namespace : namespace ,
322+ },
323+ }
324+
325+ filter := orcv1alpha1.VolumeSnapshotFilter {
326+ Name : ptr .To (orcv1alpha1 .OpenStackName ("snapshot-name" )),
327+ VolumeRef : & volumeRef ,
328+ }
329+
330+ _ , reconcileStatus := actuator .ListOSResourcesForImport (ctx , orcObject , filter )
331+ if reconcileStatus != nil {
332+ t .Fatalf ("expected nil reconcile status, got %v" , reconcileStatus )
333+ }
334+ if ! osClient .listCalled {
335+ t .Fatalf ("expected ListVolumeSnapshots to be called" )
336+ }
337+
338+ listOpts , ok := osClient .listOpts .(snapshots.ListOpts )
339+ if ! ok {
340+ t .Fatalf ("expected snapshots.ListOpts, got %T" , osClient .listOpts )
341+ }
342+ if listOpts .VolumeID != "volume-id" {
343+ t .Fatalf ("expected ListOpts.VolumeID to be volume-id, got %q" , listOpts .VolumeID )
344+ }
345+ }
0 commit comments