diff --git a/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeadershipController.java b/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeadershipController.java index 01552eda..4b2143dd 100644 --- a/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeadershipController.java +++ b/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeadershipController.java @@ -47,6 +47,21 @@ public class LeadershipController { this.leaderEventPublisher = leaderEventPublisher; } + /** + * Acquire leadership for the requested candidate, if there is no existing leader or existing leader is not valid. + *
+ * If leadership is successfully acquired {@code true} will be returned, {@link Candidate#onGranted(Context)} + * invoked and {@link LeaderEventPublisher#publishOnGranted(Object, Context, String)} event emitted. + *
+ * If requested candidate is already a leader, simply {@code true} will be returned. + *
+ * If for some reason leadership cannot be acquired (communication failure or there is another leader), + * {@code false} will be returned and {@link LeaderEventPublisher#publishOnFailedToAcquire(Object, Context, String)} + * event will be emitted. + * + * @param candidate + * @return {@code true} if at the end of execution candidate is a leader and {@code false} if it isn't. + */ public boolean acquire(Candidate candidate) { try { ConfigMap configMap = kubernetesHelper.getConfigMap(); @@ -75,29 +90,44 @@ public class LeadershipController { return false; } + /** + * Revoke leadership for the requested candidate. + *
+ * If candidate's leadership is successfully revoked, {@code true} will be returned, + * {@link Candidate#onRevoked(Context)} invoked and + * {@link LeaderEventPublisher#publishOnRevoked(Object, Context, String)} event emitted. + *
+ * If requested candidate is already not a leader, simply {@code true} will be returned. + *
+ * If leadership cannot be revoked for a communication error or a concurrent ConfigMap modification, {@code false}
+ * will be returned.
+ *
+ * @param candidate
+ * @return {@code true} if at the end of execution candidate is not a leader. {@code false} revoke operation failed.
+ */
public boolean revoke(Candidate candidate) {
try {
ConfigMap configMap = kubernetesHelper.getConfigMap();
if (configMap == null) {
- return false;
+ return true;
}
Leader leader = getLeader(candidate.getRole(), configMap);
if (leader == null) {
- return false;
+ return true;
}
if (candidate.getId().equals(leader.getId())) {
removeLeaderFromConfigMap(candidate, configMap);
handleOnRevoked(candidate);
- return true;
}
} catch (KubernetesClientException e) {
LOGGER.warn("Failed to revoke leadership with role='{}' for candidate='{}': {}", candidate.getRole(),
candidate.getId(), e.getMessage());
+ return false;
}
- return false;
+ return true;
}
public Leader getLeader(String role) {
diff --git a/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeadershipControllerTest.java b/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeadershipControllerTest.java
index 101df4e7..aa5f2f7d 100644
--- a/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeadershipControllerTest.java
+++ b/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeadershipControllerTest.java
@@ -64,7 +64,7 @@ public class LeadershipControllerTest {
}
@Test
- public void shouldAcquireWithoutExistingConfigMap() {
+ public void shouldAcquireWithoutExistingConfigMap() throws InterruptedException {
boolean result = leadershipController.acquire(mockCandidate);
assertThat(result).isTrue();
@@ -73,7 +73,7 @@ public class LeadershipControllerTest {
}
@Test
- public void shouldAcquireWithExistingConfigMap() {
+ public void shouldAcquireWithExistingConfigMap() throws InterruptedException {
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
boolean result = leadershipController.acquire(mockCandidate);
@@ -84,7 +84,7 @@ public class LeadershipControllerTest {
}
@Test
- public void shouldNotAcquireIfAlreadyLeader() {
+ public void shouldAcquireWithoutEventsIfAlreadyLeader() throws InterruptedException {
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
given(mockKubernetesHelper.isPodAlive(ID)).willReturn(true);
given(mockConfigMap.getData()).willReturn(leaderData);
@@ -92,13 +92,14 @@ public class LeadershipControllerTest {
boolean result = leadershipController.acquire(mockCandidate);
assertThat(result).isTrue();
+ verify(mockCandidate, times(0)).onGranted(any());
verify(mockKubernetesHelper, times(0)).createConfigMap(any());
verify(mockKubernetesHelper, times(0)).updateConfigMapEntry(any(), any());
verify(mockLeaderEventPublisher, times(0)).publishOnGranted(any(), any(), any());
}
@Test
- public void shouldTakeOverLeadershipFromInvalidLeader() {
+ public void shouldTakeOverLeadershipFromInvalidLeader() throws InterruptedException {
String anotherId = "another-test-id";
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
given(mockKubernetesHelper.isPodAlive(ID)).willReturn(false);
@@ -108,13 +109,13 @@ public class LeadershipControllerTest {
boolean result = leadershipController.acquire(mockCandidate);
assertThat(result).isTrue();
- verify(mockKubernetesHelper).updateConfigMapEntry(mockConfigMap,
- Collections.singletonMap(PREFIX + ROLE, anotherId));
+ Map