Skip to content

Commit ec771ca

Browse files
authored
Merge pull request #843 from shiftstack/fix-attachment-race
Fix potential race in interface/volume status update
2 parents fc7d58b + e02d662 commit ec771ca

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

internal/controllers/port/actuator.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,11 @@ func (actuator portActuator) checkAttachedServer(ctx context.Context, obj orcObj
380380
}
381381

382382
// Find server with matching ID
383+
found := false
383384
for i := range serverList.Items {
384385
server := &serverList.Items[i]
385386
if server.Status.ID != nil && *server.Status.ID == osResource.DeviceID {
387+
found = true
386388
// Check if server is in BUILD status
387389
if server.Status.Resource != nil && server.Status.Resource.Status == "BUILD" {
388390
log.V(logging.Verbose).Info("Port is attached to server in BUILD status, waiting",
@@ -396,6 +398,23 @@ func (actuator portActuator) checkAttachedServer(ctx context.Context, obj orcObj
396398
}
397399
}
398400

401+
// When the port is attached to a device but still reports DOWN,
402+
// the Neutron status has not yet transitioned to ACTIVE (e.g.
403+
// OVN is still binding the port). serverToPortMapFunc also
404+
// detects this and triggers a reconcile, but it races with the
405+
// port controller's own status write: the controller may
406+
// overwrite Progressing=True with Progressing=False before the
407+
// port becomes ACTIVE. Poll here so we keep Progressing=True
408+
// until the transition completes. Only do this when we found
409+
// the ORC Server object the port is attached to, to avoid
410+
// unnecessary polling when the device isn't a tracked server.
411+
if found && osResource.Status == PortStatusDown {
412+
log.V(logging.Verbose).Info("port needs reconciliation: attached to server but status is DOWN",
413+
"port", obj.Name,
414+
"status", osResource.Status)
415+
return progress.WaitingOnOpenStack(progress.WaitingOnReady, serverBuildPollingPeriod)
416+
}
417+
399418
return nil
400419
}
401420

internal/controllers/volume/actuator.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,31 @@ func handleDescriptionUpdate(updateOpts *volumes.UpdateOpts, resource *resourceS
283283

284284
func (actuator volumeActuator) GetResourceReconcilers(ctx context.Context, orcObject orcObjectPT, osResource *osResourceT, controller interfaces.ResourceController) ([]resourceReconciler, progress.ReconcileStatus) {
285285
return []resourceReconciler{
286+
actuator.checkAttachmentStatus,
286287
actuator.updateResource,
287288
}, nil
288289
}
289290

291+
func (volumeActuator) checkAttachmentStatus(ctx context.Context, _ orcObjectPT, osResource *osResourceT) progress.ReconcileStatus {
292+
log := ctrl.LoggerFrom(ctx)
293+
294+
// When the volume has attachments but Cinder still reports
295+
// "available" rather than "in-use", the status transition has
296+
// not completed yet. serverToVolumeMapFunc also detects this
297+
// and triggers a reconcile, but it races with the volume
298+
// controller's own status write: the controller may overwrite
299+
// Progressing=True with Progressing=False before the volume
300+
// becomes in-use. Poll here so we keep Progressing=True until
301+
// the transition completes.
302+
if len(osResource.Attachments) > 0 && osResource.Status != VolumeStatusInUse {
303+
log.V(logging.Verbose).Info("volume needs reconciliation: attached to server but status is not in-use",
304+
"status", osResource.Status)
305+
return progress.WaitingOnOpenStack(progress.WaitingOnReady, volumeAvailablePollingPeriod)
306+
}
307+
308+
return nil
309+
}
310+
290311
type volumeHelperFactory struct{}
291312

292313
var _ helperFactory = volumeHelperFactory{}

0 commit comments

Comments
 (0)