Cleanup: no mixed use of setters and properties.

This commit is contained in:
Maurice Makaay 2019-12-12 13:06:30 +01:00
parent f744dbec4f
commit 024af2d26e
4 changed files with 85 additions and 82 deletions

View File

@ -38,8 +38,8 @@ class Manager():
self.state_store.load()
def run(self):
"""Starts the manager."""
self.drop_privileges(self.config.run_user, self.config.run_group)
"""Starts the manager process."""
drop_privileges(self.config.run_user, self.config.run_group)
while True:
self.node_poller.poll()
sleep(self.config.poll_interval_in_sec)

View File

@ -29,12 +29,3 @@ class NodeConfig():
"pgbouncer_config path must exist",
"nodes[%s]" % self.node_id, "pgbouncer_config", path)
self._pgbouncer_config = path
def export(self):
"""Exports the data for the node configuration, that we want
to end up in the state data that is stored in the state store."""
return {
"pgbouncer_config": self.pgbouncer_config,
"host": self.host,
"port": self.port
}

View File

@ -170,16 +170,12 @@ class State():
self.modified = True
self._timeline_id = timeline_id
def add_node(self, node):
"""Add a node to the state. Node can be a NodeConfig object or
a node_id. In case a node_id is provided, an NodeConfig object
will be created automatically."""
if not isinstance(node, NodeConfig):
node = NodeConfig(node)
if node.node_id in self.nodes:
raise DuplicateNodeAdded(node.node_id)
node_state = NodeState(node, self)
self.nodes[node.node_id] = node_state
def add_node(self, node_config):
"""Add a node to the state, based on the provided NodeConfig object."""
if node_config.node_id in self.nodes:
raise DuplicateNodeAdded(node_config.node_id)
node_state = NodeState(node_config, self)
self.nodes[node_config.node_id] = node_state
self.modified = True
return node_state
@ -305,21 +301,21 @@ class NodeState():
"""This class encapsulates the information for a single node in a
PostgreSQL cluster."""
def __init__(self, config, parent_state):
self.node_id = config.node_id
self.config = config
def __init__(self, node_config, parent_state):
self.node_id = node_config.node_id
self.config = node_config
self.parent_state = parent_state
self._system_id = None
self._timeline_id = None
self.status = NODE_UNKNOWN
self.err = None
self._status = NODE_UNKNOWN
self._error = None
def reset(self):
"""Reset the data for the node."""
self.system_id = None
self.timeline_id = None
self.status = NODE_UNKNOWN
self.err = None
self.error = None
self.notify_parent()
def notify_parent(self):
@ -365,25 +361,35 @@ class NodeState():
self._timeline_id = timeline_id
self.notify_parent()
def set_status(self, status):
@property
def status(self):
return self._status
@status.setter
def status(self, status):
"""Set the connection status for the node. This is used to indicate
whether or not a connection can be setup to the node from the
pgbouncemgr application and if the node is running in primary
or fallback mode. Possible values are: NODE_UNKNOWN, NODE_OFFLINE,
NODE_PRIMARY and NODE_STANDBY."""
if self.status == status:
pgbouncemgr application and if the node is found to be running
in primary or fallback mode. Possible values are: NODE_UNKNOWN,
NODE_OFFLINE, NODE_PRIMARY and NODE_STANDBY."""
if self._status == status:
return
if status not in NODE_STATUSES:
raise InvalidNodeStatus(status)
self.status = status
self._status = status
self.notify_parent()
def set_error(self, err):
@property
def error(self):
return self._error
@error.setter
def error(self, msg):
"""When there is some problem with this node, this method can be used
to set an error message for it, explaining the problem."""
if self.err == err:
if self._error == msg:
return
self.err = err
self._error = msg
self.notify_parent()
def promote(self):
@ -397,10 +403,16 @@ class NodeState():
state storage."""
return {
"node_id": self.node_id,
"config": self.config.export(),
"config": {
# Not all config data are exported, just a few that might
# be useful to find back in the state export.
"pgbouncer_config": self.config.pgbouncer_config,
"host": self.config.host,
"port": self.config.port
},
"system_id": self.system_id,
"timeline_id": self.timeline_id,
"is_leader": self.parent_state.leader_node_id == self.node_id,
"status": self.status,
"error": self.err,
"error": self._error,
}

View File

