aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorMatteo Mara <matteo.mara@sonarsource.com>2024-10-24 18:00:17 +0200
committerLukasz Jarocki <lukasz.jarocki@sonarsource.com>2025-02-28 09:57:46 +0100
commit87a57454f6726dab664945304d21ea6935093e29 (patch)
tree5fce34ee10b3f83f358cb8a442253dcdc33379e2 /server
parent9d4672c454b808d6fa5496c57e5cc7d3bb5bdb25 (diff)
downloadsonarqube-87a57454f6726dab664945304d21ea6935093e29.tar.gz
sonarqube-87a57454f6726dab664945304d21ea6935093e29.zip
SONAR-23454 Add telemetry data about usage of IPv6
Diffstat (limited to 'server')
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java12
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/NetworkUtilsImpl.java14
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/NetworkUtilsImplTest.java49
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java2
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/telemetry/TelemetryIpv6EnabledProvider.java56
-rw-r--r--server/sonar-webserver/src/test/java/org/sonar/server/platform/telemetry/TelemetryIpv6EnabledProviderTest.java108
6 files changed, 223 insertions, 18 deletions
diff --git a/server/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java b/server/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java
index e1bb4f91938..056c8978407 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java
@@ -20,6 +20,7 @@
package org.sonar.process;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Optional;
import java.util.OptionalInt;
@@ -51,6 +52,8 @@ public interface NetworkUtils {
boolean isLoopback(String hostOrAddress);
+ boolean isIpv6Address(String hostOrAddress);
+
/**
* Returns the machine {@link InetAddress} that matches the specified
* predicate. If multiple addresses match then a single one
@@ -66,4 +69,13 @@ public interface NetworkUtils {
default Optional<InetAddress> getLocalNonLoopbackIpv4Address() {
return getLocalInetAddress(a -> !a.isLoopbackAddress() && a instanceof Inet4Address);
}
+
+ /**
+ * Returns a local {@link InetAddress} that is IPv6 and not
+ * loopback. If multiple addresses match then a single one
+ * is picked in a non deterministic way.
+ */
+ default Optional<InetAddress> getLocalNonLoopbackIpv6Address() {
+ return getLocalInetAddress(a -> !a.isLoopbackAddress() && a instanceof Inet6Address);
+ }
}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/NetworkUtilsImpl.java b/server/sonar-process/src/main/java/org/sonar/process/NetworkUtilsImpl.java
index 6bc1237d9de..d40a63969ac 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/NetworkUtilsImpl.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/NetworkUtilsImpl.java
@@ -22,6 +22,7 @@ package org.sonar.process;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.InetAddresses;
import java.io.IOException;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
@@ -102,7 +103,7 @@ public class NetworkUtilsImpl implements NetworkUtils {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
LOG.trace("Failed to get hostname", e);
- return "unresolved hostname";
+ return "unresolved hostname";
}
}
@@ -145,6 +146,17 @@ public class NetworkUtilsImpl implements NetworkUtils {
}
@Override
+ public boolean isIpv6Address(String hostOrAddress) {
+ Optional<InetAddress> inetAddress = toInetAddress(hostOrAddress);
+ boolean ipv6Preferred = Boolean.parseBoolean(System.getProperty("java.net.preferIPv6Addresses"));
+
+ if (inetAddress.isEmpty() || inetAddress.get().isLoopbackAddress()) {
+ return ipv6Preferred;
+ }
+ return inetAddress.get() instanceof Inet6Address;
+ }
+
+ @Override
public Optional<InetAddress> getLocalInetAddress(Predicate<InetAddress> predicate) {
try {
return Collections.list(NetworkInterface.getNetworkInterfaces()).stream()
diff --git a/server/sonar-process/src/test/java/org/sonar/process/NetworkUtilsImplTest.java b/server/sonar-process/src/test/java/org/sonar/process/NetworkUtilsImplTest.java
index 639fc720ff8..f68604a5cfc 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/NetworkUtilsImplTest.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/NetworkUtilsImplTest.java
@@ -27,19 +27,20 @@ import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import org.hamcrest.CoreMatchers;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.apache.commons.lang3.RandomStringUtils.secure;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeThat;
-public class NetworkUtilsImplTest {
+class NetworkUtilsImplTest {
+ public static final String PREFER_IPV_6_ADDRESSES = "java.net.preferIPv6Addresses";
- private NetworkUtilsImpl underTest = new NetworkUtilsImpl();
+ private final NetworkUtilsImpl underTest = new NetworkUtilsImpl();
@Test
- public void getNextAvailablePort_returns_a_port() throws Exception {
+ void getNextAvailablePort_returns_a_port() throws Exception {
String localhost = InetAddress.getLocalHost().getHostName();
int port = underTest.getNextAvailablePort(localhost).getAsInt();
assertThat(port)
@@ -48,7 +49,7 @@ public class NetworkUtilsImplTest {
}
@Test
- public void getNextAvailablePort_does_not_return_twice_the_same_port() throws Exception {
+ void getNextAvailablePort_does_not_return_twice_the_same_port() throws Exception {
String localhost = InetAddress.getLocalHost().getHostName();
Set<Integer> ports = new HashSet<>(Arrays.asList(
underTest.getNextAvailablePort(localhost).getAsInt(),
@@ -58,7 +59,7 @@ public class NetworkUtilsImplTest {
}
@Test
- public void getLocalNonLoopbackIpv4Address_returns_a_valid_local_and_non_loopback_ipv4() {
+ void getLocalNonLoopbackIpv4Address_returns_a_valid_local_and_non_loopback_ipv4() {
Optional<InetAddress> address = underTest.getLocalNonLoopbackIpv4Address();
// address is empty on offline builds
@@ -69,7 +70,7 @@ public class NetworkUtilsImplTest {
}
@Test
- public void getHostname_returns_hostname_of_localhost_otherwise_a_constant() {
+ void getHostname_returns_hostname_of_localhost_otherwise_a_constant() {
try {
InetAddress localHost = InetAddress.getLocalHost();
assertThat(underTest.getHostname()).isEqualTo(localHost.getHostName());
@@ -80,41 +81,41 @@ public class NetworkUtilsImplTest {
}
@Test
- public void getLocalInetAddress_filters_local_addresses() {
+ void getLocalInetAddress_filters_local_addresses() {
InetAddress address = underTest.getLocalInetAddress(InetAddress::isLoopbackAddress).get();
assertThat(address.isLoopbackAddress()).isTrue();
}
@Test
- public void getLocalInetAddress_returns_empty_if_no_local_addresses_match() {
+ void getLocalInetAddress_returns_empty_if_no_local_addresses_match() {
Optional<InetAddress> address = underTest.getLocalInetAddress(a -> false);
assertThat(address).isEmpty();
}
@Test
- public void toInetAddress_supports_host_names() {
+ void toInetAddress_supports_host_names() {
assertThat(underTest.toInetAddress("localhost")).isNotEmpty();
// do not test values that require DNS calls. Build must support offline mode.
}
@Test
- public void toInetAddress_supports_ipv4() {
+ void toInetAddress_supports_ipv4() {
assertThat(underTest.toInetAddress("1.2.3.4")).isNotEmpty();
}
@Test
- public void toInetAddress_supports_ipv6() {
+ void toInetAddress_supports_ipv6() {
assertThat(underTest.toInetAddress("2a01:e34:ef1f:dbb0:c2f6:a978:c5c0:9ccb")).isNotEmpty();
assertThat(underTest.toInetAddress("[2a01:e34:ef1f:dbb0:c2f6:a978:c5c0:9ccb]")).isNotEmpty();
}
@Test
- public void toInetAddress_returns_empty_on_unvalid_IP_and_hostname() {
+ void toInetAddress_returns_empty_on_unvalid_IP_and_hostname() {
assertThat(underTest.toInetAddress(secure().nextAlphabetic(32))).isEmpty();
}
@Test
- public void isLoopback_returns_true_on_loopback_address_or_host() {
+ void isLoopback_returns_true_on_loopback_address_or_host() {
InetAddress loopback = InetAddress.getLoopbackAddress();
assertThat(underTest.isLoopback(loopback.getHostAddress())).isTrue();
@@ -122,7 +123,7 @@ public class NetworkUtilsImplTest {
}
@Test
- public void isLoopback_returns_true_on_localhost_address_or_host_if_loopback() {
+ void isLoopback_returns_true_on_localhost_address_or_host_if_loopback() {
try {
InetAddress localHost = InetAddress.getLocalHost();
boolean isLoopback = localHost.isLoopbackAddress();
@@ -134,7 +135,7 @@ public class NetworkUtilsImplTest {
}
@Test
- public void isLocal_returns_true_on_loopback_address_or_host() {
+ void isLocal_returns_true_on_loopback_address_or_host() {
InetAddress loopback = InetAddress.getLoopbackAddress();
assertThat(underTest.isLocal(loopback.getHostAddress())).isTrue();
@@ -142,7 +143,7 @@ public class NetworkUtilsImplTest {
}
@Test
- public void isLocal_returns_true_on_localhost_address_or_host() {
+ void isLocal_returns_true_on_localhost_address_or_host() {
try {
InetAddress localHost = InetAddress.getLocalHost();
@@ -152,4 +153,18 @@ public class NetworkUtilsImplTest {
// ignore, host running the test has no localhost
}
}
+
+ @Test
+ void isIpv6Address_returnsAccordingToAddressAndIPv6Preference() {
+ System.setProperty(PREFER_IPV_6_ADDRESSES, "false");
+ assertThat(underTest.isIpv6Address("2001:db8:3::91")).isTrue();
+ assertThat(underTest.isIpv6Address("192.168.1.1")).isFalse();
+ assertThat(underTest.isIpv6Address("")).isFalse();
+
+ System.setProperty(PREFER_IPV_6_ADDRESSES, "true");
+ assertThat(underTest.isIpv6Address("2001:db8:3::91")).isTrue();
+ assertThat(underTest.isIpv6Address("192.168.1.1")).isFalse();
+ assertThat(underTest.isIpv6Address("")).isTrue();
+ }
+
}
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index 6e677fcfd0b..399170da48e 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -199,6 +199,7 @@ import org.sonar.server.platform.db.CheckAnyonePermissionsAtStartup;
import org.sonar.server.platform.db.migration.DatabaseMigrationPersister;
import org.sonar.server.platform.db.migration.DatabaseMigrationTelemetry;
import org.sonar.server.platform.telemetry.TelemetryFipsEnabledProvider;
+import org.sonar.server.platform.telemetry.TelemetryIpv6EnabledProvider;
import org.sonar.server.platform.telemetry.TelemetryMQRModePropertyProvider;
import org.sonar.server.platform.telemetry.TelemetryNclocProvider;
import org.sonar.server.platform.telemetry.TelemetryPortfolioSelectionModeProvider;
@@ -692,6 +693,7 @@ public class PlatformLevel4 extends PlatformLevel {
TelemetryNclocProvider.class,
TelemetryUserEnabledProvider.class,
TelemetryFipsEnabledProvider.class,
+ TelemetryIpv6EnabledProvider.class,
TelemetrySubportfolioSelectionModeProvider.class,
TelemetryPortfolioSelectionModeProvider.class,
TelemetryApplicationsCountProvider.class,
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/telemetry/TelemetryIpv6EnabledProvider.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/telemetry/TelemetryIpv6EnabledProvider.java
new file mode 100644
index 00000000000..47043be1786
--- /dev/null
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/telemetry/TelemetryIpv6EnabledProvider.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2025 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.telemetry;
+
+import java.util.Optional;
+import org.sonar.api.config.Configuration;
+import org.sonar.process.NetworkUtils;
+import org.sonar.process.ProcessProperties;
+import org.sonar.telemetry.core.AbstractTelemetryDataProvider;
+import org.sonar.telemetry.core.Dimension;
+import org.sonar.telemetry.core.Granularity;
+import org.sonar.telemetry.core.TelemetryDataType;
+
+public class TelemetryIpv6EnabledProvider extends AbstractTelemetryDataProvider<Integer> {
+
+ final NetworkUtils networkUtils;
+
+ private final Configuration configuration;
+
+ public TelemetryIpv6EnabledProvider(NetworkUtils networkUtils, Configuration configuration) {
+ super("internet_protocol_version", Dimension.INSTALLATION, Granularity.MONTHLY, TelemetryDataType.INTEGER);
+ this.networkUtils = networkUtils;
+ this.configuration = configuration;
+ }
+
+ @Override
+ public Optional<Integer> getValue() {
+ if (isIpv6Address()) {
+ return Optional.of(6);
+ } else {
+ return Optional.of(4);
+ }
+ }
+
+ private boolean isIpv6Address() {
+ String ipAddress = configuration.get(ProcessProperties.Property.WEB_HOST.getKey()).orElse("");
+ return networkUtils.isIpv6Address(ipAddress);
+ }
+}
diff --git a/server/sonar-webserver/src/test/java/org/sonar/server/platform/telemetry/TelemetryIpv6EnabledProviderTest.java b/server/sonar-webserver/src/test/java/org/sonar/server/platform/telemetry/TelemetryIpv6EnabledProviderTest.java
new file mode 100644
index 00000000000..049268ae3e7
--- /dev/null
+++ b/server/sonar-webserver/src/test/java/org/sonar/server/platform/telemetry/TelemetryIpv6EnabledProviderTest.java
@@ -0,0 +1,108 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2025 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.telemetry;
+
+import java.net.InetAddress;
+import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.function.Predicate;
+import org.junit.jupiter.api.Test;
+import org.sonar.api.config.Configuration;
+import org.sonar.process.NetworkUtils;
+import org.sonar.telemetry.core.Dimension;
+import org.sonar.telemetry.core.Granularity;
+import org.sonar.telemetry.core.TelemetryDataType;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+class TelemetryIpv6EnabledProviderTest {
+
+ private final Configuration configuration = mock(Configuration.class);
+
+ @Test
+ void testGetters_whenIpv6Address(){
+ assertIpVersion(6);
+ }
+
+ @Test
+ void testGetters_whenIpv4Address(){
+ assertIpVersion(4);
+ }
+
+ void assertIpVersion(Integer expectedProtocol) {
+ TelemetryIpv6EnabledProvider underTest = new TelemetryIpv6EnabledProvider(new NetworkUtilsTestImpl(6 == expectedProtocol), configuration);
+ assertEquals("internet_protocol_version", underTest.getMetricKey());
+ assertEquals(Dimension.INSTALLATION, underTest.getDimension());
+ assertEquals(Granularity.MONTHLY, underTest.getGranularity());
+ assertEquals(TelemetryDataType.INTEGER, underTest.getType());
+ assertEquals(Optional.of(expectedProtocol), underTest.getValue());
+ }
+
+ static class NetworkUtilsTestImpl implements NetworkUtils {
+
+ private final boolean isIpv6Address;
+
+ public NetworkUtilsTestImpl(boolean isIpv6Address) {
+ this.isIpv6Address = isIpv6Address;
+ }
+
+ @Override
+ public int getNextLoopbackAvailablePort() {
+ return 0;
+ }
+
+ @Override
+ public OptionalInt getNextAvailablePort(String hostOrAddress) {
+ return OptionalInt.empty();
+ }
+
+ @Override
+ public String getHostname() {
+ return "";
+ }
+
+ @Override
+ public Optional<InetAddress> toInetAddress(String hostOrAddress) {
+ return Optional.empty();
+ }
+
+ @Override
+ public boolean isLocal(String hostOrAddress) {
+ return false;
+ }
+
+ @Override
+ public boolean isLoopback(String hostOrAddress) {
+ return false;
+ }
+
+ @Override
+ public boolean isIpv6Address(String hostOrAddress) {
+ return isIpv6Address;
+ }
+
+ @Override
+ public Optional<InetAddress> getLocalInetAddress(Predicate<InetAddress> predicate) {
+ return Optional.empty();
+ }
+ }
+
+}