Skip to content
This repository was archived by the owner on Mar 13, 2022. It is now read-only.

Commit 40f0485

Browse files
committed
Error & exception handling for the annotation, reduced indentation
1 parent 1c29670 commit 40f0485

File tree

2 files changed

+88
-49
lines changed

2 files changed

+88
-49
lines changed

leaderelection/leaderelection.py

+68-42
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import logging
2121
logging.basicConfig(level=logging.INFO)
2222

23+
2324
class LeaderElection:
2425
def __init__(self, election_config):
2526
if election_config is None:
@@ -36,12 +37,12 @@ def __init__(self, election_config):
3637

3738

3839
# Annotation used in the lock object
39-
def leaderelector_record(self, election_config):
40+
def leaderelector_record(self, election_config, now):
4041
return {
4142
"holderIdentity": str(election_config.lock.identity),
4243
"leaseDurationSeconds": str(election_config.lease_duration),
43-
"acquireTime": str(datetime.datetime.now()),
44-
"renewTime": str(datetime.datetime.now())
44+
"acquireTime": str(now),
45+
"renewTime": str(now)
4546
}
4647

4748
# Point of entry to Leader election
@@ -97,51 +98,22 @@ def renew_loop(self):
9798
return
9899

99100
def try_acquire_or_renew(self):
101+
now = datetime.datetime.now()
102+
100103
# Check if lock is created
101-
lock_status, lock_response = self.election_config.lock.get(self.election_config.lock.name, self.election_config.lock.namespace)
104+
lock_status, lock_response, lock_record = self.election_config.lock.get(self.election_config.lock.name,
105+
self.election_config.lock.namespace)
102106

103107
# create a default Election record for this candidate
104-
leader_election_record = self.leaderelector_record(self.election_config)
105-
106-
# If a lock is already created with that name
107-
if lock_status:
108-
old_election_record = ast.literal_eval(lock_response.metadata.annotations[self.election_config.lock.leader_electionrecord_annotationkey])
109-
110-
# Report transitions
111-
if self.observed_record and self.observed_record['holderIdentity'] != old_election_record['holderIdentity']:
112-
logging.info("Leader has switched to {}".format(old_election_record['holderIdentity']))
113-
114-
115-
if old_election_record != self.observed_record:
116-
self.observed_record = old_election_record
117-
self.observed_time_milliseconds = int(time.time()*1000)
118-
119-
120-
# If This candidate is not the leader and lease duration is yet to finish
121-
if str(self.election_config.lock.identity) != self.observed_record['holderIdentity'] and self.observed_time_milliseconds + self.election_config.lease_duration*1000 > int(time.time()*1000):
122-
logging.info("yet to finish lease_duration, lease held by {} and has not expired".format(old_election_record['holderIdentity']))
123-
return False
108+
leader_election_record = self.leaderelector_record(self.election_config, now)
124109

125-
# If this candidate is the Leader
126-
if str(self.election_config.lock.identity) == self.observed_record['holderIdentity']:
127-
# Leader sets acquireTime
128-
leader_election_record['acquireTime'] = self.observed_record['acquireTime']
129-
130-
# Update object with latest election record
131-
lock_response.metadata.annotations[self.election_config.lock.leader_electionrecord_annotationkey] = str(leader_election_record)
132-
update_status, update_response = self.election_config.lock.update(self.election_config.lock.name, self.election_config.lock.namespace, lock_response)
133-
134-
if update_status is False:
135-
logging.info("{} failed to acquire lease".format(leader_election_record['holderIdentity']))
110+
# A lock is not created with that name, try to create one
111+
if not lock_status:
112+
if ast.literal_eval(lock_response.body)['code'] != 404:
113+
logging.info("Error retrieving resource lock {} as {}".format(self.election_config.lock.name, lock_response.reason))
136114
return False
137115

138-
self.observed_record = leader_election_record
139-
self.observed_time_milliseconds = int(time.time()*1000)
140-
logging.info("leader {} has successfully acquired lease".format(leader_election_record['holderIdentity']))
141-
return True
142116

