Skip to content

Commit fdb4eba

Browse files
committed
framework-cluster: use bind.interface for cluster communication
Fixes #11460 Signed-off-by: Abhishek Kumar <[email protected]>
1 parent 5a8a1e2 commit fdb4eba

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

framework/cluster/src/main/java/com/cloud/cluster/ClusterServiceServletImpl.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.io.IOException;
2020
import java.io.UnsupportedEncodingException;
21+
import java.net.InetAddress;
22+
import java.net.UnknownHostException;
2123
import java.rmi.RemoteException;
2224
import java.security.GeneralSecurityException;
2325
import java.util.ArrayList;
@@ -26,6 +28,7 @@
2628
import javax.net.ssl.SSLContext;
2729

2830
import org.apache.cloudstack.framework.ca.CAService;
31+
import org.apache.cloudstack.utils.ServerPropertiesUtil;
2932
import org.apache.commons.httpclient.HttpStatus;
3033
import org.apache.http.NameValuePair;
3134
import org.apache.http.client.config.RequestConfig;
@@ -41,6 +44,7 @@
4144

4245
import com.cloud.utils.HttpUtils;
4346
import com.cloud.utils.Profiler;
47+
import com.cloud.utils.StringUtils;
4448
import com.cloud.utils.nio.Link;
4549
import com.google.gson.Gson;
4650

@@ -162,6 +166,20 @@ private String executePostMethod(final CloseableHttpClient client, final HttpPos
162166
return result;
163167
}
164168

169+
protected InetAddress getBindAddressIfAvailable() {
170+
String bindAddressStr = ServerPropertiesUtil.getProperty("bind.interface");
171+
InetAddress bindAddress = null;
172+
try {
173+
if (StringUtils.isNotBlank(bindAddressStr)) {
174+
bindAddress = InetAddress.getByName(bindAddressStr);
175+
}
176+
} catch (UnknownHostException e) {
177+
logger.error("Unable to resolve bind address: {}", bindAddressStr, e);
178+
throw new RuntimeException(e);
179+
}
180+
return bindAddress;
181+
}
182+
165183
private CloseableHttpClient getHttpClient() {
166184
if (s_client == null) {
167185
SSLContext sslContext = null;
@@ -172,7 +190,9 @@ private CloseableHttpClient getHttpClient() {
172190
}
173191

174192
int timeout = ClusterServiceAdapter.ClusterMessageTimeOut.value() * 1000;
193+
InetAddress bindAddress = getBindAddressIfAvailable();
175194
RequestConfig config = RequestConfig.custom()
195+
.setLocalAddress(bindAddress)
176196
.setConnectTimeout(timeout)
177197
.setConnectionRequestTimeout(timeout)
178198
.setSocketTimeout(timeout).build();

framework/cluster/src/test/java/com/cloud/cluster/ClusterServiceServletImplTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@
1717

1818
package com.cloud.cluster;
1919

20+
import java.net.InetAddress;
2021
import java.util.List;
2122
import java.util.Optional;
2223

24+
import org.apache.cloudstack.utils.ServerPropertiesUtil;
2325
import org.apache.commons.collections.CollectionUtils;
2426
import org.apache.http.NameValuePair;
2527
import org.junit.Assert;
2628
import org.junit.Test;
2729
import org.junit.runner.RunWith;
2830
import org.mockito.InjectMocks;
31+
import org.mockito.MockedStatic;
2932
import org.mockito.Mockito;
3033
import org.mockito.junit.MockitoJUnitRunner;
3134

@@ -61,4 +64,40 @@ public void testPingPostParameters() {
6164
val = opt.get();
6265
Assert.assertEquals(peer, val.getValue());
6366
}
67+
68+
@Test
69+
public void getBindAddressIfAvailable_returnsInetAddress_whenBindAddressIsValid() {
70+
try (MockedStatic<ServerPropertiesUtil> ignored = Mockito.mockStatic(ServerPropertiesUtil.class)) {
71+
Mockito.when(ServerPropertiesUtil.getProperty("bind.interface")).thenReturn("127.0.0.1");
72+
73+
InetAddress result = clusterServiceServlet.getBindAddressIfAvailable();
74+
75+
Assert.assertNotNull(result);
76+
Assert.assertEquals("127.0.0.1", result.getHostAddress());
77+
} catch (RuntimeException e) {
78+
Assert.fail("Unexpected RuntimeException: " + e.getMessage());
79+
}
80+
}
81+
82+
@Test
83+
public void getBindAddressIfAvailable_returnsNull_whenBindAddressIsBlank() {
84+
try (MockedStatic<ServerPropertiesUtil> ignored = Mockito.mockStatic(ServerPropertiesUtil.class)) {
85+
Mockito.when(ServerPropertiesUtil.getProperty("bind.interface")).thenReturn("");
86+
87+
InetAddress result = clusterServiceServlet.getBindAddressIfAvailable();
88+
89+
Assert.assertNull(result);
90+
} catch (RuntimeException e) {
91+
Assert.fail("Unexpected RuntimeException: " + e.getMessage());
92+
}
93+
}
94+
95+
@Test(expected = RuntimeException.class)
96+
public void getBindAddressIfAvailable_throwsRuntimeException_whenBindAddressIsInvalid() throws RuntimeException {
97+
try (MockedStatic<ServerPropertiesUtil> ignored = Mockito.mockStatic(ServerPropertiesUtil.class)) {
98+
Mockito.when(ServerPropertiesUtil.getProperty("bind.interface")).thenReturn("invalid-address");
99+
100+
clusterServiceServlet.getBindAddressIfAvailable();
101+
}
102+
}
64103
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.cloudstack.utils;
19+
20+
import java.io.File;
21+
import java.io.FileInputStream;
22+
import java.io.IOException;
23+
import java.util.Properties;
24+
import java.util.concurrent.atomic.AtomicReference;
25+
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
29+
import com.cloud.utils.PropertiesUtil;
30+
31+
public class ServerPropertiesUtil {
32+
private static final Logger logger = LoggerFactory.getLogger(ServerPropertiesUtil.class);
33+
private static final String PROPERTIES_FILE = "server.properties";
34+
private static final AtomicReference<Properties> propertiesRef = new AtomicReference<>();
35+
36+
public static String getProperty(String name) {
37+
Properties props = propertiesRef.get();
38+
if (props != null) {
39+
return props.getProperty(name);
40+
}
41+
File propsFile = PropertiesUtil.findConfigFile(PROPERTIES_FILE);
42+
if (propsFile == null) {
43+
logger.error("{} file not found", PROPERTIES_FILE);
44+
return null;
45+
}
46+
Properties tempProps = new Properties();
47+
try (FileInputStream is = new FileInputStream(propsFile)) {
48+
tempProps.load(is);
49+
} catch (IOException e) {
50+
logger.error("Error loading {}: {}", PROPERTIES_FILE, e.getMessage(), e);
51+
return null;
52+
}
53+
if (!propertiesRef.compareAndSet(null, tempProps)) {
54+
tempProps = propertiesRef.get();
55+
}
56+
return tempProps.getProperty(name);
57+
}
58+
59+
public static String getProperty(String name, String defaultValue) {
60+
String value = getProperty(name);
61+
if (value == null) {
62+
value = defaultValue;
63+
}
64+
return value;
65+
}
66+
}

0 commit comments

Comments
 (0)