Move Code From Leader Module To spring-cloud-kubernetes-commons (#690)
* Deprecate KubernetesAutoServiceRegistration * Document service registry in kubernetes. Fixes #348 * Refactor leader election module to put common code in spring-cloud-kubernetes-commons
This commit is contained in:
@@ -30,6 +30,11 @@
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-loadbalancer</artifactId>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.leader;
|
||||
package org.springframework.cloud.kubernetes.commons.leader;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.leader;
|
||||
package org.springframework.cloud.kubernetes.commons.leader;
|
||||
|
||||
import org.springframework.integration.leader.Candidate;
|
||||
import org.springframework.integration.leader.Context;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.leader;
|
||||
package org.springframework.cloud.kubernetes.commons.leader;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.leader;
|
||||
package org.springframework.cloud.kubernetes.commons.leader;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.leader;
|
||||
package org.springframework.cloud.kubernetes.commons.leader;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.commons.leader;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public interface LeaderRecordWatcher {
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.commons.leader;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public abstract class LeadershipController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(LeadershipController.class);
|
||||
|
||||
protected static final String PROVIDER_KEY = "provider";
|
||||
|
||||
protected static final String PROVIDER = "spring-cloud-kubernetes";
|
||||
|
||||
protected static final String KIND_KEY = "kind";
|
||||
|
||||
protected static final String KIND = "leaders";
|
||||
|
||||
protected Candidate candidate;
|
||||
|
||||
protected Leader localLeader;
|
||||
|
||||
protected LeaderProperties leaderProperties;
|
||||
|
||||
protected LeaderEventPublisher leaderEventPublisher;
|
||||
|
||||
protected PodReadinessWatcher leaderReadinessWatcher;
|
||||
|
||||
public LeadershipController(Candidate candidate, LeaderProperties leaderProperties,
|
||||
LeaderEventPublisher leaderEventPublisher) {
|
||||
this.candidate = candidate;
|
||||
this.leaderProperties = leaderProperties;
|
||||
this.leaderEventPublisher = leaderEventPublisher;
|
||||
}
|
||||
|
||||
public Optional<Leader> getLocalLeader() {
|
||||
return Optional.ofNullable(this.localLeader);
|
||||
}
|
||||
|
||||
public abstract void update();
|
||||
|
||||
public abstract void revoke();
|
||||
|
||||
protected String getLeaderKey() {
|
||||
return this.leaderProperties.getLeaderIdPrefix() + this.candidate.getRole();
|
||||
}
|
||||
|
||||
protected Map<String, String> getLeaderData(Candidate candidate) {
|
||||
String leaderKey = getLeaderKey();
|
||||
return Collections.singletonMap(leaderKey, candidate.getId());
|
||||
}
|
||||
|
||||
protected Leader extractLeader(Map<String, String> data) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String leaderKey = getLeaderKey();
|
||||
String leaderId = data.get(leaderKey);
|
||||
if (leaderId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Leader(this.candidate.getRole(), leaderId);
|
||||
}
|
||||
|
||||
protected void handleLeaderChange(Leader newLeader) {
|
||||
if (Objects.equals(this.localLeader, newLeader)) {
|
||||
LOGGER.debug("Leader is still '{}'", this.localLeader);
|
||||
return;
|
||||
}
|
||||
|
||||
Leader oldLeader = this.localLeader;
|
||||
this.localLeader = newLeader;
|
||||
|
||||
if (oldLeader != null && oldLeader.isCandidate(this.candidate)) {
|
||||
notifyOnRevoked();
|
||||
}
|
||||
else if (newLeader != null && newLeader.isCandidate(this.candidate)) {
|
||||
notifyOnGranted();
|
||||
}
|
||||
|
||||
restartLeaderReadinessWatcher();
|
||||
|
||||
LOGGER.debug("New leader is '{}'", this.localLeader);
|
||||
}
|
||||
|
||||
protected void notifyOnGranted() {
|
||||
LOGGER.debug("Leadership has been granted for '{}'", this.candidate);
|
||||
|
||||
Context context = new LeaderContext(this.candidate, this);
|
||||
this.leaderEventPublisher.publishOnGranted(this, context, this.candidate.getRole());
|
||||
try {
|
||||
this.candidate.onGranted(context);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
LOGGER.warn(e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyOnRevoked() {
|
||||
LOGGER.debug("Leadership has been revoked for '{}'", this.candidate);
|
||||
|
||||
Context context = new LeaderContext(this.candidate, this);
|
||||
this.leaderEventPublisher.publishOnRevoked(this, context, this.candidate.getRole());
|
||||
this.candidate.onRevoked(context);
|
||||
}
|
||||
|
||||
protected void notifyOnFailedToAcquire() {
|
||||
if (this.leaderProperties.isPublishFailedEvents()) {
|
||||
Context context = new LeaderContext(this.candidate, this);
|
||||
this.leaderEventPublisher.publishOnFailedToAcquire(this, context, this.candidate.getRole());
|
||||
}
|
||||
}
|
||||
|
||||
protected void restartLeaderReadinessWatcher() {
|
||||
if (this.leaderReadinessWatcher != null) {
|
||||
this.leaderReadinessWatcher.stop();
|
||||
this.leaderReadinessWatcher = null;
|
||||
}
|
||||
|
||||
if (this.localLeader != null && !this.localLeader.isCandidate(this.candidate)) {
|
||||
this.leaderReadinessWatcher = createPodReadinessWatcher(this.localLeader.getId());
|
||||
this.leaderReadinessWatcher.start();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract PodReadinessWatcher createPodReadinessWatcher(String localLeaderId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.commons.leader;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public interface PodReadinessWatcher {
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
}
|
||||
@@ -27,6 +27,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderInfoContributor;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderInitiator;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderProperties;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -42,7 +45,7 @@ import org.springframework.integration.leader.event.LeaderEventPublisher;
|
||||
@EnableConfigurationProperties(LeaderProperties.class)
|
||||
@ConditionalOnBean(KubernetesClient.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.leader.enabled", matchIfMissing = true)
|
||||
public class LeaderAutoConfiguration {
|
||||
public class Fabric8LeaderAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LeaderEventPublisher.class)
|
||||
@@ -59,33 +62,36 @@ public class LeaderAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LeadershipController leadershipController(Candidate candidate, LeaderProperties leaderProperties,
|
||||
public Fabric8LeadershipController leadershipController(Candidate candidate, LeaderProperties leaderProperties,
|
||||
LeaderEventPublisher leaderEventPublisher, KubernetesClient kubernetesClient) {
|
||||
return new LeadershipController(candidate, leaderProperties, leaderEventPublisher, kubernetesClient);
|
||||
return new Fabric8LeadershipController(candidate, leaderProperties, leaderEventPublisher, kubernetesClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LeaderRecordWatcher leaderRecordWatcher(LeaderProperties leaderProperties,
|
||||
LeadershipController leadershipController, KubernetesClient kubernetesClient) {
|
||||
return new LeaderRecordWatcher(leaderProperties, leadershipController, kubernetesClient);
|
||||
public Fabric8LeaderRecordWatcher leaderRecordWatcher(LeaderProperties leaderProperties,
|
||||
Fabric8LeadershipController fabric8LeadershipController, KubernetesClient kubernetesClient) {
|
||||
return new Fabric8LeaderRecordWatcher(leaderProperties, fabric8LeadershipController, kubernetesClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PodReadinessWatcher hostPodWatcher(Candidate candidate, KubernetesClient kubernetesClient,
|
||||
LeadershipController leadershipController) {
|
||||
return new PodReadinessWatcher(candidate.getId(), kubernetesClient, leadershipController);
|
||||
public Fabric8PodReadinessWatcher hostPodWatcher(Candidate candidate, KubernetesClient kubernetesClient,
|
||||
Fabric8LeadershipController fabric8LeadershipController) {
|
||||
return new Fabric8PodReadinessWatcher(candidate.getId(), kubernetesClient, fabric8LeadershipController);
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "stop")
|
||||
public LeaderInitiator leaderInitiator(LeaderProperties leaderProperties, LeadershipController leadershipController,
|
||||
LeaderRecordWatcher leaderRecordWatcher, PodReadinessWatcher hostPodWatcher) {
|
||||
return new LeaderInitiator(leaderProperties, leadershipController, leaderRecordWatcher, hostPodWatcher);
|
||||
public LeaderInitiator leaderInitiator(LeaderProperties leaderProperties,
|
||||
Fabric8LeadershipController fabric8LeadershipController,
|
||||
Fabric8LeaderRecordWatcher fabric8LeaderRecordWatcher, Fabric8PodReadinessWatcher hostPodWatcher) {
|
||||
return new LeaderInitiator(leaderProperties, fabric8LeadershipController, fabric8LeaderRecordWatcher,
|
||||
hostPodWatcher);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(InfoContributor.class)
|
||||
public LeaderInfoContributor leaderInfoContributor(LeadershipController leadershipController, Candidate candidate) {
|
||||
return new LeaderInfoContributor(leadershipController, candidate);
|
||||
public LeaderInfoContributor leaderInfoContributor(Fabric8LeadershipController fabric8LeadershipController,
|
||||
Candidate candidate) {
|
||||
return new LeaderInfoContributor(fabric8LeadershipController, candidate);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,16 +24,19 @@ import io.fabric8.kubernetes.client.Watcher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderProperties;
|
||||
|
||||
/**
|
||||
* @author Gytis Trikleris
|
||||
*/
|
||||
public class LeaderRecordWatcher implements Watcher<ConfigMap> {
|
||||
public class Fabric8LeaderRecordWatcher
|
||||
implements org.springframework.cloud.kubernetes.commons.leader.LeaderRecordWatcher, Watcher<ConfigMap> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(LeaderRecordWatcher.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Fabric8LeaderRecordWatcher.class);
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
private final LeadershipController leadershipController;
|
||||
private final Fabric8LeadershipController fabric8LeadershipController;
|
||||
|
||||
private final LeaderProperties leaderProperties;
|
||||
|
||||
@@ -41,9 +44,9 @@ public class LeaderRecordWatcher implements Watcher<ConfigMap> {
|
||||
|
||||
private Watch watch;
|
||||
|
||||
public LeaderRecordWatcher(LeaderProperties leaderProperties, LeadershipController leadershipController,
|
||||
KubernetesClient kubernetesClient) {
|
||||
this.leadershipController = leadershipController;
|
||||
public Fabric8LeaderRecordWatcher(LeaderProperties leaderProperties,
|
||||
Fabric8LeadershipController fabric8LeadershipController, KubernetesClient kubernetesClient) {
|
||||
this.fabric8LeadershipController = fabric8LeadershipController;
|
||||
this.leaderProperties = leaderProperties;
|
||||
this.kubernetesClient = kubernetesClient;
|
||||
}
|
||||
@@ -78,7 +81,7 @@ public class LeaderRecordWatcher implements Watcher<ConfigMap> {
|
||||
LOGGER.debug("'{}' event received, triggering leadership update", action);
|
||||
|
||||
if (!Action.ERROR.equals(action)) {
|
||||
this.leadershipController.update();
|
||||
this.fabric8LeadershipController.update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.leader;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
|
||||
@@ -28,49 +25,29 @@ import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.leader.Leader;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeadershipController;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.PodReadinessWatcher;
|
||||
import org.springframework.integration.leader.Candidate;
|
||||
import org.springframework.integration.leader.Context;
|
||||
import org.springframework.integration.leader.event.LeaderEventPublisher;
|
||||
|
||||
/**
|
||||
* @author Gytis Trikleris
|
||||
*/
|
||||
public class LeadershipController {
|
||||
public class Fabric8LeadershipController extends LeadershipController {
|
||||
|
||||
private static final String PROVIDER_KEY = "provider";
|
||||
|
||||
private static final String PROVIDER = "spring-cloud-kubernetes";
|
||||
|
||||
private static final String KIND_KEY = "kind";
|
||||
|
||||
private static final String KIND = "leaders";
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(LeadershipController.class);
|
||||
|
||||
private final Candidate candidate;
|
||||
|
||||
private final LeaderProperties leaderProperties;
|
||||
|
||||
private final LeaderEventPublisher leaderEventPublisher;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Fabric8LeadershipController.class);
|
||||
|
||||
private final KubernetesClient kubernetesClient;
|
||||
|
||||
private Leader localLeader;
|
||||
|
||||
private PodReadinessWatcher leaderReadinessWatcher;
|
||||
|
||||
public LeadershipController(Candidate candidate, LeaderProperties leaderProperties,
|
||||
public Fabric8LeadershipController(Candidate candidate, LeaderProperties leaderProperties,
|
||||
LeaderEventPublisher leaderEventPublisher, KubernetesClient kubernetesClient) {
|
||||
this.candidate = candidate;
|
||||
this.leaderProperties = leaderProperties;
|
||||
this.leaderEventPublisher = leaderEventPublisher;
|
||||
super(candidate, leaderProperties, leaderEventPublisher);
|
||||
this.kubernetesClient = kubernetesClient;
|
||||
}
|
||||
|
||||
public Optional<Leader> getLocalLeader() {
|
||||
return Optional.ofNullable(this.localLeader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void update() {
|
||||
LOGGER.debug("Checking leader state");
|
||||
ConfigMap configMap = getConfigMap();
|
||||
@@ -137,91 +114,17 @@ public class LeadershipController {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLeaderChange(Leader newLeader) {
|
||||
if (Objects.equals(this.localLeader, newLeader)) {
|
||||
LOGGER.debug("Leader is still '{}'", this.localLeader);
|
||||
return;
|
||||
}
|
||||
|
||||
Leader oldLeader = this.localLeader;
|
||||
this.localLeader = newLeader;
|
||||
|
||||
if (oldLeader != null && oldLeader.isCandidate(this.candidate)) {
|
||||
notifyOnRevoked();
|
||||
}
|
||||
else if (newLeader != null && newLeader.isCandidate(this.candidate)) {
|
||||
notifyOnGranted();
|
||||
}
|
||||
|
||||
restartLeaderReadinessWatcher();
|
||||
|
||||
LOGGER.debug("New leader is '{}'", this.localLeader);
|
||||
}
|
||||
|
||||
private void notifyOnGranted() {
|
||||
LOGGER.debug("Leadership has been granted for '{}'", this.candidate);
|
||||
|
||||
Context context = new LeaderContext(this.candidate, this);
|
||||
this.leaderEventPublisher.publishOnGranted(this, context, this.candidate.getRole());
|
||||
try {
|
||||
this.candidate.onGranted(context);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
LOGGER.warn(e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyOnRevoked() {
|
||||
LOGGER.debug("Leadership has been revoked for '{}'", this.candidate);
|
||||
|
||||
Context context = new LeaderContext(this.candidate, this);
|
||||
this.leaderEventPublisher.publishOnRevoked(this, context, this.candidate.getRole());
|
||||
this.candidate.onRevoked(context);
|
||||
}
|
||||
|
||||
private void notifyOnFailedToAcquire() {
|
||||
if (this.leaderProperties.isPublishFailedEvents()) {
|
||||
Context context = new LeaderContext(this.candidate, this);
|
||||
this.leaderEventPublisher.publishOnFailedToAcquire(this, context, this.candidate.getRole());
|
||||
}
|
||||
}
|
||||
|
||||
private void restartLeaderReadinessWatcher() {
|
||||
if (this.leaderReadinessWatcher != null) {
|
||||
this.leaderReadinessWatcher.stop();
|
||||
this.leaderReadinessWatcher = null;
|
||||
}
|
||||
|
||||
if (this.localLeader != null && !this.localLeader.isCandidate(this.candidate)) {
|
||||
this.leaderReadinessWatcher = new PodReadinessWatcher(this.localLeader.getId(), this.kubernetesClient,
|
||||
this);
|
||||
this.leaderReadinessWatcher.start();
|
||||
}
|
||||
}
|
||||
|
||||
private String getLeaderKey() {
|
||||
return this.leaderProperties.getLeaderIdPrefix() + this.candidate.getRole();
|
||||
}
|
||||
|
||||
private Map<String, String> getLeaderData(Candidate candidate) {
|
||||
String leaderKey = getLeaderKey();
|
||||
return Collections.singletonMap(leaderKey, candidate.getId());
|
||||
@Override
|
||||
protected PodReadinessWatcher createPodReadinessWatcher(String localLeaderId) {
|
||||
return new Fabric8PodReadinessWatcher(localLeaderId, this.kubernetesClient, this);
|
||||
}
|
||||
|
||||
private Leader extractLeader(ConfigMap configMap) {
|
||||
if (configMap == null || configMap.getData() == null) {
|
||||
if (configMap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, String> data = configMap.getData();
|
||||
String leaderKey = getLeaderKey();
|
||||
String leaderId = data.get(leaderKey);
|
||||
if (leaderId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Leader(this.candidate.getRole(), leaderId);
|
||||
return extractLeader(configMap.getData());
|
||||
}
|
||||
|
||||
private boolean isPodReady(String name) {
|
||||
@@ -27,12 +27,14 @@ import io.fabric8.kubernetes.client.internal.readiness.Readiness;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.leader.PodReadinessWatcher;
|
||||
|
||||
/**
|
||||
* @author Gytis Trikleris
|
||||
*/
|
||||
public class PodReadinessWatcher implements Watcher<Pod> {
|
||||
public class Fabric8PodReadinessWatcher implements PodReadinessWatcher, Watcher<Pod> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PodReadinessWatcher.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Fabric8PodReadinessWatcher.class);
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
@@ -40,19 +42,20 @@ public class PodReadinessWatcher implements Watcher<Pod> {
|
||||
|
||||
private final KubernetesClient kubernetesClient;
|
||||
|
||||
private final LeadershipController leadershipController;
|
||||
private final Fabric8LeadershipController fabric8LeadershipController;
|
||||
|
||||
private boolean previousState;
|
||||
|
||||
private Watch watch;
|
||||
|
||||
public PodReadinessWatcher(String podName, KubernetesClient kubernetesClient,
|
||||
LeadershipController leadershipController) {
|
||||
public Fabric8PodReadinessWatcher(String podName, KubernetesClient kubernetesClient,
|
||||
Fabric8LeadershipController fabric8LeadershipController) {
|
||||
this.podName = podName;
|
||||
this.kubernetesClient = kubernetesClient;
|
||||
this.leadershipController = leadershipController;
|
||||
this.fabric8LeadershipController = fabric8LeadershipController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (this.watch == null) {
|
||||
synchronized (this.lock) {
|
||||
@@ -66,6 +69,7 @@ public class PodReadinessWatcher implements Watcher<Pod> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (this.watch != null) {
|
||||
synchronized (this.lock) {
|
||||
@@ -87,7 +91,7 @@ public class PodReadinessWatcher implements Watcher<Pod> {
|
||||
LOGGER.debug("'{}' readiness status changed to '{}', triggering leadership update", this.podName,
|
||||
currentState);
|
||||
this.previousState = currentState;
|
||||
this.leadershipController.update();
|
||||
this.fabric8LeadershipController.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.kubernetes.fabric8.leader.LeaderAutoConfiguration
|
||||
org.springframework.cloud.kubernetes.fabric8.leader.Fabric8LeaderAutoConfiguration
|
||||
|
||||
@@ -34,7 +34,7 @@ import static org.hamcrest.Matchers.containsString;
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
|
||||
// Make sure test passes without Kubernetes cluster
|
||||
"spring.cloud.kubernetes.leader.autoStartup=false" })
|
||||
public class LeaderAutoConfigurationTests {
|
||||
public class Fabric8LeaderAutoConfigurationTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port;
|
||||
@@ -32,6 +32,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderProperties;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -40,13 +42,13 @@ import static org.mockito.Mockito.verify;
|
||||
* @author Gytis Trikleris
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class LeaderRecordWatcherTest {
|
||||
public class Fabric8LeaderRecordWatcherTest {
|
||||
|
||||
@Mock
|
||||
private LeaderProperties mockLeaderProperties;
|
||||
|
||||
@Mock
|
||||
private LeadershipController mockLeadershipController;
|
||||
private Fabric8LeadershipController mockFabric8LeadershipController;
|
||||
|
||||
@Mock
|
||||
private KubernetesClient mockKubernetesClient;
|
||||
@@ -69,11 +71,11 @@ public class LeaderRecordWatcherTest {
|
||||
@Mock
|
||||
private KubernetesClientException mockKubernetesClientException;
|
||||
|
||||
private LeaderRecordWatcher watcher;
|
||||
private Fabric8LeaderRecordWatcher watcher;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
this.watcher = new LeaderRecordWatcher(this.mockLeaderProperties, this.mockLeadershipController,
|
||||
this.watcher = new Fabric8LeaderRecordWatcher(this.mockLeaderProperties, this.mockFabric8LeadershipController,
|
||||
this.mockKubernetesClient);
|
||||
}
|
||||
|
||||
@@ -102,14 +104,14 @@ public class LeaderRecordWatcherTest {
|
||||
this.watcher.eventReceived(Watcher.Action.DELETED, this.mockConfigMap);
|
||||
this.watcher.eventReceived(Watcher.Action.MODIFIED, this.mockConfigMap);
|
||||
|
||||
verify(this.mockLeadershipController, times(3)).update();
|
||||
verify(this.mockFabric8LeadershipController, times(3)).update();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldIgnoreErrorEvent() {
|
||||
this.watcher.eventReceived(Watcher.Action.ERROR, this.mockConfigMap);
|
||||
|
||||
verify(this.mockLeadershipController, times(0)).update();
|
||||
verify(this.mockFabric8LeadershipController, times(0)).update();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -23,6 +23,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderProperties;
|
||||
import org.springframework.integration.leader.Candidate;
|
||||
import org.springframework.integration.leader.event.LeaderEventPublisher;
|
||||
|
||||
@@ -32,7 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Gytis Trikleris
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class LeadershipControllerTest {
|
||||
public class Fabric8LeadershipControllerTest {
|
||||
|
||||
@Mock
|
||||
private Candidate mockCandidate;
|
||||
@@ -46,17 +47,17 @@ public class LeadershipControllerTest {
|
||||
@Mock
|
||||
private KubernetesClient mockKubernetesClient;
|
||||
|
||||
private LeadershipController leadershipController;
|
||||
private Fabric8LeadershipController fabric8LeadershipController;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
this.leadershipController = new LeadershipController(this.mockCandidate, this.mockLeaderProperties,
|
||||
this.mockLeaderEventPublisher, this.mockKubernetesClient);
|
||||
this.fabric8LeadershipController = new Fabric8LeadershipController(this.mockCandidate,
|
||||
this.mockLeaderProperties, this.mockLeaderEventPublisher, this.mockKubernetesClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetEmptyLocalLeader() {
|
||||
assertThat(this.leadershipController.getLocalLeader().isPresent()).isFalse();
|
||||
assertThat(this.fabric8LeadershipController.getLocalLeader().isPresent()).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,12 +40,12 @@ import static org.mockito.Mockito.verify;
|
||||
* @author Gytis Trikleris
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class PodReadinessWatcherTest {
|
||||
public class Fabric8PodReadinessWatcherTest {
|
||||
|
||||
private static final String POD_NAME = "test-pod";
|
||||
|
||||
@Mock
|
||||
private LeadershipController mockLeadershipController;
|
||||
private Fabric8LeadershipController mockFabric8LeadershipController;
|
||||
|
||||
@Mock
|
||||
private KubernetesClient mockKubernetesClient;
|
||||
@@ -68,11 +68,12 @@ public class PodReadinessWatcherTest {
|
||||
@Mock
|
||||
private KubernetesClientException mockKubernetesClientException;
|
||||
|
||||
private PodReadinessWatcher watcher;
|
||||
private Fabric8PodReadinessWatcher watcher;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
this.watcher = new PodReadinessWatcher(POD_NAME, this.mockKubernetesClient, this.mockLeadershipController);
|
||||
this.watcher = new Fabric8PodReadinessWatcher(POD_NAME, this.mockKubernetesClient,
|
||||
this.mockFabric8LeadershipController);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,7 +104,7 @@ public class PodReadinessWatcherTest {
|
||||
this.watcher.start();
|
||||
this.watcher.eventReceived(Watcher.Action.ADDED, this.mockPod);
|
||||
|
||||
verify(this.mockLeadershipController).update();
|
||||
verify(this.mockFabric8LeadershipController).update();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,7 +115,7 @@ public class PodReadinessWatcherTest {
|
||||
this.watcher.start();
|
||||
this.watcher.eventReceived(Watcher.Action.ADDED, this.mockPod);
|
||||
|
||||
verify(this.mockLeadershipController, times(0)).update();
|
||||
verify(this.mockFabric8LeadershipController, times(0)).update();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -24,6 +24,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.leader.Leader;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderContext;
|
||||
import org.springframework.integration.leader.Candidate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -40,7 +42,7 @@ public class LeaderContextTest {
|
||||
private Candidate mockCandidate;
|
||||
|
||||
@Mock
|
||||
private LeadershipController mockLeadershipController;
|
||||
private Fabric8LeadershipController mockFabric8LeadershipController;
|
||||
|
||||
@Mock
|
||||
private Leader mockLeader;
|
||||
@@ -49,12 +51,12 @@ public class LeaderContextTest {
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
this.leaderContext = new LeaderContext(this.mockCandidate, this.mockLeadershipController);
|
||||
this.leaderContext = new LeaderContext(this.mockCandidate, this.mockFabric8LeadershipController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsLeaderWithoutLeader() {
|
||||
given(this.mockLeadershipController.getLocalLeader()).willReturn(Optional.empty());
|
||||
given(this.mockFabric8LeadershipController.getLocalLeader()).willReturn(Optional.empty());
|
||||
|
||||
boolean result = this.leaderContext.isLeader();
|
||||
|
||||
@@ -63,7 +65,7 @@ public class LeaderContextTest {
|
||||
|
||||
@Test
|
||||
public void testIsLeaderWithAnotherLeader() {
|
||||
given(this.mockLeadershipController.getLocalLeader()).willReturn(Optional.of(this.mockLeader));
|
||||
given(this.mockFabric8LeadershipController.getLocalLeader()).willReturn(Optional.of(this.mockLeader));
|
||||
|
||||
boolean result = this.leaderContext.isLeader();
|
||||
|
||||
@@ -72,7 +74,7 @@ public class LeaderContextTest {
|
||||
|
||||
@Test
|
||||
public void testIsLeaderWhenLeader() {
|
||||
given(this.mockLeadershipController.getLocalLeader()).willReturn(Optional.of(this.mockLeader));
|
||||
given(this.mockFabric8LeadershipController.getLocalLeader()).willReturn(Optional.of(this.mockLeader));
|
||||
given(this.mockLeader.isCandidate(this.mockCandidate)).willReturn(true);
|
||||
|
||||
boolean result = this.leaderContext.isLeader();
|
||||
@@ -84,7 +86,7 @@ public class LeaderContextTest {
|
||||
public void shouldYieldLeadership() {
|
||||
this.leaderContext.yield();
|
||||
|
||||
verify(this.mockLeadershipController).revoke();
|
||||
verify(this.mockFabric8LeadershipController).revoke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.boot.actuate.info.Info;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.Leader;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderInfoContributor;
|
||||
import org.springframework.integration.leader.Candidate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -38,7 +40,7 @@ public class LeaderInfoContributorTest {
|
||||
private Candidate mockCandidate;
|
||||
|
||||
@Mock
|
||||
private LeadershipController mockLeadershipController;
|
||||
private Fabric8LeadershipController mockFabric8LeadershipController;
|
||||
|
||||
@Mock
|
||||
private Leader mockLeader;
|
||||
@@ -47,7 +49,8 @@ public class LeaderInfoContributorTest {
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
this.leaderInfoContributor = new LeaderInfoContributor(this.mockLeadershipController, this.mockCandidate);
|
||||
this.leaderInfoContributor = new LeaderInfoContributor(this.mockFabric8LeadershipController,
|
||||
this.mockCandidate);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -64,7 +67,7 @@ public class LeaderInfoContributorTest {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void infoWhenLeader() {
|
||||
given(this.mockLeadershipController.getLocalLeader()).willReturn(Optional.of(this.mockLeader));
|
||||
given(this.mockFabric8LeadershipController.getLocalLeader()).willReturn(Optional.of(this.mockLeader));
|
||||
given(this.mockLeader.isCandidate(this.mockCandidate)).willReturn(true);
|
||||
given(this.mockLeader.getRole()).willReturn("testRole");
|
||||
given(this.mockLeader.getId()).willReturn("id");
|
||||
@@ -81,7 +84,7 @@ public class LeaderInfoContributorTest {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void infoWhenAnotherIsLeader() {
|
||||
given(this.mockLeadershipController.getLocalLeader()).willReturn(Optional.of(this.mockLeader));
|
||||
given(this.mockFabric8LeadershipController.getLocalLeader()).willReturn(Optional.of(this.mockLeader));
|
||||
given(this.mockLeader.getRole()).willReturn("testRole");
|
||||
given(this.mockLeader.getId()).willReturn("id");
|
||||
Info.Builder builder = new Info.Builder();
|
||||
|
||||
@@ -25,6 +25,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderInitiator;
|
||||
import org.springframework.cloud.kubernetes.commons.leader.LeaderProperties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -40,13 +43,13 @@ public class LeaderInitiatorTest {
|
||||
private LeaderProperties mockLeaderProperties;
|
||||
|
||||
@Mock
|
||||
private LeadershipController mockLeadershipController;
|
||||
private Fabric8LeadershipController mockFabric8LeadershipController;
|
||||
|
||||
@Mock
|
||||
private LeaderRecordWatcher mockLeaderRecordWatcher;
|
||||
private Fabric8LeaderRecordWatcher mockFabric8LeaderRecordWatcher;
|
||||
|
||||
@Mock
|
||||
private PodReadinessWatcher mockPodReadinessWatcher;
|
||||
private Fabric8PodReadinessWatcher mockFabric8PodReadinessWatcher;
|
||||
|
||||
@Mock
|
||||
private Runnable mockRunnable;
|
||||
@@ -55,8 +58,8 @@ public class LeaderInitiatorTest {
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
this.leaderInitiator = new LeaderInitiator(this.mockLeaderProperties, this.mockLeadershipController,
|
||||
this.mockLeaderRecordWatcher, this.mockPodReadinessWatcher);
|
||||
this.leaderInitiator = new LeaderInitiator(this.mockLeaderProperties, this.mockFabric8LeadershipController,
|
||||
this.mockFabric8LeaderRecordWatcher, this.mockFabric8PodReadinessWatcher);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -78,10 +81,10 @@ public class LeaderInitiatorTest {
|
||||
this.leaderInitiator.start();
|
||||
|
||||
assertThat(this.leaderInitiator.isRunning()).isTrue();
|
||||
verify(this.mockLeaderRecordWatcher).start();
|
||||
verify(this.mockPodReadinessWatcher).start();
|
||||
verify(this.mockFabric8LeaderRecordWatcher).start();
|
||||
verify(this.mockFabric8PodReadinessWatcher).start();
|
||||
Thread.sleep(10);
|
||||
verify(this.mockLeadershipController, atLeastOnce()).update();
|
||||
verify(this.mockFabric8LeadershipController, atLeastOnce()).update();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,7 +94,7 @@ public class LeaderInitiatorTest {
|
||||
this.leaderInitiator.start();
|
||||
this.leaderInitiator.start();
|
||||
|
||||
verify(this.mockLeaderRecordWatcher).start();
|
||||
verify(this.mockFabric8LeaderRecordWatcher).start();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -102,9 +105,9 @@ public class LeaderInitiatorTest {
|
||||
this.leaderInitiator.stop();
|
||||
|
||||
assertThat(this.leaderInitiator.isRunning()).isFalse();
|
||||
verify(this.mockLeaderRecordWatcher).stop();
|
||||
verify(this.mockPodReadinessWatcher).start();
|
||||
verify(this.mockLeadershipController).revoke();
|
||||
verify(this.mockFabric8LeaderRecordWatcher).stop();
|
||||
verify(this.mockFabric8PodReadinessWatcher).start();
|
||||
verify(this.mockFabric8LeadershipController).revoke();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -115,7 +118,7 @@ public class LeaderInitiatorTest {
|
||||
this.leaderInitiator.stop();
|
||||
this.leaderInitiator.stop();
|
||||
|
||||
verify(this.mockLeaderRecordWatcher).stop();
|
||||
verify(this.mockFabric8LeaderRecordWatcher).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,9 +129,9 @@ public class LeaderInitiatorTest {
|
||||
this.leaderInitiator.stop(this.mockRunnable);
|
||||
|
||||
assertThat(this.leaderInitiator.isRunning()).isFalse();
|
||||
verify(this.mockLeaderRecordWatcher).stop();
|
||||
verify(this.mockPodReadinessWatcher).start();
|
||||
verify(this.mockLeadershipController).revoke();
|
||||
verify(this.mockFabric8LeaderRecordWatcher).stop();
|
||||
verify(this.mockFabric8PodReadinessWatcher).start();
|
||||
verify(this.mockFabric8LeadershipController).revoke();
|
||||
verify(this.mockRunnable).run();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.leader.Leader;
|
||||
import org.springframework.integration.leader.Candidate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
Reference in New Issue
Block a user