From eb3bc07d9bd19733cd601d113248c74332c64457 Mon Sep 17 00:00:00 2001 From: Gytis Trikleris Date: Mon, 4 Jun 2018 19:13:41 +0200 Subject: [PATCH] LeaderContext --- .../kubernetes/leader/LeaderContext.java | 50 ++++++++++++ .../leader/LeadershipController.java | 9 ++- .../kubernetes/leader/LeaderContextTest.java | 77 +++++++++++++++++++ .../leader/LeadershipControllerTest.java | 28 +++++-- 4 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeaderContext.java create mode 100644 spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeaderContextTest.java diff --git a/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeaderContext.java b/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeaderContext.java new file mode 100644 index 00000000..60176d0e --- /dev/null +++ b/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeaderContext.java @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Red Hat, Inc, and individual contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.leader; + +import org.springframework.integration.leader.Candidate; +import org.springframework.integration.leader.Context; + +/** + * @author Gytis Trikleris + */ +public class LeaderContext implements Context { + + private final Candidate candidate; + + private final LeadershipController leadershipController; + + public LeaderContext(Candidate candidate, LeadershipController leadershipController) { + this.candidate = candidate; + this.leadershipController = leadershipController; + } + + @Override + public boolean isLeader() { + Leader leader = leadershipController.getLeader(candidate.getRole()); + if (leader == null) { + return false; + } + + return candidate.getId().equals(leader.getId()); + } + + @Override + public void yield() { + leadershipController.revoke(candidate); + } +} 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 407040d7..13bf56e2 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 @@ -24,6 +24,7 @@ import io.fabric8.kubernetes.client.KubernetesClientException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.integration.leader.Candidate; +import org.springframework.integration.leader.Context; import org.springframework.integration.leader.event.LeaderEventPublisher; /** @@ -123,9 +124,10 @@ public class LeadershipController { } private void handleOnGranted(Candidate candidate) { - leaderEventPublisher.publishOnGranted(this, null, candidate.getRole()); + Context context = new LeaderContext(candidate, this); + leaderEventPublisher.publishOnGranted(this, context, candidate.getRole()); try { - candidate.onGranted(null); // TODO context + candidate.onGranted(context); } catch (InterruptedException e) { LOGGER.warn(e.getMessage()); Thread.currentThread().interrupt(); @@ -133,7 +135,8 @@ public class LeadershipController { } private void handleOnFailed(Candidate candidate) { - leaderEventPublisher.publishOnFailedToAcquire(this, null, candidate.getRole()); // TODO context + Context context = new LeaderContext(candidate, this); + leaderEventPublisher.publishOnFailedToAcquire(this, context, candidate.getRole()); } private Map getLeaderData(Candidate candidate) { diff --git a/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeaderContextTest.java b/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeaderContextTest.java new file mode 100644 index 00000000..6fee9391 --- /dev/null +++ b/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeaderContextTest.java @@ -0,0 +1,77 @@ +package org.springframework.cloud.kubernetes.leader; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.integration.leader.Candidate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +/** + * @author Gytis Trikleris + */ +@RunWith(MockitoJUnitRunner.class) +public class LeaderContextTest { + + private static final String ROLE = "test-role"; + + private static final String ID = "test-id"; + + @Mock + private Candidate mockCandidate; + + @Mock + private LeadershipController mockLeadershipController; + + @Mock + private Leader mockLeader; + + private LeaderContext leaderContext; + + @Before + public void before() { + given(mockCandidate.getRole()).willReturn(ROLE); + given(mockCandidate.getId()).willReturn(ID); + + leaderContext = new LeaderContext(mockCandidate, mockLeadershipController); + } + + @Test + public void testIsLeaderWithoutLeader() { + boolean result = leaderContext.isLeader(); + + assertThat(result).isFalse(); + } + + @Test + public void testIsLeaderWithAnotherLeader() { + given(mockLeadershipController.getLeader(ROLE)).willReturn(mockLeader); + given(mockLeader.getId()).willReturn("another-test-id"); + + boolean result = leaderContext.isLeader(); + + assertThat(result).isFalse(); + } + + @Test + public void testIsLeaderWhenLeader() { + given(mockLeadershipController.getLeader(ROLE)).willReturn(mockLeader); + given(mockLeader.getId()).willReturn(ID); + + boolean result = leaderContext.isLeader(); + + assertThat(result).isTrue(); + } + + @Test + public void shouldYieldLeadership() { + leaderContext.yield(); + + verify(mockLeadershipController).revoke(mockCandidate); + } + +} 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 217149ed..ecf841ce 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 @@ -8,6 +8,7 @@ import io.fabric8.kubernetes.client.KubernetesClientException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.integration.leader.Candidate; @@ -15,6 +16,7 @@ import org.springframework.integration.leader.event.LeaderEventPublisher; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; @@ -67,7 +69,7 @@ public class LeadershipControllerTest { assertThat(result).isTrue(); verify(mockKubernetesHelper).createConfigMap(Collections.singletonMap(PREFIX + ROLE, ID)); - verify(mockLeaderEventPublisher).publishOnGranted(leadershipController, null, ROLE); + verifyPublishOnGranted(); } @Test @@ -78,7 +80,7 @@ public class LeadershipControllerTest { assertThat(result).isTrue(); verify(mockKubernetesHelper).updateConfigMap(mockConfigMap, leaderData); - verify(mockLeaderEventPublisher).publishOnGranted(leadershipController, null, ROLE); + verifyPublishOnGranted(); } @Test @@ -109,7 +111,7 @@ public class LeadershipControllerTest { assertThat(result).isTrue(); verify(mockKubernetesHelper).updateConfigMap(mockConfigMap, Collections.singletonMap(PREFIX + ROLE, anotherId)); - verify(mockLeaderEventPublisher).publishOnGranted(leadershipController, null, ROLE); + verifyPublishOnGranted(); } @Test @@ -124,7 +126,7 @@ public class LeadershipControllerTest { assertThat(result).isFalse(); verify(mockKubernetesHelper, times(0)).createConfigMap(any()); verify(mockKubernetesHelper, times(0)).updateConfigMap(any(), any()); - verify(mockLeaderEventPublisher).publishOnFailedToAcquire(leadershipController, null, ROLE); + verifyPublishOnFailedToAcquire(); } @Test @@ -134,7 +136,7 @@ public class LeadershipControllerTest { boolean result = leadershipController.acquire(mockCandidate); assertThat(result).isFalse(); - verify(mockLeaderEventPublisher).publishOnFailedToAcquire(leadershipController, null, ROLE); + verifyPublishOnFailedToAcquire(); } @Test @@ -182,4 +184,20 @@ public class LeadershipControllerTest { assertThat(leader).isNull(); } + private void verifyPublishOnGranted() { + ArgumentCaptor 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); + } + + public void verifyPublishOnFailedToAcquire() { + ArgumentCaptor leaderContextCaptor = ArgumentCaptor.forClass(LeaderContext.class); + verify(mockLeaderEventPublisher).publishOnFailedToAcquire(eq(leadershipController), + leaderContextCaptor.capture(), eq(ROLE)); + LeaderContext expectedLeaderContext = new LeaderContext(mockCandidate, leadershipController); + assertThat(leaderContextCaptor.getValue()).isEqualToComparingFieldByField(expectedLeaderContext); + } + }