From 561bc9d00c125def44b77322974cf21c413a5e95 Mon Sep 17 00:00:00 2001 From: Gytis Trikleris Date: Thu, 24 May 2018 17:33:23 +0200 Subject: [PATCH] ConfigMapLockRepository --- pom.xml | 1 + spring-cloud-kubernetes-lock/pom.xml | 74 +++++++++++ .../lock/ConfigMapLockRepository.java | 116 ++++++++++++++++++ .../lock/ConfigMapLockRepositoryIT.java | 87 +++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 spring-cloud-kubernetes-lock/pom.xml create mode 100644 spring-cloud-kubernetes-lock/src/main/java/org/springframework/cloud/kubernetes/lock/ConfigMapLockRepository.java create mode 100644 spring-cloud-kubernetes-lock/src/test/java/org/springframework/cloud/kubernetes/lock/ConfigMapLockRepositoryIT.java diff --git a/pom.xml b/pom.xml index a79e8f16..204a4cf2 100644 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,7 @@ spring-cloud-starter-kubernetes-zipkin spring-cloud-starter-kubernetes-all spring-cloud-kubernetes-examples + spring-cloud-kubernetes-lock diff --git a/spring-cloud-kubernetes-lock/pom.xml b/spring-cloud-kubernetes-lock/pom.xml new file mode 100644 index 00000000..29e30e81 --- /dev/null +++ b/spring-cloud-kubernetes-lock/pom.xml @@ -0,0 +1,74 @@ + + + + 4.0.0 + + org.springframework.cloud + spring-cloud-kubernetes + 0.3.0.BUILD-SNAPSHOT + + + spring-cloud-kubernetes-lock + Spring Cloud Kubernetes :: Lock + + + + org.springframework.cloud + spring-cloud-kubernetes-core + + + org.springframework.integration + spring-integration-core + + + org.jboss.arquillian.junit + arquillian-junit-standalone + test + + + org.arquillian.cube + arquillian-cube-kubernetes + test + + + org.arquillian.cube + arquillian-cube-requirement + test + + + junit + junit + test + + + org.assertj + assertj-core + test + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + + diff --git a/spring-cloud-kubernetes-lock/src/main/java/org/springframework/cloud/kubernetes/lock/ConfigMapLockRepository.java b/spring-cloud-kubernetes-lock/src/main/java/org/springframework/cloud/kubernetes/lock/ConfigMapLockRepository.java new file mode 100644 index 00000000..6717ab2c --- /dev/null +++ b/spring-cloud-kubernetes-lock/src/main/java/org/springframework/cloud/kubernetes/lock/ConfigMapLockRepository.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2018 to the original authors. + * + * 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.lock; + +import java.util.List; +import java.util.stream.Collectors; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConfigMapLockRepository { + + static final String CONFIG_MAP_PREFIX = "lock"; + + static final String HOLDER_KEY = "holder"; + + static final String EXPIRATION_KEY = "expiration"; + + private static final String PROVIDER_LABEL = "provider"; + + private static final String PROVIDER_LABEL_VALUE = "spring-cloud-kubernetes"; + + private static final String KIND_LABEL = "kind"; + + private static final String KIND_LABEL_VALUE = "lock"; + + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigMapLockRepository.class); + + private KubernetesClient kubernetesClient; + + private String namespace; + + public ConfigMapLockRepository(KubernetesClient kubernetesClient, String namespace) { + this.kubernetesClient = kubernetesClient; + this.namespace = namespace; + } + + public ConfigMap get(String name) { + return kubernetesClient.configMaps() + .inNamespace(namespace) + .withName(getConfigMapName(name)) + .get(); + } + + public boolean create(String name, String holder, long expiration) { + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName(getConfigMapName(name)) + .addToLabels(PROVIDER_LABEL, PROVIDER_LABEL_VALUE) + .addToLabels(KIND_LABEL, KIND_LABEL_VALUE) + .endMetadata() + .addToData(HOLDER_KEY, holder) + .addToData(EXPIRATION_KEY, String.valueOf(expiration)) + // TODO add information about the creator + .build(); + + try { + kubernetesClient.configMaps() + .inNamespace(namespace) + .create(configMap); + } catch (KubernetesClientException e) { + LOGGER.warn("Failed to create ConfigMap for name '{}': ", name, e.getMessage()); + return false; + } + + return true; + } + + public void deleteAll() { + kubernetesClient.configMaps() + .inNamespace(namespace) + .withLabel(PROVIDER_LABEL, PROVIDER_LABEL_VALUE) + .withLabel(KIND_LABEL, KIND_LABEL_VALUE) + .delete(); + } + + public void deleteExpired() { + long now = System.currentTimeMillis(); + // TODO check that it was created by this process + List configMaps = kubernetesClient.configMaps() + .inNamespace(namespace) + .withLabel(PROVIDER_LABEL, PROVIDER_LABEL_VALUE) + .withLabel(KIND_LABEL, KIND_LABEL_VALUE) + .list() + .getItems() + .stream() + .filter(c -> Long.valueOf(c.getData().get("expiration")) < now) + .collect(Collectors.toList()); + + kubernetesClient.configMaps() + .inNamespace(namespace) + .delete(configMaps); + } + + private String getConfigMapName(String name) { + return String.format("%s-%s", CONFIG_MAP_PREFIX, name); + } + +} diff --git a/spring-cloud-kubernetes-lock/src/test/java/org/springframework/cloud/kubernetes/lock/ConfigMapLockRepositoryIT.java b/spring-cloud-kubernetes-lock/src/test/java/org/springframework/cloud/kubernetes/lock/ConfigMapLockRepositoryIT.java new file mode 100644 index 00000000..8d7d9044 --- /dev/null +++ b/spring-cloud-kubernetes-lock/src/test/java/org/springframework/cloud/kubernetes/lock/ConfigMapLockRepositoryIT.java @@ -0,0 +1,87 @@ +package org.springframework.cloud.kubernetes.lock; + +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; +import org.arquillian.cube.kubernetes.api.Session; +import org.arquillian.cube.kubernetes.impl.requirement.RequiresKubernetes; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(Arquillian.class) +@RequiresKubernetes +public class ConfigMapLockRepositoryIT { + + @ArquillianResource + private KubernetesClient kubernetesClient; + + @ArquillianResource + private Session session; + + private ConfigMapLockRepository repository; + + @Before + public void before() { + repository = new ConfigMapLockRepository(kubernetesClient, session.getNamespace()); + } + + @After + public void after() { + deleteConfigMap("test-name"); + deleteConfigMap("test-name-2"); + } + + @Test + public void shouldCreateConfigMap() { + long expiration = System.currentTimeMillis(); + boolean result = repository.create("test-name", "test-holder", expiration); + assertThat(result).isTrue(); + + ConfigMap configMap = repository.get("test-name"); + Map data = configMap.getData(); + assertThat(data).containsEntry(ConfigMapLockRepository.HOLDER_KEY, "test-holder"); + assertThat(data).containsEntry(ConfigMapLockRepository.EXPIRATION_KEY, String.valueOf(expiration)); + } + + @Test + public void shouldNotOverwriteConfigMap() { + boolean firstResult = repository.create("test-name", "test-holder", 0); + assertThat(firstResult).isTrue(); + + boolean secondResult = repository.create("test-name", "test-holder", 0); + assertThat(secondResult).isFalse(); + } + + @Test + public void shouldDeleteAllConfigMaps() { + repository.create("test-name", "test-holder", 0); + repository.create("test-name-2", "test-holder-2", 0); + repository.deleteAll(); + assertThat(repository.get("test-name")).isNull(); + assertThat(repository.get("test-name-2")).isNull(); + } + + @Test + public void shouldDeleteExpiredConfigMaps() { + repository.create("test-name", "test-holder", System.currentTimeMillis() - 1); + repository.create("test-name-2", "test-holder-2", System.currentTimeMillis() + 10000); + repository.deleteExpired(); + assertThat(repository.get("test-name")).isNull(); + assertThat(repository.get("test-name-2")).isNotNull(); + } + + private void deleteConfigMap(String name) { + kubernetesClient.configMaps() + .inNamespace(session.getNamespace()) + .withName(String.format("%s-%s", ConfigMapLockRepository.CONFIG_MAP_PREFIX, name)) + .delete(); + } + +}