]> source.dussan.org Git - sonarqube.git/blob
3c4b5235f5ccb5ec3b5b6b7209818e5f2a919ba5
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.application.cluster;
21
22 import com.google.common.collect.ImmutableMap;
23 import com.hazelcast.cluster.Address;
24 import com.hazelcast.cluster.Cluster;
25 import com.hazelcast.cluster.Member;
26 import com.hazelcast.cluster.MemberSelector;
27 import com.hazelcast.cp.IAtomicReference;
28 import java.net.InetAddress;
29 import java.net.UnknownHostException;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.LinkedHashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.UUID;
37 import java.util.concurrent.locks.Lock;
38 import java.util.function.Consumer;
39 import org.junit.After;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.sonar.application.config.TestAppSettings;
43 import org.sonar.core.util.RuleActivationListener;
44 import org.sonar.core.util.RuleSetChangedEvent;
45 import org.sonar.process.cluster.hz.DistributedAnswer;
46 import org.sonar.process.cluster.hz.DistributedCall;
47 import org.sonar.process.cluster.hz.DistributedCallback;
48 import org.sonar.process.cluster.hz.HazelcastMember;
49
50 import static org.assertj.core.api.Assertions.assertThatThrownBy;
51 import static org.mockito.Mockito.mock;
52 import static org.mockito.Mockito.verify;
53 import static org.mockito.Mockito.verifyNoMoreInteractions;
54 import static org.mockito.Mockito.when;
55 import static org.sonar.process.ProcessProperties.Property.CLUSTER_HZ_HOSTS;
56
57 public class AppNodesClusterHostsConsistencyTest {
58
59   @SuppressWarnings("unchecked")
60   private final Consumer<String> logger = mock(Consumer.class);
61
62   @Before
63   @After
64   public void setUp() {
65     AppNodesClusterHostsConsistency.clearInstance();
66   }
67
68   @Test
69   public void log_warning_if_configured_hosts_are_not_consistent() throws UnknownHostException {
70     Map<Member, List<String>> hostsPerMember = new LinkedHashMap<>();
71     Member m1 = newLocalHostMember(1, true);
72     Member m2 = newLocalHostMember(2);
73     Member m3 = newLocalHostMember(3);
74
75     hostsPerMember.put(m2, Arrays.asList("1.1.1.1:1000", "1.1.1.1:2000"));
76     hostsPerMember.put(m3, Arrays.asList("1.1.1.1:1000", "1.1.1.2:1000"));
77
78     TestAppSettings settings = new TestAppSettings(ImmutableMap.of(CLUSTER_HZ_HOSTS.getKey(), "1.1.1.1:1000,1.1.1.1:2000,1.1.1.2:1000"));
79
80     TestHazelcastMember member = new TestHazelcastMember(hostsPerMember, m1);
81     AppNodesClusterHostsConsistency underTest = AppNodesClusterHostsConsistency.setInstance(member, settings, logger);
82     underTest.check();
83
84     verify(logger).accept("The configuration of the current node doesn't match the list of hosts configured in the application nodes that have already joined the cluster:\n" +
85       m1.getAddress().getHost() + ":" + m1.getAddress().getPort() + " : [1.1.1.1:1000, 1.1.1.1:2000, 1.1.1.2:1000] (current)\n" +
86       m2.getAddress().getHost() + ":" + m2.getAddress().getPort() + " : [1.1.1.1:1000, 1.1.1.1:2000]\n" +
87       m3.getAddress().getHost() + ":" + m3.getAddress().getPort() + " : [1.1.1.1:1000, 1.1.1.2:1000]\n" +
88       "Make sure the configuration is consistent among all application nodes before you restart any node");
89     verifyNoMoreInteractions(logger);
90   }
91
92   @Test
93   public void dont_log_if_configured_hosts_are_consistent() throws UnknownHostException {
94     Map<Member, List<String>> hostsPerMember = new LinkedHashMap<>();
95     Member m1 = newLocalHostMember(1, true);
96     Member m2 = newLocalHostMember(2);
97     Member m3 = newLocalHostMember(3);
98
99     hostsPerMember.put(m2, Arrays.asList("1.1.1.1:1000", "1.1.1.1:2000", "1.1.1.2:1000"));
100     hostsPerMember.put(m3, Arrays.asList("1.1.1.1:1000", "1.1.1.1:2000", "1.1.1.2:1000"));
101
102     TestAppSettings settings = new TestAppSettings(ImmutableMap.of(CLUSTER_HZ_HOSTS.getKey(), "1.1.1.1:1000,1.1.1.1:2000,1.1.1.2:1000"));
103
104     TestHazelcastMember member = new TestHazelcastMember(hostsPerMember, m1);
105     AppNodesClusterHostsConsistency underTest = AppNodesClusterHostsConsistency.setInstance(member, settings, logger);
106     underTest.check();
107
108     verifyNoMoreInteractions(logger);
109   }
110
111   @Test
112   public void setInstance_fails_with_ISE_when_called_twice_with_same_arguments() throws UnknownHostException {
113     TestHazelcastMember member = new TestHazelcastMember(Collections.emptyMap(), newLocalHostMember(1, true));
114     TestAppSettings settings = new TestAppSettings();
115     AppNodesClusterHostsConsistency.setInstance(member, settings);
116
117     assertThatThrownBy(() -> AppNodesClusterHostsConsistency.setInstance(member, settings))
118       .isInstanceOf(IllegalStateException.class)
119       .hasMessage("Instance is already set");
120   }
121
122   @Test
123   public void setInstance_fails_with_ISE_when_called_twice_with_other_arguments() throws UnknownHostException {
124     TestHazelcastMember member1 = new TestHazelcastMember(Collections.emptyMap(), newLocalHostMember(1, true));
125     TestHazelcastMember member2 = new TestHazelcastMember(Collections.emptyMap(), newLocalHostMember(2, true));
126     AppNodesClusterHostsConsistency.setInstance(member1, new TestAppSettings());
127
128     assertThatThrownBy(() -> AppNodesClusterHostsConsistency.setInstance(member2, new TestAppSettings()))
129       .isInstanceOf(IllegalStateException.class)
130       .hasMessage("Instance is already set");
131   }
132
133   private Member newLocalHostMember(int port) throws UnknownHostException {
134     return newLocalHostMember(port, false);
135   }
136
137   private Member newLocalHostMember(int port, boolean localMember) throws UnknownHostException {
138     Member member = mock(Member.class);
139     when(member.localMember()).thenReturn(localMember);
140     Address address1 = new Address(InetAddress.getLocalHost(), port);
141     when(member.getAddress()).thenReturn(address1);
142     return member;
143   }
144
145   private static class TestHazelcastMember implements HazelcastMember {
146     private final Map<Member, List<String>> hostsPerMember;
147     private final Cluster cluster = mock(Cluster.class);
148
149     private TestHazelcastMember(Map<Member, List<String>> hostsPerMember, Member localMember) {
150       this.hostsPerMember = hostsPerMember;
151       when(cluster.getLocalMember()).thenReturn(localMember);
152     }
153
154     @Override
155     public <E> IAtomicReference<E> getAtomicReference(String name) {
156       throw new IllegalStateException("not expected to be called");
157     }
158
159     @Override
160     public <K, V> Map<K, V> getReplicatedMap(String name) {
161       throw new IllegalStateException("not expected to be called");
162     }
163
164     @Override
165     public UUID getUuid() {
166       throw new IllegalStateException("not expected to be called");
167     }
168
169     @Override
170     public Set<UUID> getMemberUuids() {
171       throw new IllegalStateException("not expected to be called");
172     }
173
174     @Override
175     public Lock getLock(String name) {
176       throw new IllegalStateException("not expected to be called");
177     }
178
179     @Override
180     public long getClusterTime() {
181       throw new IllegalStateException("not expected to be called");
182     }
183
184     @Override
185     public Cluster getCluster() {
186       return cluster;
187     }
188
189     @Override
190     public <T> DistributedAnswer<T> call(DistributedCall<T> callable, MemberSelector memberSelector, long timeoutMs) {
191       throw new IllegalStateException("not expected to be called");
192     }
193
194     @Override
195     public <T> void callAsync(DistributedCall<T> callable, MemberSelector memberSelector, DistributedCallback<T> callback) {
196       callback.onComplete((Map<Member, T>) hostsPerMember);
197     }
198
199     @Override
200     public void subscribeRuleActivationTopic(RuleActivationListener listener) {
201
202     }
203
204     @Override
205     public void publishEvent(RuleSetChangedEvent event) {
206
207     }
208
209     @Override
210     public void close() {
211
212     }
213   }
214 }