Fabric leader clean up 3 (#1643)

This commit is contained in:
erabii
2024-04-18 20:30:42 +03:00
committed by GitHub
parent d44edfc208
commit af02c5fd16
7 changed files with 195 additions and 144 deletions

View File

@@ -47,7 +47,7 @@ public class Leader {
return false;
}
return Objects.equals(this.role, candidate.getRole()) && Objects.equals(this.id, candidate.getId());
return Objects.equals(role, candidate.getRole()) && Objects.equals(id, candidate.getId());
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2019 the original author or authors.
* Copyright 2019-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.
@@ -18,7 +18,6 @@ package org.springframework.cloud.kubernetes.commons.leader;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.springframework.boot.actuate.info.Info.Builder;
import org.springframework.boot.actuate.info.InfoContributor;
@@ -38,16 +37,12 @@ public class LeaderInfoContributor implements InfoContributor {
@Override
public void contribute(Builder builder) {
Map<String, Object> details = new HashMap<>();
Optional<Leader> leader = leadershipController.getLocalLeader();
if (leader.isPresent()) {
Leader l = leader.get();
details.put("leaderId", l.getId());
details.put("role", l.getRole());
details.put("isLeader", l.isCandidate(candidate));
}
else {
details.put("leaderId", "Unknown");
}
leadershipController.getLocalLeader().ifPresentOrElse(leader -> {
details.put("leaderId", leader.getId());
details.put("role", leader.getRole());
details.put("isLeader", leader.isCandidate(candidate));
}, () -> details.put("leaderId", "Unknown"));
builder.withDetail("leaderElection", details);
}

View File

@@ -0,0 +1,93 @@
/*
* 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;
import java.util.Map;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.actuate.info.Info;
import org.springframework.integration.leader.Candidate;
import org.springframework.integration.leader.DefaultCandidate;
import org.springframework.integration.leader.event.LeaderEventPublisher;
/**
* @author wind57
*/
class LeaderInfoContributorTests {
@Test
void testLeaderMissing() {
Candidate candidate = new DefaultCandidate("id", "role");
LeaderProperties leaderProperties = new LeaderProperties();
LeaderEventPublisher leaderEventPublisher = Mockito.mock(LeaderEventPublisher.class);
LeadershipController leadershipController = new LeadershipControllerStub(candidate, leaderProperties,
leaderEventPublisher);
LeaderInfoContributor leaderInfoContributor = new LeaderInfoContributor(leadershipController, candidate);
Info.Builder builder = new Info.Builder();
leaderInfoContributor.contribute(builder);
Assertions.assertEquals(builder.build().getDetails().get("leaderElection"), Map.of("leaderId", "Unknown"));
}
@Test
void testLeaderPresentIsLeader() {
Candidate candidate = new DefaultCandidate("leaderId", "leaderRole");
LeaderProperties leaderProperties = new LeaderProperties();
LeaderEventPublisher leaderEventPublisher = Mockito.mock(LeaderEventPublisher.class);
LeadershipController leadershipController = new LeadershipControllerStub(candidate, leaderProperties,
leaderEventPublisher);
Leader leader = new Leader("leaderRole", "leaderId");
leadershipController.handleLeaderChange(leader);
LeaderInfoContributor leaderInfoContributor = new LeaderInfoContributor(leadershipController, candidate);
Info.Builder builder = new Info.Builder();
leaderInfoContributor.contribute(builder);
Assertions.assertEquals(builder.build().getDetails().get("leaderElection"),
Map.of("role", "leaderRole", "isLeader", true, "leaderId", "leaderId"));
}
@Test
void testLeaderPresentIsNotLeader() {
Candidate candidate = new DefaultCandidate("leaderId", "notLeaderRole");
LeaderProperties leaderProperties = new LeaderProperties();
LeaderEventPublisher leaderEventPublisher = Mockito.mock(LeaderEventPublisher.class);
LeadershipController leadershipController = new LeadershipControllerStub(candidate, leaderProperties,
leaderEventPublisher);
Leader leader = new Leader("leaderRole", "leaderId");
leadershipController.handleLeaderChange(leader);
LeaderInfoContributor leaderInfoContributor = new LeaderInfoContributor(leadershipController, candidate);
Info.Builder builder = new Info.Builder();
leaderInfoContributor.contribute(builder);
Assertions.assertEquals(builder.build().getDetails().get("leaderElection"),
Map.of("role", "leaderRole", "isLeader", false, "leaderId", "leaderId"));
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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;
import java.util.Optional;
import org.springframework.integration.leader.Candidate;
import org.springframework.integration.leader.event.LeaderEventPublisher;
/**
* @author wind57
*/
final class LeadershipControllerStub extends LeadershipController {
private Leader leader;
LeadershipControllerStub(Candidate candidate, LeaderProperties leaderProperties,
LeaderEventPublisher leaderEventPublisher) {
super(candidate, leaderProperties, leaderEventPublisher);
}
@Override
public void update() {
}
@Override
public void revoke() {
}
@Override
protected PodReadinessWatcher createPodReadinessWatcher(String localLeaderId) {
return null;
}
@Override
protected void handleLeaderChange(Leader leader) {
this.leader = leader;
}
@Override
public Optional<Leader> getLocalLeader() {
return Optional.ofNullable(leader);
}
}

View File

@@ -47,20 +47,38 @@ import org.springframework.integration.leader.event.LeaderEventPublisher;
@ConditionalOnProperty(value = "spring.cloud.kubernetes.leader.enabled", matchIfMissing = true)
public class Fabric8LeaderAutoConfiguration {
/*
* Used for publishing application events that happen: granted, revoked or failed to
* acquire mutex.
*/
@Bean
@ConditionalOnMissingBean(LeaderEventPublisher.class)
public LeaderEventPublisher defaultLeaderEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
return new DefaultLeaderEventPublisher(applicationEventPublisher);
}
/*
* This can be thought as "self" or the pod that participates in leader election
* process. The implementation that we return simply logs events that happen during
* that process.
*/
@Bean
public Candidate candidate(LeaderProperties leaderProperties) throws UnknownHostException {
String id = LeaderUtils.hostName();
String role = leaderProperties.getRole();
return new DefaultCandidate(id, role);
}
/*
* Add an info contributor with leader information.
*/
@Bean
@ConditionalOnClass(InfoContributor.class)
public LeaderInfoContributor leaderInfoContributor(Fabric8LeadershipController fabric8LeadershipController,
Candidate candidate) {
return new LeaderInfoContributor(fabric8LeadershipController, candidate);
}
@Bean
public Fabric8LeadershipController leadershipController(Candidate candidate, LeaderProperties leaderProperties,
LeaderEventPublisher leaderEventPublisher, KubernetesClient kubernetesClient) {
@@ -87,11 +105,4 @@ public class Fabric8LeaderAutoConfiguration {
hostPodWatcher);
}
@Bean
@ConditionalOnClass(InfoContributor.class)
public LeaderInfoContributor leaderInfoContributor(Fabric8LeadershipController fabric8LeadershipController,
Candidate candidate) {
return new LeaderInfoContributor(fabric8LeadershipController, candidate);
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright 2019-2019 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.fabric8.leader;
import java.util.Map;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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;
import static org.mockito.BDDMockito.given;
@ExtendWith(MockitoExtension.class)
public class LeaderInfoContributorTest {
@Mock
private Candidate mockCandidate;
@Mock
private Fabric8LeadershipController mockFabric8LeadershipController;
@Mock
private Leader mockLeader;
private LeaderInfoContributor leaderInfoContributor;
@BeforeEach
public void before() {
this.leaderInfoContributor = new LeaderInfoContributor(this.mockFabric8LeadershipController,
this.mockCandidate);
}
@SuppressWarnings("unchecked")
@Test
public void infoWithoutLeader() {
Info.Builder builder = new Info.Builder();
leaderInfoContributor.contribute(builder);
Map<String, Object> details = (Map<String, Object>) builder.build().get("leaderElection");
assertThat(details).containsEntry("leaderId", "Unknown");
}
@SuppressWarnings("unchecked")
@Test
public void infoWhenLeader() {
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");
Info.Builder builder = new Info.Builder();
leaderInfoContributor.contribute(builder);
Map<String, Object> details = (Map<String, Object>) builder.build().get("leaderElection");
assertThat(details).containsEntry("isLeader", true);
assertThat(details).containsEntry("leaderId", "id");
assertThat(details).containsEntry("role", "testRole");
}
@SuppressWarnings("unchecked")
@Test
public void infoWhenAnotherIsLeader() {
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();
leaderInfoContributor.contribute(builder);
Map<String, Object> details = (Map<String, Object>) builder.build().get("leaderElection");
assertThat(details).containsEntry("isLeader", false);
assertThat(details).containsEntry("leaderId", "id");
assertThat(details).containsEntry("role", "testRole");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* 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.
@@ -18,57 +18,48 @@ package org.springframework.cloud.kubernetes.fabric8.leader;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
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 org.springframework.integration.leader.DefaultCandidate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* @author Gytis Trikleris
*/
@ExtendWith(MockitoExtension.class)
public class LeaderTest {
class LeaderTest {
private static final String ROLE = "test-role";
private static final String ID = "test-id";
@Mock
private Candidate mockCandidate;
private Leader leader;
@BeforeEach
public void before() {
this.leader = new Leader(ROLE, ID);
void before() {
leader = new Leader(ROLE, ID);
}
@Test
public void shouldGetRole() {
assertThat(this.leader.getRole()).isEqualTo(ROLE);
void shouldGetRole() {
assertThat(leader.getRole()).isEqualTo(ROLE);
}
@Test
public void shouldGetId() {
assertThat(this.leader.getId()).isEqualTo(ID);
void shouldGetId() {
assertThat(leader.getId()).isEqualTo(ID);
}
@Test
public void shouldCheckWithNullCandidate() {
assertThat(this.leader.isCandidate(null)).isEqualTo(false);
void shouldCheckWithNullCandidate() {
assertThat(leader.isCandidate(null)).isEqualTo(false);
}
@Test
public void shouldCheckCandidate() {
given(this.mockCandidate.getId()).willReturn(ID);
given(this.mockCandidate.getRole()).willReturn(ROLE);
assertThat(this.leader.isCandidate(this.mockCandidate)).isTrue();
void shouldCheckCandidate() {
Candidate candidate = new DefaultCandidate(ID, ROLE);
assertThat(leader.isCandidate(candidate)).isTrue();
}
}