From a09cd708e52910836dd2e343572508faf7206360 Mon Sep 17 00:00:00 2001 From: Gytis Trikleris Date: Mon, 4 Jun 2018 13:05:09 +0200 Subject: [PATCH] KubernetesHelper --- .../kubernetes/leader/KubernetesHelper.java | 94 ++++++++++ .../leader/KubernetesHelperTest.java | 165 ++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/KubernetesHelper.java create mode 100644 spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/KubernetesHelperTest.java diff --git a/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/KubernetesHelper.java b/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/KubernetesHelper.java new file mode 100644 index 00000000..ec5f71ea --- /dev/null +++ b/spring-cloud-kubernetes-leader/src/main/java/org/springframework/cloud/kubernetes/leader/KubernetesHelper.java @@ -0,0 +1,94 @@ +/* + * 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.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.client.KubernetesClient; + +/** + * @author Gytis Trikleris + */ +public class KubernetesHelper { + + private static final String PROVIDER_KEY = "provider"; + + private static final String PROVIDER = "spring-cloud-kubernetes"; + + private static final String KIND_KEY = "kind"; + + public static final String KIND = "leaders"; + + private final LeaderProperties leaderProperties; + + private final KubernetesClient kubernetesClient; + + public KubernetesHelper(LeaderProperties leaderProperties, KubernetesClient kubernetesClient) { + this.leaderProperties = leaderProperties; + this.kubernetesClient = kubernetesClient; + } + + public boolean isPodAlive(String id) { + return kubernetesClient.pods() + .inNamespace(leaderProperties.getNamespace(kubernetesClient.getNamespace())) + .withLabels(leaderProperties.getLabels()) + .list() + .getItems() + .stream() + .map(Pod::getMetadata) + .map(ObjectMeta::getName) + .anyMatch(name -> name.equals(id)); + } + + public ConfigMap getConfigMap() { + return kubernetesClient.configMaps() + .inNamespace(leaderProperties.getNamespace(kubernetesClient.getNamespace())) + .withName(leaderProperties.getConfigMapName()) + .get(); + } + + public void createConfigMap(Map data) { + ConfigMap newConfigMap = new ConfigMapBuilder().withNewMetadata() + .withName(leaderProperties.getConfigMapName()) + .addToLabels(PROVIDER_KEY, PROVIDER) + .addToLabels(KIND_KEY, KIND) + .endMetadata() + .addToData(data) + .build(); + + kubernetesClient.configMaps() + .inNamespace(leaderProperties.getNamespace(kubernetesClient.getNamespace())) + .create(newConfigMap); + } + + public void updateConfigMap(ConfigMap configMap, Map newData) { + ConfigMap newConfigMap = new ConfigMapBuilder(configMap) + .addToData(newData) + .build(); + + kubernetesClient.configMaps() + .inNamespace(leaderProperties.getNamespace(kubernetesClient.getNamespace())) + .withName(leaderProperties.getConfigMapName()) + .lockResourceVersion(configMap.getMetadata().getResourceVersion()) + .replace(newConfigMap); + } + +} diff --git a/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/KubernetesHelperTest.java b/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/KubernetesHelperTest.java new file mode 100644 index 00000000..84678955 --- /dev/null +++ b/spring-cloud-kubernetes-leader/src/test/java/org/springframework/cloud/kubernetes/leader/KubernetesHelperTest.java @@ -0,0 +1,165 @@ +package org.springframework.cloud.kubernetes.leader; + +import java.util.Collections; +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.api.model.DoneablePod; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodList; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.Watch; +import io.fabric8.kubernetes.client.Watcher; +import io.fabric8.kubernetes.client.dsl.FilterWatchListDeletable; +import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.PodResource; +import io.fabric8.kubernetes.client.dsl.Replaceable; +import io.fabric8.kubernetes.client.dsl.Resource; +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 static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +/** + * @author Gytis Trikleris + */ +@RunWith(MockitoJUnitRunner.class) +public class KubernetesHelperTest { + + private static final String NAMESPACE = "test-namespace"; + + private static final String NAME = "test-name"; + + @Mock + private LeaderProperties mockLeaderProperties; + + @Mock + private KubernetesClient mockKubernetesClient; + + @Mock + private MixedOperation> podMixedOperation; + + @Mock + private MixedOperation> + configMapMixedOperation; + + @Mock + private NonNamespaceOperation> podNonNamespaceOperation; + + @Mock + private NonNamespaceOperation> + configMapNonNamespaceOperation; + + @Mock + private FilterWatchListDeletable> podWatchList; + + @Mock + private PodList mockPodList; + + @Mock + private Pod mockPod; + + @Mock + private ObjectMeta mockObjectMeta; + + @Mock + private Resource configMapResource; + + @Mock + private Replaceable configMapReplaceable; + + @Mock + private ConfigMap mockConfigMap; + + private KubernetesHelper kubernetesHelper; + + @Before + public void before() { + given(mockLeaderProperties.getConfigMapName()).willReturn(NAME); + given(mockLeaderProperties.getNamespace(NAMESPACE)).willReturn(NAMESPACE); + + given(mockKubernetesClient.getNamespace()).willReturn(NAMESPACE); + + kubernetesHelper = new KubernetesHelper(mockLeaderProperties, mockKubernetesClient); + } + + @Test + public void shouldCheckIfPodIsAlive() { + given(mockKubernetesClient.getNamespace()).willReturn(NAMESPACE); + given(mockKubernetesClient.pods()).willReturn(podMixedOperation); + given(podMixedOperation.inNamespace(NAMESPACE)).willReturn(podNonNamespaceOperation); + given(podNonNamespaceOperation.withLabels(anyMap())).willReturn(podWatchList); + given(podWatchList.list()).willReturn(mockPodList); + given(mockPodList.getItems()).willReturn(Collections.singletonList(mockPod)); + given(mockPod.getMetadata()).willReturn(mockObjectMeta); + given(mockObjectMeta.getName()).willReturn("test-id"); + + boolean result = kubernetesHelper.isPodAlive("test-id"); + + assertThat(result).isTrue(); + verify(mockObjectMeta).getName(); + } + + @Test + public void shouldGetConfigMap() { + given(mockKubernetesClient.configMaps()).willReturn(configMapMixedOperation); + given(configMapMixedOperation.inNamespace(NAMESPACE)).willReturn(configMapNonNamespaceOperation); + given(configMapNonNamespaceOperation.withName(NAME)).willReturn(configMapResource); + given(configMapResource.get()).willReturn(mockConfigMap); + + ConfigMap result = kubernetesHelper.getConfigMap(); + + assertThat(result).isEqualTo(mockConfigMap); + } + + @Test + public void shouldCreateConfigMap() { + given(mockKubernetesClient.configMaps()).willReturn(configMapMixedOperation); + given(configMapMixedOperation.inNamespace(NAMESPACE)).willReturn(configMapNonNamespaceOperation); + + Map data = Collections.singletonMap("test-key", "test-value"); + kubernetesHelper.createConfigMap(data); + + ArgumentCaptor configMapCaptor = ArgumentCaptor.forClass(ConfigMap.class); + verify(configMapNonNamespaceOperation).create(configMapCaptor.capture()); + + ConfigMap configMap = configMapCaptor.getValue(); + ObjectMeta metaData = configMap.getMetadata(); + assertThat(metaData.getName()).isEqualTo(NAME); + assertThat(metaData.getLabels()).containsEntry("provider", "spring-cloud-kubernetes"); + assertThat(metaData.getLabels()).containsEntry("kind", "leaders"); + assertThat(configMap.getData()).containsEntry("test-key", "test-value"); + } + + @Test + public void shouldUpdateConfigMap() { + given(mockConfigMap.getMetadata()).willReturn(mockObjectMeta); + given(mockObjectMeta.getResourceVersion()).willReturn("test-version"); + + given(mockKubernetesClient.configMaps()).willReturn(configMapMixedOperation); + given(configMapMixedOperation.inNamespace(NAMESPACE)).willReturn(configMapNonNamespaceOperation); + given(configMapNonNamespaceOperation.withName(NAME)).willReturn(configMapResource); + given(configMapResource.lockResourceVersion("test-version")).willReturn(configMapReplaceable); + + Map data = Collections.singletonMap("test-key", "test-value"); + kubernetesHelper.updateConfigMap(mockConfigMap, data); + + ArgumentCaptor configMapCaptor = ArgumentCaptor.forClass(ConfigMap.class); + verify(configMapReplaceable).replace(configMapCaptor.capture()); + + ConfigMap configMap = configMapCaptor.getValue(); + assertThat(configMap.getData()).containsEntry("test-key", "test-value"); + } + +}