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 new file mode 100644 index 00000000..6e4f8789 --- /dev/null +++ b/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/LeadershipController.java @@ -0,0 +1,81 @@ +/* + * 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 java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.integration.leader.Candidate; + +/** + * @author Gytis Trikleris + */ +public class LeadershipController { + + private static final Logger LOGGER = LoggerFactory.getLogger(LeadershipController.class); + + private final LeaderProperties leaderProperties; + + private final KubernetesClient kubernetesClient; + + public LeadershipController(LeaderProperties leaderProperties, KubernetesClient kubernetesClient) { + this.leaderProperties = leaderProperties; + this.kubernetesClient = kubernetesClient; + } + + public boolean acquire(Candidate candidate) { + return false; + } + + public boolean revoke(Candidate candidate) { + return false; + } + + public Leader getLeader(String role) { + ConfigMap configMap = getConfigMap(); + if (configMap == null || configMap.getData() == null) { + return null; + } + + Map data = configMap.getData(); + String leaderIdKey = leaderProperties.getLeaderIdPrefix() + role; + String leaderId = data.get(leaderIdKey); + if (leaderId == null) { + return null; + } + + return new Leader(role, leaderId); + } + + private ConfigMap getConfigMap() { + String namespace = leaderProperties.getNamespace(kubernetesClient.getNamespace()); + String name = leaderProperties.getConfigMapName(); + try { + return kubernetesClient.configMaps() + .inNamespace(namespace) + .withName(name) + .get(); + } catch (Exception e) { + LOGGER.warn("Failed to get ConfigMap '{}' in the namespace '{}': {}", namespace, name, e.getMessage()); + return null; + } + } + +} 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 new file mode 100644 index 00000000..dcd1d852 --- /dev/null +++ b/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/LeadershipControllerTest.java @@ -0,0 +1,114 @@ +package org.springframework.cloud.kubernetes.leader; + +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapList; +import io.fabric8.kubernetes.api.model.DoneableConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.Resource; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; + +/** + * @author Gytis Trikleris + */ +@RunWith(MockitoJUnitRunner.class) +public class LeadershipControllerTest { + + private final String NAMESPACE = "test-namespace"; + + private final String CONFIG_MAP_NAME = "test-config-map-name"; + + private final String ROLE = "test-role"; + + private final String ID = "test-id"; + + @Mock + private LeaderProperties mockLeaderProperties; + + @Mock + private KubernetesClient mockKubernetesClient; + + @Mock + private MixedOperation> configMapsOperation; + + @Mock + private NonNamespaceOperation> inNamespaceOperation; + + @Mock + private Resource configMapResource; + + @Mock + private ConfigMap mockConfigMap; + + @Mock + private Map mockData; + + private LeadershipController leadershipController; + + @Before + public void before() { + given(mockLeaderProperties.getLeaderIdPrefix()).willReturn(""); + given(mockLeaderProperties.getNamespace(NAMESPACE)).willReturn(NAMESPACE); + given(mockLeaderProperties.getConfigMapName()).willReturn(CONFIG_MAP_NAME); + + given(mockKubernetesClient.getNamespace()).willReturn(NAMESPACE); + given(mockKubernetesClient.configMaps()).willReturn(configMapsOperation); + given(configMapsOperation.inNamespace(NAMESPACE)).willReturn(inNamespaceOperation); + given(inNamespaceOperation.withName(CONFIG_MAP_NAME)).willReturn(configMapResource); + given(configMapResource.get()).willReturn(mockConfigMap); + given(mockConfigMap.getData()).willReturn(mockData); + given(mockData.get(ROLE)).willReturn(ID); + + leadershipController = new LeadershipController(mockLeaderProperties, mockKubernetesClient); + } + + @Test + public void shouldGetLeader() { + Leader leader = leadershipController.getLeader(ROLE); + assertThat(leader.getRole()).isEqualTo(ROLE); + assertThat(leader.getId()).isEqualTo(ID); + } + + @Test + public void shouldNotGetLeaderFromNonExistingConfigMap() { + given(mockLeaderProperties.getConfigMapName()).willReturn("unknown"); + + Leader leader = leadershipController.getLeader(ROLE); + assertThat(leader).isNull(); + } + + @Test + public void shouldNotGetLeaderFromEmptyConfigMap() { + given(mockConfigMap.getData()).willReturn(null); + + Leader leader = leadershipController.getLeader(ROLE); + assertThat(leader).isNull(); + } + + @Test + public void shouldNotGetLeaderFromInvalidConfigMap() { + given(mockData.get(ROLE)).willReturn(null); + + Leader leader = leadershipController.getLeader(ROLE); + assertThat(leader).isNull(); + } + + @Test + public void shouldHandleFailureWhenGettingLeader() { + given(mockKubernetesClient.configMaps()).willThrow(new RuntimeException("Test exception")); + + Leader leader = leadershipController.getLeader(ROLE); + assertThat(leader).isNull(); + } + +}