From 3dcbb707e9cce64dea16d869cb0c19ee46bf06b7 Mon Sep 17 00:00:00 2001 From: Jacky Hu Date: Sun, 10 Dec 2017 16:53:13 +0800 Subject: [PATCH] Handle a resource delete race condition When a resource is deleted from it's detail page, it returns to it's parent page and also returns the action result. The detail page action handler should ignore the delete event and thus not trying to load the already deleted resource. Change-Id: I5b4b33b5ea5716e5f005d388d68c9ecbcf09e2f9 --- .../healthmonitors/details/detail.controller.js | 4 ++++ .../details/detail.controller.spec.js | 14 ++++++++++++-- .../lbaasv2/listeners/details/detail.controller.js | 4 ++++ .../listeners/details/detail.controller.spec.js | 14 ++++++++++++-- .../loadbalancers/details/detail.controller.js | 4 ++++ .../details/detail.controller.spec.js | 14 ++++++++++++-- .../lbaasv2/members/details/detail.controller.js | 4 ++++ .../members/details/detail.controller.spec.js | 14 ++++++++++++-- .../lbaasv2/pools/details/detail.controller.js | 4 ++++ .../pools/details/detail.controller.spec.js | 14 ++++++++++++-- 10 files changed, 80 insertions(+), 10 deletions(-) diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.js index ed45dd3a..d0ecd718 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.js @@ -93,6 +93,10 @@ // and failed item. // Currently just refreshes the display each time. if (result) { + if (result.failed.length === 0 && result.deleted.length > 0) { + // handle a race condition where the resource is already deleted + return; + } spinnerService.showModalSpinner(gettext('Please Wait')); ctrl.showDetails = false; ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.spec.js index a4da6718..6c10c5b5 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/healthmonitors/details/detail.controller.spec.js @@ -74,7 +74,7 @@ it('handles empty results', function() { var result = $q.defer(); - result.resolve({}); + result.resolve({failed: [], deleted: []}); ctrl.resultHandler(result.promise); $timeout.flush(); expect(ctrl.showDetails).not.toBe(true); @@ -91,13 +91,23 @@ it('handles matched results', function() { spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); var result = $q.defer(); - result.resolve({some: 'thing'}); + result.resolve({some: 'thing', failed: [], deleted: []}); ctrl.resultHandler(result.promise); deferred.resolve({data: {some: 'data'}}); $timeout.flush(); expect(ctrl.showDetails).toBe(true); }); + it('handles delete race condition', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing', failed: [], deleted: [{id: 1}]}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(undefined); + }); + }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.js index 4e450614..f799cc38 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.js @@ -89,6 +89,10 @@ // and failed item. // Currently just refreshes the display each time. if (result) { + if (result.failed.length === 0 && result.deleted.length > 0) { + // handle a race condition where the resource is already deleted + return; + } spinnerService.showModalSpinner(gettext('Please Wait')); ctrl.showDetails = false; ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.spec.js index b11810bc..cb8c16c2 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.controller.spec.js @@ -70,7 +70,7 @@ it('handles empty results', function() { var result = $q.defer(); - result.resolve({}); + result.resolve({failed: [], deleted: []}); ctrl.resultHandler(result.promise); $timeout.flush(); expect(ctrl.showDetails).not.toBe(true); @@ -87,13 +87,23 @@ it('handles matched results', function() { spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); var result = $q.defer(); - result.resolve({some: 'thing'}); + result.resolve({some: 'thing', failed: [], deleted: []}); ctrl.resultHandler(result.promise); deferred.resolve({data: {some: 'data'}}); $timeout.flush(); expect(ctrl.showDetails).toBe(true); }); + it('handles delete race condition', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing', failed: [], deleted: [{id: 1}]}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(undefined); + }); + }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.js index 3869046e..74c97675 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.js @@ -85,6 +85,10 @@ // and failed item. // Currently just refreshes the display each time. if (result) { + if (result.failed.length === 0 && result.deleted.length > 0) { + // handle a race condition where the resource is already deleted + return; + } spinnerService.showModalSpinner(gettext('Please Wait')); ctrl.showDetails = false; ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.spec.js index 9e3e875a..50e415a5 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/details/detail.controller.spec.js @@ -68,7 +68,7 @@ it('handles empty results', function() { var result = $q.defer(); - result.resolve({}); + result.resolve({failed: [], deleted: []}); ctrl.resultHandler(result.promise); $timeout.flush(); expect(ctrl.showDetails).not.toBe(true); @@ -85,13 +85,23 @@ it('handles matched results', function() { spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); var result = $q.defer(); - result.resolve({some: 'thing'}); + result.resolve({some: 'thing', failed: [], deleted: []}); ctrl.resultHandler(result.promise); deferred.resolve({data: {some: 'data', floating_ip: {}}}); $timeout.flush(); expect(ctrl.showDetails).toBe(true); }); + it('handles delete race condition', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing', failed: [], deleted: [{id: 1}]}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(undefined); + }); + }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.js index b6010bd6..23f28b60 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.js @@ -94,6 +94,10 @@ // and failed item. // Currently just refreshes the display each time. if (result) { + if (result.failed.length === 0 && result.deleted.length > 0) { + // handle a race condition where the resource is already deleted + return; + } spinnerService.showModalSpinner(gettext('Please Wait')); ctrl.showDetails = false; ctrl.context.loadPromise = ctrl.resourceType.load( diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.spec.js index bc7aca0a..0853d83f 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/members/details/detail.controller.spec.js @@ -74,7 +74,7 @@ it('handles empty results', function() { var result = $q.defer(); - result.resolve({}); + result.resolve({failed: [], deleted: []}); ctrl.resultHandler(result.promise); $timeout.flush(); expect(ctrl.showDetails).not.toBe(true); @@ -91,13 +91,23 @@ it('handles matched results', function() { spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); var result = $q.defer(); - result.resolve({some: 'thing'}); + result.resolve({some: 'thing', failed: [], deleted: []}); ctrl.resultHandler(result.promise); deferred.resolve({data: {some: 'data'}}); $timeout.flush(); expect(ctrl.showDetails).toBe(true); }); + it('handles delete race condition', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing', failed: [], deleted: [{id: 1}]}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(undefined); + }); + }); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.js index 696a3ff5..b0845b85 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.js @@ -95,6 +95,10 @@ // and failed item. // Currently just refreshes the display each time. if (result) { + if (result.failed.length === 0 && result.deleted.length > 0) { + // handle a race condition where the resource is already deleted + return; + } spinnerService.showModalSpinner(gettext('Please Wait')); ctrl.showDetails = false; ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.spec.js index 92376332..2bda4630 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.controller.spec.js @@ -72,7 +72,7 @@ it('handles empty results', function() { var result = $q.defer(); - result.resolve({}); + result.resolve({failed: [], deleted: []}); ctrl.resultHandler(result.promise); $timeout.flush(); expect(ctrl.showDetails).not.toBe(true); @@ -89,13 +89,23 @@ it('handles matched results', function() { spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); var result = $q.defer(); - result.resolve({some: 'thing'}); + result.resolve({some: 'thing', failed: [], deleted: []}); ctrl.resultHandler(result.promise); deferred.resolve({data: {some: 'data'}}); $timeout.flush(); expect(ctrl.showDetails).toBe(true); }); + it('handles delete race condition', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing', failed: [], deleted: [{id: 1}]}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(undefined); + }); + }); });