Update LeadershipController#revoke logic

This commit is contained in:
Gytis Trikleris
2018-06-05 11:02:08 +02:00
committed by Ioannis Canellos
parent f3749fab75
commit ad4a9d090d
2 changed files with 57 additions and 19 deletions

View File

@@ -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.
* <p>
* If leadership is successfully acquired {@code true} will be returned, {@link Candidate#onGranted(Context)}
* invoked and {@link LeaderEventPublisher#publishOnGranted(Object, Context, String)} event emitted.
* <p>
* If requested candidate is already a leader, simply {@code true} will be returned.
* <p>
* 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.
* <p>
* 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.
* <p>
* If requested candidate is already not a leader, simply {@code true} will be returned.
* <p>
* 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) {

View File

@@ -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<String, String> anotherLeaderData = Collections.singletonMap(PREFIX + ROLE, anotherId);
verify(mockKubernetesHelper).updateConfigMapEntry(mockConfigMap, anotherLeaderData);
verifyPublishOnGranted();
}
@Test
public void shouldFailToAcquireBecauseOfExistingLeader() {
public void shouldFailToAcquireIfThereIsAnotherLeader() {
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
given(mockKubernetesHelper.isPodAlive(ID)).willReturn(true);
given(mockConfigMap.getData()).willReturn(leaderData);
@@ -151,34 +152,37 @@ public class LeadershipControllerTest {
}
@Test
public void shouldNotRevokeLeadershipIfThereIsNoConfigMap() {
public void shouldRevokeLeadershipWithoutEventsIfThereIsNoConfigMap() {
boolean result = leadershipController.revoke(mockCandidate);
assertThat(result).isFalse();
assertThat(result).isTrue();
verify(mockCandidate, times(0)).onRevoked(any());
verify(mockKubernetesHelper, times(0)).removeConfigMapEntry(any(), any());
verify(mockLeaderEventPublisher, times(0)).publishOnRevoked(any(), any(), any());
}
@Test
public void shouldNotRevokeLeadershipIfThereIsNoLeader() {
public void shouldRevokeLeadershipWithoutEventsIfThereIsNoLeader() {
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
boolean result = leadershipController.revoke(mockCandidate);
assertThat(result).isFalse();
assertThat(result).isTrue();
verify(mockCandidate, times(0)).onRevoked(any());
verify(mockKubernetesHelper, times(0)).removeConfigMapEntry(any(), any());
verify(mockLeaderEventPublisher, times(0)).publishOnRevoked(any(), any(), any());
}
@Test
public void shouldNotRevokeLeadershipIfThereIsAnotherLeader() {
public void shouldRevokeLeadershipWithoutEventsIfThereIsAnotherLeader() {
given(mockKubernetesHelper.getConfigMap()).willReturn(mockConfigMap);
given(mockConfigMap.getData()).willReturn(leaderData);
given(mockCandidate.getId()).willReturn("another-test-id");
boolean result = leadershipController.revoke(mockCandidate);
assertThat(result).isFalse();
assertThat(result).isTrue();
verify(mockCandidate, times(0)).onRevoked(any());
verify(mockKubernetesHelper, times(0)).removeConfigMapEntry(any(), any());
verify(mockLeaderEventPublisher, times(0)).publishOnRevoked(any(), any(), any());
}
@@ -193,7 +197,7 @@ public class LeadershipControllerTest {
boolean result = leadershipController.revoke(mockCandidate);
assertThat(result).isFalse();
verify(mockKubernetesHelper).removeConfigMapEntry(mockConfigMap, PREFIX + ROLE);
verify(mockCandidate, times(0)).onRevoked(any());
verify(mockLeaderEventPublisher, times(0)).publishOnRevoked(any(), any(), any());
}
@@ -242,12 +246,14 @@ public class LeadershipControllerTest {
assertThat(leader).isNull();
}
private void verifyPublishOnGranted() {
private void verifyPublishOnGranted() throws InterruptedException {
ArgumentCaptor<LeaderContext> leaderContextCaptor = ArgumentCaptor.forClass(LeaderContext.class);
verify(mockLeaderEventPublisher).publishOnGranted(eq(leadershipController),
leaderContextCaptor.capture(), eq(ROLE));
LeaderContext expectedLeaderContext = new LeaderContext(mockCandidate, leadershipController);
assertThat(leaderContextCaptor.getValue()).isEqualToComparingFieldByField(expectedLeaderContext);
verify(mockCandidate).onGranted(leaderContextCaptor.getValue());
}
private void verifyPublishOnRevoked() {
@@ -256,6 +262,8 @@ public class LeadershipControllerTest {
leaderContextCaptor.capture(), eq(ROLE));
LeaderContext expectedLeaderContext = new LeaderContext(mockCandidate, leadershipController);
assertThat(leaderContextCaptor.getValue()).isEqualToComparingFieldByField(expectedLeaderContext);
verify(mockCandidate).onRevoked(leaderContextCaptor.getValue());
}
public void verifyPublishOnFailedToAcquire() {