Skip to content

Commit 7345421

Browse files
committed
started work
Signed-off-by: wind57 <[email protected]>
1 parent 6a31cac commit 7345421

File tree

13 files changed

+619
-39
lines changed

13 files changed

+619
-39
lines changed

spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/DeleteMeSandbox.java

Lines changed: 0 additions & 34 deletions
This file was deleted.

spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/leader/LeaderUtils.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,84 @@
1616

1717
package org.springframework.cloud.kubernetes.commons.leader;
1818

19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.io.UncheckedIOException;
1922
import java.net.InetAddress;
2023
import java.net.UnknownHostException;
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
26+
import java.util.Optional;
2127
import java.util.concurrent.locks.ReentrantLock;
2228

2329
import org.springframework.cloud.kubernetes.commons.EnvReader;
30+
import org.springframework.core.log.LogAccessor;
2431
import org.springframework.util.StringUtils;
2532

33+
import static org.springframework.cloud.kubernetes.commons.KubernetesClientProperties.SERVICE_ACCOUNT_NAMESPACE_PATH;
34+
2635
/**
2736
* @author wind57
2837
*/
2938
public final class LeaderUtils {
3039

40+
/**
41+
* Coordination group for leader election.
42+
*/
43+
public static final String COORDINATION_GROUP = "coordination.k8s.io";
44+
45+
/**
46+
* Coordination version for leader election.
47+
*/
48+
public static final String COORDINATION_VERSION = "v1";
49+
50+
/**
51+
* Lease constant.
52+
*/
53+
public static final String LEASE = "Lease";
54+
55+
/**
56+
* Prefix for all properties related to leader election.
57+
*/
58+
public static final String LEADER_ELECTION_PROPERTY_PREFIX = "spring.cloud.kubernetes.leader.election";
59+
60+
/**
61+
* Property that controls whether leader election is enabled.
62+
*/
63+
public static final String LEADER_ELECTION_ENABLED_PROPERTY = LEADER_ELECTION_PROPERTY_PREFIX + ".enabled";
64+
65+
private static final String POD_NAMESPACE = "POD_NAMESPACE";
66+
67+
private static final LogAccessor LOG = new LogAccessor(LeaderUtils.class);
68+
3169
// k8s environment variable responsible for host name
3270
private static final String HOSTNAME = "HOSTNAME";
3371

3472
private LeaderUtils() {
3573

3674
}
3775

76+
/**
77+
* ideally, should always be present. If not, downward api must enable this one.
78+
*/
79+
public static Optional<String> podNamespace() {
80+
Path serviceAccountPath = new File(SERVICE_ACCOUNT_NAMESPACE_PATH).toPath();
81+
boolean serviceAccountNamespaceExists = Files.isRegularFile(serviceAccountPath);
82+
if (serviceAccountNamespaceExists) {
83+
try {
84+
String namespace = new String(Files.readAllBytes(serviceAccountPath)).replace(System.lineSeparator(),
85+
"");
86+
LOG.info(() -> "read namespace : " + namespace + " from service account " + serviceAccountPath);
87+
return Optional.of(namespace);
88+
}
89+
catch (IOException e) {
90+
throw new UncheckedIOException(e);
91+
}
92+
93+
}
94+
return Optional.ofNullable(EnvReader.getEnv(POD_NAMESPACE));
95+
}
96+
3897
public static String hostName() throws UnknownHostException {
3998
String hostName = EnvReader.getEnv(HOSTNAME);
4099
if (StringUtils.hasText(hostName)) {
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.cloud.kubernetes.commons;
18-
19-
import jakarta.annotation.Nonnull;
17+
package org.springframework.cloud.kubernetes.commons.leader.election;
2018

2119
import java.util.concurrent.Executors;
2220
import java.util.concurrent.ScheduledFuture;
@@ -25,9 +23,13 @@
2523
import java.util.concurrent.TimeUnit;
2624
import java.util.concurrent.locks.ReentrantLock;
2725

26+
import jakarta.annotation.Nonnull;
27+
2828
/**
2929
* This is taken from fabric8 with some minor changes (we need it, so it could be placed
30-
* in the common package).
30+
* in the common package). A single thread scheduler that will shutdown itself when there
31+
* are no more jobs running inside it. When all ScheduledFuture::cancel are called, the
32+
* queue of tasks will be empty and there is an internal runnable that checks that.
3133
*
3234
* @author wind57
3335
*/
@@ -70,7 +72,7 @@ private void startExecutor() {
7072
this.executor = new ScheduledThreadPoolExecutor(1, threadFactory());
7173
this.executor.setRemoveOnCancelPolicy(true);
7274
this.executor.scheduleWithFixedDelay(this::shutdownCheck, this.ttlMillis, this.ttlMillis,
73-
TimeUnit.MILLISECONDS);
75+
TimeUnit.MILLISECONDS);
7476
}
7577

7678
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2013-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.kubernetes.commons.leader.election;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Inherited;
22+
import java.lang.annotation.Retention;
23+
import java.lang.annotation.RetentionPolicy;
24+
import java.lang.annotation.Target;
25+
26+
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
27+
import org.springframework.context.annotation.Conditional;
28+
29+
/**
30+
* @author wind57
31+
*/
32+
@Target({ ElementType.TYPE, ElementType.METHOD })
33+
@Retention(RetentionPolicy.RUNTIME)
34+
@Documented
35+
@Inherited
36+
@Conditional(ConditionalOnLeaderElectionDisabled.OnLeaderElectionDisabled.class)
37+
public @interface ConditionalOnLeaderElectionDisabled {
38+
39+
class OnLeaderElectionDisabled extends NoneNestedConditions {
40+
41+
OnLeaderElectionDisabled() {
42+
super(ConfigurationPhase.REGISTER_BEAN);
43+
}
44+
45+
@ConditionalOnLeaderElectionEnabled
46+
static class OnLeaderElectionDisabledClass {
47+
48+
}
49+
50+
}
51+
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2013-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.kubernetes.commons.leader.election;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Inherited;
22+
import java.lang.annotation.Retention;
23+
import java.lang.annotation.RetentionPolicy;
24+
import java.lang.annotation.Target;
25+
26+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
27+
28+
import static org.springframework.cloud.kubernetes.commons.leader.LeaderUtils.LEADER_ELECTION_ENABLED_PROPERTY;
29+
30+
/**
31+
* Provides a more succinct conditional for:
32+
* <code>spring.cloud.kubernetes.leader.election.enabled</code>.
33+
*
34+
* @author wind57
35+
*/
36+
@Target({ ElementType.TYPE, ElementType.METHOD })
37+
@Retention(RetentionPolicy.RUNTIME)
38+
@Documented
39+
@Inherited
40+
@ConditionalOnProperty(value = LEADER_ELECTION_ENABLED_PROPERTY, havingValue = "true", matchIfMissing = false)
41+
public @interface ConditionalOnLeaderElectionEnabled {
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2013-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.kubernetes.commons.leader.election;
18+
19+
import java.net.UnknownHostException;
20+
import java.util.function.Consumer;
21+
22+
import org.springframework.cloud.kubernetes.commons.leader.LeaderUtils;
23+
import org.springframework.cloud.kubernetes.commons.leader.election.events.NewLeaderEvent;
24+
import org.springframework.cloud.kubernetes.commons.leader.election.events.StartLeadingEvent;
25+
import org.springframework.cloud.kubernetes.commons.leader.election.events.StopLeadingEvent;
26+
import org.springframework.context.ApplicationEventPublisher;
27+
import org.springframework.context.annotation.Bean;
28+
import org.springframework.core.log.LogAccessor;
29+
30+
/**
31+
* common leader election callbacks that are supposed to be used in both fabric8 and
32+
* k8s-native clients.
33+
*
34+
* @author wind57
35+
*/
36+
public class LeaderElectionCallbacks {
37+
38+
private static final LogAccessor LOG = new LogAccessor(LeaderElectionCallbacks.class);
39+
40+
@Bean
41+
public final String holderIdentity() throws UnknownHostException {
42+
String podHostName = LeaderUtils.hostName();
43+
LOG.debug(() -> "using pod hostname : " + podHostName);
44+
return podHostName;
45+
}
46+
47+
@Bean
48+
public final String podNamespace() {
49+
String podNamespace = LeaderUtils.podNamespace().orElse("default");
50+
LOG.debug(() -> "using pod namespace : " + podNamespace);
51+
return podNamespace;
52+
}
53+
54+
@Bean
55+
public final Runnable onStartLeadingCallback(ApplicationEventPublisher applicationEventPublisher,
56+
String holderIdentity, LeaderElectionProperties properties) {
57+
return () -> {
58+
LOG.info(() -> "id : " + holderIdentity + " is now a leader");
59+
if (properties.publishEvents()) {
60+
applicationEventPublisher.publishEvent(new StartLeadingEvent(holderIdentity));
61+
}
62+
};
63+
}
64+
65+
@Bean
66+
public final Runnable onStopLeadingCallback(ApplicationEventPublisher applicationEventPublisher,
67+
String holderIdentity, LeaderElectionProperties properties) {
68+
return () -> {
69+
LOG.info(() -> "id : " + holderIdentity + " stopped being a leader");
70+
if (properties.publishEvents()) {
71+
applicationEventPublisher.publishEvent(new StopLeadingEvent(holderIdentity));
72+
}
73+
};
74+
}
75+
76+
@Bean
77+
public final Consumer<String> onNewLeaderCallback(ApplicationEventPublisher applicationEventPublisher,
78+
LeaderElectionProperties properties) {
79+
return holderIdentity -> {
80+
LOG.info(() -> "id : " + holderIdentity + " is the new leader");
81+
if (properties.publishEvents()) {
82+
applicationEventPublisher.publishEvent(new NewLeaderEvent(holderIdentity));
83+
}
84+
};
85+
}
86+
87+
}

0 commit comments

Comments
 (0)