3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.application.cluster.health;
22 import java.util.Properties;
23 import java.util.Random;
24 import javax.annotation.Nullable;
25 import org.junit.Test;
26 import org.sonar.application.cluster.ClusterAppState;
27 import org.sonar.process.NetworkUtils;
28 import org.sonar.process.ProcessId;
29 import org.sonar.process.Props;
30 import org.sonar.process.cluster.health.NodeHealth;
32 import static java.lang.String.valueOf;
33 import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
34 import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
35 import static org.assertj.core.api.Assertions.assertThatThrownBy;
36 import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
37 import static org.mockito.Mockito.mock;
38 import static org.mockito.Mockito.when;
39 import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HOST;
40 import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HZ_PORT;
41 import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_NAME;
43 public class SearchNodeHealthProviderTest {
45 private final Random random = new Random();
46 private SearchNodeHealthProvider.Clock clock = mock(SearchNodeHealthProvider.Clock.class);
47 private NetworkUtils networkUtils = mock(NetworkUtils.class);
48 private ClusterAppState clusterAppState = mock(ClusterAppState.class);
51 public void constructor_throws_IAE_if_property_node_name_is_not_set() {
52 Props props = new Props(new Properties());
54 assertThatThrownBy(() -> new SearchNodeHealthProvider(props, clusterAppState, networkUtils))
55 .isInstanceOf(IllegalArgumentException.class)
56 .hasMessage("Missing property: sonar.cluster.node.name");
60 public void constructor_throws_NPE_if_NetworkUtils_getHostname_returns_null_and_property_is_not_set() {
61 Properties properties = new Properties();
62 properties.put(CLUSTER_NODE_NAME.getKey(), randomAlphanumeric(3));
63 Props props = new Props(properties);
65 assertThatThrownBy(() -> new SearchNodeHealthProvider(props, clusterAppState, networkUtils, clock))
66 .isInstanceOf(NullPointerException.class);
70 public void constructor_throws_IAE_if_property_node_port_is_not_set() {
71 Properties properties = new Properties();
72 properties.put(CLUSTER_NODE_NAME.getKey(), randomAlphanumeric(3));
73 when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(34));
74 Props props = new Props(properties);
76 assertThatThrownBy(() -> new SearchNodeHealthProvider(props, clusterAppState, networkUtils, clock))
77 .isInstanceOf(IllegalArgumentException.class)
78 .hasMessage("Missing property: sonar.cluster.node.port");
82 public void constructor_throws_FormatException_if_property_node_port_is_not_an_integer() {
83 String port = randomAlphabetic(3);
84 Properties properties = new Properties();
85 properties.put(CLUSTER_NODE_NAME.getKey(), randomAlphanumeric(3));
86 properties.put(CLUSTER_NODE_HZ_PORT.getKey(), port);
87 when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(34));
88 Props props = new Props(properties);
90 assertThatThrownBy(() -> new SearchNodeHealthProvider(props, clusterAppState, networkUtils, clock))
91 .isInstanceOf(NumberFormatException.class)
92 .hasMessage("For input string: \"" + port + "\"");
96 public void get_returns_name_and_port_from_properties_at_constructor_time() {
97 String name = randomAlphanumeric(3);
98 int port = 1 + random.nextInt(4);
99 Properties properties = new Properties();
100 properties.setProperty(CLUSTER_NODE_NAME.getKey(), name);
101 properties.setProperty(CLUSTER_NODE_HZ_PORT.getKey(), valueOf(port));
102 when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(34));
103 when(clock.now()).thenReturn(1L + random.nextInt(87));
104 SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), clusterAppState, networkUtils, clock);
106 NodeHealth nodeHealth = underTest.get();
108 assertThat(nodeHealth.getDetails().getName()).isEqualTo(name);
109 assertThat(nodeHealth.getDetails().getPort()).isEqualTo(port);
111 // change values in properties
112 properties.setProperty(CLUSTER_NODE_NAME.getKey(), randomAlphanumeric(6));
113 properties.setProperty(CLUSTER_NODE_HZ_PORT.getKey(), valueOf(1 + random.nextInt(99)));
115 NodeHealth newNodeHealth = underTest.get();
117 assertThat(newNodeHealth.getDetails().getName()).isEqualTo(name);
118 assertThat(newNodeHealth.getDetails().getPort()).isEqualTo(port);
122 public void get_returns_host_from_property_if_set_at_constructor_time() {
123 String host = randomAlphanumeric(55);
124 Properties properties = new Properties();
125 properties.setProperty(CLUSTER_NODE_NAME.getKey(), randomAlphanumeric(3));
126 properties.setProperty(CLUSTER_NODE_HZ_PORT.getKey(), valueOf(1 + random.nextInt(4)));
127 properties.setProperty(CLUSTER_NODE_HOST.getKey(), host);
128 when(clock.now()).thenReturn(1L + random.nextInt(87));
129 SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), clusterAppState, networkUtils, clock);
131 NodeHealth nodeHealth = underTest.get();
133 assertThat(nodeHealth.getDetails().getHost()).isEqualTo(host);
136 properties.setProperty(CLUSTER_NODE_HOST.getKey(), randomAlphanumeric(96));
138 NodeHealth newNodeHealth = underTest.get();
140 assertThat(newNodeHealth.getDetails().getHost()).isEqualTo(host);
144 public void get_returns_host_from_NetworkUtils_getHostname_if_property_is_not_set_at_constructor_time() {
145 getReturnsHostFromNetworkUtils(null);
149 public void get_returns_host_from_NetworkUtils_getHostname_if_property_is_empty_at_constructor_time() {
150 getReturnsHostFromNetworkUtils(random.nextBoolean() ? "" : " ");
153 private void getReturnsHostFromNetworkUtils(@Nullable String hostPropertyValue) {
154 String host = randomAlphanumeric(34);
155 Properties properties = new Properties();
156 properties.setProperty(CLUSTER_NODE_NAME.getKey(), randomAlphanumeric(3));
157 properties.setProperty(CLUSTER_NODE_HZ_PORT.getKey(), valueOf(1 + random.nextInt(4)));
158 if (hostPropertyValue != null) {
159 properties.setProperty(CLUSTER_NODE_HOST.getKey(), hostPropertyValue);
161 when(clock.now()).thenReturn(1L + random.nextInt(87));
162 when(networkUtils.getHostname()).thenReturn(host);
163 SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), clusterAppState, networkUtils, clock);
165 NodeHealth nodeHealth = underTest.get();
167 assertThat(nodeHealth.getDetails().getHost()).isEqualTo(host);
170 when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(96));
172 NodeHealth newNodeHealth = underTest.get();
174 assertThat(newNodeHealth.getDetails().getHost()).isEqualTo(host);
178 public void get_returns_started_from_System2_now_at_constructor_time() {
179 Properties properties = new Properties();
180 long now = setRequiredPropertiesAndMocks(properties);
181 SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), clusterAppState, networkUtils, clock);
183 NodeHealth nodeHealth = underTest.get();
185 assertThat(nodeHealth.getDetails().getStartedAt()).isEqualTo(now);
188 when(clock.now()).thenReturn(now);
190 NodeHealth newNodeHealth = underTest.get();
192 assertThat(newNodeHealth.getDetails().getStartedAt()).isEqualTo(now);
196 public void get_returns_status_GREEN_if_elasticsearch_process_is_operational_in_ClusterAppState() {
197 Properties properties = new Properties();
198 setRequiredPropertiesAndMocks(properties);
199 when(clusterAppState.isOperational(ProcessId.ELASTICSEARCH, true)).thenReturn(true);
200 SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), clusterAppState, networkUtils, clock);
202 NodeHealth nodeHealth = underTest.get();
204 assertThat(nodeHealth.getStatus()).isEqualTo(NodeHealth.Status.GREEN);
208 public void get_returns_status_RED_with_cause_if_elasticsearch_process_is_not_operational_in_ClusterAppState() {
209 Properties properties = new Properties();
210 setRequiredPropertiesAndMocks(properties);
211 when(clusterAppState.isOperational(ProcessId.ELASTICSEARCH, true)).thenReturn(false);
212 SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), clusterAppState, networkUtils, clock);
214 NodeHealth nodeHealth = underTest.get();
216 assertThat(nodeHealth.getStatus()).isEqualTo(NodeHealth.Status.RED);
217 assertThat(nodeHealth.getCauses()).containsOnly("Elasticsearch is not operational");
220 private long setRequiredPropertiesAndMocks(Properties properties) {
221 properties.setProperty(CLUSTER_NODE_NAME.getKey(), randomAlphanumeric(3));
222 properties.setProperty(CLUSTER_NODE_HZ_PORT.getKey(), valueOf(1 + random.nextInt(4)));
223 long now = 1L + random.nextInt(87);
224 when(clock.now()).thenReturn(now);
225 when(networkUtils.getHostname()).thenReturn(randomAlphanumeric(34));