diff options
author | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2017-08-22 16:33:47 +0200 |
---|---|---|
committer | Teryk Bellahsene <teryk@users.noreply.github.com> | 2017-08-30 16:24:53 +0200 |
commit | 5c2b4dc4c2467df7885b05f497b7f006c5a23ded (patch) | |
tree | 0a47c8d09c39882c2f7bbb37dee40368f12e334d | |
parent | 6d6359057ee0e992780f51b9e6b42faa76075c6a (diff) | |
download | sonarqube-5c2b4dc4c2467df7885b05f497b7f006c5a23ded.tar.gz sonarqube-5c2b4dc4c2467df7885b05f497b7f006c5a23ded.zip |
SONAR-9721 Opt-out of telemetry with a property
6 files changed, 96 insertions, 6 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryClient.java b/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryClient.java index d318eef39e9..ac3a6c42529 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryClient.java +++ b/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryClient.java @@ -28,10 +28,13 @@ import okhttp3.Request; import okhttp3.RequestBody; import org.sonar.api.config.Configuration; import org.sonar.api.server.ServerSide; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; @ServerSide public class TelemetryClient { private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + private static final Logger LOG = Loggers.get(TelemetryClient.class); private final OkHttpClient okHttpClient; private final TelemetryUrl serverUrl; @@ -50,6 +53,19 @@ public class TelemetryClient { } } + void optOut(String json) { + Request.Builder request = new Request.Builder(); + request.url(serverUrl.get()); + RequestBody body = RequestBody.create(JSON, json); + request.delete(body); + + try { + okHttpClient.newCall(request.build()).execute(); + } catch (IOException e) { + LOG.debug("Error when sending opt-out usage statistics: %s", e.getMessage()); + } + } + private Request buildHttpRequest(String json) { Request.Builder request = new Request.Builder(); request.url(serverUrl.get()); @@ -57,4 +73,5 @@ public class TelemetryClient { request.post(body); return request.build(); } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDaemon.java b/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDaemon.java index d32b773bcd6..c303529b94e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDaemon.java +++ b/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDaemon.java @@ -30,19 +30,26 @@ import org.sonar.api.config.Configuration; import org.sonar.api.platform.Server; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.System2; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.text.JsonWriter; import org.sonar.server.property.InternalProperties; import static org.sonar.api.utils.DateUtils.formatDate; import static org.sonar.api.utils.DateUtils.parseDate; +import static org.sonar.server.telemetry.TelemetryProperties.PROP_ENABLE; +import static org.sonar.server.telemetry.TelemetryProperties.PROP_URL; @ServerSide public class TelemetryDaemon implements Startable { private static final String THREAD_NAME_PREFIX = "sq-telemetry-service-"; private static final int SEVEN_DAYS = 7 * 24 * 60 * 60 * 1_000; private static final String I_PROP_LAST_PING = "sonar.telemetry.lastPing"; + private static final String I_PROP_OPT_OUT = "sonar.telemetry.optOut"; + private static final Logger LOG = Loggers.get(TelemetryDaemon.class); private final TelemetryClient telemetryClient; + private final Configuration config; private final InternalProperties internalProperties; private final Server server; private final System2 system2; @@ -50,16 +57,38 @@ public class TelemetryDaemon implements Startable { private ScheduledExecutorService executorService; - public TelemetryDaemon(TelemetryClient telemetryClient, InternalProperties internalProperties, Server server, System2 system2, Configuration config) { + public TelemetryDaemon(TelemetryClient telemetryClient, Configuration config, InternalProperties internalProperties, Server server, System2 system2) { this.telemetryClient = telemetryClient; + this.config = config; + this.frequencyInSeconds = new TelemetryFrequency(config); this.internalProperties = internalProperties; this.server = server; - this.frequencyInSeconds = new TelemetryFrequency(config); this.system2 = system2; } @Override public void start() { + boolean isTelemetryActivated = config.getBoolean(PROP_ENABLE).orElseThrow(() -> new IllegalStateException(String.format("Setting '%s' must be provided.", PROP_URL))); + if (!internalProperties.read(I_PROP_OPT_OUT).isPresent()) { + if (!isTelemetryActivated) { + StringWriter json = new StringWriter(); + try (JsonWriter writer = JsonWriter.of(json)) { + writer.beginObject(); + writer.prop("id", server.getId()); + writer.endObject(); + } + telemetryClient.optOut(json.toString()); + internalProperties.write(I_PROP_OPT_OUT, String.valueOf(system2.now())); + LOG.info("Sharing of SonarQube statistics is disabled."); + } else { + internalProperties.write(I_PROP_OPT_OUT, null); + } + } + + if (!isTelemetryActivated) { + return; + } + LOG.info("Sharing of SonarQube statistics is enabled."); executorService = Executors.newSingleThreadScheduledExecutor( new ThreadFactoryBuilder() .setNameFormat(THREAD_NAME_PREFIX + "%d") diff --git a/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryProperties.java b/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryProperties.java index a77a67fa67c..90a5f437986 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryProperties.java +++ b/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryProperties.java @@ -25,6 +25,11 @@ import org.sonar.api.Property; @Properties({ @Property( + key = TelemetryProperties.PROP_ENABLE, + defaultValue = "true", + name = "Share SonarQube statistics", + global = false), + @Property( key = TelemetryProperties.PROP_FREQUENCY, // 6 hours in seconds defaultValue = "21600", @@ -37,6 +42,7 @@ import org.sonar.api.Property; global = false) }) public class TelemetryProperties { + static final String PROP_ENABLE = "sonar.telemetry.enable"; static final String PROP_FREQUENCY = "sonar.telemetry.frequency"; static final String PROP_URL = "sonar.telemetry.url"; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java b/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java index 62011777a39..3d0d6643638 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java @@ -55,7 +55,7 @@ public class TelemetryDaemonTest { settings = new MapSettings(new PropertyDefinitions(TelemetryProperties.class)); system2.setNow(System.currentTimeMillis()); - underTest = new TelemetryDaemon(client, internalProperties, server, system2, settings.asConfig()); + underTest = new TelemetryDaemon(client, settings.asConfig(), internalProperties, server, system2); } @Test @@ -63,7 +63,7 @@ public class TelemetryDaemonTest { settings.setProperty("sonar.telemetry.frequency", "1"); underTest.start(); - verify(client, timeout(2_000).atLeastOnce()).send(anyString()); + verify(client, timeout(1_000).atLeastOnce()).send(anyString()); } @Test @@ -73,7 +73,6 @@ public class TelemetryDaemonTest { long sevenDaysAgo = now - (ONE_DAY * 7L); internalProperties.write("sonar.telemetry.lastPing", String.valueOf(sixDaysAgo)); settings.setProperty("sonar.telemetry.frequency", "1"); - underTest = new TelemetryDaemon(client, internalProperties, server, system2, settings.asConfig()); underTest.start(); verify(client, timeout(1_000).never()).send(anyString()); internalProperties.write("sonar.telemetry.lastPing", String.valueOf(sevenDaysAgo)); @@ -114,7 +113,19 @@ public class TelemetryDaemonTest { underTest.start(); - verify(client, timeout(2_000)).send(anyString()); + verify(client, timeout(1_000).atLeastOnce()).send(anyString()); assertThat(internalProperties.read("sonar.telemetry.lastPing").get()).isEqualTo(String.valueOf(today)); } + + @Test + public void opt_out_sent_once() { + settings.setProperty("sonar.telemetry.frequency", "1"); + settings.setProperty("sonar.telemetry.enable", "false"); + underTest.start(); + underTest.start(); + + + verify(client, timeout(1_000).never()).send(anyString()); + verify(client, timeout(1_000).times(1)).optOut(anyString()); + } } diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties index dbc2994af1b..4b5de0da3ec 100644 --- a/sonar-application/src/main/assembly/conf/sonar.properties +++ b/sonar-application/src/main/assembly/conf/sonar.properties @@ -342,6 +342,12 @@ #sonar.path.data=data #sonar.path.temp=temp +# Telemetry - Share anonymous SonarQube statistics +# By sharing anonymous SonarQube statistics, you help us understand how SonarQube is used so we can improve the product to work even better for you. +# We don't collect source code or IP addresses. And we don't share the data with anyone else. +# To see an example of the data shared: login as a global administrator, call the WS api/system/info and check the Statistics field. +#sonar.telemetry.enable=true + #-------------------------------------------------------------------------------------------------- # DEVELOPMENT - only for developers diff --git a/tests/src/test/java/org/sonarqube/tests/telemetry/TelemetryTest.java b/tests/src/test/java/org/sonarqube/tests/telemetry/TelemetryTest.java index c6b20b58fb7..e65fb865180 100644 --- a/tests/src/test/java/org/sonarqube/tests/telemetry/TelemetryTest.java +++ b/tests/src/test/java/org/sonarqube/tests/telemetry/TelemetryTest.java @@ -70,4 +70,25 @@ public class TelemetryTest { orchestrator.stop(); } + + @Test + public void opt_out_of_telemetry() throws Exception { + String serverId = randomAlphanumeric(40); + orchestrator = Orchestrator.builderEnv() + .addPlugin(xooPlugin()) + .setServerProperty("sonar.telemetry.enable", "false") + .setServerProperty("sonar.telemetry.url", url) + .setServerProperty("sonar.telemetry.frequency", "1") + .setServerProperty("sonar.core.id", serverId) + .build(); + orchestrator.start(); + + RecordedRequest request = server.takeRequest(1, TimeUnit.SECONDS); + + assertThat(request.getMethod()).isEqualTo("DELETE"); + assertThat(request.getBody().readUtf8()).contains(serverId); + assertThat(request.getHeader(HttpHeaders.USER_AGENT)).contains("SonarQube"); + + orchestrator.stop(); + } } |