Skip to content
This repository was archived by the owner on Dec 16, 2025. It is now read-only.

Commit 2974836

Browse files
✨ Improve image filtering (#124)
* Improve image filtering Signed-off-by: michal.gubricky <[email protected]> * Return empty string if image with ID is not found Signed-off-by: michal.gubricky <[email protected]> * Update internal/controller/openstacknodeimagerelease_controller.go Co-authored-by: Matej Feder <[email protected]> Signed-off-by: Michal Gubricky <[email protected]> * Fix unit-tests after added suggestion Signed-off-by: michal.gubricky <[email protected]> --------- Signed-off-by: michal.gubricky <[email protected]> Signed-off-by: Michal Gubricky <[email protected]> Co-authored-by: Matej Feder <[email protected]>
1 parent b94dfb6 commit 2974836

File tree

2 files changed

+124
-24
lines changed

2 files changed

+124
-24
lines changed

internal/controller/openstacknodeimagerelease_controller.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func (r *OpenStackNodeImageReleaseReconciler) Reconcile(ctx context.Context, req
147147

148148
conditions.MarkTrue(openstacknodeimagerelease, apiv1alpha1.OpenStackImageServiceClientAvailableCondition)
149149

150-
imageID, err := findImageByName(imageClient, openstacknodeimagerelease.Spec.Image.CreateOpts.Name)
150+
imageID, err := getImageID(imageClient, openstacknodeimagerelease.Spec.Image.CreateOpts)
151151
if err != nil {
152152
conditions.MarkFalse(openstacknodeimagerelease,
153153
apiv1alpha1.OpenStackImageReadyCondition,
@@ -331,27 +331,38 @@ func (r *OpenStackNodeImageReleaseReconciler) getCloudFromSecret(ctx context.Con
331331
return cloud, nil
332332
}
333333

334-
func findImageByName(imagesClient *gophercloud.ServiceClient, imageName string) (string, error) {
335-
listOpts := images.ListOpts{
336-
Name: imageName,
334+
func getImageID(imagesClient *gophercloud.ServiceClient, imageCreateOps *apiv1alpha1.CreateOpts) (string, error) {
335+
var listOpts images.ListOpts
336+
337+
if imageCreateOps.ID != "" {
338+
listOpts = images.ListOpts{
339+
ID: imageCreateOps.ID,
340+
}
341+
} else {
342+
listOpts = images.ListOpts{
343+
Name: imageCreateOps.Name,
344+
Tags: imageCreateOps.Tags,
345+
}
337346
}
338347

339348
allPages, err := images.List(imagesClient, listOpts).AllPages()
340349
if err != nil {
341-
return "", fmt.Errorf("failed to list images with name %s: %w", imageName, err)
350+
return "", fmt.Errorf("failed to list images with name %s: %w", imageCreateOps.Name, err)
342351
}
343352

344353
imageList, err := images.ExtractImages(allPages)
345354
if err != nil {
346-
return "", fmt.Errorf("failed to extract images with name %s: %w", imageName, err)
355+
return "", fmt.Errorf("failed to extract images with name %s: %w", imageCreateOps.Name, err)
347356
}
348357

349-
for i := range imageList {
350-
if imageList[i].Name == imageName {
351-
return imageList[i].ID, nil
352-
}
358+
switch len(imageList) {
359+
case 0:
360+
return "", nil
361+
case 1:
362+
return imageList[0].ID, nil
363+
default:
364+
return "", fmt.Errorf("too many images were found with the given image name: %s and tags: %s", imageCreateOps.Name, imageCreateOps.Tags)
353365
}
354-
return "", nil
355366
}
356367

357368
func createImage(imageClient *gophercloud.ServiceClient, createOpts *apiv1alpha1.CreateOpts) (*images.Image, error) {

internal/controller/openstacknodeimagerelease_controller_test.go

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,34 @@ clouds:
188188
assert.NoError(t, err)
189189
}
190190

191-
func TestFindImageByName(t *testing.T) {
191+
func TestGetImageID(t *testing.T) {
192192
th.SetupHTTP()
193193
defer th.TeardownHTTP()
194194

195195
HandleImageListSuccessfully(t)
196196

197-
imageName := "test_image"
197+
imageFilter := &apiv1alpha1.CreateOpts{
198+
ID: "123",
199+
}
200+
201+
imageID, err := getImageID(fakeclient.ServiceClient(), imageFilter)
202+
203+
assert.NoError(t, err)
204+
assert.Equal(t, "123", imageID)
205+
}
206+
207+
func TestGetImageIDByNameAndTags(t *testing.T) {
208+
th.SetupHTTP()
209+
defer th.TeardownHTTP()
210+
211+
HandleImageListSuccessfully(t)
212+
213+
imageFilter := &apiv1alpha1.CreateOpts{
214+
Name: "test_image",
215+
Tags: []string{"v1"},
216+
}
198217

199-
imageID, err := findImageByName(fakeclient.ServiceClient(), imageName)
218+
imageID, err := getImageID(fakeclient.ServiceClient(), imageFilter)
200219

201220
assert.NoError(t, err)
202221
assert.Equal(t, "123", imageID)
@@ -213,33 +232,103 @@ func HandleImageListSuccessfully(t *testing.T) { //nolint: gocritic
213232
w.WriteHeader(http.StatusOK)
214233
fmt.Fprintf(w, `{
215234
"images": [
216-
{"id": "123", "name": "test_image"},
217-
{"id": "456", "name": "test_image2"},
218-
{"id": "789", "name": "test_image3"}
235+
{"id": "123", "name": "test_image", "tags": ["v1"]}
236+
]
237+
}`)
238+
})
239+
}
240+
241+
func TestGetImageIDWithTwoSameImageNames(t *testing.T) {
242+
th.SetupHTTP()
243+
defer th.TeardownHTTP()
244+
245+
HandleImageListWithTwoImagesSuccessfully(t)
246+
247+
imageFilter := &apiv1alpha1.CreateOpts{
248+
Name: "test_image",
249+
Tags: []string{"v1"},
250+
}
251+
252+
imageID, err := getImageID(fakeclient.ServiceClient(), imageFilter)
253+
254+
assert.Error(t, err) // Expecting an error due to multiple images with the same name
255+
assert.Equal(t, "", imageID)
256+
assert.Equal(t, err.Error(), "too many images were found with the given image name: test_image and tags: [v1]")
257+
}
258+
259+
// HandleImageListWithTwoImagesSuccessfully sets up a fake response for image list request.
260+
func HandleImageListWithTwoImagesSuccessfully(t *testing.T) { //nolint: gocritic
261+
t.Helper() // Indicate that this is a test helper function
262+
th.Mux.HandleFunc("/images", func(w http.ResponseWriter, r *http.Request) {
263+
th.TestMethod(t, r, "GET")
264+
th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID)
265+
266+
w.Header().Set("Content-Type", "application/json")
267+
w.WriteHeader(http.StatusOK)
268+
fmt.Fprintf(w, `{
269+
"images": [
270+
{"id": "123", "name": "test_image", "tags": ["v1"]},
271+
{"id": "456", "name": "test_image", "tags": ["v1"]}
219272
]
220273
}`)
221274
})
222275
}
223276

224-
func TestFindImageByNameWrongImageName(t *testing.T) {
277+
func TestGetImageIDNoImageFound(t *testing.T) {
278+
th.SetupHTTP()
279+
defer th.TeardownHTTP()
280+
281+
HandleImageListWithNoImageFoundSuccessfully(t)
282+
283+
imageFilter := &apiv1alpha1.CreateOpts{
284+
Name: "test_image",
285+
Tags: []string{"v1"},
286+
}
287+
288+
imageID, err := getImageID(fakeclient.ServiceClient(), imageFilter)
289+
290+
assert.NoError(t, err)
291+
assert.Equal(t, "", imageID)
292+
}
293+
294+
// HandleImageListWithNoImageFoundSuccessfully sets up a fake response for image list request.
295+
func HandleImageListWithNoImageFoundSuccessfully(t *testing.T) { //nolint: gocritic
296+
t.Helper() // Indicate that this is a test helper function
297+
th.Mux.HandleFunc("/images", func(w http.ResponseWriter, r *http.Request) {
298+
th.TestMethod(t, r, "GET")
299+
th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID)
300+
301+
w.Header().Set("Content-Type", "application/json")
302+
w.WriteHeader(http.StatusOK)
303+
fmt.Fprintf(w, `{
304+
"images": []
305+
}`)
306+
})
307+
}
308+
309+
func TestGetImageIDWrongImageName(t *testing.T) {
225310
th.SetupHTTP()
226311
defer th.TeardownHTTP()
227312

228313
HandleImageListSuccessfully(t)
229314

230-
imageName := "test_bad_image"
315+
imageFilter := &apiv1alpha1.CreateOpts{
316+
Name: "test_bad_image",
317+
}
231318

232-
imageID, err := findImageByName(fakeclient.ServiceClient(), imageName)
319+
imageID, err := getImageID(fakeclient.ServiceClient(), imageFilter)
233320

234321
assert.NoError(t, err)
235322
assert.NotEqual(t, "231", imageID)
236323
}
237324

238-
func TestFindImageByNameNotFound(t *testing.T) {
325+
func TestGetImageIDNotFound(t *testing.T) {
239326
th.SetupHTTP()
240327
defer th.TeardownHTTP()
241328

242-
imageName := "test_image"
329+
imageFilter := &apiv1alpha1.CreateOpts{
330+
Name: "test_image",
331+
}
243332

244333
th.Mux.HandleFunc("/images", func(w http.ResponseWriter, r *http.Request) {
245334
th.TestMethod(t, r, http.MethodGet)
@@ -253,11 +342,11 @@ func TestFindImageByNameNotFound(t *testing.T) {
253342

254343
fakeClient := fakeclient.ServiceClient()
255344

256-
imageID, err := findImageByName(fakeClient, imageName)
345+
imageID, err := getImageID(fakeClient, imageFilter)
257346

258347
assert.Error(t, err)
259348
assert.Equal(t, "", imageID)
260-
assert.Contains(t, err.Error(), fmt.Sprintf("failed to list images with name %s: Resource not found", imageName))
349+
assert.Contains(t, err.Error(), fmt.Sprintf("failed to list images with name %s: Resource not found", imageFilter.Name))
261350
}
262351

263352
// HandleImageCreationSuccessfully test setup.

0 commit comments

Comments
 (0)