mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-15 04:52:20 +00:00
[JEP-214] Telemetry with initial escape hatch implementation (#3604)
This commit is contained in:
1 parent
0774832734
commit
722ff407c4
12 files changed
+639
-135
No files matched your search
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2018, CloudBees, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package jenkins.telemetry;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.Describable;
|
||||
import hudson.model.Descriptor;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class stores a UUID identifying this instance for telemetry reporting to allow deduplication or merging of submitted records.
|
||||
*
|
||||
* We're not using anything derived from instance identity so we cannot connect an instance's public appearance with its submissions.
|
||||
*
|
||||
* This really only uses Descriptor/Describable to get a Saveable implementation for free.
|
||||
*/
|
||||
@Extension
|
||||
@Restricted(NoExternalUse.class)
|
||||
public class Correlator extends Descriptor<Correlator> implements Describable<Correlator> {
|
||||
private String correlationId;
|
||||
|
||||
public Correlator() {
|
||||
super(Correlator.class);
|
||||
load();
|
||||
if (correlationId == null) {
|
||||
correlationId = UUID.randomUUID().toString();
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
public String getCorrelationId() {
|
||||
return correlationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Descriptor<Correlator> getDescriptor() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2018, CloudBees, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package jenkins.telemetry;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import hudson.Extension;
|
||||
import hudson.ExtensionList;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.ProxyConfiguration;
|
||||
import hudson.model.AsyncPeriodicWork;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.model.UsageStatistics;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.util.SystemProperties;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Extension point for collecting JEP-214 telemetry.
|
||||
*
|
||||
* @see <a href="https://github.com/jenkinsci/jep/tree/master/jep/214">JEP-214</a>
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
public abstract class Telemetry implements ExtensionPoint {
|
||||
|
||||
// https://webhook.site is a nice stand-in for this during development; just needs to end in ? to submit the ID as query parameter
|
||||
@Restricted(NoExternalUse.class)
|
||||
@VisibleForTesting
|
||||
static String ENDPOINT = SystemProperties.getString(Telemetry.class.getName() + ".endpoint", "https://uplink.jenkins.io/events");
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(Telemetry.class.getName());
|
||||
|
||||
/**
|
||||
* ID of this collector, typically a basic alphanumeric string (and _- characters).
|
||||
*
|
||||
* Good IDs are globally unique and human readable (i.e. no UUIDs).
|
||||
*
|
||||
* For a periodically updated list of all public implementations, see https://jenkins.io/doc/developer/extensions/jenkins-core/#telemetry
|
||||
*
|
||||
* @return ID of the collector, never null or empty
|
||||
*/
|
||||
@Nonnull
|
||||
public abstract String getId();
|
||||
|
||||
/**
|
||||
* User friendly display name for this telemetry collector, ideally localized.
|
||||
*
|
||||
* @return display name, never null or empty
|
||||
*/
|
||||
@Nonnull
|
||||
public abstract String getDisplayName();
|
||||
|
||||
/**
|
||||
* Start date for the collection.
|
||||
* Will be checked in Jenkins to not collect outside the defined time span.
|
||||
* This does not have to be precise enough for time zones to be a consideration.
|
||||
*
|
||||
* @return collection start date
|
||||
*/
|
||||
@Nonnull
|
||||
public abstract LocalDate getStart();
|
||||
|
||||
/**
|
||||
* End date for the collection.
|
||||
* Will be checked in Jenkins to not collect outside the defined time span.
|
||||
* This does not have to be precise enough for time zones to be a consideration.
|
||||
*
|
||||
* @return collection end date
|
||||
*/
|
||||
@Nonnull
|
||||
public abstract LocalDate getEnd();
|
||||
|
||||
/**
|
||||
* Returns the content to be sent to the telemetry service.
|
||||
*
|
||||
* This method is called periodically, once per content submission.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Nonnull
|
||||
public abstract JSONObject createContent();
|
||||
|
||||
public static ExtensionList<Telemetry> all() {
|
||||
return ExtensionList.lookup(Telemetry.class);
|
||||
}
|
||||
|
||||
@Extension
|
||||
public static class TelemetryReporter extends AsyncPeriodicWork {
|
||||
|
||||
public TelemetryReporter() {
|
||||
super("telemetry collection");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRecurrencePeriod() {
|
||||
return TimeUnit.HOURS.toMillis(24);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(TaskListener listener) throws IOException, InterruptedException {
|
||||
if (UsageStatistics.DISABLED || !Jenkins.getInstance().isUsageStatisticsCollected()) {
|
||||
LOGGER.info("Collection of anonymous usage statistics is disabled, skipping telemetry collection and submission");
|
||||
return;
|
||||
}
|
||||
Telemetry.all().forEach(telemetry -> {
|
||||
if (telemetry.getStart().isAfter(LocalDate.now())) {
|
||||
LOGGER.config("Skipping telemetry for '" + telemetry.getId() + "' as it is configured to start later");
|
||||
return;
|
||||
}
|
||||
if (telemetry.getEnd().isBefore(LocalDate.now())) {
|
||||
LOGGER.config("Skipping telemetry for '" + telemetry.getId() + "' as it is configured to end in the past");
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
try {
|
||||
data = telemetry.createContent();
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to build telemetry content for: '" + telemetry.getId() + "'", e);
|
||||
}
|
||||
|
||||
JSONObject wrappedData = new JSONObject();
|
||||
wrappedData.put("type", telemetry.getId());
|
||||
wrappedData.put("payload", data);
|
||||
wrappedData.put("correlator", ExtensionList.lookupSingleton(Correlator.class).getCorrelationId());
|
||||
|
||||
try {
|
||||
URL url = new URL(ENDPOINT);
|
||||
URLConnection conn = ProxyConfiguration.open(url);
|
||||
if (!(conn instanceof HttpURLConnection)) {
|
||||
LOGGER.config("URL did not result in an HttpURLConnection: " + ENDPOINT);
|
||||
return;
|
||||
}
|
||||
HttpURLConnection http = (HttpURLConnection) conn;
|
||||
http.setRequestProperty("Content-Type", "application/json; charset=utf-8");
|
||||
http.setDoOutput(true);
|
||||
|
||||
String body = wrappedData.toString();
|
||||
if (LOGGER.isLoggable(Level.FINEST)) {
|
||||
LOGGER.finest("Submitting JSON: " + body);
|
||||
}
|
||||
|
||||
try (OutputStream out = http.getOutputStream();
|
||||
OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
|
||||
writer.append(body);
|
||||
}
|
||||
|
||||
LOGGER.config("Telemetry submission received response '" + http.getResponseCode() + " " + http.getResponseMessage() + "' for: " + telemetry.getId());
|
||||
} catch (MalformedURLException e) {
|
||||
LOGGER.config("Malformed endpoint URL: " + ENDPOINT + " for telemetry: " + telemetry.getId());
|
||||
} catch (IOException e) {
|
||||
// deliberately low visibility, as temporary infra problems aren't a big deal and we'd
|
||||
// rather have some unsuccessful submissions than admins opting out to clean up logs
|
||||
LOGGER.log(Level.CONFIG, "Failed to submit telemetry: " + telemetry.getId() + " to: " + ENDPOINT, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2018, CloudBees, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package jenkins.telemetry.impl;
|
||||
|
||||
import hudson.Extension;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.telemetry.Telemetry;
|
||||
import jenkins.util.SystemProperties;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Telemetry implementation gathering information about system properties.
|
||||
*/
|
||||
@Extension
|
||||
@Restricted(NoExternalUse.class)
|
||||
public class SecuritySystemProperties extends Telemetry {
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getId() {
|
||||
return "security-system-properties";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public LocalDate getStart() {
|
||||
return LocalDate.of(2018, 9, 1);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public LocalDate getEnd() {
|
||||
return LocalDate.of(2018, 12, 1);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Use of Security-related Java system properties";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public JSONObject createContent() {
|
||||
Map<String, String> security = new TreeMap<>();
|
||||
putBoolean(security, "hudson.ConsoleNote.INSECURE", false);
|
||||
putBoolean(security, "hudson.model.ParametersAction.keepUndefinedParameters", false);
|
||||
putBoolean(security, "hudson.model.User.allowNonExistentUserToLogin", false);
|
||||
putBoolean(security, "hudson.model.User.allowUserCreationViaUrl", false);
|
||||
putBoolean(security, "hudson.model.User.SECURITY_243_FULL_DEFENSE", true);
|
||||
putBoolean(security, "hudson.remoting.URLDeserializationHelper.avoidUrlWrapping", false);
|
||||
putBoolean(security, "jenkins.security.ClassFilterImpl.SUPPRESS_WHITELIST", false);
|
||||
putBoolean(security, "jenkins.security.ClassFilterImpl.SUPPRESS_ALL", false);
|
||||
|
||||
putStringInfo(security, "hudson.model.ParametersAction.safeParameters");
|
||||
putStringInfo(security, "hudson.model.DirectoryBrowserSupport.CSP");
|
||||
putStringInfo(security, "hudson.security.HudsonPrivateSecurityRealm.ID_REGEX");
|
||||
|
||||
Map<String, Object> info = new TreeMap<>();
|
||||
info.put("core", Jenkins.getVersion().toString());
|
||||
info.put("clientDate", clientDateString());
|
||||
info.put("properties", security);
|
||||
|
||||
return JSONObject.fromObject(info);
|
||||
}
|
||||
|
||||
private static String clientDateString() {
|
||||
TimeZone tz = TimeZone.getTimeZone("UTC");
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
|
||||
df.setTimeZone(tz); // strip timezone
|
||||
return df.format(new Date());
|
||||
}
|
||||
|
||||
private static void putBoolean(Map<String, String> propertiesMap, String systemProperty, boolean defaultValue) {
|
||||
propertiesMap.put(systemProperty, Boolean.toString(SystemProperties.getBoolean(systemProperty, defaultValue)));
|
||||
}
|
||||
|
||||
private static void putStringInfo(Map<String, String> propertiesMap, String systemProperty) {
|
||||
String reportedValue = "null";
|
||||
String value = SystemProperties.getString(systemProperty);
|
||||
if (value != null) {
|
||||
reportedValue = Integer.toString(value.length());
|
||||
}
|
||||
propertiesMap.put(systemProperty, reportedValue);
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
<div>
|
||||
Knowing how Jenkins is used is a tremendous help in guiding the direction of the development, especially
|
||||
for open-source projects where users are inherently hard to track down. When this option is enabled,
|
||||
Jenkins periodically send information about your usage of Jenkins.
|
||||
|
||||
<p>
|
||||
Specifically, it contains the following information:
|
||||
|
||||
<ul>
|
||||
<li>Your Jenkins version
|
||||
<li>For your master and each agent, OS type, and # of executors
|
||||
<li>Plugins that are installed and their versions
|
||||
<li>Number of jobs per each job type in your Jenkins
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
The information doesn't contain anything that identifies you or allows us to contact you
|
||||
(except information inherently revealed by HTTP, such as the IP address). Tabulated
|
||||
data of these usage statistics submissions will be shared with the community.
|
||||
|
||||
</div>
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
|
||||
<div>
|
||||
For any project, it's critical to know how the software is used, but tracking usage data is inherently difficult in open-source projects.
|
||||
Anonymous usage statistics address this need.
|
||||
When enabled, Jenkins periodically sends information to the Jenkins project.
|
||||
The Jenkins project uses this information to set development priorities.
|
||||
</div>
|
||||
<h1>General usage statistics</h1>
|
||||
<div>
|
||||
<p>Jenkins reports the following general usage statistics:</p>
|
||||
<ul>
|
||||
<li>Your Jenkins version</li>
|
||||
<li>For your master and each agent, the OS type and number of executors</li>
|
||||
<li>Installed plugins and their versions</li>
|
||||
<li>Number of items (like jobs) of each item type</li>
|
||||
</ul>
|
||||
<p>
|
||||
This <b>does not</b> report any personally identifiable information. The only information reported by Jenkins is information inherently revealed by the HTTP protocol, such as the IP address.
|
||||
|
||||
These usage statistics are aggregated, updated monthly, and published to <a href="https://stats.jenkins.io">stats.jenkins.io</a>
|
||||
</p>
|
||||
</div>
|
||||
<h1>Telemetry collection</h1>
|
||||
<div>
|
||||
<p>
|
||||
In addition to the general usage statistics listed above, the Jenkins project collects telemetry data from specific trials to inform future development.
|
||||
Each trial has a specific purpose and a defined end date, after which collection stops, independent of the installed versions of Jenkins or plugins.
|
||||
Once a trial is complete, the trial results may be aggregated and shared with the developer community.
|
||||
</p>
|
||||
<p>
|
||||
The following trials are currently set up on this instance:
|
||||
</p>
|
||||
<j:invokeStatic className="jenkins.telemetry.Telemetry" method="all" var="collectors"/>
|
||||
<dl>
|
||||
<j:forEach items="${collectors}" var="collector">
|
||||
<dt>${collector.displayName}</dt>
|
||||
<dd>
|
||||
<st:include from="${collector}" optional="true" page="description.jelly"/>
|
||||
<p>
|
||||
Start date: ${collector.start}
|
||||
End date: ${collector.end}
|
||||
</p>
|
||||
</dd>
|
||||
</j:forEach>
|
||||
</dl>
|
||||
</div>
|
||||
</j:jelly>
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
<div>
|
||||
От голяма помощ е да знаем как Jenkins се ползва. Това може да определи
|
||||
посоката на разработка, което иначе е трудно, защото няма как да се проследяват
|
||||
потребителите на проект с отворен код. Като изберете тази опция Jenkins
|
||||
периодично ще изпраща анонимни данни за използването.
|
||||
|
||||
<p>
|
||||
Това е пълното описание на включената информация:
|
||||
|
||||
<ul>
|
||||
<li>версията на jenkins;
|
||||
<li>операционната система и броя изпълнявани изграждания от основния и подчинените компютри;
|
||||
<li>списък с инсталираните приставки и версиите им;
|
||||
<li>броят задачи за всеки вид задача в инсталацията на Jenkins
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Информацията не съдържа нищо, които да ви идентифицира или да позволява да се свържем
|
||||
с вас (с изключение на информацията указана поради естеството на HTTP, като IP адреси).
|
||||
Тези данни ще бъдат споделени с общността в табличен вид.
|
||||
|
||||
</div>
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
<div>
|
||||
Comprendre comment Jenkins est utilisé est d'une grande aide pour guider les développements,
|
||||
particulièrement pour les projets open-source où les utilisateurs sont typiquement difficiles
|
||||
à connaitre. Quand cette option est activée, Jenkins envoie périodiquement des informations sur votre
|
||||
utilisation de Jenkins.
|
||||
|
||||
<p>
|
||||
Plus précisément, il envoie les informations suivantes:
|
||||
|
||||
<ul>
|
||||
<li>Votre version de Jenkins
|
||||
<li>Pour le Jenkins maître et pour chaque esclave, le type d'OS et le nombre d'exécuteurs
|
||||
<li>Les plugins qui sont installés et leurs versions
|
||||
<li>Le nombre de jobs pour chaque type de job dans votre installation de Jenkins
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Cette information ne contient rien qui vous identifie ou qui nous permette de vous contacter
|
||||
(hors certaines informations intrinsèques à HTTP, comme votre adresse IP).
|
||||
Les données compilées de ces statistiques d'utilisation seront partagées avec la communauté.
|
||||
|
||||
<p>
|
||||
N'hésitez pas à lire <a href="http://jenkins.361315.n4.nabble.com/request-for-comments-tracking-usage-metrics-td375557.html">
|
||||
la discussion sur ce mécanisme</a> et à contribuer aux débats.
|
||||
</div>
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
<div>
|
||||
Sapere come viene utilizzato Jenkins aiuta notevolmente a guidare la
|
||||
direzione dello sviluppo, specialmente nel caso dei progetti open source
|
||||
in cui è inerentemente difficile rintracciare gli utenti. Quando
|
||||
quest'opzione è abilitata, Jenkins invierà periodicamente informazioni
|
||||
sull'utilizzo di Jenkins.
|
||||
|
||||
<p>
|
||||
Specificamente, tali informazioni conterranno quanto segue:
|
||||
|
||||
<ul>
|
||||
<li>La versione di Jenkins
|
||||
<li>Per il master e ogni agente, il tipo di sistema operativo e il numero di esecutori
|
||||
<li>I plugin installati e le rispettive versioni
|
||||
<li>Il numero di processi per ogni tipo di processo in Jenkins
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Le informazioni non conterranno nulla che possa identificare l'utente o
|
||||
consentirci di contattare l'utente (ad eccezione delle informazioni rivelate
|
||||
naturalmente dal protocollo HTTP, come l'indirizzo IP). I dati tabulati
|
||||
relativi agli invii di queste statistiche di utilizzo saranno condivisi con
|
||||
la comunità.
|
||||
|
||||
</div>
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
<div>
|
||||
Jenkinsがどのように使用されているか把握することは、特にユーザーを特定できないオープンソースプロジェクトにとって、
|
||||
開発の方向性を決定する上で非常に役に立ちます。このオプションを有効にすると、Jenkinsの利用状況を定期的に送信します。
|
||||
|
||||
<p>
|
||||
送信する情報は次の情報を含んでいます。
|
||||
|
||||
<ul>
|
||||
<li>Jenkinsのバージョン
|
||||
<li>マスターと各スレーブの、OSとエグゼキューターの数
|
||||
<li>インストールされているプラグインとそのバージョン
|
||||
<li>ジョブのタイプ毎のジョブ数
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
情報には、あなたを特定できる、もしくはあなたに連絡できるようなものは含みません(ただし、IPアドレスのようなHTTP関連の情報は除きます)。
|
||||
送信された利用状況の一覧データは、コミュニティで共有されます。
|
||||
|
||||
<p>
|
||||
<a href="http://jenkins.361315.n4.nabble.com/request-for-comments-tracking-usage-metrics-td375557.html">
|
||||
コミュニティでのこの仕組みに関するディスカッション</a>を参考にしてください。ご意見はこちらにお願いします。
|
||||
</div>
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
<div>
|
||||
Saber como Jenkins é utilizado é uma tremenda ajuda guiando-nos no desenvolvimento, especificamente para
|
||||
projetos de código livre onde os usuários são inerentemente difíceis de rastrear. Ao ativar esta opção,
|
||||
Jenkins periodicamente enviará informações sobre o seu uso.
|
||||
|
||||
<p>
|
||||
Especificamente, as informações enviadas serão:
|
||||
|
||||
<ul>
|
||||
<li>A versão do seu Jenkins
|
||||
<li>Para cada master e cada slave, o tipo de Sistema Operacional, e número de executores
|
||||
<li>Os plugins instalados e suas versões
|
||||
<li>Número de jobs por tipo de job no seu Jenkins
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Essas informaçãos não contem nada o identifique ou nos permita contatá-lo
|
||||
(exceto pelas informações reveladas via HTTP, tal como Endereço IP).
|
||||
Dados das estatísticas de uso submetidas são compartilhados com a comunidade.
|
||||
</div>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
|
||||
For many security fixes, the Jenkins security team adds hidden options, sometimes called <em>escape hatches</em>, to disable the security fix.
|
||||
This hedges against severe regressions not caught in testing, or unexpected legitimate use cases.
|
||||
It is unknown to the team which of these options are actually in use, so this trial collects anonymous information about the values of (typically boolean) security-related Java system properties.
|
||||
</j:jelly>
|
||||
@@ -0,0 +1,209 @@
|
||||
package jenkins.telemetry;
|
||||
|
||||
import hudson.ExtensionList;
|
||||
import hudson.model.UnprotectedRootAction;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import hudson.security.csrf.CrumbExclusion;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.jvnet.hudson.test.JenkinsRule;
|
||||
import org.jvnet.hudson.test.LoggerRule;
|
||||
import org.jvnet.hudson.test.TestExtension;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class TelemetryTest {
|
||||
@Rule
|
||||
public JenkinsRule j = new JenkinsRule();
|
||||
|
||||
@Rule
|
||||
public LoggerRule logger = new LoggerRule().record(Telemetry.class, Level.ALL).capture(100);
|
||||
|
||||
private static int counter = 0;
|
||||
|
||||
@Test
|
||||
public void testSubmission() throws Exception {
|
||||
j.jenkins.setNoUsageStatistics(false); // tests usually don't submit this, but we need this
|
||||
assertEquals("no requests received", 0, counter);
|
||||
Telemetry.ENDPOINT = j.getURL().toString() + "uplink/events";
|
||||
ExtensionList.lookupSingleton(Telemetry.TelemetryReporter.class).doRun();
|
||||
do {
|
||||
Thread.sleep(250);
|
||||
} while (counter == 0); // this might end up being flaky due to 1 to many active telemetry trials
|
||||
assertThat(logger.getMessages(), hasItem("Telemetry submission received response '200 OK' for: test-data"));
|
||||
assertThat(logger.getMessages(), hasItem("Skipping telemetry for 'future' as it is configured to start later"));
|
||||
assertThat(logger.getMessages(), hasItem("Skipping telemetry for 'past' as it is configured to end in the past"));
|
||||
assertThat(types, hasItem("test-data"));
|
||||
assertThat(types, not(hasItem("future")));
|
||||
assertThat(types, not(hasItem("past")));
|
||||
assertThat(correlators.size(), is(1));
|
||||
assertTrue("at least one request received", counter > 0); // TestTelemetry plus whatever real impls exist
|
||||
}
|
||||
|
||||
@TestExtension
|
||||
public static class DisabledFutureTelemetry extends Telemetry {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getId() {
|
||||
return "future";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "future";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public LocalDate getStart() {
|
||||
return LocalDate.now().plus(1, ChronoUnit.DAYS);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public LocalDate getEnd() {
|
||||
return LocalDate.MAX;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public JSONObject createContent() {
|
||||
return new JSONObject();
|
||||
}
|
||||
}
|
||||
|
||||
@TestExtension
|
||||
public static class DisabledPastTelemetry extends Telemetry {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getId() {
|
||||
return "past";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "past";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public LocalDate getStart() {
|
||||
return LocalDate.MIN;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public LocalDate getEnd() {
|
||||
return LocalDate.now().minus(1, ChronoUnit.DAYS);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public JSONObject createContent() {
|
||||
return new JSONObject();
|
||||
}
|
||||
}
|
||||
|
||||
@TestExtension
|
||||
public static class TestTelemetry extends Telemetry {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getId() {
|
||||
return "test-data";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "test-data";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public LocalDate getStart() {
|
||||
return LocalDate.MIN;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public LocalDate getEnd() {
|
||||
return LocalDate.MAX;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public JSONObject createContent() {
|
||||
return new JSONObject();
|
||||
}
|
||||
}
|
||||
|
||||
@TestExtension
|
||||
public static class NoCrumb extends CrumbExclusion {
|
||||
@Override
|
||||
public boolean process(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
String pathInfo = request.getPathInfo();
|
||||
if (pathInfo != null && pathInfo.startsWith("/uplink")) {
|
||||
chain.doFilter(request, response);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> correlators = new HashSet<>();
|
||||
private static Set<String> types = new HashSet<>();
|
||||
|
||||
@TestExtension
|
||||
public static class TelemetryReceiver implements UnprotectedRootAction {
|
||||
public void doEvents(StaplerRequest request, StaplerResponse response) throws IOException {
|
||||
StringWriter sw = new StringWriter();
|
||||
IOUtils.copy(request.getInputStream(), sw, StandardCharsets.UTF_8);
|
||||
JSONObject json = JSONObject.fromObject(sw.toString());
|
||||
correlators.add(json.getString("correlator"));
|
||||
types.add(json.getString("type"));
|
||||
counter++;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
@Override
|
||||
public String getIconFileName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "uplink";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user