LeaderAutoConfiguration and LeaderProperties
This commit is contained in:
committed by
Ioannis Canellos
parent
bea2f06b60
commit
9a0f25e841
@@ -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 java.net.Inet4Address;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.leader.Candidate;
|
||||
import org.springframework.integration.leader.DefaultCandidate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:gytis@redhat.com">Gytis Trikleris</a>
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(LeaderProperties.class)
|
||||
@ConditionalOnBean(KubernetesClient.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.leader.enabled", matchIfMissing = true)
|
||||
public class LeaderAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public LeaderInitiator leaderInitiator(KubernetesClient kubernetesClient, LeaderProperties leaderProperties)
|
||||
throws UnknownHostException {
|
||||
String candidateId = Inet4Address.getLocalHost().getHostName();
|
||||
Candidate candidate = new DefaultCandidate(candidateId, leaderProperties.getRole());
|
||||
LeaderInitiator leaderInitiator = new LeaderInitiator(kubernetesClient, candidate, leaderProperties);
|
||||
leaderInitiator.start();
|
||||
return leaderInitiator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,22 +42,21 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
|
||||
private final Candidate candidate;
|
||||
|
||||
private final LeaderConfiguration leaderConfiguration;
|
||||
private final LeaderProperties leaderProperties;
|
||||
|
||||
private ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
private String currentLeaderId; // TODO remove once events are implemented
|
||||
|
||||
public LeaderInitiator(KubernetesClient kubernetesClient, Candidate candidate,
|
||||
LeaderConfiguration leaderConfiguration) {
|
||||
public LeaderInitiator(KubernetesClient kubernetesClient, Candidate candidate, LeaderProperties leaderProperties) {
|
||||
this.kubernetesClient = kubernetesClient;
|
||||
this.candidate = candidate;
|
||||
this.leaderConfiguration = leaderConfiguration;
|
||||
this.leaderProperties = leaderProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return leaderConfiguration.isAutoStartup();
|
||||
return leaderProperties.isAutoStartup();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,40 +114,40 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
try {
|
||||
takeLeadership(configMap);
|
||||
currentLeaderId = candidate.getId();
|
||||
scheduleUpdate(leaderConfiguration.getLeaseDuration());
|
||||
scheduleUpdate(leaderProperties.getLeaseDuration());
|
||||
} catch (Exception e) {
|
||||
// Leadership takeover failed, try again later
|
||||
System.out.println("Leadership takeover failed: " + e.getMessage());
|
||||
scheduleUpdate(leaderConfiguration.getRetryPeriod());
|
||||
scheduleUpdate(leaderProperties.getRetryPeriod());
|
||||
}
|
||||
} else if (!isValidLeader(leader)) {
|
||||
System.out.println("Old leader is not valid any more, try to take over");
|
||||
try {
|
||||
takeLeadership(configMap);
|
||||
currentLeaderId = candidate.getId();
|
||||
scheduleUpdate(leaderConfiguration.getLeaseDuration()); // Is this needed?
|
||||
scheduleUpdate(leaderProperties.getLeaseDuration()); // Is this needed?
|
||||
} catch (Exception e) {
|
||||
// Leadership takeover failed, try again later
|
||||
System.out.println("Leadership takeover failed: " + e.getMessage());
|
||||
scheduleUpdate(leaderConfiguration.getRetryPeriod());
|
||||
scheduleUpdate(leaderProperties.getRetryPeriod());
|
||||
}
|
||||
} else if (!isCandidateALeader(leader)) {
|
||||
currentLeaderId = leader.getId();
|
||||
System.out.println(currentLeaderId + " is a leader, check in later");
|
||||
scheduleUpdate(leaderConfiguration.getLeaseDuration());
|
||||
scheduleUpdate(leaderProperties.getLeaseDuration());
|
||||
} else {
|
||||
System.out.println("I am a leader, check in later");
|
||||
currentLeaderId = candidate.getId();
|
||||
scheduleUpdate(leaderConfiguration.getLeaseDuration()); // Is this needed?
|
||||
scheduleUpdate(leaderProperties.getLeaseDuration()); // Is this needed?
|
||||
}
|
||||
}
|
||||
|
||||
private void takeLeadership(ConfigMap oldConfigMap) {
|
||||
String leaderIdKey = leaderConfiguration.getLeaderIdPrefix() + candidate.getRole();
|
||||
String leaderIdKey = leaderProperties.getLeaderIdPrefix() + candidate.getRole();
|
||||
|
||||
if (oldConfigMap == null) {
|
||||
ConfigMap newConfigMap = new ConfigMapBuilder().withNewMetadata()
|
||||
.withName(leaderConfiguration.getConfigMapName())
|
||||
.withName(leaderProperties.getConfigMapName())
|
||||
.addToLabels("provider", "spring-cloud-kubernetes")
|
||||
.addToLabels("kind", "locks")
|
||||
.endMetadata()
|
||||
@@ -156,7 +155,7 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
.build();
|
||||
|
||||
kubernetesClient.configMaps()
|
||||
.inNamespace(leaderConfiguration.getNamespace(kubernetesClient.getNamespace()))
|
||||
.inNamespace(leaderProperties.getNamespace(kubernetesClient.getNamespace()))
|
||||
.create(newConfigMap);
|
||||
} else {
|
||||
ConfigMap newConfigMap = new ConfigMapBuilder(oldConfigMap)
|
||||
@@ -164,8 +163,8 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
.build();
|
||||
|
||||
kubernetesClient.configMaps()
|
||||
.inNamespace(leaderConfiguration.getNamespace(kubernetesClient.getNamespace()))
|
||||
.withName(leaderConfiguration.getConfigMapName())
|
||||
.inNamespace(leaderProperties.getNamespace(kubernetesClient.getNamespace()))
|
||||
.withName(leaderProperties.getConfigMapName())
|
||||
.lockResourceVersion(oldConfigMap.getMetadata().getResourceVersion())
|
||||
.replace(newConfigMap);
|
||||
}
|
||||
@@ -176,8 +175,8 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
private ConfigMap getConfigMap() {
|
||||
try {
|
||||
return kubernetesClient.configMaps()
|
||||
.inNamespace(leaderConfiguration.getNamespace(kubernetesClient.getNamespace()))
|
||||
.withName(leaderConfiguration.getConfigMapName())
|
||||
.inNamespace(leaderProperties.getNamespace(kubernetesClient.getNamespace()))
|
||||
.withName(leaderProperties.getConfigMapName())
|
||||
.get();
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to get a ConfigMap: " + e.getMessage());
|
||||
@@ -191,7 +190,7 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
}
|
||||
|
||||
Map<String, String> data = configMap.getData();
|
||||
String leaderIdKey = leaderConfiguration.getLeaderIdPrefix() + candidate.getRole();
|
||||
String leaderIdKey = leaderProperties.getLeaderIdPrefix() + candidate.getRole();
|
||||
String leaderId = data.get(leaderIdKey);
|
||||
if (leaderId == null) {
|
||||
return null;
|
||||
@@ -205,7 +204,7 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
}
|
||||
|
||||
private long jitter(long num) {
|
||||
return (long) (num * (1 + Math.random() * (leaderConfiguration.getJitterFactor() - 1)));
|
||||
return (long) (num * (1 + Math.random() * (leaderProperties.getJitterFactor() - 1)));
|
||||
}
|
||||
|
||||
private boolean isCandidateALeader(Leader leader) {
|
||||
@@ -214,8 +213,8 @@ public class LeaderInitiator implements SmartLifecycle {
|
||||
|
||||
private boolean isValidLeader(Leader leader) {
|
||||
return kubernetesClient.pods()
|
||||
.inNamespace(leaderConfiguration.getNamespace(kubernetesClient.getNamespace()))
|
||||
.withLabels(leaderConfiguration.getLabels())
|
||||
.inNamespace(leaderProperties.getNamespace(kubernetesClient.getNamespace()))
|
||||
.withLabels(leaderProperties.getLabels())
|
||||
.list()
|
||||
.getItems()
|
||||
.stream()
|
||||
|
||||
@@ -19,10 +19,13 @@ package org.springframework.cloud.kubernetes.leader;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:gytis@redhat.com">Gytis Trikleris</a>
|
||||
*/
|
||||
public class LeaderConfiguration {
|
||||
@ConfigurationProperties("spring.cloud.kubernetes.leader")
|
||||
public class LeaderProperties {
|
||||
|
||||
private static final String DEFAULT_LEADER_ID_PREFIX = "leader.id.";
|
||||
|
||||
@@ -36,20 +39,56 @@ public class LeaderConfiguration {
|
||||
|
||||
private static final double DEFAULT_JITTER_FACTOR = 1.2;
|
||||
|
||||
/**
|
||||
* Should leader election be started automatically on startup.
|
||||
* Default: true
|
||||
*/
|
||||
private boolean autoStartup = DEFAULT_AUTO_STARTUP;
|
||||
|
||||
/**
|
||||
* Role for which leadership this candidate will compete.
|
||||
*/
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* Kubernetes namespace where the leaders ConfigMap and candidates are located.
|
||||
*/
|
||||
private String namespace;
|
||||
|
||||
/**
|
||||
* Kubernetes ConfigMap where leaders information will be stored.
|
||||
* Default: leaders
|
||||
*/
|
||||
private String configMapName = DEFAULT_CONFIG_MAP_NAME;
|
||||
|
||||
/**
|
||||
* Kubernetes labels common to all leadership candidates.
|
||||
* Default: empty
|
||||
*/
|
||||
private Map<String, String> labels = Collections.emptyMap();
|
||||
|
||||
/**
|
||||
* Leader id property prefix for the ConfigMap.
|
||||
* Default: leader.id.
|
||||
*/
|
||||
private String leaderIdPrefix = DEFAULT_LEADER_ID_PREFIX;
|
||||
|
||||
/**
|
||||
* Time period after which leader should check it's leadership or new leader should be elected.
|
||||
* Default: 30s
|
||||
*/
|
||||
private long leaseDuration = DEFAULT_LEASE_DURATION;
|
||||
|
||||
/**
|
||||
* Time period after connections should be retired after failure.
|
||||
* Default: 5s
|
||||
*/
|
||||
private long retryPeriod = DEFAULT_RETRY_PERIOD;
|
||||
|
||||
/**
|
||||
* A parameter to randomise scheduler.
|
||||
* Default: 1.2
|
||||
*/
|
||||
private double jitterFactor = DEFAULT_JITTER_FACTOR;
|
||||
|
||||
public boolean isAutoStartup() {
|
||||
@@ -60,6 +99,14 @@ public class LeaderConfiguration {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.kubernetes.leader.LeaderAutoConfiguration
|
||||
Reference in New Issue
Block a user