-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fabric8 leader election (CAN ONLY GO IN THE NEXT MAJOR RELEASE) #1658
base: main
Are you sure you want to change the base?
Changes from 215 commits
032014e
c3d0ad2
10889fd
8f0375a
96ebf42
db8403e
90a5345
b041c00
9580444
a34ac47
6613f78
1b3eae9
4275382
618f25a
100a9cd
4b9056b
315a85b
ed3c264
446d630
6821a28
9e95a8a
02840b1
7e31d65
926f4d7
344e1d4
e91ac12
ba6e088
49025e8
a647bb3
16b09ab
c8d8a72
591f340
c331bd0
f48b22b
900843d
76ecdd5
b69d4a6
df2ddcc
3ff1cdd
bd89cc8
39ef510
d389953
b676bac
b1473c9
83ff113
33dfbb7
316af58
425d30b
763280a
3afbaef
cb97ba1
b33edc8
7a1fdf1
a8beed9
2e445d9
5b51db1
b0ce76c
918bb7f
c629951
ccfc0bf
0f3fa24
b60a283
901cb66
251002b
fedeb17
004f95b
10168be
dc811cf
3dfe77d
619c8d0
12ca211
a91c1e1
be2e809
1c84e33
1319fe7
9424c2e
cddd550
f8d34f2
31d2fb8
fa9fd09
22f3f51
bb44f4b
9714f2c
0f9cb3d
6e60174
78c5575
5eb5c51
48f7cb0
8a18085
e4cab39
ba4cab0
bbc6e30
0e9f1ad
8a5243c
cbf83c4
3dd0260
1408d44
2925ee1
af3a12b
f5a27dc
494454a
add0f67
b6a4b45
00a559d
882f9c1
6c9071e
09c10bb
3615396
116c199
f1447cd
14b165b
3ce8fac
41eaa8d
4f89298
ee1031d
f6707d5
9930c7d
0043006
4e292ec
3129276
1c90fc2
fa0888a
ab680d1
4023ca3
7ed7f5d
3be81b7
dd710ec
4fdd06a
bf61054
f059697
013cc6a
13ff3b3
88021bb
dcf4e62
a600c69
3f1a180
85ca441
65acfe6
5526b50
0c131b5
af8a8ee
9c74e14
a410606
8c38ece
dcb8fb4
73e9765
1a3a4d9
926d2b0
02737d4
24defb8
14c9780
719ccbc
5784dfa
ecf13b8
658a5f6
4eb0e3c
256332d
d64dd18
2861ea5
9fffd36
a84bef6
a7000eb
219289f
ab12e1d
b32e8a0
2ddaca9
8ee9390
ad54a3f
bdf347b
70d6a7c
f42a5ca
2e88bc4
83e4453
2e6cfb3
9683fad
61904dd
aeb5f93
6dc82b5
e3e6334
e8653ad
acb8550
152f6bf
a279c0c
f6e608e
6a51e1a
a02efb2
fa8ca61
24dd2e4
5864ab1
d34806d
8acf002
2ac6bdd
fdddf41
d872393
e690faf
f73667f
97010ab
f38ec53
b178548
750a4fe
713824a
56d4b40
c298dfe
aa47ab7
554112b
44389b2
8116a39
90176ef
ae40722
af673ba
e3ca2f8
f96a8fa
2847f75
462e568
0893cc0
ed78705
7468875
4405694
2f76b5d
f45d763
af56e7c
2b42942
c416eea
99a0b08
7c5a312
ed2080b
b87c349
8fe3240
0355d7a
5e157bc
bf0cc39
193aa2b
f342faf
391dcf2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,21 +16,33 @@ | |
|
||
package org.springframework.cloud.kubernetes.commons.leader; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.net.InetAddress; | ||
import java.net.UnknownHostException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.Optional; | ||
import java.util.concurrent.locks.ReentrantLock; | ||
|
||
import org.springframework.cloud.kubernetes.commons.EnvReader; | ||
import org.springframework.core.log.LogAccessor; | ||
import org.springframework.util.StringUtils; | ||
|
||
import static org.springframework.cloud.kubernetes.commons.KubernetesClientProperties.SERVICE_ACCOUNT_NAMESPACE_PATH; | ||
|
||
/** | ||
* @author wind57 | ||
*/ | ||
public final class LeaderUtils { | ||
|
||
private static final LogAccessor LOG = new LogAccessor(LeaderUtils.class); | ||
|
||
// k8s environment variable responsible for host name | ||
private static final String HOSTNAME = "HOSTNAME"; | ||
|
||
private static final String POD_NAMESPACE = "POD_NAMESPACE"; | ||
|
||
private LeaderUtils() { | ||
|
||
} | ||
|
@@ -45,6 +57,27 @@ public static String hostName() throws UnknownHostException { | |
} | ||
} | ||
|
||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need to know in which namespace the pod is running, so that I could query it for readiness. |
||
* ideally, should always be present. If not, downward api must enable this one. | ||
*/ | ||
public static Optional<String> podNamespace() { | ||
Path serviceAccountPath = new File(SERVICE_ACCOUNT_NAMESPACE_PATH).toPath(); | ||
boolean serviceAccountNamespaceExists = Files.isRegularFile(serviceAccountPath); | ||
if (serviceAccountNamespaceExists) { | ||
try { | ||
String namespace = new String(Files.readAllBytes(serviceAccountPath)).replace(System.lineSeparator(), | ||
""); | ||
LOG.info(() -> "read namespace : " + namespace + " from service account " + serviceAccountPath); | ||
return Optional.of(namespace); | ||
} | ||
catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
|
||
} | ||
return Optional.ofNullable(EnvReader.getEnv(POD_NAMESPACE)); | ||
} | ||
|
||
public static void guarded(ReentrantLock lock, Runnable runnable) { | ||
try { | ||
lock.lock(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright 2013-2024 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.election; | ||
|
||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Inherited; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
|
||
/** | ||
* Provides a more succinct conditional for: | ||
* <code>spring.cloud.kubernetes.leader.election.enabled</code>. | ||
* | ||
* @author wind57 | ||
*/ | ||
@Target({ ElementType.TYPE, ElementType.METHOD }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Documented | ||
@Inherited | ||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.leader.election.enabled", matchIfMissing = true, | ||
havingValue = "false") | ||
public @interface ConditionalOnLeaderElectionDisabled { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright 2013-2024 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.election; | ||
|
||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Inherited; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
|
||
/** | ||
* Provides a more succinct conditional for: | ||
* <code>spring.cloud.kubernetes.leader.election.enabled</code>. | ||
* | ||
* @author wind57 | ||
*/ | ||
@Target({ ElementType.TYPE, ElementType.METHOD }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Documented | ||
@Inherited | ||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.leader.election.enabled", matchIfMissing = false, | ||
havingValue = "true") | ||
public @interface ConditionalOnLeaderElectionEnabled { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* Copyright 2013-2024 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.election; | ||
|
||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.boot.context.properties.bind.DefaultValue; | ||
|
||
/** | ||
* @author wind57 | ||
*/ | ||
// @formatter:off | ||
@ConfigurationProperties("spring.cloud.kubernetes.leader.election") | ||
public record LeaderElectionProperties( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the properties needed in order to configure the new leader election:
I'll try to explain this a bit more verbose. The process internally in fabric8 is something like this:
So they can only try to acquire the leadership if As such, But in case of a graceful shutdown (and I implemented this via |
||
@DefaultValue("true") boolean waitForPodReady, | ||
@DefaultValue("true") boolean publishEvents, | ||
@DefaultValue("15") int leaseDuration, | ||
@DefaultValue("default") String lockNamespace, | ||
@DefaultValue("spring-k8s-leader-election-lock") String lockName, | ||
@DefaultValue("10") int renewDeadline, | ||
@DefaultValue("2") int retryPeriod, | ||
@DefaultValue("0") int waitAfterRenewalFailure) { | ||
// @formatter:on | ||
|
||
/** | ||
* Coordination group for leader election. | ||
*/ | ||
public static final String COORDINATION_GROUP = "coordination.k8s.io"; | ||
|
||
/** | ||
* Coordination version for leader election. | ||
*/ | ||
public static final String COORDINATION_VERSION = "v1"; | ||
|
||
/** | ||
* Lease constant. | ||
*/ | ||
public static final String LEASE = "Lease"; | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* Copyright 2013-2024 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.election.events; | ||
|
||
import org.springframework.context.ApplicationEvent; | ||
|
||
public final class NewLeaderEvent extends ApplicationEvent { | ||
|
||
private final String holderIdentity; | ||
|
||
public NewLeaderEvent(Object source) { | ||
super(source); | ||
holderIdentity = (String) source; | ||
} | ||
|
||
public String holderIdentity() { | ||
return holderIdentity; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* Copyright 2013-2024 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.election.events; | ||
|
||
import org.springframework.context.ApplicationEvent; | ||
|
||
/** | ||
* @author wind57 | ||
*/ | ||
public final class StartLeadingEvent extends ApplicationEvent { | ||
|
||
private final String holderIdentity; | ||
|
||
public StartLeadingEvent(Object source) { | ||
super(source); | ||
holderIdentity = (String) source; | ||
} | ||
|
||
public String holderIdentity() { | ||
return holderIdentity; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* Copyright 2013-2024 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.election.events; | ||
|
||
import org.springframework.context.ApplicationEvent; | ||
|
||
/** | ||
* @author wind57 | ||
*/ | ||
public final class StopLeadingEvent extends ApplicationEvent { | ||
|
||
private final String holderIdentity; | ||
|
||
public StopLeadingEvent(Object source) { | ||
super(source); | ||
holderIdentity = (String) source; | ||
} | ||
|
||
public String holderIdentity() { | ||
return holderIdentity; | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the point of disabling this if it won't work without it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there are two reasons I did this. First one is that in the current (old) implementation, we already have such a check: "if pod is not ready, don't start leader election, re-check after some interval". So having this check in the first place, is to do what the old implementation was doing.
The second one, to why I would like to give an option for users to disable this, is what if readiness is made in such a way that has no influence on the leadership election? So when pods scale up, disabling readiness checks here would mean that pods can start faster.