Skip to content

Commit ad4a9d0

Browse files
Gytis Triklerisiocanel
Gytis Trikleris
authored andcommitted
Update LeadershipController#revoke logic
1 parent f3749fa commit ad4a9d0

File tree

2 files changed

+57
-19
lines changed

2 files changed

+57
-19
lines changed

spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeadershipController.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ public LeadershipController(LeaderProperties leaderProperties, KubernetesHelper
4747
this.leaderEventPublisher = leaderEventPublisher;
4848
}
4949

50+
/**
51+
* Acquire leadership for the requested candidate, if there is no existing leader or existing leader is not valid.
52+
* <p>
53+
* If leadership is successfully acquired {@code true} will be returned, {@link Candidate#onGranted(Context)}
54+
* invoked and {@link LeaderEventPublisher#publishOnGranted(Object, Context, String)} event emitted.
55+
* <p>
56+
* If requested candidate is already a leader, simply {@code true} will be returned.
57+
* <p>
58+
* If for some reason leadership cannot be acquired (communication failure or there is another leader),
59+
* {@code false} will be returned and {@link LeaderEventPublisher#publishOnFailedToAcquire(Object, Context, String)}
60+
* event will be emitted.
61+
*
62+
* @param candidate
63+
* @return {@code true} if at the end of execution candidate is a leader and {@code false} if it isn't.
64+
*/
5065
public boolean acquire(Candidate candidate) {
5166
try {
5267
ConfigMap configMap = kubernetesHelper.getConfigMap();
@@ -75,29 +90,44 @@ public boolean acquire(Candidate candidate) {
7590
return false;
7691
}
7792

93+
/**
94+
* Revoke leadership for the requested candidate.
95+
* <p>
96+
* If candidate's leadership is successfully revoked, {@code true} will be returned,
97+
* {@link Candidate#onRevoked(Context)} invoked and
98+
* {@link LeaderEventPublisher#publishOnRevoked(Object, Context, String)} event emitted.
99+
* <p>
100+
* If requested candidate is already not a leader, simply {@code true} will be returned.
101+
* <p>
102+
* If leadership cannot be revoked for a communication error or a concurrent ConfigMap modification, {@code false}
103+
* will be returned.
104+
*
105+
* @param candidate
106+
* @return {@code true} if at the end of execution candidate is not a leader. {@code false} revoke operation failed.
107+
*/
78108
public boolean revoke(Candidate candidate) {
79109
try {
80110
ConfigMap configMap = kubernetesHelper.getConfigMap();
81111
if (configMap == null) {
82-
return false;
112+
return true;
83113
}
84114

85115
Leader leader = getLeader(candidate.getRole(), configMap);
86116
if (leader == null) {
87-
return false;
117+
return true;
88118
}
89119

90120
if (candidate.getId().equals(leader.getId())) {
91121
removeLeaderFromConfigMap(candidate, configMap);
92122
handleOnRevoked(candidate);
93-
return true;
94123
}
95124
} catch (KubernetesClientException e) {
96125
LOGGER.warn("Failed to revoke leadership with role='{}' for candidate='{}': {}", candidate.getRole(),
97126
candidate.getId(), e.getMessage());
127+
return false;
98128
}
99129

100-
return false;
130+
return true;
101131
}
102132

103133
public Leader getLeader(String role) {

spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeadershipControllerTest.java

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public void before() {
6464
}
6565

6666
@Test
67-
public void shouldAcquireWithoutExistingConfigMap() {
67+
public void shouldAcquireWithoutExistingConfigMap() throws InterruptedException {
6868
boolean result = leadershipController.acquire(mockCandidate);
6969

7070
assertThat(result).isTrue();
@@ -73,7 +73,7 @@ public void shouldAcquireWithoutExistingConfigMap() {
7373
}
7474

7575
@Test
76-
public void shouldAcquireWithExistingConfigMap() {
76+
public void shouldAcquireWithExistingConfigMap() throws InterruptedException {
7777
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
7878

7979
boolean result = leadershipController.acquire(mockCandidate);
@@ -84,21 +84,22 @@ public void shouldAcquireWithExistingConfigMap() {
8484
}
8585

8686
@Test
87-
public void shouldNotAcquireIfAlreadyLeader() {
87+
public void shouldAcquireWithoutEventsIfAlreadyLeader() throws InterruptedException {
8888
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
8989
given(mockKubernetesHelper.isPodAlive(ID)).willReturn(true);
9090
given(mockConfigMap.getData()).willReturn(leaderData);
9191

9292
boolean result = leadershipController.acquire(mockCandidate);
9393

9494
assertThat(result).isTrue();
95+
verify(mockCandidate, times(0)).onGranted(any());
9596
verify(mockKubernetesHelper, times(0)).createConfigMap(any());
9697
verify(mockKubernetesHelper, times(0)).updateConfigMapEntry(any(), any());
9798
verify(mockLeaderEventPublisher, times(0)).publishOnGranted(any(), any(), any());
9899
}
99100

100101
@Test
101-
public void shouldTakeOverLeadershipFromInvalidLeader() {
102+
public void shouldTakeOverLeadershipFromInvalidLeader() throws InterruptedException {
102103
String anotherId = "another-test-id";
103104
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
104105
given(mockKubernetesHelper.isPodAlive(ID)).willReturn(false);
@@ -108,13 +109,13 @@ public void shouldTakeOverLeadershipFromInvalidLeader() {
108109
boolean result = leadershipController.acquire(mockCandidate);
109110

110111
assertThat(result).isTrue();
111-
verify(mockKubernetesHelper).updateConfigMapEntry(mockConfigMap,
112-
Collections.singletonMap(PREFIX + ROLE, anotherId));
112+
Map<String, String> anotherLeaderData = Collections.singletonMap(PREFIX + ROLE, anotherId);
113+
verify(mockKubernetesHelper).updateConfigMapEntry(mockConfigMap, anotherLeaderData);
113114
verifyPublishOnGranted();
114115
}
115116

116117
@Test
117-
public void shouldFailToAcquireBecauseOfExistingLeader() {
118+
public void shouldFailToAcquireIfThereIsAnotherLeader() {
118119
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
119120
given(mockKubernetesHelper.isPodAlive(ID)).willReturn(true);
120121
given(mockConfigMap.getData()).willReturn(leaderData);
@@ -151,34 +152,37 @@ public void shouldRevokeLeadership() {
151152
}
152153

153154
@Test
154-
public void shouldNotRevokeLeadershipIfThereIsNoConfigMap() {
155+
public void shouldRevokeLeadershipWithoutEventsIfThereIsNoConfigMap() {
155156
boolean result = leadershipController.revoke(mockCandidate);
156157

157-
assertThat(result).isFalse();
158+
assertThat(result).isTrue();
159+
verify(mockCandidate, times(0)).onRevoked(any());
158160
verify(mockKubernetesHelper, times(0)).removeConfigMapEntry(any(), any());
159161
verify(mockLeaderEventPublisher, times(0)).publishOnRevoked(any(), any(), any());
160162
}
161163

162164
@Test
163-
public void shouldNotRevokeLeadershipIfThereIsNoLeader() {
165+
public void shouldRevokeLeadershipWithoutEventsIfThereIsNoLeader() {
164166
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
165167

166168
boolean result = leadershipController.revoke(mockCandidate);
167169

168-
assertThat(result).isFalse();
170+
assertThat(result).isTrue();
171+
verify(mockCandidate, times(0)).onRevoked(any());
169172
verify(mockKubernetesHelper, times(0)).removeConfigMapEntry(any(), any());
170173
verify(mockLeaderEventPublisher, times(0)).publishOnRevoked(any(), any(), any());
171174
}
172175

173176
@Test
174-
public void shouldNotRevokeLeadershipIfThereIsAnotherLeader() {
177+
public void shouldRevokeLeadershipWithoutEventsIfThereIsAnotherLeader() {
175178
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
176179
given(mockConfigMap.getData()).willReturn(leaderData);
177180
given(mockCandidate.getId()).willReturn("another-test-id");
178181

179182
boolean result = leadershipController.revoke(mockCandidate);
180183

181-
assertThat(result).isFalse();
184+
assertThat(result).isTrue();
185+
verify(mockCandidate, times(0)).onRevoked(any());
182186
verify(mockKubernetesHelper, times(0)).removeConfigMapEntry(any(), any());
183187
verify(mockLeaderEventPublisher, times(0)).publishOnRevoked(any(), any(), any());
184188
}
@@ -193,7 +197,7 @@ public void shouldFailToRevokeLeadership() {
193197
boolean result = leadershipController.revoke(mockCandidate);
194198

195199
assertThat(result).isFalse();
196-
verify(mockKubernetesHelper).removeConfigMapEntry(mockConfigMap, PREFIX + ROLE);
200+
verify(mockCandidate, times(0)).onRevoked(any());
197201
verify(mockLeaderEventPublisher, times(0)).publishOnRevoked(any(), any(), any());
198202
}
199203

@@ -242,12 +246,14 @@ public void shouldHandleFailureWhenGettingLeader() {
242246
assertThat(leader).isNull();
243247
}
244248

245-
private void verifyPublishOnGranted() {
249+
private void verifyPublishOnGranted() throws InterruptedException {
246250
ArgumentCaptor<LeaderContext> leaderContextCaptor = ArgumentCaptor.forClass(LeaderContext.class);
247251
verify(mockLeaderEventPublisher).publishOnGranted(eq(leadershipController),
248252
leaderContextCaptor.capture(), eq(ROLE));
249253
LeaderContext expectedLeaderContext = new LeaderContext(mockCandidate, leadershipController);
250254
assertThat(leaderContextCaptor.getValue()).isEqualToComparingFieldByField(expectedLeaderContext);
255+
256+
verify(mockCandidate).onGranted(leaderContextCaptor.getValue());
251257
}
252258

253259
private void verifyPublishOnRevoked() {
@@ -256,6 +262,8 @@ private void verifyPublishOnRevoked() {
256262
leaderContextCaptor.capture(), eq(ROLE));
257263
LeaderContext expectedLeaderContext = new LeaderContext(mockCandidate, leadershipController);
258264
assertThat(leaderContextCaptor.getValue()).isEqualToComparingFieldByField(expectedLeaderContext);
265+
266+
verify(mockCandidate).onRevoked(leaderContextCaptor.getValue());
259267
}
260268

261269
public void verifyPublishOnFailedToAcquire() {

0 commit comments

Comments
 (0)