Merge ~mpontillo/maas:fix-random-choice-no-choices--2.2 into maas:2.2

Proposed by Mike Pontillo
Status: Merged
Approved by: Mike Pontillo
Approved revision: 462a87f5e22fbd7707da860f1c47473eb879f864
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~mpontillo/maas:fix-random-choice-no-choices--2.2
Merge into: maas:2.2
Diff against target: 117 lines (+36/-11)
4 files modified
docs/changelog.rst (+3/-1)
requirements.txt (+2/-2)
src/maasserver/rpc/regionservice.py (+11/-7)
src/maasserver/rpc/tests/test_regionservice.py (+20/-1)
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+327716@code.launchpad.net

Commit message

Prevent RegionService from using empty lists.

LP: #1705254
Backports: 3dd8eedfba921760f98ea54805d05c86e63266ec

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Self-approve backport.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 7eaaeb7..abea670 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -28,7 +28,9 @@ LP: #1703845 Set the re-check interval for rack to region RPC connections to
2828
29LP: #1703403 Lower the maximum number of connections per worker to 9 connections is allowed as 1 is reserved.29LP: #1703403 Lower the maximum number of connections per worker to 9 connections is allowed as 1 is reserved.
3030
31LP: #1704489 [2.2] Duplicate communication is occurring to the same rack controller31LP: #1704489 Duplicate communication is occurring to the same rack controller
32
33LP: #1705254 RegionService can raise unexpected exceptions for an empty connection list
3234
3335
342.2.1362.2.1
diff --git a/requirements.txt b/requirements.txt
index ae87b46..098e026 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,3 @@
1bson==0.4.3
2convoy==0.4.41convoy==0.4.4
3crochet==1.5.02crochet==1.5.0
4django==1.83django==1.8
@@ -13,7 +12,8 @@ paramiko==1.16.3
13petname==2.012petname==2.0
14pexpect==4.2.013pexpect==4.2.0
15psycopg2==2.6.214psycopg2==2.6.2
16pyopenssl==16.0.015pymongo==3.2
16pyopenssl==16.2.0
17pyparsing==2.0.717pyparsing==2.0.7
18pyvmomi==6.0.0.2016.618pyvmomi==6.0.0.2016.6
19requests==2.11.019requests==2.11.0
diff --git a/src/maasserver/rpc/regionservice.py b/src/maasserver/rpc/regionservice.py
index a68b038..4d962e8 100644
--- a/src/maasserver/rpc/regionservice.py
+++ b/src/maasserver/rpc/regionservice.py
@@ -967,21 +967,25 @@ class RegionService(service.Service, object):
967 def getAllClients(self):967 def getAllClients(self):
968 """Return a list with one connection per rack controller."""968 """Return a list with one connection per rack controller."""
969 return [969 return [
970 common.Client(random.choice(list(conns)))970 common.Client(random.choice(list(connections)))
971 for conns in self.connections.values()971 for connections in self.connections.values()
972 if len(connections) > 0
972 ]973 ]
973974
974 @asynchronous(timeout=FOREVER)975 @asynchronous(timeout=FOREVER)
975 def getRandomClient(self):976 def getRandomClient(self):
976 """Return a list of all connected :class:`common.Client`s."""977 """Return a random connected :class:`common.Client`."""
977 clients = list(self.connections.values())978 connections = list(self.connections.values())
978 if len(clients) == 0:979 if len(connections) == 0:
979 raise exceptions.NoConnectionsAvailable(980 raise exceptions.NoConnectionsAvailable(
980 "Unable to connect to any rack controller; no connections "981 "Unable to connect to any rack controller; no connections "
981 "available.")982 "available.")
982 else:983 else:
983 conns = list(random.choice(clients))984 connection = random.choice(connections)
984 return common.Client(random.choice(conns))985 # The connection object is a set of RegionServer objects.
986 # Make sure a sane set was returned.
987 assert len(connection) > 0, "Connection set empty."
988 return common.Client(random.choice(list(connection)))
985989
986990
987def ignoreCancellation(failure):991def ignoreCancellation(failure):
diff --git a/src/maasserver/rpc/tests/test_regionservice.py b/src/maasserver/rpc/tests/test_regionservice.py
index e845b30..972a3d7 100644
--- a/src/maasserver/rpc/tests/test_regionservice.py
+++ b/src/maasserver/rpc/tests/test_regionservice.py
@@ -80,7 +80,10 @@ from provisioningserver.rpc import (
80 common,80 common,
81 exceptions,81 exceptions,
82)82)
83from provisioningserver.rpc.exceptions import CannotRegisterRackController83from provisioningserver.rpc.exceptions import (
84 CannotRegisterRackController,
85 NoConnectionsAvailable,
86)
84from provisioningserver.rpc.interfaces import IConnection87from provisioningserver.rpc.interfaces import IConnection
85from provisioningserver.rpc.region import RegisterRackController88from provisioningserver.rpc.region import RegisterRackController
86from provisioningserver.rpc.testing import call_responder89from provisioningserver.rpc.testing import call_responder
@@ -91,6 +94,7 @@ from provisioningserver.utils.twisted import (
91 callInReactorWithTimeout,94 callInReactorWithTimeout,
92 DeferredValue,95 DeferredValue,
93)96)
97from testtools import ExpectedException
94from testtools.deferredruntest import assert_fails_with98from testtools.deferredruntest import assert_fails_with
95from testtools.matchers import (99from testtools.matchers import (
96 AfterPreprocessing,100 AfterPreprocessing,
@@ -875,6 +879,21 @@ class TestRegionService(MAASTestCase):
875 self.assertThat(service.getAllClients(), Equals([]))879 self.assertThat(service.getAllClients(), Equals([]))
876880
877 @wait_for_reactor881 @wait_for_reactor
882 def test_getAllClients_empty_connections(self):
883 service = RegionService(sentinel.advertiser)
884 service.connections.clear()
885 uuid1 = factory.make_UUID()
886 service.connections[uuid1] = set()
887 self.assertThat(service.getAllClients(), Equals([]))
888
889 @wait_for_reactor
890 def test_getRandomClient_empty_raises_NoConnectionsAvailable(self):
891 service = RegionService(sentinel.advertiser)
892 service.connections.clear()
893 with ExpectedException(NoConnectionsAvailable):
894 service.getRandomClient()
895
896 @wait_for_reactor
878 def test_getAllClients(self):897 def test_getAllClients(self):
879 service = RegionService(sentinel.advertiser)898 service = RegionService(sentinel.advertiser)
880 uuid1 = factory.make_UUID()899 uuid1 = factory.make_UUID()

Subscribers

People subscribed via source and target branches