143-
# A lock is not created with that name, try to create one
144-
else:
145117
logging.info("{} is trying to create a lock".format(leader_election_record['holderIdentity']))
146118
create_status, create_response = self.election_config.lock.create(name=self.election_config.lock.name,
147119
namespace=self.election_config.lock.namespace,
@@ -151,6 +123,60 @@ def try_acquire_or_renew(self):
151123
return False
152124

153125
self.observed_record = leader_election_record
154-
self.observed_time_milliseconds = int(time.time()*1000)
126+
self.observed_time_milliseconds = int(time.time() * 1000)
155127
return True
156128

129+
# A lock exists with that name
130+
# Validate lock_record
131+
if lock_record is None:
132+
# try to update lock with proper annotation and election record
133+
return self.update_lock(lock_response, leader_election_record)
134+
135+
# check for any key, value errors in the record
136+
try:
137+
old_election_record = ast.literal_eval(lock_record)
138+
if (old_election_record['holderIdentity'] == '' or old_election_record['leaseDurationSeconds'] == ''
139+
or old_election_record['acquireTime'] == '' or old_election_record['renewTime'] == ''):
140+
# try to update lock with proper annotation and election record
141+
return self.update_lock(lock_response, leader_election_record)
142+
except:
143+
# try to update lock with proper annotation and election record
144+
return self.update_lock(lock_response, leader_election_record)
145+
146+
147+
# Report transitions
148+
if self.observed_record and self.observed_record['holderIdentity'] != old_election_record['holderIdentity']:
149+
logging.info("Leader has switched to {}".format(old_election_record['holderIdentity']))
150+
151+
if old_election_record != self.observed_record:
152+
self.observed_record = old_election_record
153+
self.observed_time_milliseconds = int(time.time() * 1000)
154+
155+
# If This candidate is not the leader and lease duration is yet to finish
156+
if (str(self.election_config.lock.identity) != self.observed_record['holderIdentity']
157+
and self.observed_time_milliseconds + self.election_config.lease_duration*1000 > int(now.timestamp()*1000)):
158+
logging.info("yet to finish lease_duration, lease held by {} and has not expired".format(old_election_record['holderIdentity']))
159+
return False
160+
161+
# If this candidate is the Leader
162+
if str(self.election_config.lock.identity) == self.observed_record['holderIdentity']:
163+
# Leader sets acquireTime
164+
leader_election_record['acquireTime'] = self.observed_record['acquireTime']
165+
166+
return self.update_lock(lock_response, leader_election_record)
167+
168+
def update_lock(self, lock_response, leader_election_record):
169+
# Update object with latest election record
170+
update_status, update_response = self.election_config.lock.update(self.election_config.lock.name,
171+
self.election_config.lock.namespace,
172+
lock_response, leader_election_record)
173+
174+
if update_status is False:
175+
logging.info("{} failed to acquire lease".format(leader_election_record['holderIdentity']))
176+
return False
177+
178+
self.observed_record = leader_election_record
179+
self.observed_time_milliseconds = int(time.time() * 1000)
180+
logging.info("leader {} has successfully acquired lease".format(leader_election_record['holderIdentity']))
181+
return True
182+

leaderelection/resourcelock/configmaplock.py

+20-7
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from kubernetes import client, config
1717
from kubernetes.client.api_client import ApiClient
1818

19-
2019
class ConfigMapLock:
2120
def __init__(self, name, namespace, identity):
2221
"""
@@ -35,13 +34,24 @@ def get(self, name, namespace):
3534
"""
3635
:param name: Name of the configmap object information to get
3736
:param namespace: Namespace in which the configmap object is to be searched
38-
:return: True, response if object found else False, exception
37+
:return: 'True, response, record' if object found else 'False, exception, None'
3938
"""
4039
try:
4140
api_response = self.api_instance.read_namespaced_config_map(name, namespace)
42-
return True, api_response
41+
42+
# If an annotation does not exist - add the leader_electionrecord_annotationkey
43+
if api_response.metadata.annotations is None or api_response.metadata.annotations == '':
44+
api_response.metadata.annotations = {self.leader_electionrecord_annotationkey: ''}
45+
return True, api_response, None
46+
47+
# If an annotation exists but, the leader_electionrecord_annotationkey does not then add it as a key
48+
if not api_response.metadata.annotations.get(self.leader_electionrecord_annotationkey):
49+
api_response.metadata.annotations = {self.leader_electionrecord_annotationkey: ''}
50+
return True, api_response, None
51+
52+
return True, api_response, api_response.metadata.annotations[self.leader_electionrecord_annotationkey]
4353
except ApiException as e:
44-
return False, e
54+
return False, e, None
4555

4656
def create(self, name, namespace, election_record):
4757
"""
@@ -59,15 +69,18 @@ def create(self, name, namespace, election_record):
5969
except ApiException as e:
6070
return False, e
6171

62-
def update(self, name, namespace, updated_record):
72+
def update(self, name, namespace, update_object, updated_record):
6373
"""
6474
:param name: name of the lock to be updated
6575
:param namespace: namespace the lock is in
66-
:param ElectionRecord: the updated record
76+
:param update_object: the object used to update the lock
77+
:param updated_record: the updated election record
6778
:return: True if update is succesful False if it fails
6879
"""
6980
try:
70-
api_response = self.api_instance.replace_namespaced_config_map(name=name, namespace=namespace, body=updated_record)
81+
# Set the updated record
82+
update_object.metadata.annotations[self.leader_electionrecord_annotationkey] = str(updated_record)
83+
api_response = self.api_instance.replace_namespaced_config_map(name=name, namespace=namespace, body=update_object)
7184
return True, api_response
7285
except ApiException as e:
7386
return False, e

0 commit comments

Comments
 (0)