1.0.x update local leader before notifications (#410)

* Fix leader election example

* Update local leader before sending events
This commit is contained in:
Gytis Trikleris
2019-06-14 19:51:24 +02:00
committed by Ryan Baxter
parent aef102dc72
commit f22175ca2d
9 changed files with 73 additions and 49 deletions

View File

@@ -27,9 +27,17 @@ And finally, if the leadership is yielded or revoked for some reason, the old le
## Example application usage
To begin with, build and deploy the application:
Leader election mechanism uses Kubernetes ConfigMap feature to coordinate leadership information.
To access ConfigMap user needs correct role and role binding.
Create them using the following commands:
```
mvn clean package fabric8:deploy -Pkubernetes
kubectl apply -f leader-role.yml
kubectl apply -f leader-rolebinding.yml
```
Now build and deploy the application:
```
mvn clean fabric8:deploy -Pkubernetes
```
This will deploy a single application instance to the cluster and that instance will automatically become a leader.
@@ -76,8 +84,4 @@ Thus, when trying to yield the leadership, request might go to a non-leader node
> Note: instances periodically try to acquire leadership and Spring Cloud Kubernetes doesn't decide which one of them is more worth to become one.
Thus, it is possible that the instance which just yielded the leadership, made another leadership take over request faster than another instances and became a leader again.
## Access control notice
Leader election mechanism uses Kubernetes ConfigMap feature to coordinate leadership information.
In order to access it, [Role](./src/main/fabric8/role.yaml) and [RoleBinding](./src/main/fabric8/rb.yaml) objects are defined.

View File

@@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: leader
labels:
app: kubernetes-leader-election-example
group: org.springframework.cloud
rules:
- apiGroups:
- ""
resources:
- pods
- configmaps
verbs:
- '*'

View File

@@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: kubernetes-leader-election-example
group: org.springframework.cloud
name: leader
roleRef:
apiGroup: ""
kind: Role
name: leader
subjects:
- kind: ServiceAccount
name: default
apiGroup: ""

View File

@@ -36,6 +36,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-leader</artifactId>

View File

@@ -1,13 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: leader
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: leader
subjects:
- kind: ServiceAccount
name: default
namespace: default

View File

@@ -1,12 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: leader
namespace: default
rules:
- apiGroups:
resources:
- pods
- configmaps
verbs:
- "*"

View File

@@ -41,7 +41,8 @@ import org.springframework.integration.leader.event.LeaderEventPublisher;
@Configuration
@EnableConfigurationProperties(LeaderProperties.class)
@ConditionalOnBean(KubernetesClient.class)
@ConditionalOnProperty(value = "spring.cloud.kubernetes.leader.enabled", matchIfMissing = true)
@ConditionalOnProperty(value = "spring.cloud.kubernetes.leader.enabled",
matchIfMissing = true)
public class LeaderAutoConfiguration {
@Bean

View File

@@ -149,24 +149,18 @@ public class LeadershipController {
LOGGER.debug("Leader is still '{}'", this.localLeader);
return;
}
else if (this.localLeader != null
&& this.localLeader.isCandidate(this.candidate)) {
Leader oldLeader = this.localLeader;
this.localLeader = newLeader;
if (oldLeader != null && oldLeader.isCandidate(this.candidate)) {
notifyOnRevoked();
}
else if (newLeader != null && newLeader.isCandidate(this.candidate)) {
notifyOnGranted();
}
this.localLeader = newLeader;
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();
}
restartLeaderReadinessWatcher();
LOGGER.debug("New leader is '{}'", this.localLeader);
}
@@ -203,6 +197,19 @@ public class LeadershipController {
}
}
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();
}

View File

@@ -31,10 +31,13 @@ import org.springframework.test.web.reactive.server.WebTestClient;
import static org.hamcrest.Matchers.containsString;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
"spring.cloud.kubernetes.leader.autoStartup=false" // Make sure test passes
// without Kubernetes cluster
})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "spring.cloud.kubernetes.leader.autoStartup=false" // Make sure
// test passes
// without
// Kubernetes
// cluster
})
public class LeaderAutoConfigurationTests {
@Value("${local.server.port}")