Skip to content

Commit d0d6434

Browse files
committed
Use field indexer instead of labels for image dependencies
1 parent 2e82249 commit d0d6434

File tree

5 files changed

+42
-160
lines changed

5 files changed

+42
-160
lines changed

controllers/openstackserver_controller.go

Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ import (
5858
"sigs.k8s.io/cluster-api-provider-openstack/pkg/scope"
5959
capoerrors "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors"
6060
"sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/names"
61-
"sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/objects"
6261
)
6362

6463
const (
@@ -171,24 +170,33 @@ func patchServer(ctx context.Context, patchHelper *patch.Helper, openStackServer
171170
}
172171

173172
func (r *OpenStackServerReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, _ controller.Options) error {
173+
const imageRefPath = "spec.image.imageRef.name"
174+
174175
log := ctrl.LoggerFrom(ctx)
175176

177+
// Index servers by referenced image
178+
if err := mgr.GetFieldIndexer().IndexField(ctx, &infrav1alpha1.OpenStackServer{}, imageRefPath, func(obj client.Object) []string {
179+
server, ok := obj.(*infrav1alpha1.OpenStackServer)
180+
if !ok {
181+
return nil
182+
}
183+
if server.Spec.Image.ImageRef == nil {
184+
return nil
185+
}
186+
return []string{server.Spec.Image.ImageRef.Name}
187+
}); err != nil {
188+
return fmt.Errorf("adding servers by image index: %w", err)
189+
}
190+
176191
return ctrl.NewControllerManagedBy(mgr).
177192
For(&infrav1alpha1.OpenStackServer{}).
178193
Watches(&orcv1alpha1.Image{},
179194
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
180195
log = log.WithValues("watch", "Image")
181196

182197
k8sClient := mgr.GetClient()
183-
label, err := objToDependencyLabel(obj, k8sClient.Scheme())
184-
if err != nil {
185-
log.Error(err, "objToDependencyLabel")
186-
}
187-
188-
labels := map[string]string{label: ""}
189198
serverList := &infrav1alpha1.OpenStackServerList{}
190-
err = k8sClient.List(ctx, serverList, client.MatchingLabels(labels), client.InNamespace(obj.GetNamespace()))
191-
if err != nil {
199+
if err := k8sClient.List(ctx, serverList, client.InNamespace(obj.GetNamespace()), client.MatchingFields{imageRefPath: obj.GetName()}); err != nil {
192200
log.Error(err, "listing OpenStackServers")
193201
return nil
194202
}
@@ -267,27 +275,6 @@ func (r *OpenStackServerReconciler) reconcileDelete(scope *scope.WithLogger, ope
267275
return nil
268276
}
269277

270-
func objToDependencyLabel(obj client.Object, scheme *runtime.Scheme) (string, error) {
271-
gvk, err := objects.GetGVK(obj, scheme)
272-
if err != nil {
273-
return "", err
274-
}
275-
276-
if gvk.Group != orcv1alpha1.SchemeGroupVersion.Group {
277-
return "", fmt.Errorf("dependency object has unexpected group %s", gvk.Group)
278-
}
279-
280-
var prefixType string
281-
switch gvk.Kind {
282-
case "Image":
283-
prefixType = "image"
284-
default:
285-
return "", fmt.Errorf("dependency object has unexpected kind %s", gvk.Kind)
286-
}
287-
288-
return "orc-dependency-" + prefixType + "/" + obj.GetName(), nil
289-
}
290-
291278
func IsServerTerminalError(server *infrav1alpha1.OpenStackServer) bool {
292279
if server.Status.InstanceState != nil && *server.Status.InstanceState == infrav1.InstanceStateError {
293280
return true
@@ -310,31 +297,7 @@ func (r *OpenStackServerReconciler) reconcileNormal(ctx context.Context, scope *
310297
openStackServer.SetLabels(labels)
311298
}
312299

313-
changed, dependencies, resolveDone, err := compute.ResolveServerSpec(ctx, scope, r.Client, openStackServer)
314-
315-
errs := make([]error, 0, len(dependencies)+1)
316-
errs = append(errs, err)
317-
318-
// Add a dependency label for every object we depend on
319-
// Set changed if we update any labels
320-
// Collate all errors and return in a single transaction
321-
for i := range dependencies {
322-
errs = append(errs, func() error {
323-
dependency := dependencies[i]
324-
label, err := objToDependencyLabel(dependency, r.Client.Scheme())
325-
if err != nil {
326-
return err
327-
}
328-
329-
if _, ok := labels[label]; !ok {
330-
labels[label] = ""
331-
changed = true
332-
}
333-
return nil
334-
}())
335-
}
336-
err = errors.Join(errs...)
337-
300+
changed, resolveDone, err := compute.ResolveServerSpec(ctx, scope, r.Client, openStackServer)
338301
if err != nil || !resolveDone {
339302
return ctrl.Result{}, err
340303
}

pkg/cloud/services/compute/instance.go

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -346,25 +346,25 @@ func (s *Service) getBlockDevices(eventObject runtime.Object, instanceSpec *Inst
346346
}
347347

348348
// Helper function for getting image ID from name, ID, or tags.
349-
func (s *Service) GetImageID(ctx context.Context, k8sClient client.Client, namespace string, image infrav1.ImageParam) (*string, client.Object, error) {
349+
func (s *Service) GetImageID(ctx context.Context, k8sClient client.Client, namespace string, image infrav1.ImageParam) (*string, error) {
350350
switch {
351351
case image.ID != nil:
352-
return image.ID, nil, nil
352+
return image.ID, nil
353353
case image.Filter != nil:
354354
return s.getImageIDByFilter(image.Filter)
355355
case image.ImageRef != nil:
356356
return s.getImageIDByReference(ctx, k8sClient, namespace, image.ImageRef)
357357
default:
358358
// Should have been caught by validation
359-
return nil, nil, errors.New("image id, filter, and reference are all nil")
359+
return nil, errors.New("image id, filter, and reference are all nil")
360360
}
361361
}
362362

363-
func (s *Service) getImageIDByFilter(filter *infrav1.ImageFilter) (*string, client.Object, error) {
363+
func (s *Service) getImageIDByFilter(filter *infrav1.ImageFilter) (*string, error) {
364364
listOpts := filterconvert.ImageFilterToListOpts(filter)
365365
allImages, err := s.getImageClient().ListImages(listOpts)
366366
if err != nil {
367-
return nil, nil, err
367+
return nil, err
368368
}
369369

370370
switch len(allImages) {
@@ -373,51 +373,48 @@ func (s *Service) getImageIDByFilter(filter *infrav1.ImageFilter) (*string, clie
373373
if filter.Name != nil {
374374
name = *filter.Name
375375
}
376-
return nil, nil, fmt.Errorf("no images were found with the given image filter: name=%v, tags=%v", name, filter.Tags)
376+
return nil, fmt.Errorf("no images were found with the given image filter: name=%v, tags=%v", name, filter.Tags)
377377
case 1:
378-
return &allImages[0].ID, nil, nil
378+
return &allImages[0].ID, nil
379379
default:
380380
// this should never happen
381381
var name string
382382
if filter.Name != nil {
383383
name = *filter.Name
384384
}
385-
return nil, nil, fmt.Errorf("too many images were found with the given image filter: name=%v, tags=%v", name, filter.Tags)
385+
return nil, fmt.Errorf("too many images were found with the given image filter: name=%v, tags=%v", name, filter.Tags)
386386
}
387387
}
388388

389-
func (s *Service) getImageIDByReference(ctx context.Context, k8sClient client.Client, namespace string, ref *infrav1.ResourceReference) (*string, client.Object, error) {
389+
func (s *Service) getImageIDByReference(ctx context.Context, k8sClient client.Client, namespace string, ref *infrav1.ResourceReference) (*string, error) {
390390
orcImage := &orcv1alpha1.Image{}
391391
err := k8sClient.Get(ctx, client.ObjectKey{
392392
Namespace: namespace,
393393
Name: ref.Name,
394394
}, orcImage)
395395
if err != nil {
396-
// If the object doesn't exist yet, we still need to return a hydrated reference to the object we're waiting on
396+
// Not an error if it doesn't exist yet
397397
if apierrors.IsNotFound(err) {
398-
orcImage.SetName(ref.Name)
399-
orcImage.SetNamespace(namespace)
400-
401-
return nil, orcImage, nil
398+
return nil, nil
402399
}
403400

404-
return nil, nil, err
401+
return nil, err
405402
}
406403

407404
if orcv1alpha1.IsAvailable(orcImage) {
408-
return orcImage.Status.ID, orcImage, nil
405+
return orcImage.Status.ID, nil
409406
}
410407

411408
if !orcv1alpha1.IsReconciliationComplete(orcImage) {
412-
return nil, orcImage, nil
409+
return nil, nil
413410
}
414411

415412
err = orcv1alpha1.GetTerminalError(orcImage)
416413
if err != nil {
417-
return nil, orcImage, capoerrors.Terminal(infrav1.DependencyFailedReason, orcImage.Kind+" "+orcImage.GetNamespace()+"/"+orcImage.GetName()+" failed: "+err.Error())
414+
return nil, capoerrors.Terminal(infrav1.DependencyFailedReason, orcImage.Kind+" "+orcImage.GetNamespace()+"/"+orcImage.GetName()+" failed: "+err.Error())
418415
}
419416

420-
return nil, orcImage, nil
417+
return nil, nil
421418
}
422419

423420
// GetManagementPort returns the port which is used for management and external

pkg/cloud/services/compute/instance_test.go

Lines changed: 1 addition & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import (
3333
"github.com/gophercloud/gophercloud/v2/openstack/image/v2/images"
3434
. "github.com/onsi/gomega" //nolint:revive
3535
"go.uber.org/mock/gomock"
36-
apiequality "k8s.io/apimachinery/pkg/api/equality"
3736
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3837
"k8s.io/apimachinery/pkg/runtime"
3938
"k8s.io/utils/ptr"
@@ -52,8 +51,6 @@ func TestService_getImageID(t *testing.T) {
5251
imageID = "ce96e584-7ebc-46d6-9e55-987d72e3806c"
5352
imageName = "test-image"
5453
namespace = "test-namespace"
55-
56-
fakeClientResourceVersion = "999"
5754
)
5855
imageTags := []string{"test-tag"}
5956

@@ -68,7 +65,6 @@ func TestService_getImageID(t *testing.T) {
6865
fakeObjects []runtime.Object
6966
expect func(m *mock.MockImageClientMockRecorder)
7067
want *string
71-
wantDep runtime.Object
7268
wantErr bool
7369
wantTerminalError bool
7470
}{
@@ -164,12 +160,6 @@ func TestService_getImageID(t *testing.T) {
164160
},
165161
},
166162
want: nil,
167-
wantDep: &orcv1alpha1.Image{
168-
ObjectMeta: metav1.ObjectMeta{
169-
Name: imageName,
170-
Namespace: namespace,
171-
},
172-
},
173163
},
174164
{
175165
testName: "Image by reference exists, is available",
@@ -196,22 +186,6 @@ func TestService_getImageID(t *testing.T) {
196186
},
197187
},
198188
want: ptr.To(imageID),
199-
wantDep: &orcv1alpha1.Image{
200-
ObjectMeta: metav1.ObjectMeta{
201-
Name: imageName,
202-
Namespace: namespace,
203-
ResourceVersion: fakeClientResourceVersion,
204-
},
205-
Status: orcv1alpha1.ImageStatus{
206-
Conditions: []metav1.Condition{
207-
{
208-
Type: orcv1alpha1.OpenStackConditionAvailable,
209-
Status: metav1.ConditionTrue,
210-
},
211-
},
212-
ID: ptr.To(imageID),
213-
},
214-
},
215189
},
216190
{
217191
testName: "Image by reference exists, still reconciling",
@@ -243,27 +217,6 @@ func TestService_getImageID(t *testing.T) {
243217
},
244218
},
245219
want: nil,
246-
wantDep: &orcv1alpha1.Image{
247-
ObjectMeta: metav1.ObjectMeta{
248-
Name: imageName,
249-
Namespace: namespace,
250-
ResourceVersion: fakeClientResourceVersion,
251-
},
252-
Status: orcv1alpha1.ImageStatus{
253-
Conditions: []metav1.Condition{
254-
{
255-
Type: orcv1alpha1.OpenStackConditionAvailable,
256-
Status: metav1.ConditionFalse,
257-
},
258-
{
259-
Type: orcv1alpha1.OpenStackConditionProgressing,
260-
Status: metav1.ConditionTrue,
261-
Reason: orcv1alpha1.OpenStackConditionReasonProgressing,
262-
},
263-
},
264-
ID: ptr.To(imageID),
265-
},
266-
},
267220
},
268221
{
269222
testName: "Image by reference exists, terminal failure",
@@ -297,28 +250,6 @@ func TestService_getImageID(t *testing.T) {
297250
},
298251
want: nil,
299252
wantTerminalError: true,
300-
wantDep: &orcv1alpha1.Image{
301-
ObjectMeta: metav1.ObjectMeta{
302-
Name: imageName,
303-
Namespace: namespace,
304-
ResourceVersion: fakeClientResourceVersion,
305-
},
306-
Status: orcv1alpha1.ImageStatus{
307-
Conditions: []metav1.Condition{
308-
{
309-
Type: orcv1alpha1.OpenStackConditionAvailable,
310-
Status: metav1.ConditionFalse,
311-
},
312-
{
313-
Type: orcv1alpha1.OpenStackConditionProgressing,
314-
Status: metav1.ConditionFalse,
315-
Reason: orcv1alpha1.OpenStackConditionReasonUnrecoverableError,
316-
Message: "test error",
317-
},
318-
},
319-
ID: ptr.To(imageID),
320-
},
321-
},
322253
},
323254
}
324255
for _, tt := range tests {
@@ -337,7 +268,7 @@ func TestService_getImageID(t *testing.T) {
337268

338269
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.fakeObjects...).Build()
339270

340-
got, dependency, err := s.GetImageID(context.TODO(), fakeClient, namespace, tt.image)
271+
got, err := s.GetImageID(context.TODO(), fakeClient, namespace, tt.image)
341272

342273
if tt.wantTerminalError {
343274
tt.wantErr = true
@@ -353,10 +284,6 @@ func TestService_getImageID(t *testing.T) {
353284
t.Errorf("Terminal error: wanted = %v, got = %v", tt.wantTerminalError, !tt.wantTerminalError)
354285
}
355286

356-
if !apiequality.Semantic.DeepEqual(tt.wantDep, dependency) {
357-
t.Errorf("Dependency does not match: %s", cmp.Diff(tt.wantDep, dependency))
358-
}
359-
360287
// NOTE(mdbooth): there must be a simpler way to write this!
361288
if (tt.want == nil && got != nil) || (tt.want != nil && (got == nil || *tt.want != *got)) {
362289
t.Errorf("Service.getImageID() = '%v', want '%v'", ptr.Deref(got, ""), ptr.Deref(tt.want, ""))

0 commit comments

Comments
 (0)