@ -138,17 +138,17 @@ class StateTests(unittest.TestCase):
class NodeCollectionTests(unittest.TestCase):
def test_WithNodeNotYetInState_AddNode_AddsNodeState(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
self.assertEqual(1, node.node_id)
self.assertTrue(state.modified)
def test_WithNodeAlreadyInState_AddNode_RaisesException(self):
state = State(Logger())
state.add_node(123)
state.add_node(NodeConfig(123))
with self.assertRaises(DuplicateNodeAdded) as context:
state.add_node(123)
state.add_node(NodeConfig(123))
self.assertIn("duplicate node_id=123", str(context.exception))
def test_WithNodeNotInState_getNode_RaisesException(self):
@ -159,7 +159,7 @@ class NodeCollectionTests(unittest.TestCase):
def test_WithNodeInState_getNode_ReturnsNode(self):
state = State(Logger())
node = state.add_node("that does exist")
node = state.add_node(NodeConfig("that does exist"))
node_from_state = state.get_node("that does exist")
@ -168,14 +168,14 @@ class NodeCollectionTests(unittest.TestCase):
def test_WithUnknownNode_SetLeaderNode_RaisesException(self):
state1 = State(Logger())
state2 = State(Logger())
node = state2.add_node(1337)
node = state2.add_node(NodeConfig(1337))
with self.assertRaises(UnknownNodeRequested) as context:
state1.promote_node(node)
self.assertIn("unknown node_id=1337", str(context.exception))
def test_WithNodeWithoutSystemId_SetLeaderNode_RaisesException(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
node.timeline_id = 1
node.config.pgbouncer_config = PGBOUNCER_CONFIG
with self.assertRaises(NodeCannotBePromoted) as context:
@ -185,7 +185,7 @@ class NodeCollectionTests(unittest.TestCase):
def test_WithNodeWithoutTimelineId_SetLeaderNode_RaisesException(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
node.system_id = "system"
node.config.pgbouncer_config = PGBOUNCER_CONFIG
with self.assertRaises(NodeCannotBePromoted) as context:
@ -195,7 +195,7 @@ class NodeCollectionTests(unittest.TestCase):
def test_WithNodeWithoutPgbouncerConfig_SetLeaderNode_RaisesException(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
node.system_id = "a7d8a9347df789saghdfs"
node.timeline_id = 11111111111
with self.assertRaises(NodeCannotBePromoted) as context:
@ -205,7 +205,7 @@ class NodeCollectionTests(unittest.TestCase):
def test_SetLeaderNode_SetsLeaderNode_WithUnknownLeaderStatus(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
node.config.pgbouncer_config = PGBOUNCER_CONFIG
node.system_id = "SuperCluster"
node.timeline_id = " 005 "
@ -220,7 +220,7 @@ class NodeCollectionTests(unittest.TestCase):
def test_SetLeaderNode_ToSameLeader_ResetsLeaderNode_WithUnknownLeaderStatus(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
node.config.pgbouncer_config = PGBOUNCER_CONFIG
node.system_id = "1.2.3.4.5.6.7.8.9.10.11"
node.timeline_id = 12
@ -236,11 +236,11 @@ class NodeCollectionTests(unittest.TestCase):
def test_SetLeaderNode_ToNodeWithDifferentSystemId_RaisesException(self):
state = State(Logger())
node1 = state.add_node(1)
node1 = state.add_node(NodeConfig(1))
node1.config.pgbouncer_config = PGBOUNCER_CONFIG
node1.system_id = "systemA"
node1.timeline_id = 10
node2 = state.add_node(2)
node2 = state.add_node(NodeConfig(2))
node2.config.pgbouncer_config = PGBOUNCER_CONFIG
node2.system_id = "systemB"
node2.timeline_id = 10
@ -251,11 +251,11 @@ class NodeCollectionTests(unittest.TestCase):
def test_SetLeaderNode_ToNodeWithLowerTimelineId_RaisesException(self):
state = State(Logger())
node1 = state.add_node(1)
node1 = state.add_node(NodeConfig(1))
node1.config.pgbouncer_config = PGBOUNCER_CONFIG
node1.system_id = "systemX"
node1.timeline_id = 10
node2 = state.add_node(2)
node2 = state.add_node(NodeConfig(2))
node2.config.pgbouncer_config = PGBOUNCER_CONFIG
node2.system_id = "systemX"
node2.timeline_id = 9
@ -266,15 +266,15 @@ class NodeCollectionTests(unittest.TestCase):
def test_SetLeaderNode_ToNodeWithSameOrHigherTimelineId_IsOk(self):
state = State(Logger())
node1 = state.add_node(1)
node1 = state.add_node(NodeConfig(1))
node1.config.pgbouncer_config = PGBOUNCER_CONFIG
node1.system_id = "systemX"
node1.timeline_id = 42
node2 = state.add_node(2)
node2 = state.add_node(NodeConfig(2))
node2.config.pgbouncer_config = PGBOUNCER_CONFIG
node2.system_id = "systemX"
node2.timeline_id = 42
node3 = state.add_node(3)
node3 = state.add_node(NodeConfig(3))
node3.config.pgbouncer_config = PGBOUNCER_CONFIG
node3.system_id = "systemX"
node3.timeline_id = 43
@ -301,7 +301,7 @@ class NodeCollectionTests(unittest.TestCase):
class NodeTests(unittest.TestCase):
def test_WithNoneValue_SetSystemId_RaisesException(self):
state = State(Logger())
node = state.add_node("break me")
node = state.add_node(NodeConfig("break me"))
state.modified = False
with self.assertRaises(InvalidSystemId) as context:
@ -311,7 +311,7 @@ class NodeTests(unittest.TestCase):
def test_WithEmptyString_SetSystemId_RaisesException(self):
state = State(Logger())
node = state.add_node("break me")
node = state.add_node(NodeConfig("break me"))
state.modified = False
with self.assertRaises(InvalidSystemId) as context:
@ -321,7 +321,7 @@ class NodeTests(unittest.TestCase):
def test_SetSystemId_SetsSystemId_AndNotifiesChangeToState(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
state.modified = False
node.system_id = "X"
@ -331,7 +331,7 @@ class NodeTests(unittest.TestCase):
def test_WithNonIntegerInput_SetTimelineId_RaisesException(self):
state = State(Logger())
node = state.add_node("break me")
node = state.add_node(NodeConfig("break me"))
with self.assertRaises(InvalidTimelineId) as context:
node.timeline_id = "TARDIS"
@ -339,7 +339,7 @@ class NodeTests(unittest.TestCase):
def test_SetTimelineId_SetsTimelineId_AndNotifiesChangeToState(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
state.modified = False
node.timeline_id = 25
@ -349,58 +349,58 @@ class NodeTests(unittest.TestCase):
def test_SetStatus_SetsStatus_AndNotifiesChangeToState(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
state.modified = False
node.set_status(NODE_PRIMARY)
node.status = NODE_PRIMARY
self.assertEqual(NODE_PRIMARY, node.status)
self.assertTrue(state.modified)
def test_WithInvalidStatus_SetStatus_RaisesException(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
state.modified = False
with self.assertRaises(InvalidNodeStatus) as context:
node.set_status("DERAILED")
node.status = "DERAILED"
self.assertIn("'DERAILED'", str(context.exception))
self.assertFalse(state.modified)
def test_SetError_ToString_SetsError_AndNotifiesChangeToState(self):
state = State(Logger())
node = state.add_node(1)
node = state.add_node(NodeConfig(1))
state.modified = False
node.set_error("Found some spare bits at IKEA.py line 141")
node.error = "Found some spare bits at IKEA.py line 141"
self.assertEqual("Found some spare bits at IKEA.py line 141", node.err)
self.assertEqual("Found some spare bits at IKEA.py line 141", node.error)
self.assertTrue(state.modified)
def test_SetError_ToNone_ClearsError_AndNotifiesChangeToState(self):
state = State(Logger())
node = state.add_node(1)
node.set_error("Mouse lost its ball")
node = state.add_node(NodeConfig(1))
node.error = "Mouse lost its ball"
state.modified = False
node.set_error(None)
node.error = None
self.assertEqual(None, node.err)
self.assertEqual(None, node.error)
self.assertTrue(state.modified)
def test_WhenNothingChanges_NoChangeIsNotifiedToState(self):
state = State(Logger())
node = state.add_node("x")
node = state.add_node(NodeConfig("x"))
node.system_id = "aaaaaaa"
node.timeline_id = 55
node.set_status(NODE_PRIMARY)
node.set_error("Just testin'")
node.status = NODE_PRIMARY
node.error = "Just testin'"
state.modified = False
node.system_id = "aaaaaaa"
node.timeline_id = 55
node.set_status(NODE_PRIMARY)
node.set_error("Just testin'")
node.status = NODE_PRIMARY
node.error = "Just testin'"
self.assertFalse(state.modified)
@ -411,19 +411,19 @@ class StateExportTests(unittest.TestCase):
self.maxDiff = 5000;
state = State(Logger())
node1 = state.add_node(1)
node1 = state.add_node(NodeConfig(1))
node1.config.pgbouncer_config = PGBOUNCER_CONFIG
node1.system_id = "System X"
node1.timeline_id = 555
node1.set_status(NODE_PRIMARY)
node1.set_error("Some error for 1")
node1.status = NODE_PRIMARY
node1.error = "Some error for 1"
node2 = state.add_node(2)
node2 = state.add_node(NodeConfig(2))
node2.config.pgbouncer_config = PGBOUNCER_CONFIG
node2.system_id = "System Y"
node2.timeline_id = 1
node2.set_status(NODE_STANDBY)
node2.set_error("Some error for 2")
node2.status = NODE_STANDBY
node2.error = "Some error for 2"
node1.promote()