From 7961badd9af9aa15144e4c4442d2fb1f81545ed6 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 22 Mar 2018 11:49:29 +0100 Subject: [PATCH 01/45] [JENKINS-50164] Remove UI option to modify WS and builds locations Test (almost) only: checking the previous behaviour is preserved, and the system property is accounted for when set. --- core/src/main/java/jenkins/model/Jenkins.java | 31 +++++- .../model/JenkinsInitializationTest.java | 98 +++++++++++++++++++ 2 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 test/src/test/java/jenkins/model/JenkinsInitializationTest.java diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 15a4edce00..ce0570f509 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -399,7 +399,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * This value will be variable-expanded as per {@link #expandVariablesForDirectory}. * @see #getWorkspaceFor(TopLevelItem) */ - private String workspaceDir = "${ITEM_ROOTDIR}/"+WORKSPACE_DIRNAME; + private String workspaceDir = OLD_DEFAULT_WORKSPACES_DIR; /** * Root directory for the builds. @@ -846,7 +846,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve if (!new File(root,"jobs").exists()) { // if this is a fresh install, use more modern default layout that's consistent with agents - workspaceDir = "${JENKINS_HOME}/workspace/${ITEM_FULL_NAME}"; + workspaceDir = DEFAULT_WORKSPACES_DIR; } // doing this early allows InitStrategy to set environment upfront @@ -2404,6 +2404,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return DEFAULT_BUILDS_DIR.equals(buildsDir); } + @Restricted(NoExternalUse.class) + boolean isDefaultWorkspaceDir() { + return OLD_DEFAULT_WORKSPACES_DIR.equals(workspaceDir) || DEFAULT_WORKSPACES_DIR.equals(workspaceDir); + } + private File expandVariablesForDirectory(String base, Item item) { return new File(expandVariablesForDirectory(base, item.getFullName(), item.getRootDir().getPath())); } @@ -5054,6 +5059,28 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * @see #getRawBuildsDir() */ private static final String DEFAULT_BUILDS_DIR = "${ITEM_ROOTDIR}/builds"; + /** + * Old layout for workspaces. + * @see #DEFAULT_WORKSPACES_DIR + */ + private static final String OLD_DEFAULT_WORKSPACES_DIR = "${ITEM_ROOTDIR}/" + WORKSPACE_DIRNAME; + + /** + * Default value for the workspace's directories layout. + */ + private static final String DEFAULT_WORKSPACES_DIR = "${JENKINS_HOME}/workspace/${ITEM_FULL_NAME}"; + + /** + * System property name to set {@link #buildsDir}. + * @see #getRawBuildsDir() + */ + public static final String BUILDS_DIR_PROP = Jenkins.class.getName() + ".BUILDS_DIR"; + + /** + * System property name to set {@link #workspaceDir}. + * @see #getRawWorkspaceDir() + */ + public static final String WORKSPACES_DIR_PROP = Jenkins.class.getName() + ".WORKSPACES_DIR"; /** * Automatically try to launch an agent when Jenkins is initialized or a new agent computer is created. diff --git a/test/src/test/java/jenkins/model/JenkinsInitializationTest.java b/test/src/test/java/jenkins/model/JenkinsInitializationTest.java new file mode 100644 index 0000000000..f7d523b370 --- /dev/null +++ b/test/src/test/java/jenkins/model/JenkinsInitializationTest.java @@ -0,0 +1,98 @@ +package jenkins.model; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runners.model.Statement; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.LoggerRule; +import org.jvnet.hudson.test.RestartableJenkinsRule; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@Issue("JENKINS-50164") +public class JenkinsInitializationTest { + + private static final Map PROPS_TO_RESTORE = new LinkedHashMap<>(); + @Rule + public RestartableJenkinsRule story = new RestartableJenkinsRule(); + + @Rule + public LoggerRule loggerRule = new LoggerRule(); + + @BeforeClass + public static void beforeClass() { + Stream.of(Jenkins.BUILDS_DIR_PROP, Jenkins.WORKSPACES_DIR_PROP) + .forEach(property -> PROPS_TO_RESTORE.put(property, System.getProperty(property))); + } + + @AfterClass + public static void afterClass() { + PROPS_TO_RESTORE.forEach((k, v) -> { + if (v == null) { + System.clearProperty(k); + } else { + System.setProperty(k, v); + } + }); + } + + @Test + public void buildsDir() throws Exception { + loggerRule.record(Jenkins.class, Level.WARNING).capture(10); + story.addStep(new Statement() { + @Override + public void evaluate() throws Throwable { + assertTrue(Jenkins.get().isDefaultBuildDir()); + System.setProperty("jenkins.model.Jenkins.BUILDS_DIR", "blah"); + assertFalse(logWasFound("Changing builds directories from ")); + } + }); + + story.addStep(new Statement() { + @Override + public void evaluate() throws Throwable { + assertFalse(Jenkins.get().isDefaultBuildDir()); + assertEquals("blah", story.j.getInstance().getRawBuildsDir()); + assertTrue(logWasFound("Changing builds directories from ")); + } + }); + } + + @Test + public void workspacesDir() throws Exception { + loggerRule.record(Jenkins.class, Level.WARNING).capture(10); + story.addStep(new Statement() { + @Override + public void evaluate() throws Throwable { + assertTrue(Jenkins.get().isDefaultWorkspaceDir()); + System.setProperty("jenkins.model.Jenkins.WORKSPACES_DIR", "bluh"); + assertFalse(logWasFound("Changing workspaces directories from ")); + } + }); + + story.addStep(new Statement() { + @Override + public void evaluate() throws Throwable { + assertFalse(Jenkins.get().isDefaultBuildDir()); + assertEquals("bluh", story.j.getInstance().getRawWorkspaceDir()); + assertTrue(logWasFound("Changing workspaces directories from ")); + } + }); + } + + private boolean logWasFound(String searched) { + return loggerRule.getRecords().stream() + .anyMatch(record -> record.getMessage().contains(searched)); + } + + +} From cce49e7b3cba50bd46e5b8d6c6d3182f398bc277 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 22 Mar 2018 12:23:17 +0100 Subject: [PATCH 02/45] [JENKINS-50164] Remove UI option to modify WS and builds locations Add necessary code to fix tests, and make the UI readonly. --- core/src/main/java/jenkins/model/Jenkins.java | 19 +++++++++++++++++++ .../jenkins/model/Jenkins/configure.jelly | 6 +++--- .../model/Jenkins/help-rawBuildsDir.html | 7 ++++++- .../model/Jenkins/help-rawWorkspaceDir.html | 6 ++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index ce0570f509..4ec4a96b95 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3040,6 +3040,25 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve // load from disk cfg.unmarshal(Jenkins.this); } + + setBuildsAndWorkspacesDir(); + } + + private void setBuildsAndWorkspacesDir() { + String newBuildsDir = SystemProperties.getString(BUILDS_DIR_PROP); + if (newBuildsDir != null && !buildsDir.equals(newBuildsDir)) { + LOGGER.log(Level.WARNING, "Changing builds directories from {0} to {1}. Beware that no automated data migration will occur.", + new String[]{buildsDir, newBuildsDir}); + buildsDir = newBuildsDir; + } + + String newWorkspacesDir = SystemProperties.getString(WORKSPACES_DIR_PROP); + if (newWorkspacesDir != null && !workspaceDir.equals(newBuildsDir)) { + LOGGER.log(Level.WARNING, "Changing workspaces directories from {0} to {1}. Beware that no automated data migration will occur.", + new String[]{workspaceDir, newWorkspacesDir}); + workspaceDir = newWorkspacesDir; + } + } private synchronized TaskBuilder loadTasks() throws IOException { diff --git a/core/src/main/resources/jenkins/model/Jenkins/configure.jelly b/core/src/main/resources/jenkins/model/Jenkins/configure.jelly index e95226abb1..32f87e43c6 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/configure.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/configure.jelly @@ -40,11 +40,11 @@ THE SOFTWARE. ${it.rootDir} - - + + - + diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir.html index ebe0002239..7fd19290c0 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir.html +++ b/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir.html @@ -1,6 +1,11 @@
- Specifies where Jenkins will store build records on the file system. This +

This value is only modifiable using a system property. + To do so, pass -Djenkins.model.Jenkins.BUILDS_DIR="the_value" + to the Jenkins JVM options during startup. +

+

Specifies where Jenkins will store build records on the file system. This includes the console output and other metadata generated by a build. +

This value may include the following variables:

    diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir.html index 0249a88dd4..38ddd245d8 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir.html +++ b/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir.html @@ -1,7 +1,13 @@
    +

    This value is only modifiable using a system property. + To do so, pass -Djenkins.model.Jenkins.WORKSPACES_DIR="the_value" + to the Jenkins JVM options during startup. +

    +

    Specifies where Jenkins will store workspaces for builds that are executed on the master.
    It has no effect on builds that are executed on agents. +

    This value may include the following variables:

      From 99734308fedd45c105fdda1e88581f87e24ab2bd Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 22 Mar 2018 16:34:45 +0100 Subject: [PATCH 03/45] Make package visible to avoid public usage --- core/src/main/java/jenkins/model/Jenkins.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 4ec4a96b95..6332164dc7 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -5093,13 +5093,13 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * System property name to set {@link #buildsDir}. * @see #getRawBuildsDir() */ - public static final String BUILDS_DIR_PROP = Jenkins.class.getName() + ".BUILDS_DIR"; + static final String BUILDS_DIR_PROP = Jenkins.class.getName() + ".BUILDS_DIR"; /** * System property name to set {@link #workspaceDir}. * @see #getRawWorkspaceDir() */ - public static final String WORKSPACES_DIR_PROP = Jenkins.class.getName() + ".WORKSPACES_DIR"; + static final String WORKSPACES_DIR_PROP = Jenkins.class.getName() + ".WORKSPACES_DIR"; /** * Automatically try to launch an agent when Jenkins is initialized or a new agent computer is created. From fa9326c6527ed2967531e5ad2af77cb9da6dbbb4 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 22 Mar 2018 16:35:44 +0100 Subject: [PATCH 04/45] workspaceDir & buildDir not modifiable anymore from the UI! --- core/src/main/java/jenkins/model/Jenkins.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 6332164dc7..7240d971f5 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3659,9 +3659,6 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve JSONObject json = req.getSubmittedForm(); - workspaceDir = json.getString("rawWorkspaceDir"); - buildsDir = json.getString("rawBuildsDir"); - systemMessage = Util.nullify(req.getParameter("system_message")); boolean result = true; From 819aeeb9b9b70b1dfa2f52ce13f65c7826c8f673 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 22 Mar 2018 16:36:58 +0100 Subject: [PATCH 05/45] Remove the whole UI for buildsDir and workspaceDir --- .../jenkins/model/Jenkins/configure.jelly | 8 ----- .../model/Jenkins/help-rawBuildsDir.html | 33 ----------------- .../model/Jenkins/help-rawBuildsDir_bg.html | 29 --------------- .../model/Jenkins/help-rawBuildsDir_it.html | 31 ---------------- .../model/Jenkins/help-rawBuildsDir_ja.html | 14 -------- .../Jenkins/help-rawBuildsDir_zh_TW.html | 13 ------- .../model/Jenkins/help-rawWorkspaceDir.html | 36 ------------------- .../Jenkins/help-rawWorkspaceDir_bg.html | 30 ---------------- .../Jenkins/help-rawWorkspaceDir_it.html | 32 ----------------- .../Jenkins/help-rawWorkspaceDir_ja.html | 14 -------- 10 files changed, 240 deletions(-) delete mode 100644 core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir.html delete mode 100644 core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_bg.html delete mode 100644 core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_it.html delete mode 100644 core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_ja.html delete mode 100644 core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_zh_TW.html delete mode 100644 core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir.html delete mode 100644 core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_bg.html delete mode 100644 core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_it.html delete mode 100644 core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_ja.html diff --git a/core/src/main/resources/jenkins/model/Jenkins/configure.jelly b/core/src/main/resources/jenkins/model/Jenkins/configure.jelly index 32f87e43c6..c083fdb3d4 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/configure.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/configure.jelly @@ -39,14 +39,6 @@ THE SOFTWARE. ${it.rootDir} - - - - - - - - diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir.html deleted file mode 100644 index 7fd19290c0..0000000000 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir.html +++ /dev/null @@ -1,33 +0,0 @@ -
      -

      This value is only modifiable using a system property. - To do so, pass -Djenkins.model.Jenkins.BUILDS_DIR="the_value" - to the Jenkins JVM options during startup. -

      -

      Specifies where Jenkins will store build records on the file system. This - includes the console output and other metadata generated by a build. -

      -

      - This value may include the following variables: -

        -
      • ${JENKINS_HOME} — Absolute path of the Jenkins home - directory -
      • ${ITEM_ROOTDIR} — Absolute path of the directory - where Jenkins stores the configuration and related metadata for a - given job -
      • ${ITEM_FULL_NAME} — The full name of a given job, - which may be slash-separated, e.g. foo/bar for the job - bar in folder foo -
      - The value must include ${ITEM_ROOTDIR} or - ${ITEM_FULL_NAME}, so that each job can store its build records - separately. -

      - Changing this value allows you to store build records on a larger, but - slower disk, rather than on the same disk where builds are executed. -

      - Any changes to this value will take effect as soon as this configuration - page is saved, but note that Jenkins will not automatically migrate any data - from the current build record root directory. -

      - The default value is ${ITEM_ROOTDIR}/builds. -

      diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_bg.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_bg.html deleted file mode 100644 index ff240d9d76..0000000000 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_bg.html +++ /dev/null @@ -1,29 +0,0 @@ -
      - Указва мястото във файловата система, където Jenkins ще запазва - записите за изгражданията. Това включва изхода от конзолата, както и другите - метаданни, които се генерират при изграждане. -

      - Стойността може да включва следните променливи: -

        -
      • ${JENKINS_HOME} — абсолютният път до домашната директория - на Jenkins; -
      • ${ITEM_ROOTDIR} — абсолютният път до директорията, в която - Jenkins запазва настройките и свързаните метаданни за определено - задание; -
      • ${ITEM_FULL_NAME} — пълното име на заданието, което може да - съдържа наклонени черти, напр. foo/bar за заданието - bar в папка foo -
      - Стойността трябва да включва ${ITEM_ROOTDIR} или - ${ITEM_FULL_NAME}, за да може всяко задание да запазва записите за - изгражданията отделно. -

      - Можете да промените стойността, за да държите записите на по-голям, но - по-бавен диск в сравнение с този, на който се извършват изгражданията. -

      - Промените по стойността ще влязат в сила веднага след подаването на тази - страница с настройки. Jenkins няма автоматично да мигрира каквито и да е - данни от текущата директория за изграждане. -

      - Стандартната стойност е ${ITEM_ROOTDIR}/builds. -

      diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_it.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_it.html deleted file mode 100644 index f1bd4dc29a..0000000000 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_it.html +++ /dev/null @@ -1,31 +0,0 @@ -
      - Specifica la directory in cui Jenkins salverà i record di compilazione sul - filesystem. Essi includono l'output della console e gli altri metadati - generati da una compilazione. -

      - Questo valore può includere le seguenti variabili: -

        -
      • ${JENKINS_HOME} — Percorso assoluto della directory - home di Jenkins -
      • ${ITEM_ROOTDIR} — Percorso assoluto della directory - in cui Jenkins salva la configurazione e i metadati correlati per - un dato processo -
      • ${ITEM_FULL_NAME} — Il nome completo di un dato - processo; può essere separato da barre, ad es. pippo/pluto - per il processo pluto nella cartella pippo -
      - Il valore deve includere ${ITEM_ROOTDIR} o - ${ITEM_FULL_NAME} in modo che ogni processo possa salvare - separatamente i propri record di compilazione. -

      - La modifica di questo valore consente di salvare i record di compilazione - su un disco di maggiori dimensioni, ma più lento, anziché sullo stesso - disco su cui vengono eseguite le compilazioni. -

      - Le modifiche a questo valore avranno effetto non appena questa pagina di - configurazione sarà stata salvata, ma si noti che Jenkins non migrerà - automaticamente i dati dalla directory radice corrente dei record di - compilazione. -

      - Il valore predefinito è ${ITEM_ROOTDIR}/builds. -

      diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_ja.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_ja.html deleted file mode 100644 index 070d6be636..0000000000 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_ja.html +++ /dev/null @@ -1,14 +0,0 @@ -
      - 過去のビルドのデータを保存する場所を指定します。 - 次の変数を使用して場所を指定することができます。 - -
        -
      • ${JENKINS_HOME} — Jenkinsのホームディレクトリ -
      • ${ITEM_ROOTDIR} — ワークスペースを配置するジョブのルートディレクトリ -
      • ${ITEM_FULL_NAME} — "foo/bar"のように、'/'区切りのジョブ名 -
      - -

      - 例えば、この値を変更することで、JENKINS_HOMEを可用性の高いバックアップされたドライブに配置しつつ、 - ビルドのデータを大きいけれど遅いディスクに配置することも可能です。 - デフォルト値は、${ITEM_ROOTDIR}/buildsです。 diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_zh_TW.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_zh_TW.html deleted file mode 100644 index 8e55b40f0d..0000000000 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawBuildsDir_zh_TW.html +++ /dev/null @@ -1,13 +0,0 @@ -

      - 設定 Jenkins 存放過去建置記錄的地方。可以使用下列變數: - -
        -
      • ${JENKINS_HOME} — Jenkins 主目錄。 -
      • ${ITEM_ROOTDIR} — 作業根目錄,也就是放預設工作區的地方。 -
      • ${ITEM_FULL_NAME} — 用 '/' 隔開的作業名稱,例如 "foo/bar"。 -
      - -

      - 舉例來說,您可以將建置記錄放在較大但是沒那麼快的磁碟,而把 JENKINS_HOME 留在穩定且定期會備份的磁碟上。 - 預設值是 ${ITEM_ROOTDIR}/builds。 -

      \ No newline at end of file diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir.html deleted file mode 100644 index 38ddd245d8..0000000000 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir.html +++ /dev/null @@ -1,36 +0,0 @@ -
      -

      This value is only modifiable using a system property. - To do so, pass -Djenkins.model.Jenkins.WORKSPACES_DIR="the_value" - to the Jenkins JVM options during startup. -

      -

      - Specifies where Jenkins will store workspaces for builds that are executed - on the master.
      - It has no effect on builds that are executed on agents. -

      -

      - This value may include the following variables: -

        -
      • ${JENKINS_HOME} — Absolute path of the Jenkins home - directory -
      • ${ITEM_ROOTDIR} — Absolute path of the directory - where Jenkins stores the configuration and related metadata for a - given job -
      • ${ITEM_FULL_NAME} — The full name of a given job, - which may be slash-separated, e.g. foo/bar for the job - bar in folder foo -
      - The value should normally include ${ITEM_ROOTDIR} or - ${ITEM_FULL_NAME}, otherwise different jobs will end up sharing the - same workspace. -

      - As builds tend to be disk I/O intensive, changing this value enables you to - put build workspaces on faster storage hardware, such as SSDs or even RAM - disks. -

      - Any changes to this value will take effect as soon as this configuration - page is saved, but note that Jenkins will not automatically migrate any data - from the current workspace root directory. -

      - The default value is ${JENKINS_HOME}/workspace/${ITEM_FULLNAME}. -

      diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_bg.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_bg.html deleted file mode 100644 index 75967f9c18..0000000000 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_bg.html +++ /dev/null @@ -1,30 +0,0 @@ -
      - Указва мястото във файловата система, където Jenkins ще запазва - изгражданията на основния компютър.
      - Това не влияе на подчинените компютри. -

      - Стойността може да включва следните променливи: -

        -
      • ${JENKINS_HOME} — абсолютният път до домашната директория - на Jenkins; -
      • ${ITEM_ROOTDIR} — абсолютният път до директорията, в която - Jenkins запазва настройките и свързаните метаданни за определено - задание; -
      • ${ITEM_FULL_NAME} — пълното име на заданието, което може да - съдържа наклонени черти, напр. foo/bar за заданието - bar в папка foo -
      - Стойността трябва да включва ${ITEM_ROOTDIR} или - ${ITEM_FULL_NAME}, за да може всяко задание да запазва - изгражданията отделно. -

      - Понеже при изгражданията доминират входно-изходните операции, може да - смените стойността, за да ползвате по-бърз хардуер като SSDs или дори - дискове в RAM паметта. -

      - Промените по стойността ще влязат в сила веднага след подаването на тази - страница с настройки. Jenkins няма автоматично да мигрира каквито и да е - данни от текущата директория за изграждане. -

      - Стандартната стойност е ${JENKINS_HOME}/workspace/${ITEM_FULLNAME}. -

      diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_it.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_it.html deleted file mode 100644 index 007d2df2c4..0000000000 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_it.html +++ /dev/null @@ -1,32 +0,0 @@ -
      - Specifica la directory in cui Jenkins salverà gli spazi di lavoro per le - compilazioni eseguite sul master.
      - Non ha influenza sulle compilazioni eseguite sugli agenti. -

      - Questo valore può includere le seguenti variabili: -

        -
      • ${JENKINS_HOME} — Percorso assoluto della directory - home di Jenkins -
      • ${ITEM_ROOTDIR} — Percorso assoluto della directory - in cui Jenkins salva la configurazione e i metadati correlati per - un dato processo -
      • ${ITEM_FULL_NAME} — Il nome completo di un dato - processo; può essere separato da barre, ad es. pippo/pluto - per il processo pluto nella cartella pippo -
      - Il valore di norma dovrebbe includere ${ITEM_ROOTDIR} o - ${ITEM_FULL_NAME}, altrimenti processi differenti si ritroveranno - a condividere il medesimo spazio di lavoro. -

      - Dal momento che le compilazioni tendono ad utilizzare in modo elevato l'I/O - su disco, la modifica di questo valore consente di spostare gli spazi di - lavoro delle compilazioni su periferiche di archiviazione più veloci, come - SSD o perfino dischi RAM. -

      - Le modifiche a questo valore avranno effetto non appena questa pagina di - configurazione sarà stata salvata, ma si noti che Jenkins non migrerà - automaticamente i dati dalla directory radice corrente dello spazio di - lavoro. -

      - Il valore predefinito è ${JENKINS_HOME}/workspace/${ITEM_FULL_NAME}. -

      diff --git a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_ja.html b/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_ja.html deleted file mode 100644 index 6967b33ac6..0000000000 --- a/core/src/main/resources/jenkins/model/Jenkins/help-rawWorkspaceDir_ja.html +++ /dev/null @@ -1,14 +0,0 @@ -
      - マスターノード上のジョブのワークスペースを配置する場所を指定します。 - 次の変数を使用して場所を指定することができます。 - -
        -
      • ${JENKINS_HOME} — Jenkinsのホームディレクトリ -
      • ${ITEM_ROOTDIR} — ワークスペースを配置するジョブのルートディレクトリ -
      • ${ITEM_FULL_NAME} — "foo/bar"のように、'/'区切りのジョブ名 -
      - -

      - この値を変更することで、ワークスペースをSSD、SCSIあるいはRAMディスク上にワークスペースを配置することができます。 - デフォルト値は、${JENKINS_HOME}/workspace/${ITEM_FULLNAME}です。 -

      From 34bff661f2485c7adf6534d4bc1f2d7cfa566b55 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 22 Mar 2018 16:37:23 +0100 Subject: [PATCH 06/45] Lambda-ify --- .../model/JenkinsInitializationTest.java | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/test/src/test/java/jenkins/model/JenkinsInitializationTest.java b/test/src/test/java/jenkins/model/JenkinsInitializationTest.java index f7d523b370..59fd857972 100644 --- a/test/src/test/java/jenkins/model/JenkinsInitializationTest.java +++ b/test/src/test/java/jenkins/model/JenkinsInitializationTest.java @@ -4,7 +4,6 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; -import org.junit.runners.model.Statement; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.RestartableJenkinsRule; @@ -48,44 +47,33 @@ public class JenkinsInitializationTest { @Test public void buildsDir() throws Exception { loggerRule.record(Jenkins.class, Level.WARNING).capture(10); - story.addStep(new Statement() { - @Override - public void evaluate() throws Throwable { - assertTrue(Jenkins.get().isDefaultBuildDir()); - System.setProperty("jenkins.model.Jenkins.BUILDS_DIR", "blah"); - assertFalse(logWasFound("Changing builds directories from ")); - } + story.then(steps -> { + assertTrue(Jenkins.get().isDefaultBuildDir()); + System.setProperty("jenkins.model.Jenkins.BUILDS_DIR", "blah"); + assertFalse(JenkinsInitializationTest.this.logWasFound("Changing builds directories from ")); }); - story.addStep(new Statement() { - @Override - public void evaluate() throws Throwable { - assertFalse(Jenkins.get().isDefaultBuildDir()); - assertEquals("blah", story.j.getInstance().getRawBuildsDir()); - assertTrue(logWasFound("Changing builds directories from ")); - } - }); + story.then(step -> { + assertFalse(Jenkins.get().isDefaultBuildDir()); + assertEquals("blah", story.j.getInstance().getRawBuildsDir()); + assertTrue(logWasFound("Changing builds directories from ")); + } + ); } @Test public void workspacesDir() throws Exception { loggerRule.record(Jenkins.class, Level.WARNING).capture(10); - story.addStep(new Statement() { - @Override - public void evaluate() throws Throwable { - assertTrue(Jenkins.get().isDefaultWorkspaceDir()); - System.setProperty("jenkins.model.Jenkins.WORKSPACES_DIR", "bluh"); - assertFalse(logWasFound("Changing workspaces directories from ")); - } + story.then(step -> { + assertTrue(Jenkins.get().isDefaultWorkspaceDir()); + System.setProperty("jenkins.model.Jenkins.WORKSPACES_DIR", "bluh"); + assertFalse(logWasFound("Changing workspaces directories from ")); }); - story.addStep(new Statement() { - @Override - public void evaluate() throws Throwable { - assertFalse(Jenkins.get().isDefaultBuildDir()); - assertEquals("bluh", story.j.getInstance().getRawWorkspaceDir()); - assertTrue(logWasFound("Changing workspaces directories from ")); - } + story.then(step -> { + assertFalse(Jenkins.get().isDefaultBuildDir()); + assertEquals("bluh", story.j.getInstance().getRawWorkspaceDir()); + assertTrue(logWasFound("Changing workspaces directories from ")); }); } From 770c70a90d60fa98e0d673bbd8bdbdc4373bfe06 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 22 Mar 2018 16:37:33 +0100 Subject: [PATCH 07/45] Better javadoc --- core/src/main/java/jenkins/model/Jenkins.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 7240d971f5..6f68db7fb5 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -5083,6 +5083,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * Default value for the workspace's directories layout. + * @see #workspaceDir */ private static final String DEFAULT_WORKSPACES_DIR = "${JENKINS_HOME}/workspace/${ITEM_FULL_NAME}"; From 36fe2399e3522b45b4cf22eb207f9fac904ee4d0 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 22 Mar 2018 17:19:15 +0100 Subject: [PATCH 08/45] Log always as INFO minimum, WARNING if values were changed --- core/src/main/java/jenkins/model/Jenkins.java | 6 ++- .../model/JenkinsInitializationTest.java | 43 +++++++++++++++---- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 6f68db7fb5..20e863635e 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3050,13 +3050,17 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve LOGGER.log(Level.WARNING, "Changing builds directories from {0} to {1}. Beware that no automated data migration will occur.", new String[]{buildsDir, newBuildsDir}); buildsDir = newBuildsDir; + } else if (!isDefaultBuildDir()) { + LOGGER.log(Level.INFO, "Using non default builds directories: {0}.", buildsDir); } String newWorkspacesDir = SystemProperties.getString(WORKSPACES_DIR_PROP); - if (newWorkspacesDir != null && !workspaceDir.equals(newBuildsDir)) { + if (newWorkspacesDir != null && !workspaceDir.equals(newWorkspacesDir)) { LOGGER.log(Level.WARNING, "Changing workspaces directories from {0} to {1}. Beware that no automated data migration will occur.", new String[]{workspaceDir, newWorkspacesDir}); workspaceDir = newWorkspacesDir; + } else if (!isDefaultWorkspaceDir()) { + LOGGER.log(Level.INFO, "Using non default workspaces directories: {0}.", workspaceDir); } } diff --git a/test/src/test/java/jenkins/model/JenkinsInitializationTest.java b/test/src/test/java/jenkins/model/JenkinsInitializationTest.java index 59fd857972..3e5c5513da 100644 --- a/test/src/test/java/jenkins/model/JenkinsInitializationTest.java +++ b/test/src/test/java/jenkins/model/JenkinsInitializationTest.java @@ -46,41 +46,68 @@ public class JenkinsInitializationTest { @Test public void buildsDir() throws Exception { - loggerRule.record(Jenkins.class, Level.WARNING).capture(10); + loggerRule.record(Jenkins.class, Level.WARNING) + .record(Jenkins.class, Level.INFO) + .capture(100); + + story.then(step -> { + assertFalse(logWasFound("Using non default builds directories")); + } + ); + story.then(steps -> { - assertTrue(Jenkins.get().isDefaultBuildDir()); + assertTrue(story.j.getInstance().isDefaultBuildDir()); System.setProperty("jenkins.model.Jenkins.BUILDS_DIR", "blah"); assertFalse(JenkinsInitializationTest.this.logWasFound("Changing builds directories from ")); }); story.then(step -> { - assertFalse(Jenkins.get().isDefaultBuildDir()); + assertFalse(story.j.getInstance().isDefaultBuildDir()); assertEquals("blah", story.j.getInstance().getRawBuildsDir()); assertTrue(logWasFound("Changing builds directories from ")); } ); + + story.then(step -> { + assertTrue(logWasFound("Using non default builds directories")); + } + ); } @Test public void workspacesDir() throws Exception { - loggerRule.record(Jenkins.class, Level.WARNING).capture(10); + loggerRule.record(Jenkins.class, Level.WARNING) + .record(Jenkins.class, Level.INFO) + .capture(1000); + story.then(step -> { - assertTrue(Jenkins.get().isDefaultWorkspaceDir()); + assertFalse(logWasFound("Using non default workspaces directories")); + } + ); + + story.then(step -> { + assertTrue(story.j.getInstance().isDefaultWorkspaceDir()); System.setProperty("jenkins.model.Jenkins.WORKSPACES_DIR", "bluh"); assertFalse(logWasFound("Changing workspaces directories from ")); }); story.then(step -> { - assertFalse(Jenkins.get().isDefaultBuildDir()); + assertFalse(story.j.getInstance().isDefaultWorkspaceDir()); assertEquals("bluh", story.j.getInstance().getRawWorkspaceDir()); assertTrue(logWasFound("Changing workspaces directories from ")); }); + + + story.then(step -> { + assertFalse(story.j.getInstance().isDefaultWorkspaceDir()); + assertTrue(logWasFound("Using non default workspaces directories")); + } + ); + } private boolean logWasFound(String searched) { return loggerRule.getRecords().stream() .anyMatch(record -> record.getMessage().contains(searched)); } - - } From 32bac4b33668b3db5501447ba6b7dfcf27f81b51 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 22 Mar 2018 17:23:42 +0100 Subject: [PATCH 09/45] save() if fields were touched --- core/src/main/java/jenkins/model/Jenkins.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 20e863635e..2e9030c4c6 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3044,12 +3044,14 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve setBuildsAndWorkspacesDir(); } - private void setBuildsAndWorkspacesDir() { + private void setBuildsAndWorkspacesDir() throws IOException { + boolean mustSave = false; String newBuildsDir = SystemProperties.getString(BUILDS_DIR_PROP); if (newBuildsDir != null && !buildsDir.equals(newBuildsDir)) { LOGGER.log(Level.WARNING, "Changing builds directories from {0} to {1}. Beware that no automated data migration will occur.", new String[]{buildsDir, newBuildsDir}); buildsDir = newBuildsDir; + mustSave = true; } else if (!isDefaultBuildDir()) { LOGGER.log(Level.INFO, "Using non default builds directories: {0}.", buildsDir); } @@ -3059,10 +3061,15 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve LOGGER.log(Level.WARNING, "Changing workspaces directories from {0} to {1}. Beware that no automated data migration will occur.", new String[]{workspaceDir, newWorkspacesDir}); workspaceDir = newWorkspacesDir; + mustSave = true; } else if (!isDefaultWorkspaceDir()) { LOGGER.log(Level.INFO, "Using non default workspaces directories: {0}.", workspaceDir); } + if (mustSave) { + save(); + } + } private synchronized TaskBuilder loadTasks() throws IOException { From 3d9b6f3ffdb72e82ce3724ed25046dae6e245905 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Fri, 23 Mar 2018 14:56:33 +0100 Subject: [PATCH 10/45] Move and refactor test as now the UI is not anymore --- .../hudson/model/AbstractProjectTest.java | 54 ---- ...insBuildsAndWorkspacesDirectoriesTest.java | 241 ++++++++++++++++++ .../model/JenkinsInitializationTest.java | 113 -------- .../test/java/jenkins/model/JenkinsTest.java | 16 -- 4 files changed, 241 insertions(+), 183 deletions(-) create mode 100644 test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java delete mode 100644 test/src/test/java/jenkins/model/JenkinsInitializationTest.java diff --git a/test/src/test/java/hudson/model/AbstractProjectTest.java b/test/src/test/java/hudson/model/AbstractProjectTest.java index bbde800484..0cb25cc194 100644 --- a/test/src/test/java/hudson/model/AbstractProjectTest.java +++ b/test/src/test/java/hudson/model/AbstractProjectTest.java @@ -331,60 +331,6 @@ public class AbstractProjectTest { assert job.lastBuild != null; } */ - @Test - @Issue("JENKINS-17137") - public void externalBuildDirectorySymlinks() throws Exception { - Assume.assumeFalse(Functions.isWindows()); // symlinks may not be available - HtmlForm form = j.createWebClient().goTo("configure").getFormByName("config"); - File builds = j.createTmpDir(); - form.getInputByName("_.rawBuildsDir").setValueAttribute(builds.toString() + "/${ITEM_FULL_NAME}"); - j.submit(form); - assertEquals(builds.toString() + "/${ITEM_FULL_NAME}", j.jenkins.getRawBuildsDir()); - FreeStyleProject p = j.jenkins.createProject(MockFolder.class, "d").createProject(FreeStyleProject.class, "p"); - FreeStyleBuild b1 = p.scheduleBuild2(0).get(); - File link = new File(p.getRootDir(), "lastStable"); - assertTrue(link.exists()); - assertEquals(resolveAll(link).getAbsolutePath(), b1.getRootDir().getAbsolutePath()); - FreeStyleBuild b2 = p.scheduleBuild2(0).get(); - assertTrue(link.exists()); - assertEquals(resolveAll(link).getAbsolutePath(), b2.getRootDir().getAbsolutePath()); - b2.delete(); - assertTrue(link.exists()); - assertEquals(resolveAll(link).getAbsolutePath(), b1.getRootDir().getAbsolutePath()); - b1.delete(); - assertFalse(link.exists()); - } - - private File resolveAll(File link) throws InterruptedException, IOException { - while (true) { - File f = Util.resolveSymlinkToFile(link); - if (f == null) { - return link; - } - link = f; - } - } - - @Test - @Issue("JENKINS-17138") - public void externalBuildDirectoryRenameDelete() throws Exception { - HtmlForm form = j.createWebClient().goTo("configure").getFormByName("config"); - File builds = j.createTmpDir(); - form.getInputByName("_.rawBuildsDir").setValueAttribute(builds.toString() + "/${ITEM_FULL_NAME}"); - j.submit(form); - assertEquals(builds.toString() + "/${ITEM_FULL_NAME}", j.jenkins.getRawBuildsDir()); - FreeStyleProject p = j.jenkins.createProject(MockFolder.class, "d").createProject(FreeStyleProject.class, "prj"); - FreeStyleBuild b = p.scheduleBuild2(0).get(); - File oldBuildDir = new File(builds, "d/prj"); - assertEquals(new File(oldBuildDir, b.getId()), b.getRootDir()); - assertTrue(b.getRootDir().isDirectory()); - p.renameTo("proj"); - File newBuildDir = new File(builds, "d/proj"); - assertEquals(new File(newBuildDir, b.getId()), b.getRootDir()); - assertTrue(b.getRootDir().isDirectory()); - p.delete(); - assertFalse(b.getRootDir().isDirectory()); - } @Test @Issue("JENKINS-18678") diff --git a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java new file mode 100644 index 0000000000..1a76c3103d --- /dev/null +++ b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java @@ -0,0 +1,241 @@ +package jenkins.model; + +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import hudson.Functions; +import hudson.Util; +import hudson.maven.MavenModuleSet; +import hudson.maven.MavenModuleSetBuild; +import hudson.model.FreeStyleBuild; +import hudson.model.FreeStyleProject; +import org.junit.After; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.ExtractResourceSCM; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.LoggerRule; +import org.jvnet.hudson.test.MockFolder; +import org.jvnet.hudson.test.RestartableJenkinsRule; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Since JENKINS-50164, Jenkins#workspacesDir and Jenkins#buildsDir had their associated UI deleted. + * So instead of configuring through the UI, we now have to use sysprops for this. + *

      + * So this test class uses a {@link RestartableJenkinsRule} to check the behaviour of this sysprop being + * present or not between two restarts. + * + * @since TODO + */ +public class JenkinsBuildsAndWorkspacesDirectoriesTest { + + private static final Map PROPS_TO_RESTORE = new LinkedHashMap<>(); + private static final String LOG_WHEN_CHANGING_BUILDS_DIR = "Changing builds directories from "; + + @Rule + public RestartableJenkinsRule story = new RestartableJenkinsRule(); + + @Rule + public LoggerRule loggerRule = new LoggerRule(); + + @Before + public void before() { + Stream.of(Jenkins.BUILDS_DIR_PROP, Jenkins.WORKSPACES_DIR_PROP) + .forEach(System::clearProperty); + } + + @After + public void after() { + Stream.of(Jenkins.BUILDS_DIR_PROP, Jenkins.WORKSPACES_DIR_PROP) + .forEach(System::clearProperty); + } + + @Issue("JENKINS-50164") + @Test + public void buildsDir() throws Exception { + loggerRule.record(Jenkins.class, Level.WARNING) + .record(Jenkins.class, Level.INFO) + .capture(100); + + story.then(step -> { + assertFalse(logWasFound("Using non default builds directories")); + } + ); + + story.then(steps -> { + assertTrue(story.j.getInstance().isDefaultBuildDir()); + setBuildsDirProperty("blah"); + assertFalse(JenkinsBuildsAndWorkspacesDirectoriesTest.this.logWasFound(LOG_WHEN_CHANGING_BUILDS_DIR)); + }); + + story.then(step -> { + assertFalse(story.j.getInstance().isDefaultBuildDir()); + assertEquals("blah", story.j.getInstance().getRawBuildsDir()); + assertTrue(logWasFound("Changing builds directories from ")); + } + ); + + story.then(step -> assertTrue(logWasFound("Using non default builds directories")) + ); + } + + @Issue("JENKINS-50164") + @Test + public void workspacesDir() throws Exception { + loggerRule.record(Jenkins.class, Level.WARNING) + .record(Jenkins.class, Level.INFO) + .capture(1000); + + story.then(step -> assertFalse(logWasFound("Using non default workspaces directories"))); + + story.then(step -> { + assertTrue(story.j.getInstance().isDefaultWorkspaceDir()); + final String workspacesDir = "bluh"; + setWorkspacesDirProperty(workspacesDir); + assertFalse(logWasFound("Changing workspaces directories from ")); + }); + + story.then(step -> { + assertFalse(story.j.getInstance().isDefaultWorkspaceDir()); + assertEquals("bluh", story.j.getInstance().getRawWorkspaceDir()); + assertTrue(logWasFound("Changing workspaces directories from ")); + }); + + + story.then(step -> { + assertFalse(story.j.getInstance().isDefaultWorkspaceDir()); + assertTrue(logWasFound("Using non default workspaces directories")); + } + ); + + } + + private void setWorkspacesDirProperty(String workspacesDir) { + System.setProperty("jenkins.model.Jenkins.WORKSPACES_DIR", workspacesDir); + } + + private void setBuildsDirProperty(String buildsDir) { + System.setProperty("jenkins.model.Jenkins.BUILDS_DIR", buildsDir); + } + + private boolean logWasFound(String searched) { + return loggerRule.getRecords().stream() + .anyMatch(record -> record.getMessage().contains(searched)); + } + + @Test + @Issue("JENKINS-12251") + public void testItemFullNameExpansion() throws Exception { + loggerRule.record(Jenkins.class, Level.WARNING) + .record(Jenkins.class, Level.INFO) + .capture(1000); + + story.then(steps -> { + assertTrue(story.j.getInstance().isDefaultBuildDir()); + assertTrue(story.j.getInstance().isDefaultWorkspaceDir()); + setBuildsDirProperty("${JENKINS_HOME}/test12251_builds/${ITEM_FULL_NAME}"); + setWorkspacesDirProperty("${JENKINS_HOME}/test12251_ws/${ITEM_FULL_NAME}"); + }); + + story.then(steps -> { + assertTrue(JenkinsBuildsAndWorkspacesDirectoriesTest.this.logWasFound("Changing builds directories from ")); + assertFalse(story.j.getInstance().isDefaultBuildDir()); + assertFalse(story.j.getInstance().isDefaultWorkspaceDir()); + + // build a dummy project + MavenModuleSet m = story.j.jenkins.createProject(MavenModuleSet.class, "p"); + m.setScm(new ExtractResourceSCM(getClass().getResource("/simple-projects.zip"))); + MavenModuleSetBuild b = m.scheduleBuild2(0).get(); + + // make sure these changes are effective + assertTrue(b.getWorkspace().getRemote().contains("test12251_ws")); + assertTrue(b.getRootDir().toString().contains("test12251_builds")); + }); + + } + + @Test + @Issue("JENKINS-17138") + public void externalBuildDirectoryRenameDelete() throws Exception { + + // Hack to get String builds usable in lambda below + final List builds = new ArrayList<>(); + + story.then(steps -> { + builds.add(story.j.createTmpDir().toString()); + assertTrue(story.j.getInstance().isDefaultBuildDir()); + setBuildsDirProperty(builds.get(0) + "/${ITEM_FULL_NAME}"); + }); + + story.then(steps -> { + assertEquals(builds.get(0) + "/${ITEM_FULL_NAME}", story.j.jenkins.getRawBuildsDir()); + FreeStyleProject p = story.j.jenkins.createProject(MockFolder.class, "d").createProject(FreeStyleProject.class, "prj"); + FreeStyleBuild b = p.scheduleBuild2(0).get(); + File oldBuildDir = new File(builds.get(0), "d/prj"); + assertEquals(new File(oldBuildDir, b.getId()), b.getRootDir()); + assertTrue(b.getRootDir().isDirectory()); + p.renameTo("proj"); + File newBuildDir = new File(builds.get(0), "d/proj"); + assertEquals(new File(newBuildDir, b.getId()), b.getRootDir()); + assertTrue(b.getRootDir().isDirectory()); + p.delete(); + assertFalse(b.getRootDir().isDirectory()); + }); + } + + @Test + @Issue("JENKINS-17137") + public void externalBuildDirectorySymlinks() throws Exception { + Assume.assumeFalse(Functions.isWindows()); // symlinks may not be available + + // Hack to get String builds usable in lambda below + final List builds = new ArrayList<>(); + + story.then(steps -> { + builds.add(story.j.createTmpDir().toString()); + setBuildsDirProperty(builds.get(0) + "/${ITEM_FULL_NAME}"); + }); + + story.then(steps -> { + + assertEquals(builds.get(0) + "/${ITEM_FULL_NAME}", story.j.jenkins.getRawBuildsDir()); + FreeStyleProject p = story.j.jenkins.createProject(MockFolder.class, "d").createProject(FreeStyleProject.class, "p"); + FreeStyleBuild b1 = p.scheduleBuild2(0).get(); + File link = new File(p.getRootDir(), "lastStable"); + assertTrue(link.exists()); + assertEquals(resolveAll(link).getAbsolutePath(), b1.getRootDir().getAbsolutePath()); + FreeStyleBuild b2 = p.scheduleBuild2(0).get(); + assertTrue(link.exists()); + assertEquals(resolveAll(link).getAbsolutePath(), b2.getRootDir().getAbsolutePath()); + b2.delete(); + assertTrue(link.exists()); + assertEquals(resolveAll(link).getAbsolutePath(), b1.getRootDir().getAbsolutePath()); + b1.delete(); + assertFalse(link.exists()); + }); + } + + private File resolveAll(File link) throws InterruptedException, IOException { + while (true) { + File f = Util.resolveSymlinkToFile(link); + if (f == null) { + return link; + } + link = f; + } + } + +} diff --git a/test/src/test/java/jenkins/model/JenkinsInitializationTest.java b/test/src/test/java/jenkins/model/JenkinsInitializationTest.java deleted file mode 100644 index 3e5c5513da..0000000000 --- a/test/src/test/java/jenkins/model/JenkinsInitializationTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package jenkins.model; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.LoggerRule; -import org.jvnet.hudson.test.RestartableJenkinsRule; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.stream.Stream; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -@Issue("JENKINS-50164") -public class JenkinsInitializationTest { - - private static final Map PROPS_TO_RESTORE = new LinkedHashMap<>(); - @Rule - public RestartableJenkinsRule story = new RestartableJenkinsRule(); - - @Rule - public LoggerRule loggerRule = new LoggerRule(); - - @BeforeClass - public static void beforeClass() { - Stream.of(Jenkins.BUILDS_DIR_PROP, Jenkins.WORKSPACES_DIR_PROP) - .forEach(property -> PROPS_TO_RESTORE.put(property, System.getProperty(property))); - } - - @AfterClass - public static void afterClass() { - PROPS_TO_RESTORE.forEach((k, v) -> { - if (v == null) { - System.clearProperty(k); - } else { - System.setProperty(k, v); - } - }); - } - - @Test - public void buildsDir() throws Exception { - loggerRule.record(Jenkins.class, Level.WARNING) - .record(Jenkins.class, Level.INFO) - .capture(100); - - story.then(step -> { - assertFalse(logWasFound("Using non default builds directories")); - } - ); - - story.then(steps -> { - assertTrue(story.j.getInstance().isDefaultBuildDir()); - System.setProperty("jenkins.model.Jenkins.BUILDS_DIR", "blah"); - assertFalse(JenkinsInitializationTest.this.logWasFound("Changing builds directories from ")); - }); - - story.then(step -> { - assertFalse(story.j.getInstance().isDefaultBuildDir()); - assertEquals("blah", story.j.getInstance().getRawBuildsDir()); - assertTrue(logWasFound("Changing builds directories from ")); - } - ); - - story.then(step -> { - assertTrue(logWasFound("Using non default builds directories")); - } - ); - } - - @Test - public void workspacesDir() throws Exception { - loggerRule.record(Jenkins.class, Level.WARNING) - .record(Jenkins.class, Level.INFO) - .capture(1000); - - story.then(step -> { - assertFalse(logWasFound("Using non default workspaces directories")); - } - ); - - story.then(step -> { - assertTrue(story.j.getInstance().isDefaultWorkspaceDir()); - System.setProperty("jenkins.model.Jenkins.WORKSPACES_DIR", "bluh"); - assertFalse(logWasFound("Changing workspaces directories from ")); - }); - - story.then(step -> { - assertFalse(story.j.getInstance().isDefaultWorkspaceDir()); - assertEquals("bluh", story.j.getInstance().getRawWorkspaceDir()); - assertTrue(logWasFound("Changing workspaces directories from ")); - }); - - - story.then(step -> { - assertFalse(story.j.getInstance().isDefaultWorkspaceDir()); - assertTrue(logWasFound("Using non default workspaces directories")); - } - ); - - } - - private boolean logWasFound(String searched) { - return loggerRule.getRecords().stream() - .anyMatch(record -> record.getMessage().contains(searched)); - } -} diff --git a/test/src/test/java/jenkins/model/JenkinsTest.java b/test/src/test/java/jenkins/model/JenkinsTest.java index 833230dbe8..a888e356a1 100644 --- a/test/src/test/java/jenkins/model/JenkinsTest.java +++ b/test/src/test/java/jenkins/model/JenkinsTest.java @@ -275,22 +275,6 @@ public class JenkinsTest { } } - @Test @Issue("JENKINS-12251") - public void testItemFullNameExpansion() throws Exception { - HtmlForm f = j.createWebClient().goTo("configure").getFormByName("config"); - f.getInputByName("_.rawBuildsDir").setValueAttribute("${JENKINS_HOME}/test12251_builds/${ITEM_FULL_NAME}"); - f.getInputByName("_.rawWorkspaceDir").setValueAttribute("${JENKINS_HOME}/test12251_ws/${ITEM_FULL_NAME}"); - j.submit(f); - - // build a dummy project - MavenModuleSet m = j.jenkins.createProject(MavenModuleSet.class, "p"); - m.setScm(new ExtractResourceSCM(getClass().getResource("/simple-projects.zip"))); - MavenModuleSetBuild b = m.scheduleBuild2(0).get(); - - // make sure these changes are effective - assertTrue(b.getWorkspace().getRemote().contains("test12251_ws")); - assertTrue(b.getRootDir().toString().contains("test12251_builds")); - } /** * Makes sure access to "/foobar" for UnprotectedRootAction gets through. From 774388598e1bf861330c29470484d7957d86fe69 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Tue, 27 Mar 2018 11:55:13 +0200 Subject: [PATCH 11/45] Add to check removing sysprop keeps the previous value Addressing @jtnord's concern/following use case: * Custom build/workspace are set in existing Jenkins config (from version without this change) * Jenkins is upgraded * Jenkins config is saved * configuration should not be reset - existing custom values should be retained until they set the system property --- ...insBuildsAndWorkspacesDirectoriesTest.java | 56 ++++++++++++++++--- .../fromPreviousCustomSetup/README.adoc | 13 +++++ .../fromPreviousCustomSetup/config.xml | 49 ++++++++++++++++ 3 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 test/src/test/resources/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest/fromPreviousCustomSetup/README.adoc create mode 100644 test/src/test/resources/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest/fromPreviousCustomSetup/config.xml diff --git a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java index 1a76c3103d..b3e3c7f8a7 100644 --- a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java +++ b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java @@ -1,12 +1,13 @@ package jenkins.model; -import com.gargoylesoftware.htmlunit.html.HtmlForm; import hudson.Functions; import hudson.Util; +import hudson.init.InitMilestone; import hudson.maven.MavenModuleSet; import hudson.maven.MavenModuleSetBuild; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; +import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Assume; import org.junit.Before; @@ -17,6 +18,7 @@ import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.MockFolder; import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.jvnet.hudson.test.recipes.LocalData; import java.io.File; import java.io.IOException; @@ -27,8 +29,10 @@ import java.util.Map; import java.util.logging.Level; import java.util.stream.Stream; +import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -42,7 +46,6 @@ import static org.junit.Assert.assertTrue; */ public class JenkinsBuildsAndWorkspacesDirectoriesTest { - private static final Map PROPS_TO_RESTORE = new LinkedHashMap<>(); private static final String LOG_WHEN_CHANGING_BUILDS_DIR = "Changing builds directories from "; @Rule @@ -53,12 +56,15 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { @Before public void before() { - Stream.of(Jenkins.BUILDS_DIR_PROP, Jenkins.WORKSPACES_DIR_PROP) - .forEach(System::clearProperty); + clearSystemProperties(); } @After public void after() { + clearSystemProperties(); + } + + private void clearSystemProperties() { Stream.of(Jenkins.BUILDS_DIR_PROP, Jenkins.WORKSPACES_DIR_PROP) .forEach(System::clearProperty); } @@ -120,6 +126,43 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { assertTrue(logWasFound("Using non default workspaces directories")); } ); + } + + @Issue("JENKINS-50164") + @LocalData + @Test + public void fromPreviousCustomSetup() { + // check starting point and change config for next run + final String newBuildsDirValueBySysprop = "bling/${ITEM_ROOTDIR}/bluh"; + story.then(j -> { + assertEquals("${ITEM_ROOTDIR}/ze-previous-custom-builds", j.jenkins.getRawBuildsDir()); + setBuildsDirProperty(newBuildsDirValueBySysprop); + }); + + // Check the sysprop setting was taken in account + story.then(j -> { + assertEquals(newBuildsDirValueBySysprop, j.jenkins.getRawBuildsDir()); + + // ** HACK AROUND JENKINS-50422: manually restarting ** // + // Check the disk (cannot just restart normally with the rule, ) + assertThat(FileUtils.readFileToString(new File(j.jenkins.getRootDir(), "config.xml")), + containsString("" + newBuildsDirValueBySysprop + "")); + + String rootDirBeforeRestart = j.jenkins.getRootDir().toString(); + clearSystemProperties(); + j.jenkins.restart(); + + int maxLoops = 50; + while (j.jenkins.getInitLevel() != InitMilestone.COMPLETED && maxLoops-- > 0) { + Thread.sleep(300); + } + + assertEquals(rootDirBeforeRestart, j.jenkins.getRootDir().toString()); + assertThat(FileUtils.readFileToString(new File(j.jenkins.getRootDir(), "config.xml")), + containsString("" + newBuildsDirValueBySysprop + "")); + assertEquals(newBuildsDirValueBySysprop, j.jenkins.getRawBuildsDir()); + // ** END HACK ** // + }); } @@ -204,7 +247,7 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { // Hack to get String builds usable in lambda below final List builds = new ArrayList<>(); - story.then(steps -> { + story.then(steps -> { builds.add(story.j.createTmpDir().toString()); setBuildsDirProperty(builds.get(0) + "/${ITEM_FULL_NAME}"); }); @@ -237,5 +280,4 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { link = f; } } - -} +} \ No newline at end of file diff --git a/test/src/test/resources/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest/fromPreviousCustomSetup/README.adoc b/test/src/test/resources/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest/fromPreviousCustomSetup/README.adoc new file mode 100644 index 0000000000..297f281591 --- /dev/null +++ b/test/src/test/resources/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest/fromPreviousCustomSetup/README.adoc @@ -0,0 +1,13 @@ += Description of the intent of that setup + +The goal of this dataset is to configure a custom directory layout for workspaces and builds. +This for use with @LocalData to test JENKINS-50164 + +Specifically, the config.xml should contain: + +``` +${JENKINS_HOME}/previous-custom-workspace/${ITEM_FULL_NAME} +${ITEM_ROOTDIR}/ze-previous-custom-builds +``` + + diff --git a/test/src/test/resources/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest/fromPreviousCustomSetup/config.xml b/test/src/test/resources/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest/fromPreviousCustomSetup/config.xml new file mode 100644 index 0000000000..230b8e49f0 --- /dev/null +++ b/test/src/test/resources/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest/fromPreviousCustomSetup/config.xml @@ -0,0 +1,49 @@ + + + + 2.112 + + RUNNING + + 2 + NORMAL + true + + true + + + true + false + + false + + ${JENKINS_HOME}/previous-custom-workspace/${ITEM_FULL_NAME} + ${ITEM_ROOTDIR}/ze-previous-custom-builds + + + + + 5 + 0 + + + + all + false + false + + + + all + -1 + + JNLP-connect + JNLP2-connect + + + + false + + + + \ No newline at end of file From 58b875359ec55d0f7a1b2b2f3f12302e291a32be Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Tue, 27 Mar 2018 16:32:30 +0200 Subject: [PATCH 12/45] Disable fromPreviousCustomSetup on Windows Because I use restart and it's not supported there. --- .../model/JenkinsBuildsAndWorkspacesDirectoriesTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java index b3e3c7f8a7..462e2d1cbb 100644 --- a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java +++ b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java @@ -9,7 +9,6 @@ import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import org.apache.commons.io.FileUtils; import org.junit.After; -import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -23,9 +22,7 @@ import org.jvnet.hudson.test.recipes.LocalData; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.logging.Level; import java.util.stream.Stream; @@ -34,6 +31,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; /** * Since JENKINS-50164, Jenkins#workspacesDir and Jenkins#buildsDir had their associated UI deleted. @@ -132,6 +130,9 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { @LocalData @Test public void fromPreviousCustomSetup() { + + assumeFalse(Functions.isWindows()); // Default Windows lifecycle does not support restart. + // check starting point and change config for next run final String newBuildsDirValueBySysprop = "bling/${ITEM_ROOTDIR}/bluh"; story.then(j -> { @@ -242,7 +243,7 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { @Test @Issue("JENKINS-17137") public void externalBuildDirectorySymlinks() throws Exception { - Assume.assumeFalse(Functions.isWindows()); // symlinks may not be available + assumeFalse(Functions.isWindows()); // symlinks may not be available // Hack to get String builds usable in lambda below final List builds = new ArrayList<>(); From 2c1fdd26990b417a4e57fdaaa7fb30ddc2a9be17 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Wed, 28 Mar 2018 10:07:32 +0200 Subject: [PATCH 13/45] Name sysprops more consistently with others https://wiki.jenkins.io/display/JENKINS/Features+controlled+by+system+properties --- core/src/main/java/jenkins/model/Jenkins.java | 4 ++-- .../model/JenkinsBuildsAndWorkspacesDirectoriesTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 2e9030c4c6..e9235835cc 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -5102,13 +5102,13 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * System property name to set {@link #buildsDir}. * @see #getRawBuildsDir() */ - static final String BUILDS_DIR_PROP = Jenkins.class.getName() + ".BUILDS_DIR"; + static final String BUILDS_DIR_PROP = Jenkins.class.getName() + ".buildsDir"; /** * System property name to set {@link #workspaceDir}. * @see #getRawWorkspaceDir() */ - static final String WORKSPACES_DIR_PROP = Jenkins.class.getName() + ".WORKSPACES_DIR"; + static final String WORKSPACES_DIR_PROP = Jenkins.class.getName() + ".workspacesDir"; /** * Automatically try to launch an agent when Jenkins is initialized or a new agent computer is created. diff --git a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java index 462e2d1cbb..7f6d6c4502 100644 --- a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java +++ b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java @@ -168,11 +168,11 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { } private void setWorkspacesDirProperty(String workspacesDir) { - System.setProperty("jenkins.model.Jenkins.WORKSPACES_DIR", workspacesDir); + System.setProperty(Jenkins.WORKSPACES_DIR_PROP, workspacesDir); } private void setBuildsDirProperty(String buildsDir) { - System.setProperty("jenkins.model.Jenkins.BUILDS_DIR", buildsDir); + System.setProperty(Jenkins.BUILDS_DIR_PROP, buildsDir); } private boolean logWasFound(String searched) { From 8521b8bf1970cf00dd30831796e29f23e5b884c9 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Thu, 29 Mar 2018 12:14:42 +0200 Subject: [PATCH 14/45] Check buildsDir value and fail Jenkins startup if wrong --- .../java/jenkins/model/InvalidBuildsDir.java | 16 ++++ core/src/main/java/jenkins/model/Jenkins.java | 95 +++++++++++-------- .../jenkins/model/JenkinsDescriptorTest.java | 86 ----------------- test/pom.xml | 2 +- ...insBuildsAndWorkspacesDirectoriesTest.java | 86 ++++++++++++++++- 5 files changed, 153 insertions(+), 132 deletions(-) create mode 100644 core/src/main/java/jenkins/model/InvalidBuildsDir.java delete mode 100644 core/src/test/java/jenkins/model/JenkinsDescriptorTest.java diff --git a/core/src/main/java/jenkins/model/InvalidBuildsDir.java b/core/src/main/java/jenkins/model/InvalidBuildsDir.java new file mode 100644 index 0000000000..7e9b8f348d --- /dev/null +++ b/core/src/main/java/jenkins/model/InvalidBuildsDir.java @@ -0,0 +1,16 @@ +package jenkins.model; + +import hudson.util.BootFailure; + +public class InvalidBuildsDir extends BootFailure { + private String message; + + public InvalidBuildsDir(String message) { + this.message = message; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index e9235835cc..d9c9af7974 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -27,6 +27,7 @@ package jenkins.model; import antlr.ANTLRException; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; @@ -2137,46 +2138,6 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return FormValidation.validateNonNegativeInteger(value); } - public FormValidation doCheckRawBuildsDir(@QueryParameter String value) { - // do essentially what expandVariablesForDirectory does, without an Item - String replacedValue = expandVariablesForDirectory(value, - "doCheckRawBuildsDir-Marker:foo", - Jenkins.getInstance().getRootDir().getPath() + "/jobs/doCheckRawBuildsDir-Marker$foo"); - - File replacedFile = new File(replacedValue); - if (!replacedFile.isAbsolute()) { - return FormValidation.error(value + " does not resolve to an absolute path"); - } - - if (!replacedValue.contains("doCheckRawBuildsDir-Marker")) { - return FormValidation.error(value + " does not contain ${ITEM_FULL_NAME} or ${ITEM_ROOTDIR}, cannot distinguish between projects"); - } - - if (replacedValue.contains("doCheckRawBuildsDir-Marker:foo")) { - // make sure platform can handle colon - try { - File tmp = File.createTempFile("Jenkins-doCheckRawBuildsDir", "foo:bar"); - tmp.delete(); - } catch (IOException e) { - return FormValidation.error(value + " contains ${ITEM_FULLNAME} but your system does not support it (JENKINS-12251). Use ${ITEM_FULL_NAME} instead"); - } - } - - File d = new File(replacedValue); - if (!d.isDirectory()) { - // if dir does not exist (almost guaranteed) need to make sure nearest existing ancestor can be written to - d = d.getParentFile(); - while (!d.exists()) { - d = d.getParentFile(); - } - if (!d.canWrite()) { - return FormValidation.error(value + " does not exist and probably cannot be created"); - } - } - - return FormValidation.ok(); - } - // to route /descriptor/FQCN/xxx to getDescriptor(FQCN).xxx public Object getDynamic(String token) { return Jenkins.getInstance().getDescriptor(token); @@ -3041,13 +3002,21 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve cfg.unmarshal(Jenkins.this); } - setBuildsAndWorkspacesDir(); + try { + checkRawBuildsDir(buildsDir); + setBuildsAndWorkspacesDir(); + } catch (InvalidBuildsDir invalidBuildsDir) { + throw new IOException(invalidBuildsDir); + } + } - private void setBuildsAndWorkspacesDir() throws IOException { + private void setBuildsAndWorkspacesDir() throws IOException, InvalidBuildsDir { boolean mustSave = false; String newBuildsDir = SystemProperties.getString(BUILDS_DIR_PROP); if (newBuildsDir != null && !buildsDir.equals(newBuildsDir)) { + + checkRawBuildsDir(newBuildsDir); LOGGER.log(Level.WARNING, "Changing builds directories from {0} to {1}. Beware that no automated data migration will occur.", new String[]{buildsDir, newBuildsDir}); buildsDir = newBuildsDir; @@ -3069,7 +3038,49 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve if (mustSave) { save(); } + } + /** + * @param value + */ + @VisibleForTesting + /*private*/ static void checkRawBuildsDir(String value) throws InvalidBuildsDir { + + // do essentially what expandVariablesForDirectory does, without an Item + String replacedValue = expandVariablesForDirectory(value, + "doCheckRawBuildsDir-Marker:foo", + Jenkins.getInstance().getRootDir().getPath() + "/jobs/doCheckRawBuildsDir-Marker$foo"); + + File replacedFile = new File(replacedValue); + if (!replacedFile.isAbsolute()) { + throw new InvalidBuildsDir(value + " does not resolve to an absolute path"); + } + + if (!replacedValue.contains("doCheckRawBuildsDir-Marker")) { + throw new InvalidBuildsDir(value + " does not contain ${ITEM_FULL_NAME} or ${ITEM_ROOTDIR}, cannot distinguish between projects"); + } + + if (replacedValue.contains("doCheckRawBuildsDir-Marker:foo")) { + // make sure platform can handle colon + try { + File tmp = File.createTempFile("Jenkins-doCheckRawBuildsDir", "foo:bar"); + tmp.delete(); + } catch (IOException e) { + throw new InvalidBuildsDir(value + " contains ${ITEM_FULLNAME} but your system does not support it (JENKINS-12251). Use ${ITEM_FULL_NAME} instead"); + } + } + + File d = new File(replacedValue); + if (!d.isDirectory()) { + // if dir does not exist (almost guaranteed) need to make sure nearest existing ancestor can be written to + d = d.getParentFile(); + while (!d.exists()) { + d = d.getParentFile(); + } + if (!d.canWrite()) { + throw new InvalidBuildsDir(value + " does not exist and probably cannot be created"); + } + } } private synchronized TaskBuilder loadTasks() throws IOException { diff --git a/core/src/test/java/jenkins/model/JenkinsDescriptorTest.java b/core/src/test/java/jenkins/model/JenkinsDescriptorTest.java deleted file mode 100644 index 5c0e5fb863..0000000000 --- a/core/src/test/java/jenkins/model/JenkinsDescriptorTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2014 Daniel Beck - * - * 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.model; - -import hudson.util.FormValidation; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import java.io.File; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.when; - -@RunWith(PowerMockRunner.class) -public class JenkinsDescriptorTest { - - @Mock - Jenkins jenkins; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - } - - private FormValidation checkBuildsDir(String buildsDir) { - return Jenkins.DescriptorImpl.INSTANCE.doCheckRawBuildsDir(buildsDir); - } - - private boolean isOK(String buildsDir) { - return checkBuildsDir(buildsDir).kind == FormValidation.Kind.OK; - } - - private boolean isError(String buildsDir) { - return checkBuildsDir(buildsDir).kind == FormValidation.Kind.ERROR; - } - - @Test - @PrepareForTest(Jenkins.class) - public void testBuildDirValidation() { - PowerMockito.mockStatic(Jenkins.class); - PowerMockito.when(Jenkins.getInstance()).thenReturn(jenkins); - PowerMockito.when(Jenkins.expandVariablesForDirectory(anyString(), anyString(), anyString())).thenCallRealMethod(); - when(jenkins.getRootDir()).thenReturn(new File(".").getAbsoluteFile()); - - assertTrue(isOK("$JENKINS_HOME/foo/$ITEM_FULL_NAME")); - assertTrue(isOK("${ITEM_ROOTDIR}/builds")); - - assertTrue(isError("$JENKINS_HOME")); - assertTrue(isError("$JENKINS_HOME/builds")); - assertTrue(isError("$ITEM_FULL_NAME")); - assertTrue(isError("/path/to/builds")); - assertTrue(isError("/invalid/$JENKINS_HOME")); - assertTrue(isError("relative/ITEM_FULL_NAME")); - - // TODO test literal absolute paths (e.g. /foo/$ITEM_FULL_NAME), ITEM_FULLNAME - } - -} \ No newline at end of file diff --git a/test/pom.xml b/test/pom.xml index 2e918d046f..54bc586740 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -53,7 +53,7 @@ THE SOFTWARE. ${project.groupId} jenkins-test-harness - 2.33 + 2.35-20180329.095418-3 test diff --git a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java index 7f6d6c4502..baf3ae1416 100644 --- a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java +++ b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java @@ -22,6 +22,7 @@ import org.jvnet.hudson.test.recipes.LocalData; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.stream.Stream; @@ -31,6 +32,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; /** @@ -67,6 +69,84 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { .forEach(System::clearProperty); } + @Issue("JENKINS-50164") + @Test + public void badValueForBuildsDir() { + story.then((rule) -> { + final List badValues = Arrays.asList( + "blah", + "$JENKINS_HOME", + "$JENKINS_HOME/builds", + "$ITEM_FULL_NAME", + "/path/to/builds", + "/invalid/$JENKINS_HOME", + "relative/ITEM_FULL_NAME", + "/foo/$ITEM_FULL_NAME", + "/$ITEM_FULLNAME"); + + for (String badValue : badValues) { + try { + Jenkins.checkRawBuildsDir(badValue); + fail(badValue + " should have been rejected"); + } catch (InvalidBuildsDir invalidBuildsDir) { + // expected failure + } + } + }); + } + + @Issue("JENKINS-50164") + @Test + public void goodValueForBuildsDir() { + story.then((rule) -> { + final List badValues = Arrays.asList( + "$JENKINS_HOME/foo/$ITEM_FULL_NAME", + "${ITEM_ROOTDIR}/builds"); + + for (String goodValue : badValues) { + Jenkins.checkRawBuildsDir(goodValue); + } + }); + } + + @Issue("JENKINS-50164") + @Test + public void jenkinsDoesNotStartWithBadSysProp() { + + loggerRule.record(Jenkins.class, Level.WARNING) + .record(Jenkins.class, Level.INFO) + .capture(100); + + story.then(rule -> { + assertTrue(story.j.getInstance().isDefaultBuildDir()); + setBuildsDirProperty("/bluh"); + }); + + story.thenDoesNotStart(); + } + + @Issue("JENKINS-50164") + @Test + public void jenkinsDoesNotStartWithScrewedUpConfigXml() { + + loggerRule.record(Jenkins.class, Level.WARNING) + .record(Jenkins.class, Level.INFO) + .capture(100); + + story.then(rule -> { + + assertTrue(story.j.getInstance().isDefaultBuildDir()); + + // Now screw up the value by writing into the file directly, like one could do using external XML manipulation tools + final File configFile = new File(rule.jenkins.getRootDir(), "config.xml"); + final String screwedUp = FileUtils.readFileToString(configFile). + replaceFirst(".*", "eeeeeeeeek"); + FileUtils.write(configFile, screwedUp); + }); + + story.thenDoesNotStart(); + } + @Issue("JENKINS-50164") @Test public void buildsDir() throws Exception { @@ -81,13 +161,13 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { story.then(steps -> { assertTrue(story.j.getInstance().isDefaultBuildDir()); - setBuildsDirProperty("blah"); + setBuildsDirProperty("$JENKINS_HOME/plouf/$ITEM_FULL_NAME/bluh"); assertFalse(JenkinsBuildsAndWorkspacesDirectoriesTest.this.logWasFound(LOG_WHEN_CHANGING_BUILDS_DIR)); }); story.then(step -> { assertFalse(story.j.getInstance().isDefaultBuildDir()); - assertEquals("blah", story.j.getInstance().getRawBuildsDir()); + assertEquals("$JENKINS_HOME/plouf/$ITEM_FULL_NAME/bluh", story.j.getInstance().getRawBuildsDir()); assertTrue(logWasFound("Changing builds directories from ")); } ); @@ -134,7 +214,7 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { assumeFalse(Functions.isWindows()); // Default Windows lifecycle does not support restart. // check starting point and change config for next run - final String newBuildsDirValueBySysprop = "bling/${ITEM_ROOTDIR}/bluh"; + final String newBuildsDirValueBySysprop = "/tmp/${ITEM_ROOTDIR}/bluh"; story.then(j -> { assertEquals("${ITEM_ROOTDIR}/ze-previous-custom-builds", j.jenkins.getRawBuildsDir()); setBuildsDirProperty(newBuildsDirValueBySysprop); From 434ddbdcf7fd60dc526c175a547e5fd3aaaef70f Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Mon, 2 Apr 2018 16:37:36 +0200 Subject: [PATCH 15/45] Use released version of jenkins-test-harness --- test/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pom.xml b/test/pom.xml index 54bc586740..643266ade7 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -53,7 +53,7 @@ THE SOFTWARE. ${project.groupId} jenkins-test-harness - 2.35-20180329.095418-3 + 2.36 test From d8ef16e422df6177dfc16679ab17001f536af47b Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Fri, 20 Apr 2018 14:26:22 +0200 Subject: [PATCH 16/45] Update javadoc & clarify parameter name --- core/src/main/java/jenkins/model/Jenkins.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index d9c9af7974..d3f7453e52 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -3041,23 +3041,24 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } /** - * @param value + * Checks the correctness of the newBuildsDirValue for use as {@link #buildsDir}. + * @param newBuildsDirValue the candidate newBuildsDirValue for updating {@link #buildsDir}. */ @VisibleForTesting - /*private*/ static void checkRawBuildsDir(String value) throws InvalidBuildsDir { + /*private*/ static void checkRawBuildsDir(String newBuildsDirValue) throws InvalidBuildsDir { // do essentially what expandVariablesForDirectory does, without an Item - String replacedValue = expandVariablesForDirectory(value, + String replacedValue = expandVariablesForDirectory(newBuildsDirValue, "doCheckRawBuildsDir-Marker:foo", Jenkins.getInstance().getRootDir().getPath() + "/jobs/doCheckRawBuildsDir-Marker$foo"); File replacedFile = new File(replacedValue); if (!replacedFile.isAbsolute()) { - throw new InvalidBuildsDir(value + " does not resolve to an absolute path"); + throw new InvalidBuildsDir(newBuildsDirValue + " does not resolve to an absolute path"); } if (!replacedValue.contains("doCheckRawBuildsDir-Marker")) { - throw new InvalidBuildsDir(value + " does not contain ${ITEM_FULL_NAME} or ${ITEM_ROOTDIR}, cannot distinguish between projects"); + throw new InvalidBuildsDir(newBuildsDirValue + " does not contain ${ITEM_FULL_NAME} or ${ITEM_ROOTDIR}, cannot distinguish between projects"); } if (replacedValue.contains("doCheckRawBuildsDir-Marker:foo")) { @@ -3066,7 +3067,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve File tmp = File.createTempFile("Jenkins-doCheckRawBuildsDir", "foo:bar"); tmp.delete(); } catch (IOException e) { - throw new InvalidBuildsDir(value + " contains ${ITEM_FULLNAME} but your system does not support it (JENKINS-12251). Use ${ITEM_FULL_NAME} instead"); + throw new InvalidBuildsDir(newBuildsDirValue + " contains ${ITEM_FULLNAME} but your system does not support it (JENKINS-12251). Use ${ITEM_FULL_NAME} instead"); } } @@ -3078,7 +3079,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve d = d.getParentFile(); } if (!d.canWrite()) { - throw new InvalidBuildsDir(value + " does not exist and probably cannot be created"); + throw new InvalidBuildsDir(newBuildsDirValue + " does not exist and probably cannot be created"); } } } From 84e423d77a220341bd0471432f736e45a0f7ce84 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Fri, 20 Apr 2018 14:28:19 +0200 Subject: [PATCH 17/45] Make the potential skip reason bubble up --- .../model/JenkinsBuildsAndWorkspacesDirectoriesTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java index baf3ae1416..2051c1a26a 100644 --- a/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java +++ b/test/src/test/java/jenkins/model/JenkinsBuildsAndWorkspacesDirectoriesTest.java @@ -211,7 +211,7 @@ public class JenkinsBuildsAndWorkspacesDirectoriesTest { @Test public void fromPreviousCustomSetup() { - assumeFalse(Functions.isWindows()); // Default Windows lifecycle does not support restart. + assumeFalse("Default Windows lifecycle does not support restart.", Functions.isWindows()); // check starting point and change config for next run final String newBuildsDirValueBySysprop = "/tmp/${ITEM_ROOTDIR}/bluh"; From d6800ac9eb12307a01aab1ab4199adff503f6c31 Mon Sep 17 00:00:00 2001 From: surenpi Date: Sun, 22 Apr 2018 19:32:37 +0800 Subject: [PATCH 18/45] Add Chinese translation for slave --- .../model/Computer/_api_zh_CN.properties | 23 ++++++++++++++++ .../model/Computer/index_zh_CN.properties | 4 ++- .../Computer/markOffline_zh_CN.properties | 25 +++++++++++++++++ .../Computer/setOfflineCause_zh_CN.properties | 25 +++++++++++++++++ .../model/Computer/sidepanel_zh_CN.properties | 3 ++- .../model/ComputerSet/_new_zh_CN.properties | 1 + .../model/ComputerSet/index_zh_CN.properties | 3 ++- .../ComputerSet/sidepanel_zh_CN.properties | 4 +-- .../ComputerLauncher/main_zh_CN.properties | 26 ++++++++++++++++++ .../newInstanceDetail_zh_CN.properties | 25 +++++++++++++++++ .../JNLPLauncher/config_zh_CN.properties | 24 +++++++++++++++++ .../JNLPLauncher/help-vmargs_zh_CN.html | 5 ++++ .../slaves/JNLPLauncher/main_zh_CN.properties | 9 ++++++- .../hudson/slaves/Messages_zh_CN.properties | 2 +- .../model/Jenkins/MasterComputer/_api.jelly | 2 +- .../MasterComputer/_api_zh_CN.properties | 23 ++++++++++++++++ .../Jenkins/load-statistics_zh_CN.properties | 23 ++++++++++++++++ .../jenkins/slaves/Messages_zh_CN.properties | 27 +++++++++++++++++++ .../config_zh_CN.properties | 26 ++++++++++++++++++ .../help-disabled_zh_CN.html | 4 +++ .../help-failIfWorkDirIsMissing_zh_CN.html | 3 +++ .../help-internalDir_zh_CN.html | 3 +++ .../help-workDirPath_zh_CN.html | 3 +++ .../systemInfo/Messages_zh_CN.properties | 26 ++++++++++++++++++ 24 files changed, 311 insertions(+), 8 deletions(-) create mode 100644 core/src/main/resources/hudson/model/Computer/_api_zh_CN.properties create mode 100644 core/src/main/resources/hudson/model/Computer/markOffline_zh_CN.properties create mode 100644 core/src/main/resources/hudson/model/Computer/setOfflineCause_zh_CN.properties create mode 100644 core/src/main/resources/hudson/slaves/ComputerLauncher/main_zh_CN.properties create mode 100644 core/src/main/resources/hudson/slaves/DumbSlave/newInstanceDetail_zh_CN.properties create mode 100644 core/src/main/resources/hudson/slaves/JNLPLauncher/config_zh_CN.properties create mode 100644 core/src/main/resources/hudson/slaves/JNLPLauncher/help-vmargs_zh_CN.html create mode 100644 core/src/main/resources/jenkins/model/Jenkins/MasterComputer/_api_zh_CN.properties create mode 100644 core/src/main/resources/jenkins/model/Jenkins/load-statistics_zh_CN.properties create mode 100644 core/src/main/resources/jenkins/slaves/Messages_zh_CN.properties create mode 100644 core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/config_zh_CN.properties create mode 100644 core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-disabled_zh_CN.html create mode 100644 core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-failIfWorkDirIsMissing_zh_CN.html create mode 100644 core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-internalDir_zh_CN.html create mode 100644 core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-workDirPath_zh_CN.html create mode 100644 core/src/main/resources/jenkins/slaves/systemInfo/Messages_zh_CN.properties diff --git a/core/src/main/resources/hudson/model/Computer/_api_zh_CN.properties b/core/src/main/resources/hudson/model/Computer/_api_zh_CN.properties new file mode 100644 index 0000000000..fe83665bdf --- /dev/null +++ b/core/src/main/resources/hudson/model/Computer/_api_zh_CN.properties @@ -0,0 +1,23 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +Load\ Statistics=\u8d1f\u8f7d\u7edf\u8ba1 diff --git a/core/src/main/resources/hudson/model/Computer/index_zh_CN.properties b/core/src/main/resources/hudson/model/Computer/index_zh_CN.properties index ecb166e08c..a6f6a29eff 100644 --- a/core/src/main/resources/hudson/model/Computer/index_zh_CN.properties +++ b/core/src/main/resources/hudson/model/Computer/index_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2010, Sun Microsystems, Inc. +# Copyright (c) 2004-2018, Sun Microsystems, Inc, suren . # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -24,3 +24,5 @@ Labels:=\u6807\u7B7E None=\u65E0 submit.not.temporarilyOffline=\u4E34\u65F6\u65AD\u5F00\u6B64\u8282\u70B9 title.projects_tied_on=\u5173\u8054\u5230{0}\u7684\u9879\u76EE +submit.temporarilyOffline=\u4e0a\u7ebf\u8282\u70b9 +submit.updateOfflineCause=\u66f4\u65b0\u4e0b\u7ebf\u539f\u56e0 diff --git a/core/src/main/resources/hudson/model/Computer/markOffline_zh_CN.properties b/core/src/main/resources/hudson/model/Computer/markOffline_zh_CN.properties new file mode 100644 index 0000000000..99ee4b527e --- /dev/null +++ b/core/src/main/resources/hudson/model/Computer/markOffline_zh_CN.properties @@ -0,0 +1,25 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +title=\u8ba9 {0} \u4e0b\u7ebf +blurb=\u4f60\u53ef\u4ee5\u89e3\u91ca\u4f7f\u8be5\u8282\u70b9\u4e0b\u7ebf\u7684\u539f\u56e0\uff08\u975e\u5fc5\u586b\uff09\uff0c\u4ee5\u4fbf\u8ba9\u5176\u4ed6\u4eba\u4e86\u89e3\uff1a +submit=\u8ba9\u8be5\u8282\u70b9\u4e34\u65f6\u4e0b\u7ebf diff --git a/core/src/main/resources/hudson/model/Computer/setOfflineCause_zh_CN.properties b/core/src/main/resources/hudson/model/Computer/setOfflineCause_zh_CN.properties new file mode 100644 index 0000000000..d33512a105 --- /dev/null +++ b/core/src/main/resources/hudson/model/Computer/setOfflineCause_zh_CN.properties @@ -0,0 +1,25 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +title=\u8bbe\u7f6e {0} \u4e0b\u7ebf\u539f\u56e0 +blurb=\u4f60\u53ef\u4ee5\u8bbe\u7f6e\u6216\u8005\u66f4\u65b0\u8be5\u8282\u70b9\u4e0b\u7ebf\u7684\u539f\u56e0\uff0c\u4ee5\u4fbf\u8ba9\u5176\u4ed6\u4eba\u4e86\u89e3\uff1a +submit=\u66f4\u65b0\u539f\u56e0 diff --git a/core/src/main/resources/hudson/model/Computer/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/model/Computer/sidepanel_zh_CN.properties index 0861540bb9..4757829803 100644 --- a/core/src/main/resources/hudson/model/Computer/sidepanel_zh_CN.properties +++ b/core/src/main/resources/hudson/model/Computer/sidepanel_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2010, Sun Microsystems, Inc. +# Copyright (c) 2004-2018, Sun Microsystems, Inc, suren . # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -26,3 +26,4 @@ Configure=\u914D\u7F6E\u4ECE\u8282\u70B9 Load\ Statistics=\u8d1f\u8f7d\u7edf\u8ba1 Script\ Console=\u811a\u672c\u547d\u4ee4\u884c Status=\u72b6\u6001 +Delete\ Agent=\u5220\u9664\u8282\u70b9 diff --git a/core/src/main/resources/hudson/model/ComputerSet/_new_zh_CN.properties b/core/src/main/resources/hudson/model/ComputerSet/_new_zh_CN.properties index 79d34d46af..08b2b7be72 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/_new_zh_CN.properties +++ b/core/src/main/resources/hudson/model/ComputerSet/_new_zh_CN.properties @@ -2,3 +2,4 @@ Name=\u540D\u5B57 Name\ is\ mandatory=\u540D\u79F0\uFF08\u5FC5\u586B\uFF09 +Save=\u4fdd\u5b58 diff --git a/core/src/main/resources/hudson/model/ComputerSet/index_zh_CN.properties b/core/src/main/resources/hudson/model/ComputerSet/index_zh_CN.properties index 25d63958ff..ca6e959922 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/index_zh_CN.properties +++ b/core/src/main/resources/hudson/model/ComputerSet/index_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2010, Sun Microsystems, Inc. +# Copyright (c) 2004-2018, Sun Microsystems, Inc, suren . # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,6 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +Nodes=\u8282\u70b9\u5217\u8868 Configure=\u8BBE\u7F6E Data\ obtained=\u83B7\u53D6\u5230\u7684\u6570\u636E Name=\u540D\u79F0 diff --git a/core/src/main/resources/hudson/model/ComputerSet/sidepanel_zh_CN.properties b/core/src/main/resources/hudson/model/ComputerSet/sidepanel_zh_CN.properties index c823d04e2c..0b167ecd6d 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/sidepanel_zh_CN.properties +++ b/core/src/main/resources/hudson/model/ComputerSet/sidepanel_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2010, Sun Microsystems, Inc. +# Copyright (c) 2004-2018, Sun Microsystems, Inc, suren . # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -Back\ to\ Dashboard=\u8fd4\u56de +Back\ to\ Dashboard=\u8fd4\u56de\u5de5\u4f5c\u53f0 Manage\ Jenkins=\u7cfb\u7edf\u7ba1\u7406 Configure=\u914D\u7F6E New\ Node=\u65b0\u5efa\u8282\u70b9 diff --git a/core/src/main/resources/hudson/slaves/ComputerLauncher/main_zh_CN.properties b/core/src/main/resources/hudson/slaves/ComputerLauncher/main_zh_CN.properties new file mode 100644 index 0000000000..9b685ac70c --- /dev/null +++ b/core/src/main/resources/hudson/slaves/ComputerLauncher/main_zh_CN.properties @@ -0,0 +1,26 @@ +# The MIT License +# +# Copyright (c) 2018 suren +# +# 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. + +launchingDescription=\u8be5\u8282\u70b9\u5df2\u7ecf\u542f\u52a8\u3002 +See\ log\ for\ more\ details=\u66f4\u591a\u4fe1\u606f\u8bf7\u67e5\u770b\u65e5\u5fd7 +Relaunch\ agent=\u91cd\u542f\u4ee3\u7406 +Launch\ agent=\u542f\u52a8\u4ee3\u7406 diff --git a/core/src/main/resources/hudson/slaves/DumbSlave/newInstanceDetail_zh_CN.properties b/core/src/main/resources/hudson/slaves/DumbSlave/newInstanceDetail_zh_CN.properties new file mode 100644 index 0000000000..f1e1f47dab --- /dev/null +++ b/core/src/main/resources/hudson/slaves/DumbSlave/newInstanceDetail_zh_CN.properties @@ -0,0 +1,25 @@ +# The MIT License +# +# Copyright (c) 2004-2018, Sun Microsystems, Inc., Kohsuke Kawaguchi, +# suren +# +# 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. + +detail=\ + \u6dfb\u52a0\u4e00\u4e2a\u666e\u901a\u3001\u56fa\u5b9a\u7684\u8282\u70b9\u5230Jenkins\u3002\u4e4b\u6240\u4ee5\u53eb\u505a\u201c\u56fa\u5b9a\u201d\uff0c\u662f\u56e0\u4e3aJenkins\u6ca1\u7ed9\u8fd9\u79cd\u8282\u70b9\u63d0\u4f9b\u66f4\u9ad8\u7ea7\u7684\u96c6\u6210\u65b9\u5f0f\uff0c\u4f8b\u5982\uff1a\u52a8\u6001\u914d\u7f6e\u3002\u6ca1\u6709\u5176\u4ed6\u7c7b\u578b\u80fd\u9009\u62e9\u7684\u8bdd\u53ef\u4ee5\u9009\u62e9\u8be5\u7c7b\u578b\uff1b\u4f8b\u5982\uff0c\u4f60\u5728\u6dfb\u52a0\u4e0d\u53d7Jenkins\u7ba1\u7406\u7684\u7269\u7406\u673a\u3001\u865a\u62df\u673a\u7b49\u7b49\u3002 diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/config_zh_CN.properties b/core/src/main/resources/hudson/slaves/JNLPLauncher/config_zh_CN.properties new file mode 100644 index 0000000000..feb6df4894 --- /dev/null +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/config_zh_CN.properties @@ -0,0 +1,24 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +JVM\ options=JVM \u9009\u9879 +Tunnel\ connection\ through=Tunnel \u8fde\u63a5\u4f4d\u7f6e diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/help-vmargs_zh_CN.html b/core/src/main/resources/hudson/slaves/JNLPLauncher/help-vmargs_zh_CN.html new file mode 100644 index 0000000000..7ea86c5025 --- /dev/null +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/help-vmargs_zh_CN.html @@ -0,0 +1,5 @@ +

      + 如果代理的Java虚拟机启动时需要附加参数,例如 "-Xmx256m",可以在这里设置。 + 这里 + 列出了所有可用的参数。 +
      \ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/main_zh_CN.properties b/core/src/main/resources/hudson/slaves/JNLPLauncher/main_zh_CN.properties index 1f1a4b8bdc..cf34147ccd 100644 --- a/core/src/main/resources/hudson/slaves/JNLPLauncher/main_zh_CN.properties +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/main_zh_CN.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2010, Sun Microsystems, Inc. +# Copyright (c) 2004-2018, Sun Microsystems, Inc, suren . # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +slaveAgentPort.disabled=\u4ece\u8282\u70b9\u7aef\u53e3\u88ab\u7981\u7528 +configure.link.text=\u914d\u7f6e\u94fe\u63a5 +Connect\ agent\ to\ Jenkins\ one\ of\ these\ ways\:=\u8282\u70b9\u8fde\u63a5Jenkins\u7684\u65b9\u5f0f\u5982\u4e0b\uff1a +launch\ agent=\u542f\u52a8\u8282\u70b9 +Launch\ agent\ from\ browser=\u5728\u6d4f\u89c8\u5668\u4e2d\u542f\u52a8\u8282\u70b9 +Run\ from\ agent\ command\ line\:=\u5728\u547d\u4ee4\u884c\u4e2d\u542f\u52a8\u8282\u70b9 +Or\ if\ the\ agent\ is\ headless\:=\u6216\u8005\uff0c\u5f53\u8282\u70b9\u662f\u65e0\u754c\u9762\u7684\u8bdd Connected\ via\ JNLP\ agent.=\u5DF2\u901A\u8FC7JNLP Agent\u8FDE\u63A5 diff --git a/core/src/main/resources/hudson/slaves/Messages_zh_CN.properties b/core/src/main/resources/hudson/slaves/Messages_zh_CN.properties index c8ef214cd4..3c136db31f 100644 --- a/core/src/main/resources/hudson/slaves/Messages_zh_CN.properties +++ b/core/src/main/resources/hudson/slaves/Messages_zh_CN.properties @@ -28,7 +28,7 @@ JNLPLauncher.displayName=\u901A\u8FC7Java Web\u542F\u52A8\u4EE3\u7406 ComputerLauncher.unexpectedError=\u542F\u52A8\u4EE3\u7406\u662F\u51FA\u73B0\u4F4D\u7F6E\u9519\u8BEF\u3002\u8FD9\u53EF\u80FD\u662FJenkins\u7684\u95EE\u9898\u3002 ComputerLauncher.abortedLaunch=\u4EE3\u7406\u542F\u52A8\u8FC7\u7A0B\u88AB\u4E2D\u6B62\u3002 CommandLauncher.NoLaunchCommand=\u6CA1\u6709\u6307\u5B9A\u542F\u52A8\u547D\u4EE4 -DumbSlave.displayName=\u56FA\u5B9A\u4EE3\u7406 +DumbSlave.displayName=\u56fa\u5b9a\u8282\u70b9 NodeProvisioner.EmptyString= OfflineCause.LaunchFailed=\u7531\u4E8E\u4EE3\u7406\u542F\u52A8\u8FC7\u7A0B\u5931\u8D25\u5BFC\u81F4\u7684\u79BB\u7EBF\u3002 OfflineCause.connection_was_broken_=\u8FDE\u63A5\u65AD\u5F00\uFF1A{0} diff --git a/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/_api.jelly b/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/_api.jelly index e38f41caa6..a1478f66ea 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/_api.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/_api.jelly @@ -24,7 +24,7 @@ THE SOFTWARE. -

      Load Statistics

      +

      ${%Load Statistics}

      Load statistics of this computer has its own separate API.

      diff --git a/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/_api_zh_CN.properties b/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/_api_zh_CN.properties new file mode 100644 index 0000000000..fe83665bdf --- /dev/null +++ b/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/_api_zh_CN.properties @@ -0,0 +1,23 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +Load\ Statistics=\u8d1f\u8f7d\u7edf\u8ba1 diff --git a/core/src/main/resources/jenkins/model/Jenkins/load-statistics_zh_CN.properties b/core/src/main/resources/jenkins/model/Jenkins/load-statistics_zh_CN.properties new file mode 100644 index 0000000000..fe83665bdf --- /dev/null +++ b/core/src/main/resources/jenkins/model/Jenkins/load-statistics_zh_CN.properties @@ -0,0 +1,23 @@ +# The MIT License +# +# Copyright (c) 2018, suren +# +# 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. + +Load\ Statistics=\u8d1f\u8f7d\u7edf\u8ba1 diff --git a/core/src/main/resources/jenkins/slaves/Messages_zh_CN.properties b/core/src/main/resources/jenkins/slaves/Messages_zh_CN.properties new file mode 100644 index 0000000000..9e43a06aa0 --- /dev/null +++ b/core/src/main/resources/jenkins/slaves/Messages_zh_CN.properties @@ -0,0 +1,27 @@ +# The MIT License +# +# Copyright 2018 suren +# +# 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. + +JnlpSlaveAgentProtocol.displayName=Java Web Start Agent Protocol/1 (\u5f03\u7528\uff0c\u672a\u52a0\u5bc6) +JnlpSlaveAgentProtocol2.displayName=Java Web Start Agent Protocol/2 (\u5f03\u7528\uff0c\u672a\u52a0\u5bc6) +JnlpSlaveAgentProtocol3.displayName=Java Web Start Agent Protocol/3 (\u5f03\u7528\uff0c\u57fa\u7840\u52a0\u5bc6) +JnlpSlaveAgentProtocol4.displayName=Java Web Start Agent Protocol/4 (TLS \u52a0\u5bc6) +DeprecatedAgentProtocolMonitor.displayName=\u5df2\u5f03\u7528\u7684\u4ee3\u7406\u534f\u8bae\u76d1\u63a7 diff --git a/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/config_zh_CN.properties b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/config_zh_CN.properties new file mode 100644 index 0000000000..ba7b9b5022 --- /dev/null +++ b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/config_zh_CN.properties @@ -0,0 +1,26 @@ +# The MIT License +# +# Copyright (c) 2018 suren +# +# 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. + +Disable\ WorkDir=\u7981\u7528\u5de5\u4f5c\u76ee\u5f55 +Fail\ if\ workspace\ is\ missing=\u5f53\u5de5\u4f5c\u76ee\u5f55\u7f3a\u5931\u65f6\u5931\u8d25 +Internal\ data\ directory=\u5185\u90e8\u6570\u636e\u76ee\u5f55 +Custom\ WorkDir\ path=\u81ea\u5b9a\u4e49\u5de5\u4f5c\u76ee\u5f55 diff --git a/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-disabled_zh_CN.html b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-disabled_zh_CN.html new file mode 100644 index 0000000000..dac1751904 --- /dev/null +++ b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-disabled_zh_CN.html @@ -0,0 +1,4 @@ +
      + 允许禁用该代理的 远程工作目录 。 + 这种情况,代理会以旧版本模式下运行,而且默认没有日志记录。 +
      diff --git a/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-failIfWorkDirIsMissing_zh_CN.html b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-failIfWorkDirIsMissing_zh_CN.html new file mode 100644 index 0000000000..29f0821ea7 --- /dev/null +++ b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-failIfWorkDirIsMissing_zh_CN.html @@ -0,0 +1,3 @@ +
      + 勾选后,如果目标目录不存在的话,那么代理启动过程会失败。该选项用于检测基础设施的问题,例如挂载失败。 +
      diff --git a/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-internalDir_zh_CN.html b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-internalDir_zh_CN.html new file mode 100644 index 0000000000..7629487b34 --- /dev/null +++ b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-internalDir_zh_CN.html @@ -0,0 +1,3 @@ +
      + 定义内部数据的存储目录。该目录会在远程工作目录内创建。 +
      diff --git a/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-workDirPath_zh_CN.html b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-workDirPath_zh_CN.html new file mode 100644 index 0000000000..e3d0c6338d --- /dev/null +++ b/core/src/main/resources/jenkins/slaves/RemotingWorkDirSettings/help-workDirPath_zh_CN.html @@ -0,0 +1,3 @@ +
      + 如果定义了自定义远程工作目录,就不会使用代理的根目录。该选项目前无法使用环境变量,建议使用绝对路径。 +
      diff --git a/core/src/main/resources/jenkins/slaves/systemInfo/Messages_zh_CN.properties b/core/src/main/resources/jenkins/slaves/systemInfo/Messages_zh_CN.properties new file mode 100644 index 0000000000..4f8fd058e0 --- /dev/null +++ b/core/src/main/resources/jenkins/slaves/systemInfo/Messages_zh_CN.properties @@ -0,0 +1,26 @@ +# The MIT License +# +# Copyright (c) 2018 suren +# +# 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. + +SystemPropertySlaveInfo.DisplayName=\u7cfb\u7edf\u5c5e\u6027 +EnvVarsSlaveInfo.DisplayName=\u73af\u5883\u53d8\u91cf +ThreadDumpSlaveInfo.DisplayName=\u7ebf\u7a0b\u8f6c\u5b58 +ClassLoaderStatisticsSlaveInfo.DisplayName=\u8fdc\u7a0b\u7c7b\u52a0\u8f7d\u7edf\u8ba1 From e2abc734dace7181b9c9357d0a741e72afa3ad12 Mon Sep 17 00:00:00 2001 From: Marcos Bento Date: Mon, 23 Apr 2018 09:44:53 +0100 Subject: [PATCH 19/45] Whitelist java.util.EnumMap --- core/src/main/resources/jenkins/security/whitelisted-classes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/resources/jenkins/security/whitelisted-classes.txt b/core/src/main/resources/jenkins/security/whitelisted-classes.txt index 1f69b062c2..2769458c36 100644 --- a/core/src/main/resources/jenkins/security/whitelisted-classes.txt +++ b/core/src/main/resources/jenkins/security/whitelisted-classes.txt @@ -102,6 +102,7 @@ java.util.Collections$UnmodifiableSortedSet java.util.Date java.util.EnumMap +java.util.EnumSet java.util.GregorianCalendar java.util.HashMap java.util.HashSet From 909f55b77da1d4a0a16a818fe592504538e49430 Mon Sep 17 00:00:00 2001 From: Vincent Latombe Date: Mon, 23 Apr 2018 15:23:41 +0200 Subject: [PATCH 20/45] [JENKINS-50767] Control crumb issuer proxy compatibility through system property (#3389) -Djenkins.model.Jenkins.crumbIssuerProxyCompatibility=true enables proxy compatibility on startup --- core/src/main/java/jenkins/install/SetupWizard.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index 67f68606b2..e36fd1808b 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -137,7 +137,7 @@ public class SetupWizard extends PageDecorator { jenkins.setAgentProtocols(newProtocols); // require a crumb issuer - jenkins.setCrumbIssuer(new DefaultCrumbIssuer(false)); + jenkins.setCrumbIssuer(new DefaultCrumbIssuer(SystemProperties.getBoolean(Jenkins.class.getName() + ".crumbIssuerProxyCompatibility",false))); // set master -> slave security: jenkins.getInjector().getInstance(AdminWhitelistRule.class) From fc6137873956ef1645dccd1ff3688dbf42dff7d5 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Tue, 24 Apr 2018 14:39:57 +0200 Subject: [PATCH 21/45] [JENKINS-50616] - Add org.jruby.RubyNil to the whitelist (#3404) * [JENKINS-50616] - Add org.jruby.RubyNil to the whitelist * [JENKINS-50616] - Fix the typo in the comment --- .../resources/jenkins/security/whitelisted-classes.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/jenkins/security/whitelisted-classes.txt b/core/src/main/resources/jenkins/security/whitelisted-classes.txt index 2769458c36..3ad58670b6 100644 --- a/core/src/main/resources/jenkins/security/whitelisted-classes.txt +++ b/core/src/main/resources/jenkins/security/whitelisted-classes.txt @@ -175,7 +175,13 @@ org.jenkinsci.jruby.JRubyMapper$DynamicProxy org.jenkinsci.lib.xtrigger.XTriggerCause org.jenkinsci.lib.xtrigger.XTriggerCauseAction -# TODO remove (also XStream2.BlacklistedTypesConverter.JRUBY_PROXY) when https://github.com/jenkinsci/ruby-runtime-plugin/pull/5 is widely adopted +# TODO remove (also XStream2.BlacklistedTypesConverter.JRUBY_PROXY) when Ruby Runtime is fixed +# Related PRs: +# - https://github.com/jenkinsci/ruby-runtime-plugin/pull/5, +# - https://github.com/jenkinsci/ruby-runtime-plugin/pull/6 +# +# oleg-nenashev in PR#6 we are trying to get help from last maintainers due to the plugin codebase splitbrain. +# It is required to fix JENKINS-50616 in a proper way for 2.107.x org.jruby.RubyArray org.jruby.RubyBignum org.jruby.RubyBoolean @@ -183,6 +189,7 @@ org.jruby.RubyBoolean$False org.jruby.RubyBoolean$True org.jruby.RubyFixnum org.jruby.RubyHash +org.jruby.RubyNil org.jruby.RubyObject org.jruby.RubyString org.jruby.RubySymbol From 97a76e6344c6470bec14cf6574986a07be8e48e6 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 24 Apr 2018 10:19:47 -0400 Subject: [PATCH 22/45] Simplify Jenkins.installState serial form to avoid relying on readResolve. --- .../java/jenkins/install/InstallState.java | 2 ++ core/src/main/java/jenkins/model/Jenkins.java | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/jenkins/install/InstallState.java b/core/src/main/java/jenkins/install/InstallState.java index dc6f9dc534..e77faec568 100644 --- a/core/src/main/java/jenkins/install/InstallState.java +++ b/core/src/main/java/jenkins/install/InstallState.java @@ -220,7 +220,9 @@ public class InstallState implements ExtensionPoint { *

      Creating a {@code writeReplace} does not help much since XStream then just saves: * {@code } * @see #UNUSED_INNER_CLASSES + * @deprecated Should no longer be used, as {@link Jenkins} now saves only {@link #name}. */ + @Deprecated protected Object readResolve() { // If we get invalid state from the configuration, fallback to unknown if (StringUtils.isBlank(name)) { diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 1d200ebd2a..4709c99c5b 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -328,6 +328,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * The Jenkins instance startup type i.e. NEW, UPGRADE etc */ + private String installStateName; + + @Deprecated private InstallState installState; /** @@ -1014,10 +1017,12 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve @Nonnull @Restricted(NoExternalUse.class) public InstallState getInstallState() { - if (installState == null || installState.name() == null) { - return InstallState.UNKNOWN; + if (installState != null) { + installStateName = installState.name(); + installState = null; } - return installState; + InstallState is = installStateName != null ? InstallState.valueOf(installStateName) : InstallState.UNKNOWN; + return is != null ? is : InstallState.UNKNOWN; } /** @@ -1026,10 +1031,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve */ @Restricted(NoExternalUse.class) public void setInstallState(@Nonnull InstallState newState) { - InstallState prior = installState; - installState = newState; - LOGGER.log(Main.isDevelopmentMode ? Level.INFO : Level.FINE, "Install state transitioning from: {0} to : {1}", new Object[] { prior, installState }); - if (!newState.equals(prior)) { + String prior = installStateName; + installStateName = newState.name(); + LOGGER.log(Main.isDevelopmentMode ? Level.INFO : Level.FINE, "Install state transitioning from: {0} to : {1}", new Object[] { prior, installStateName }); + if (!installStateName.equals(prior)) { getSetupWizard().onInstallStateUpdate(newState); newState.initializeState(); } From aaa068a3050438bd4e3c61241d18defe497297ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20F=C3=BCrbacher?= Date: Thu, 26 Apr 2018 10:45:01 +0200 Subject: [PATCH 23/45] Fixed encoding in German translation (log recorder levels) --- .../logging/LogRecorderManager/levels_de.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_de.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_de.properties index 67c63809ce..6917971793 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_de.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_de.properties @@ -23,7 +23,7 @@ Logger\ Configuration=Logger-Konfiguration url=https://jenkins.io/redirect/log-levels Name=Name -Level=Priorit\uFFFDt -Adjust\ Levels=Priorit\uFFFDten (levels) anpassen -defaultLoggerMsg=Der Logger ohne Namen ist der Default-Logger. Seine Priorit\uFFFDt \ - wird von allen Loggern \uFFFDbernommen, f\uFFFDr die keine Priorit\uFFFDt explizit angegeben wurde. \ No newline at end of file +Level=Priorit\u00e4t +Adjust\ Levels=Priorit\u00e4ten (levels) anpassen +defaultLoggerMsg=Der Logger ohne Namen ist der Default-Logger. Seine Priorit\u00e4t \ + wird von allen Loggern \u00fcbernommen, f\u00fcr die keine Priorit\u00e4t explizit angegeben wurde. \ No newline at end of file From 3a124bfdb68daaec5050597c6f15e35a850d2cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20F=C3=BCrbacher?= Date: Thu, 26 Apr 2018 12:28:55 +0200 Subject: [PATCH 24/45] Fixed regression of ed8d905 --- .../hudson/logging/LogRecorderManager/levels_es.properties | 4 ++-- .../hudson/logging/LogRecorderManager/levels_it.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_es.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_es.properties index 3aa52ced12..b0619b2434 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_es.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_es.properties @@ -22,8 +22,8 @@ url=https://jenkins.io/redirect/log-levels Level=Nivel de log -Logger\ Configuration=Configuraci\uFFFDn del logger +Logger\ Configuration=Configuraci\u00f3n del logger Name=Nombre defaultLoggerMsg=Por defecto se usa un Logger sin mombre. \ - Este nivel ser\uFFFD heredado por todos los loggers que no est\uFFFDn configurados con un nivel de log. + Este nivel ser\u00e0 heredado por todos los loggers que no est\u00e9n configurados con un nivel de log. Adjust\ Levels=Ajustar niveles de log. diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_it.properties b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_it.properties index 7e27373cde..e34d443d08 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/levels_it.properties +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/levels_it.properties @@ -4,5 +4,5 @@ Adjust\ Levels=Regola livelli Level=Livello Logger\ Configuration=Configurazione registro Name=Nome -defaultLoggerMsg=Il registro senza nome \uFFFD quello predefinito. Questo livello sar\uFFFD ereditato da tutti i registri senza un livello configurato. +defaultLoggerMsg=Il registro senza nome \u00e8 quello predefinito. Questo livello sar\u00e0 ereditato da tutti i registri senza un livello configurato. url=https://jenkins.io/redirect/log-levels From 17b94a044a03e49ca7f4a87af69a993354ec0df1 Mon Sep 17 00:00:00 2001 From: Wadeck Follonier Date: Thu, 26 Apr 2018 16:58:29 +0200 Subject: [PATCH 25/45] [JENKINS-31661] Check the root url (Admin monitor + Wizard) (#3082) * [JENKINS-31661] Add an administrative monitor to check the root url configuration - add the monitor + test * - corrections requested by Oleg in code review * - add a field to fill during SetupWizard for the rootUrl (default value using javascript but editable) - WIP : missing the translation * - revert whitespace change * - little refactoring of js methods * - translation added * - modification requested during PR by Jesse * - renaming proposed by Keith * - rework to have a new page in the SetupWizard for the root URL * Correct relevant non-conflicting merge changes * - in case of skip => also skip the instance configuration - terminate => finish (+ uppercase) - replace double single quotes - add help message in the wizard to inform admins - correct the installState serialization problem - update working (admin monitor + wizard) * address Daniel's comment - skip all steps will show two messages instead of one (like before when clicking on skip twice) - skip => not now - remove margin and adjust padding for the wizard forms - add the "if possible" * address Arnaud's comment on the sauter vs passer * address Liam's comment * correct previous commit typo * correct previous commit typo (2) * add blank end line * address James' comment * address Daniel's comment * address Devin's comment - implement error management in the wizard - provide different message in the monitor in case of null / invalid url - add an helper to validate URL (even with _ in domain) - add @Restricted * - also update the monitor test to be consistent with its new behavior * address Devin's comments * correct header files * include relevant classes for the url validation from commons-validator:1.6 * Move Apache classes in a more standard package --- .../main/java/hudson/util/HttpResponses.java | 26 +- .../diagnostics/RootUrlNotSetMonitor.java | 66 + .../java/jenkins/install/InstallState.java | 44 +- .../java/jenkins/install/InstallUtil.java | 47 +- .../java/jenkins/install/SetupWizard.java | 81 +- .../model/JenkinsLocationConfiguration.java | 18 +- .../validator/routines/DomainValidator.java | 2072 +++++++++++++++++ .../routines/InetAddressValidator.java | 196 ++ .../validator/routines/RegexValidator.java | 237 ++ .../validator/routines/UrlValidator.java | 551 +++++ .../src/main/java/jenkins/util/UrlHelper.java | 58 + .../jenkins/diagnostics/Messages.properties | 1 + .../RootUrlNotSetMonitor/message.jelly | 36 + .../RootUrlNotSetMonitor/message.properties | 6 + .../jenkins/install/Messages.properties | 24 + .../setupWizardConfigureInstance.jelly | 122 + .../setupWizardConfigureInstance.properties | 6 + ...setupWizardConfigureInstance_fr.properties | 2 + .../SetupWizard/setupWizardFirstUser.jelly | 4 +- .../setupWizardFirstUser.properties | 2 +- .../setupWizardFirstUser_fr.properties | 2 +- .../install/pluginSetupWizard.properties | 16 +- .../install/pluginSetupWizard_fr.properties | 24 +- .../test/java/jenkins/util/UrlHelperTest.java | 44 + .../diagnostics/RootUrlNotSetMonitorTest.java | 64 + war/src/main/js/api/securityConfig.js | 21 +- war/src/main/js/pluginSetupWizardGui.js | 100 +- .../main/js/templates/configureInstance.hbs | 18 + 28 files changed, 3807 insertions(+), 81 deletions(-) create mode 100644 core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java create mode 100644 core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java create mode 100644 core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java create mode 100644 core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java create mode 100644 core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java create mode 100644 core/src/main/java/jenkins/util/UrlHelper.java create mode 100644 core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.jelly create mode 100644 core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.properties create mode 100644 core/src/main/resources/jenkins/install/Messages.properties create mode 100644 core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.jelly create mode 100644 core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.properties create mode 100644 core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance_fr.properties create mode 100644 core/src/test/java/jenkins/util/UrlHelperTest.java create mode 100644 test/src/test/java/jenkins/diagnostics/RootUrlNotSetMonitorTest.java create mode 100644 war/src/main/js/templates/configureInstance.hbs diff --git a/core/src/main/java/hudson/util/HttpResponses.java b/core/src/main/java/hudson/util/HttpResponses.java index 8c4bdeda9a..2616533f9c 100644 --- a/core/src/main/java/hudson/util/HttpResponses.java +++ b/core/src/main/java/hudson/util/HttpResponses.java @@ -89,16 +89,28 @@ public class HttpResponses extends org.kohsuke.stapler.HttpResponses { return new JSONObjectResponse(data); } - /** - * Set the response as an error response. - * @param message The error "message" set on the response. - * @return {@code this} object. - * - * @since 2.0 - */ + /** + * Set the response as an error response. + * @param message The error "message" set on the response. + * @return {@code this} object. + * + * @since 2.0 + */ public static HttpResponse errorJSON(@Nonnull String message) { return new JSONObjectResponse().error(message); } + + /** + * Set the response as an error response plus some data. + * @param message The error "message" set on the response. + * @param data The data. + * @return {@code this} object. + * + * @since TODO + */ + public static HttpResponse errorJSON(@Nonnull String message, @Nonnull Map data) { + return new JSONObjectResponse(data).error(message); + } /** * Set the response as an error response plus some data. diff --git a/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java b/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java new file mode 100644 index 0000000000..3e9a245f73 --- /dev/null +++ b/core/src/main/java/jenkins/diagnostics/RootUrlNotSetMonitor.java @@ -0,0 +1,66 @@ +/* + * 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.diagnostics; + +import hudson.Extension; +import hudson.model.AdministrativeMonitor; +import jenkins.model.JenkinsLocationConfiguration; +import jenkins.util.UrlHelper; +import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +/** + * Jenkins root URL is required for a lot of operations in both core and plugins. + * There is a default behavior (infer the URL from the request object), but inaccurate in some scenarios. + * Normally this root URL is set during SetupWizard phase, this monitor is there to ensure that behavior. + * Potential exceptions are the dev environment, if someone disable the wizard or + * the administrator put an empty string on the configuration page. + * + * @since TODO + */ +@Extension +@Symbol("rootUrlNotSet") +@Restricted(NoExternalUse.class) +public class RootUrlNotSetMonitor extends AdministrativeMonitor { + @Override + public String getDisplayName() { + return Messages.RootUrlNotSetMonitor_DisplayName(); + } + + @Override + public boolean isActivated() { + JenkinsLocationConfiguration loc = JenkinsLocationConfiguration.get(); + return loc != null && ( + loc.getUrl() == null || !UrlHelper.isValidRootUrl(loc.getUrl()) + ); + } + + // used by jelly to determined if it's a null url or invalid one + @Restricted(NoExternalUse.class) + public boolean isUrlNull(){ + JenkinsLocationConfiguration loc = JenkinsLocationConfiguration.get(); + return loc != null && loc.getUrl() == null; + } +} diff --git a/core/src/main/java/jenkins/install/InstallState.java b/core/src/main/java/jenkins/install/InstallState.java index dc6f9dc534..0e69ac60cd 100644 --- a/core/src/main/java/jenkins/install/InstallState.java +++ b/core/src/main/java/jenkins/install/InstallState.java @@ -32,6 +32,7 @@ import hudson.ExtensionPoint; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.Jenkins; +import jenkins.model.JenkinsLocationConfiguration; import org.apache.commons.lang.StringUtils; /** * Jenkins install state. @@ -47,7 +48,13 @@ import org.apache.commons.lang.StringUtils; public class InstallState implements ExtensionPoint { /** - * Only here for XStream compatibility. + * Only here for XStream compatibility.

      + * + * Please DO NOT ADD ITEM TO THIS LIST.

      + * If you add an item here, the deserialization process will break + * because it is used for serialized state like "jenkins.install.InstallState$4" + * before the change from anonymous class to named class. If you need to add a new InstallState, you can just add a new inner named class but nothing to change in this list. + * * @see #readResolve */ @Deprecated @@ -92,7 +99,7 @@ public class InstallState implements ExtensionPoint { super("INITIAL_SETUP_COMPLETED", true); } public void initializeState() { - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); try { j.getSetupWizard().completeSetup(); } catch (Exception e) { @@ -100,7 +107,7 @@ public class InstallState implements ExtensionPoint { } j.setInstallState(RUNNING); } - }; + } /** * Creating an admin user for an initial Jenkins install. @@ -112,14 +119,29 @@ public class InstallState implements ExtensionPoint { super("CREATE_ADMIN_USER", false); } public void initializeState() { - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); // Skip this state if not using the security defaults // e.g. in an init script set up security already if (!j.getSetupWizard().isUsingSecurityDefaults()) { InstallUtil.proceedToNextStateFrom(this); } } - }; + } + + @Extension + public static final InstallState CONFIGURE_INSTANCE = new ConfigureInstance(); + private static final class ConfigureInstance extends InstallState { + ConfigureInstance() { + super("CONFIGURE_INSTANCE", false); + } + public void initializeState() { + // Skip this state if a boot script already configured the root URL + // in case we add more fields in this page, this should be adapted + if (StringUtils.isNotBlank(JenkinsLocationConfiguration.getOrDie().getUrl())) { + InstallUtil.proceedToNextStateFrom(this); + } + } + } /** * New Jenkins install. The user has kicked off the process of installing an @@ -139,14 +161,14 @@ public class InstallState implements ExtensionPoint { } public void initializeState() { try { - Jenkins.getInstance().getSetupWizard().init(true); + Jenkins.get().getSetupWizard().init(true); } catch (Exception e) { throw new RuntimeException(e); } InstallUtil.proceedToNextStateFrom(INITIAL_SECURITY_SETUP); } - }; + } /** * New Jenkins install. @@ -166,7 +188,7 @@ public class InstallState implements ExtensionPoint { public void initializeState() { InstallUtil.saveLastExecVersion(); } - }; + } /** * Upgrade of an existing Jenkins install. @@ -186,7 +208,7 @@ public class InstallState implements ExtensionPoint { public void initializeState() { InstallUtil.saveLastExecVersion(); } - }; + } private static final Logger LOGGER = Logger.getLogger(InstallState.class.getName()); @@ -202,6 +224,10 @@ public class InstallState implements ExtensionPoint { public static final InstallState DEVELOPMENT = new InstallState("DEVELOPMENT", true); private final transient boolean isSetupComplete; + + /** + * Link with the pluginSetupWizardGui.js map: "statsHandlers" + */ private final String name; public InstallState(@Nonnull String name, boolean isSetupComplete) { diff --git a/core/src/main/java/jenkins/install/InstallUtil.java b/core/src/main/java/jenkins/install/InstallUtil.java index 48b8d33ab6..7552f9fa35 100644 --- a/core/src/main/java/jenkins/install/InstallUtil.java +++ b/core/src/main/java/jenkins/install/InstallUtil.java @@ -101,37 +101,30 @@ public class InstallUtil { /** * Returns the next state during a transition from the current install state */ - /*package*/ static InstallState getNextInstallState(final InstallState current) { + /*package*/ static InstallState getNextInstallState(InstallState current) { List,InstallState>> installStateFilterChain = new ArrayList<>(); - for (final InstallStateFilter setupExtension : InstallStateFilter.all()) { - installStateFilterChain.add(new Function, InstallState>() { - @Override - public InstallState apply(Provider next) { - return setupExtension.getNextInstallState(current, next); - } - }); + for (InstallStateFilter setupExtension : InstallStateFilter.all()) { + installStateFilterChain.add(next -> setupExtension.getNextInstallState(current, next)); } // Terminal condition: getNextState() on the current install state - installStateFilterChain.add(new Function, InstallState>() { - @Override - public InstallState apply(Provider input) { - // Initially, install state is unknown and - // needs to be determined - if (current == null || InstallState.UNKNOWN.equals(current)) { - return getDefaultInstallState(); - } - final Map states = new HashMap(); - { - states.put(InstallState.CREATE_ADMIN_USER, InstallState.INITIAL_SETUP_COMPLETED); - states.put(InstallState.INITIAL_PLUGINS_INSTALLING, InstallState.CREATE_ADMIN_USER); - states.put(InstallState.INITIAL_SECURITY_SETUP, InstallState.NEW); - states.put(InstallState.RESTART, InstallState.RUNNING); - states.put(InstallState.UPGRADE, InstallState.INITIAL_SETUP_COMPLETED); - states.put(InstallState.DOWNGRADE, InstallState.INITIAL_SETUP_COMPLETED); - states.put(InstallState.INITIAL_SETUP_COMPLETED, InstallState.RUNNING); - } - return states.get(current); + installStateFilterChain.add(input -> { + // Initially, install state is unknown and + // needs to be determined + if (current == null || InstallState.UNKNOWN.equals(current)) { + return getDefaultInstallState(); } + Map states = new HashMap<>(); + { + states.put(InstallState.CONFIGURE_INSTANCE, InstallState.INITIAL_SETUP_COMPLETED); + states.put(InstallState.CREATE_ADMIN_USER, InstallState.CONFIGURE_INSTANCE); + states.put(InstallState.INITIAL_PLUGINS_INSTALLING, InstallState.CREATE_ADMIN_USER); + states.put(InstallState.INITIAL_SECURITY_SETUP, InstallState.NEW); + states.put(InstallState.RESTART, InstallState.RUNNING); + states.put(InstallState.UPGRADE, InstallState.INITIAL_SETUP_COMPLETED); + states.put(InstallState.DOWNGRADE, InstallState.INITIAL_SETUP_COMPLETED); + states.put(InstallState.INITIAL_SETUP_COMPLETED, InstallState.RUNNING); + } + return states.get(current); }); ProviderChain chain = new ProviderChain<>(installStateFilterChain.iterator()); diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index e36fd1808b..b9cd417e83 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -4,7 +4,9 @@ import static org.apache.commons.io.FileUtils.readFileToString; import static org.apache.commons.lang.StringUtils.defaultIfBlank; import java.io.IOException; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -20,7 +22,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; +import jenkins.model.JenkinsLocationConfiguration; import jenkins.util.SystemProperties; +import jenkins.util.UrlHelper; import org.acegisecurity.Authentication; import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; @@ -28,6 +32,7 @@ import org.acegisecurity.userdetails.UsernameNotFoundException; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -92,7 +97,7 @@ public class SetupWizard extends PageDecorator { * Initialize the setup wizard, this will process any current state initializations */ /*package*/ void init(boolean newInstall) throws IOException, InterruptedException { - Jenkins jenkins = Jenkins.getInstance(); + Jenkins jenkins = Jenkins.get(); if(newInstall) { // this was determined to be a new install, don't run the update wizard here @@ -196,14 +201,14 @@ public class SetupWizard extends PageDecorator { throw new RuntimeException("Unable to remove PluginServletFilter for the SetupWizard", e); } } - + /** * Indicates a generated password should be used - e.g. this is a new install, no security realm set up */ @SuppressWarnings("unused") // used by jelly public boolean isUsingSecurityToken() { try { - return !Jenkins.getInstance().getInstallState().isSetupComplete() + return !Jenkins.get().getInstallState().isSetupComplete() && isUsingSecurityDefaults(); } catch (Exception e) { // ignore @@ -217,7 +222,7 @@ public class SetupWizard extends PageDecorator { * Other settings are irrelevant. */ /*package*/ boolean isUsingSecurityDefaults() { - Jenkins j = Jenkins.getInstance(); + Jenkins j = Jenkins.get(); if (j.getSecurityRealm() instanceof HudsonPrivateSecurityRealm) { HudsonPrivateSecurityRealm securityRealm = (HudsonPrivateSecurityRealm)j.getSecurityRealm(); try { @@ -244,6 +249,7 @@ public class SetupWizard extends PageDecorator { @Restricted(NoExternalUse.class) public HttpResponse doCreateAdminUser(StaplerRequest req, StaplerResponse rsp) throws IOException { Jenkins j = Jenkins.getInstance(); + j.checkPermission(Jenkins.ADMINISTER); // This will be set up by default. if not, something changed, ok to fail @@ -293,6 +299,51 @@ public class SetupWizard extends PageDecorator { } } } + + @RequirePOST + @Restricted(NoExternalUse.class) + public HttpResponse doConfigureInstance(StaplerRequest req, @QueryParameter String rootUrl) { + Jenkins.get().checkPermission(Jenkins.ADMINISTER); + + Map errors = new HashMap<>(); + // pre-check data + checkRootUrl(errors, rootUrl); + + if(!errors.isEmpty()){ + return HttpResponses.errorJSON(Messages.SetupWizard_ConfigureInstance_ValidationErrors(), errors); + } + + // use the parameters to configure the instance + useRootUrl(errors, rootUrl); + + if(!errors.isEmpty()){ + return HttpResponses.errorJSON(Messages.SetupWizard_ConfigureInstance_ValidationErrors(), errors); + } + + InstallUtil.proceedToNextStateFrom(InstallState.CONFIGURE_INSTANCE); + + CrumbIssuer crumbIssuer = Jenkins.get().getCrumbIssuer(); + JSONObject data = new JSONObject(); + if (crumbIssuer != null) { + data.accumulate("crumbRequestField", crumbIssuer.getCrumbRequestField()).accumulate("crumb", crumbIssuer.getCrumb(req)); + } + return HttpResponses.okJSON(data); + } + + private void checkRootUrl(Map errors, @CheckForNull String rootUrl){ + if(rootUrl == null){ + errors.put("rootUrl", Messages.SetupWizard_ConfigureInstance_RootUrl_Empty()); + return; + } + if(!UrlHelper.isValidRootUrl(rootUrl)){ + errors.put("rootUrl", Messages.SetupWizard_ConfigureInstance_RootUrl_Invalid()); + } + } + + private void useRootUrl(Map errors, @CheckForNull String rootUrl){ + LOGGER.log(Level.FINE, "Root URL set during SetupWizard to {0}", new Object[]{ rootUrl }); + JenkinsLocationConfiguration.getOrDie().setUrl(rootUrl); + } /*package*/ void setCurrentLevel(VersionNumber v) throws IOException { FileUtils.writeStringToFile(getUpdateStateFile(), v.toString()); @@ -304,7 +355,7 @@ public class SetupWizard extends PageDecorator { * This file records the version number that the installation has upgraded to. */ /*package*/ static File getUpdateStateFile() { - return new File(Jenkins.getInstance().getRootDir(),"jenkins.install.UpgradeWizard.state"); + return new File(Jenkins.get().getRootDir(),"jenkins.install.UpgradeWizard.state"); } /** @@ -333,9 +384,9 @@ public class SetupWizard extends PageDecorator { */ @Restricted(DoNotUse.class) // WebOnly public HttpResponse doPlatformPluginList() throws IOException { - jenkins.install.SetupWizard setupWizard = Jenkins.getInstance().getSetupWizard(); + SetupWizard setupWizard = Jenkins.get().getSetupWizard(); if (setupWizard != null) { - if (InstallState.UPGRADE.equals(Jenkins.getInstance().getInstallState())) { + if (InstallState.UPGRADE.equals(Jenkins.get().getInstallState())) { JSONArray initialPluginData = getPlatformPluginUpdates(); if(initialPluginData != null) { return HttpResponses.okJSON(initialPluginData); @@ -357,7 +408,7 @@ public class SetupWizard extends PageDecorator { @Restricted(DoNotUse.class) // WebOnly public HttpResponse doRestartStatus() throws IOException { JSONObject response = new JSONObject(); - Jenkins jenkins = Jenkins.getInstance(); + Jenkins jenkins = Jenkins.get(); response.put("restartRequired", jenkins.getUpdateCenter().isRestartRequiredForCompletion()); response.put("restartSupported", jenkins.getLifecycle().canRestart()); return HttpResponses.okJSON(response); @@ -383,9 +434,9 @@ public class SetupWizard extends PageDecorator { */ @CheckForNull /*package*/ JSONArray getPlatformPluginList() { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); JSONArray initialPluginList = null; - updateSiteList: for (UpdateSite updateSite : Jenkins.getInstance().getUpdateCenter().getSiteList()) { + updateSiteList: for (UpdateSite updateSite : Jenkins.get().getUpdateCenter().getSiteList()) { String updateCenterJsonUrl = updateSite.getUrl(); String suggestedPluginUrl = updateCenterJsonUrl.replace("/update-center.json", "/platform-plugins.json"); try { @@ -429,7 +480,7 @@ public class SetupWizard extends PageDecorator { * Get the platform plugins added in the version range */ /*package*/ JSONArray getPlatformPluginsForUpdate(VersionNumber from, VersionNumber to) { - Jenkins jenkins = Jenkins.getInstance(); + Jenkins jenkins = Jenkins.get(); JSONArray pluginCategories = JSONArray.fromObject(getPlatformPluginList().toString()); for (Iterator categoryIterator = pluginCategories.iterator(); categoryIterator.hasNext();) { Object category = categoryIterator.next(); @@ -486,7 +537,7 @@ public class SetupWizard extends PageDecorator { * Gets the file used to store the initial admin password */ public FilePath getInitialAdminPasswordFile() { - return Jenkins.getInstance().getRootPath().child("secrets/initialAdminPassword"); + return Jenkins.get().getRootPath().child("secrets/initialAdminPassword"); } /** @@ -499,7 +550,7 @@ public class SetupWizard extends PageDecorator { } /*package*/ void completeSetup() throws IOException, ServletException { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); InstallUtil.saveLastExecVersion(); setCurrentLevel(Jenkins.getVersion()); InstallUtil.proceedToNextStateFrom(InstallState.INITIAL_SETUP_COMPLETED); @@ -561,7 +612,7 @@ public class SetupWizard extends PageDecorator { ((HttpServletResponse) response).sendRedirect(req.getContextPath() + "/"); return; } else if (req.getRequestURI().equals(req.getContextPath() + "/")) { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + Jenkins.get().checkPermission(Jenkins.ADMINISTER); chain.doFilter(new HttpServletRequestWrapper(req) { public String getRequestURI() { return getContextPath() + "/setupWizard/"; @@ -583,7 +634,7 @@ public class SetupWizard extends PageDecorator { * Sets up the Setup Wizard filter if the current state requires it. */ private void checkFilter() { - if (!Jenkins.getInstance().getInstallState().isSetupComplete()) { + if (!Jenkins.get().getInstallState().isSetupComplete()) { setUpFilter(); } } diff --git a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java index b7390ad3d3..9f15cd918e 100644 --- a/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java +++ b/core/src/main/java/jenkins/model/JenkinsLocationConfiguration.java @@ -6,6 +6,8 @@ import hudson.XmlFile; import hudson.util.FormValidation; import hudson.util.XStream2; import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.QueryParameter; import javax.mail.internet.AddressException; @@ -50,6 +52,18 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration { public static @CheckForNull JenkinsLocationConfiguration get() { return GlobalConfiguration.all().get(JenkinsLocationConfiguration.class); } + + /** + * Gets local configuration. For explanation when it could die, see {@link #get()} + */ + @Restricted(NoExternalUse.class) + public static @Nonnull JenkinsLocationConfiguration getOrDie(){ + JenkinsLocationConfiguration config = JenkinsLocationConfiguration.get(); + if (config == null) { + throw new IllegalStateException("JenkinsLocationConfiguration instance is missing. Probably the Jenkins instance is not fully loaded at this time."); + } + return config; + } public JenkinsLocationConfiguration() { load(); @@ -63,7 +77,7 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration { if(!file.exists()) { XStream2 xs = new XStream2(); xs.addCompatibilityAlias("hudson.tasks.Mailer$DescriptorImpl",JenkinsLocationConfiguration.class); - file = new XmlFile(xs,new File(Jenkins.getInstance().getRootDir(),"hudson.tasks.Mailer.xml")); + file = new XmlFile(xs,new File(Jenkins.get().getRootDir(),"hudson.tasks.Mailer.xml")); if (file.exists()) { try { file.unmarshal(this); @@ -125,7 +139,7 @@ public class JenkinsLocationConfiguration extends GlobalConfiguration { */ private void updateSecureSessionFlag() { try { - ServletContext context = Jenkins.getInstance().servletContext; + ServletContext context = Jenkins.get().servletContext; Method m; try { m = context.getClass().getMethod("getSessionCookieConfig"); diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java new file mode 100644 index 0000000000..bd3a8da924 --- /dev/null +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/DomainValidator.java @@ -0,0 +1,2072 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +package jenkins.org.apache.commons.validator.routines; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.io.Serializable; +import java.net.IDN; +import java.util.Arrays; +import java.util.Locale; + +/** + *

      Domain name validation routines.

      + * + *

      + * This validator provides methods for validating Internet domain names + * and top-level domains. + *

      + * + *

      Domain names are evaluated according + * to the standards RFC1034, + * section 3, and RFC1123, + * section 2.1. No accommodation is provided for the specialized needs of + * other applications; if the domain name has been URL-encoded, for example, + * validation will fail even though the equivalent plaintext version of the + * same name would have passed. + *

      + * + *

      + * Validation is also provided for top-level domains (TLDs) as defined and + * maintained by the Internet Assigned Numbers Authority (IANA): + *

      + * + *
        + *
      • {@link #isValidInfrastructureTld} - validates infrastructure TLDs + * (.arpa, etc.)
      • + *
      • {@link #isValidGenericTld} - validates generic TLDs + * (.com, .org, etc.)
      • + *
      • {@link #isValidCountryCodeTld} - validates country code TLDs + * (.us, .uk, .cn, etc.)
      • + *
      + * + *

      + * (NOTE: This class does not provide IP address lookup for domain names or + * methods to ensure that a given domain name matches a specific IP; see + * {@link java.net.InetAddress} for that functionality.) + *

      + * + * @version $Revision: 1781829 $ + * @since Validator 1.4 + */ +//[PATCH] +@Restricted(NoExternalUse.class) +// end of [PATCH] +public class DomainValidator implements Serializable { + + private static final int MAX_DOMAIN_LENGTH = 253; + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + + private static final long serialVersionUID = -4407125112880174009L; + + // Regular expression strings for hostnames (derived from RFC2396 and RFC 1123) + + // RFC2396: domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + // Max 63 characters + private static final String DOMAIN_LABEL_REGEX = "\\p{Alnum}(?>[\\p{Alnum}-]{0,61}\\p{Alnum})?"; + + // RFC2396 toplabel = alpha | alpha *( alphanum | "-" ) alphanum + // Max 63 characters + private static final String TOP_LABEL_REGEX = "\\p{Alpha}(?>[\\p{Alnum}-]{0,61}\\p{Alnum})?"; + + // RFC2396 hostname = *( domainlabel "." ) toplabel [ "." ] + // Note that the regex currently requires both a domain label and a top level label, whereas + // the RFC does not. This is because the regex is used to detect if a TLD is present. + // If the match fails, input is checked against DOMAIN_LABEL_REGEX (hostnameRegex) + // RFC1123 sec 2.1 allows hostnames to start with a digit + private static final String DOMAIN_NAME_REGEX = + "^(?:" + DOMAIN_LABEL_REGEX + "\\.)+" + "(" + TOP_LABEL_REGEX + ")\\.?$"; + + private final boolean allowLocal; + + /** + * Singleton instance of this validator, which + * doesn't consider local addresses as valid. + */ + private static final DomainValidator DOMAIN_VALIDATOR = new DomainValidator(false); + + /** + * Singleton instance of this validator, which does + * consider local addresses valid. + */ + private static final DomainValidator DOMAIN_VALIDATOR_WITH_LOCAL = new DomainValidator(true); + + /** + * RegexValidator for matching domains. + */ + private final RegexValidator domainRegex = + new RegexValidator(DOMAIN_NAME_REGEX); + /** + * RegexValidator for matching a local hostname + */ + // RFC1123 sec 2.1 allows hostnames to start with a digit + private final RegexValidator hostnameRegex = + new RegexValidator(DOMAIN_LABEL_REGEX); + + /** + * Returns the singleton instance of this validator. It + * will not consider local addresses as valid. + * @return the singleton instance of this validator + */ + public static synchronized DomainValidator getInstance() { + inUse = true; + return DOMAIN_VALIDATOR; + } + + /** + * Returns the singleton instance of this validator, + * with local validation as required. + * @param allowLocal Should local addresses be considered valid? + * @return the singleton instance of this validator + */ + public static synchronized DomainValidator getInstance(boolean allowLocal) { + inUse = true; + if(allowLocal) { + return DOMAIN_VALIDATOR_WITH_LOCAL; + } + return DOMAIN_VALIDATOR; + } + + /** Private constructor. */ + private DomainValidator(boolean allowLocal) { + this.allowLocal = allowLocal; + } + + /** + * Returns true if the specified String parses + * as a valid domain name with a recognized top-level domain. + * The parsing is case-insensitive. + * @param domain the parameter to check for domain name syntax + * @return true if the parameter is a valid domain name + */ + public boolean isValid(String domain) { + if (domain == null) { + return false; + } + domain = unicodeToASCII(domain); + // hosts must be equally reachable via punycode and Unicode; + // Unicode is never shorter than punycode, so check punycode + // if domain did not convert, then it will be caught by ASCII + // checks in the regexes below + if (domain.length() > MAX_DOMAIN_LENGTH) { + return false; + } + String[] groups = domainRegex.match(domain); + if (groups != null && groups.length > 0) { + return isValidTld(groups[0]); + } + return allowLocal && hostnameRegex.isValid(domain); + } + + // package protected for unit test access + // must agree with isValidRootUrl() above + final boolean isValidDomainSyntax(String domain) { + if (domain == null) { + return false; + } + domain = unicodeToASCII(domain); + // hosts must be equally reachable via punycode and Unicode; + // Unicode is never shorter than punycode, so check punycode + // if domain did not convert, then it will be caught by ASCII + // checks in the regexes below + if (domain.length() > MAX_DOMAIN_LENGTH) { + return false; + } + String[] groups = domainRegex.match(domain); + return (groups != null && groups.length > 0) + || hostnameRegex.isValid(domain); + } + + /** + * Returns true if the specified String matches any + * IANA-defined top-level domain. Leading dots are ignored if present. + * The search is case-insensitive. + * @param tld the parameter to check for TLD status, not null + * @return true if the parameter is a TLD + */ + public boolean isValidTld(String tld) { + tld = unicodeToASCII(tld); + if(allowLocal && isValidLocalTld(tld)) { + return true; + } + return isValidInfrastructureTld(tld) + || isValidGenericTld(tld) + || isValidCountryCodeTld(tld); + } + + /** + * Returns true if the specified String matches any + * IANA-defined infrastructure top-level domain. Leading dots are + * ignored if present. The search is case-insensitive. + * @param iTld the parameter to check for infrastructure TLD status, not null + * @return true if the parameter is an infrastructure TLD + */ + public boolean isValidInfrastructureTld(String iTld) { + final String key = chompLeadingDot(unicodeToASCII(iTld).toLowerCase(Locale.ENGLISH)); + return arrayContains(INFRASTRUCTURE_TLDS, key); + } + + /** + * Returns true if the specified String matches any + * IANA-defined generic top-level domain. Leading dots are ignored + * if present. The search is case-insensitive. + * @param gTld the parameter to check for generic TLD status, not null + * @return true if the parameter is a generic TLD + */ + public boolean isValidGenericTld(String gTld) { + final String key = chompLeadingDot(unicodeToASCII(gTld).toLowerCase(Locale.ENGLISH)); + return (arrayContains(GENERIC_TLDS, key) || arrayContains(genericTLDsPlus, key)) + && !arrayContains(genericTLDsMinus, key); + } + + /** + * Returns true if the specified String matches any + * IANA-defined country code top-level domain. Leading dots are + * ignored if present. The search is case-insensitive. + * @param ccTld the parameter to check for country code TLD status, not null + * @return true if the parameter is a country code TLD + */ + public boolean isValidCountryCodeTld(String ccTld) { + final String key = chompLeadingDot(unicodeToASCII(ccTld).toLowerCase(Locale.ENGLISH)); + return (arrayContains(COUNTRY_CODE_TLDS, key) || arrayContains(countryCodeTLDsPlus, key)) + && !arrayContains(countryCodeTLDsMinus, key); + } + + /** + * Returns true if the specified String matches any + * widely used "local" domains (localhost or localdomain). Leading dots are + * ignored if present. The search is case-insensitive. + * @param lTld the parameter to check for local TLD status, not null + * @return true if the parameter is an local TLD + */ + public boolean isValidLocalTld(String lTld) { + final String key = chompLeadingDot(unicodeToASCII(lTld).toLowerCase(Locale.ENGLISH)); + return arrayContains(LOCAL_TLDS, key); + } + + private String chompLeadingDot(String str) { + if (str.startsWith(".")) { + return str.substring(1); + } + return str; + } + + // --------------------------------------------- + // ----- TLDs defined by IANA + // ----- Authoritative and comprehensive list at: + // ----- http://data.iana.org/TLD/tlds-alpha-by-domain.txt + + // Note that the above list is in UPPER case. + // The code currently converts strings to lower case (as per the tables below) + + // IANA also provide an HTML list at http://www.iana.org/domains/root/db + // Note that this contains several country code entries which are NOT in + // the text file. These all have the "Not assigned" in the "Sponsoring Organisation" column + // For example (as of 2015-01-02): + // .bl country-code Not assigned + // .um country-code Not assigned + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static final String[] INFRASTRUCTURE_TLDS = new String[] { + "arpa", // internet infrastructure + }; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static final String[] GENERIC_TLDS = new String[] { + // Taken from Version 2017020400, Last Updated Sat Feb 4 07:07:01 2017 UTC + "aaa", // aaa American Automobile Association, Inc. + "aarp", // aarp AARP + "abarth", // abarth Fiat Chrysler Automobiles N.V. + "abb", // abb ABB Ltd + "abbott", // abbott Abbott Laboratories, Inc. + "abbvie", // abbvie AbbVie Inc. + "abc", // abc Disney Enterprises, Inc. + "able", // able Able Inc. + "abogado", // abogado Top Level Domain Holdings Limited + "abudhabi", // abudhabi Abu Dhabi Systems and Information Centre + "academy", // academy Half Oaks, LLC + "accenture", // accenture Accenture plc + "accountant", // accountant dot Accountant Limited + "accountants", // accountants Knob Town, LLC + "aco", // aco ACO Severin Ahlmann GmbH & Co. KG + "active", // active The Active Network, Inc + "actor", // actor United TLD Holdco Ltd. + "adac", // adac Allgemeiner Deutscher Automobil-Club e.V. (ADAC) + "ads", // ads Charleston Road Registry Inc. + "adult", // adult ICM Registry AD LLC + "aeg", // aeg Aktiebolaget Electrolux + "aero", // aero Societe Internationale de Telecommunications Aeronautique (SITA INC USA) + "aetna", // aetna Aetna Life Insurance Company + "afamilycompany", // afamilycompany Johnson Shareholdings, Inc. + "afl", // afl Australian Football League + "agakhan", // agakhan Fondation Aga Khan (Aga Khan Foundation) + "agency", // agency Steel Falls, LLC + "aig", // aig American International Group, Inc. + "aigo", // aigo aigo Digital Technology Co,Ltd. + "airbus", // airbus Airbus S.A.S. + "airforce", // airforce United TLD Holdco Ltd. + "airtel", // airtel Bharti Airtel Limited + "akdn", // akdn Fondation Aga Khan (Aga Khan Foundation) + "alfaromeo", // alfaromeo Fiat Chrysler Automobiles N.V. + "alibaba", // alibaba Alibaba Group Holding Limited + "alipay", // alipay Alibaba Group Holding Limited + "allfinanz", // allfinanz Allfinanz Deutsche Vermögensberatung Aktiengesellschaft + "allstate", // allstate Allstate Fire and Casualty Insurance Company + "ally", // ally Ally Financial Inc. + "alsace", // alsace REGION D ALSACE + "alstom", // alstom ALSTOM + "americanexpress", // americanexpress American Express Travel Related Services Company, Inc. + "americanfamily", // americanfamily AmFam, Inc. + "amex", // amex American Express Travel Related Services Company, Inc. + "amfam", // amfam AmFam, Inc. + "amica", // amica Amica Mutual Insurance Company + "amsterdam", // amsterdam Gemeente Amsterdam + "analytics", // analytics Campus IP LLC + "android", // android Charleston Road Registry Inc. + "anquan", // anquan QIHOO 360 TECHNOLOGY CO. LTD. + "anz", // anz Australia and New Zealand Banking Group Limited + "aol", // aol AOL Inc. + "apartments", // apartments June Maple, LLC + "app", // app Charleston Road Registry Inc. + "apple", // apple Apple Inc. + "aquarelle", // aquarelle Aquarelle.com + "aramco", // aramco Aramco Services Company + "archi", // archi STARTING DOT LIMITED + "army", // army United TLD Holdco Ltd. + "art", // art UK Creative Ideas Limited + "arte", // arte Association Relative à la Télévision Européenne G.E.I.E. + "asda", // asda Wal-Mart Stores, Inc. + "asia", // asia DotAsia Organisation Ltd. + "associates", // associates Baxter Hill, LLC + "athleta", // athleta The Gap, Inc. + "attorney", // attorney United TLD Holdco, Ltd + "auction", // auction United TLD HoldCo, Ltd. + "audi", // audi AUDI Aktiengesellschaft + "audible", // audible Amazon Registry Services, Inc. + "audio", // audio Uniregistry, Corp. + "auspost", // auspost Australian Postal Corporation + "author", // author Amazon Registry Services, Inc. + "auto", // auto Uniregistry, Corp. + "autos", // autos DERAutos, LLC + "avianca", // avianca Aerovias del Continente Americano S.A. Avianca + "aws", // aws Amazon Registry Services, Inc. + "axa", // axa AXA SA + "azure", // azure Microsoft Corporation + "baby", // baby Johnson & Johnson Services, Inc. + "baidu", // baidu Baidu, Inc. + "banamex", // banamex Citigroup Inc. + "bananarepublic", // bananarepublic The Gap, Inc. + "band", // band United TLD Holdco, Ltd + "bank", // bank fTLD Registry Services, LLC + "bar", // bar Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable + "barcelona", // barcelona Municipi de Barcelona + "barclaycard", // barclaycard Barclays Bank PLC + "barclays", // barclays Barclays Bank PLC + "barefoot", // barefoot Gallo Vineyards, Inc. + "bargains", // bargains Half Hallow, LLC + "baseball", // baseball MLB Advanced Media DH, LLC + "basketball", // basketball Fédération Internationale de Basketball (FIBA) + "bauhaus", // bauhaus Werkhaus GmbH + "bayern", // bayern Bayern Connect GmbH + "bbc", // bbc British Broadcasting Corporation + "bbt", // bbt BB&T Corporation + "bbva", // bbva BANCO BILBAO VIZCAYA ARGENTARIA, S.A. + "bcg", // bcg The Boston Consulting Group, Inc. + "bcn", // bcn Municipi de Barcelona + "beats", // beats Beats Electronics, LLC + "beauty", // beauty L'Oréal + "beer", // beer Top Level Domain Holdings Limited + "bentley", // bentley Bentley Motors Limited + "berlin", // berlin dotBERLIN GmbH & Co. KG + "best", // best BestTLD Pty Ltd + "bestbuy", // bestbuy BBY Solutions, Inc. + "bet", // bet Afilias plc + "bharti", // bharti Bharti Enterprises (Holding) Private Limited + "bible", // bible American Bible Society + "bid", // bid dot Bid Limited + "bike", // bike Grand Hollow, LLC + "bing", // bing Microsoft Corporation + "bingo", // bingo Sand Cedar, LLC + "bio", // bio STARTING DOT LIMITED + "biz", // biz Neustar, Inc. + "black", // black Afilias Limited + "blackfriday", // blackfriday Uniregistry, Corp. + "blanco", // blanco BLANCO GmbH + Co KG + "blockbuster", // blockbuster Dish DBS Corporation + "blog", // blog Knock Knock WHOIS There, LLC + "bloomberg", // bloomberg Bloomberg IP Holdings LLC + "blue", // blue Afilias Limited + "bms", // bms Bristol-Myers Squibb Company + "bmw", // bmw Bayerische Motoren Werke Aktiengesellschaft + "bnl", // bnl Banca Nazionale del Lavoro + "bnpparibas", // bnpparibas BNP Paribas + "boats", // boats DERBoats, LLC + "boehringer", // boehringer Boehringer Ingelheim International GmbH + "bofa", // bofa NMS Services, Inc. + "bom", // bom Núcleo de Informação e Coordenação do Ponto BR - NIC.br + "bond", // bond Bond University Limited + "boo", // boo Charleston Road Registry Inc. + "book", // book Amazon Registry Services, Inc. + "booking", // booking Booking.com B.V. + "boots", // boots THE BOOTS COMPANY PLC + "bosch", // bosch Robert Bosch GMBH + "bostik", // bostik Bostik SA + "boston", // boston Boston TLD Management, LLC + "bot", // bot Amazon Registry Services, Inc. + "boutique", // boutique Over Galley, LLC + "box", // box NS1 Limited + "bradesco", // bradesco Banco Bradesco S.A. + "bridgestone", // bridgestone Bridgestone Corporation + "broadway", // broadway Celebrate Broadway, Inc. + "broker", // broker DOTBROKER REGISTRY LTD + "brother", // brother Brother Industries, Ltd. + "brussels", // brussels DNS.be vzw + "budapest", // budapest Top Level Domain Holdings Limited + "bugatti", // bugatti Bugatti International SA + "build", // build Plan Bee LLC + "builders", // builders Atomic Madison, LLC + "business", // business Spring Cross, LLC + "buy", // buy Amazon Registry Services, INC + "buzz", // buzz DOTSTRATEGY CO. + "bzh", // bzh Association www.bzh + "cab", // cab Half Sunset, LLC + "cafe", // cafe Pioneer Canyon, LLC + "cal", // cal Charleston Road Registry Inc. + "call", // call Amazon Registry Services, Inc. + "calvinklein", // calvinklein PVH gTLD Holdings LLC + "cam", // cam AC Webconnecting Holding B.V. + "camera", // camera Atomic Maple, LLC + "camp", // camp Delta Dynamite, LLC + "cancerresearch", // cancerresearch Australian Cancer Research Foundation + "canon", // canon Canon Inc. + "capetown", // capetown ZA Central Registry NPC trading as ZA Central Registry + "capital", // capital Delta Mill, LLC + "capitalone", // capitalone Capital One Financial Corporation + "car", // car Cars Registry Limited + "caravan", // caravan Caravan International, Inc. + "cards", // cards Foggy Hollow, LLC + "care", // care Goose Cross, LLC + "career", // career dotCareer LLC + "careers", // careers Wild Corner, LLC + "cars", // cars Uniregistry, Corp. + "cartier", // cartier Richemont DNS Inc. + "casa", // casa Top Level Domain Holdings Limited + "case", // case CNH Industrial N.V. + "caseih", // caseih CNH Industrial N.V. + "cash", // cash Delta Lake, LLC + "casino", // casino Binky Sky, LLC + "cat", // cat Fundacio puntCAT + "catering", // catering New Falls. LLC + "catholic", // catholic Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) + "cba", // cba COMMONWEALTH BANK OF AUSTRALIA + "cbn", // cbn The Christian Broadcasting Network, Inc. + "cbre", // cbre CBRE, Inc. + "cbs", // cbs CBS Domains Inc. + "ceb", // ceb The Corporate Executive Board Company + "center", // center Tin Mill, LLC + "ceo", // ceo CEOTLD Pty Ltd + "cern", // cern European Organization for Nuclear Research ("CERN") + "cfa", // cfa CFA Institute + "cfd", // cfd DOTCFD REGISTRY LTD + "chanel", // chanel Chanel International B.V. + "channel", // channel Charleston Road Registry Inc. + "chase", // chase JPMorgan Chase & Co. + "chat", // chat Sand Fields, LLC + "cheap", // cheap Sand Cover, LLC + "chintai", // chintai CHINTAI Corporation + "chloe", // chloe Richemont DNS Inc. + "christmas", // christmas Uniregistry, Corp. + "chrome", // chrome Charleston Road Registry Inc. + "chrysler", // chrysler FCA US LLC. + "church", // church Holly Fileds, LLC + "cipriani", // cipriani Hotel Cipriani Srl + "circle", // circle Amazon Registry Services, Inc. + "cisco", // cisco Cisco Technology, Inc. + "citadel", // citadel Citadel Domain LLC + "citi", // citi Citigroup Inc. + "citic", // citic CITIC Group Corporation + "city", // city Snow Sky, LLC + "cityeats", // cityeats Lifestyle Domain Holdings, Inc. + "claims", // claims Black Corner, LLC + "cleaning", // cleaning Fox Shadow, LLC + "click", // click Uniregistry, Corp. + "clinic", // clinic Goose Park, LLC + "clinique", // clinique The Estée Lauder Companies Inc. + "clothing", // clothing Steel Lake, LLC + "cloud", // cloud ARUBA S.p.A. + "club", // club .CLUB DOMAINS, LLC + "clubmed", // clubmed Club Méditerranée S.A. + "coach", // coach Koko Island, LLC + "codes", // codes Puff Willow, LLC + "coffee", // coffee Trixy Cover, LLC + "college", // college XYZ.COM LLC + "cologne", // cologne NetCologne Gesellschaft für Telekommunikation mbH + "com", // com VeriSign Global Registry Services + "comcast", // comcast Comcast IP Holdings I, LLC + "commbank", // commbank COMMONWEALTH BANK OF AUSTRALIA + "community", // community Fox Orchard, LLC + "company", // company Silver Avenue, LLC + "compare", // compare iSelect Ltd + "computer", // computer Pine Mill, LLC + "comsec", // comsec VeriSign, Inc. + "condos", // condos Pine House, LLC + "construction", // construction Fox Dynamite, LLC + "consulting", // consulting United TLD Holdco, LTD. + "contact", // contact Top Level Spectrum, Inc. + "contractors", // contractors Magic Woods, LLC + "cooking", // cooking Top Level Domain Holdings Limited + "cookingchannel", // cookingchannel Lifestyle Domain Holdings, Inc. + "cool", // cool Koko Lake, LLC + "coop", // coop DotCooperation LLC + "corsica", // corsica Collectivité Territoriale de Corse + "country", // country Top Level Domain Holdings Limited + "coupon", // coupon Amazon Registry Services, Inc. + "coupons", // coupons Black Island, LLC + "courses", // courses OPEN UNIVERSITIES AUSTRALIA PTY LTD + "credit", // credit Snow Shadow, LLC + "creditcard", // creditcard Binky Frostbite, LLC + "creditunion", // creditunion CUNA Performance Resources, LLC + "cricket", // cricket dot Cricket Limited + "crown", // crown Crown Equipment Corporation + "crs", // crs Federated Co-operatives Limited + "cruise", // cruise Viking River Cruises (Bermuda) Ltd. + "cruises", // cruises Spring Way, LLC + "csc", // csc Alliance-One Services, Inc. + "cuisinella", // cuisinella SALM S.A.S. + "cymru", // cymru Nominet UK + "cyou", // cyou Beijing Gamease Age Digital Technology Co., Ltd. + "dabur", // dabur Dabur India Limited + "dad", // dad Charleston Road Registry Inc. + "dance", // dance United TLD Holdco Ltd. + "data", // data Dish DBS Corporation + "date", // date dot Date Limited + "dating", // dating Pine Fest, LLC + "datsun", // datsun NISSAN MOTOR CO., LTD. + "day", // day Charleston Road Registry Inc. + "dclk", // dclk Charleston Road Registry Inc. + "dds", // dds Minds + Machines Group Limited + "deal", // deal Amazon Registry Services, Inc. + "dealer", // dealer Dealer Dot Com, Inc. + "deals", // deals Sand Sunset, LLC + "degree", // degree United TLD Holdco, Ltd + "delivery", // delivery Steel Station, LLC + "dell", // dell Dell Inc. + "deloitte", // deloitte Deloitte Touche Tohmatsu + "delta", // delta Delta Air Lines, Inc. + "democrat", // democrat United TLD Holdco Ltd. + "dental", // dental Tin Birch, LLC + "dentist", // dentist United TLD Holdco, Ltd + "desi", // desi Desi Networks LLC + "design", // design Top Level Design, LLC + "dev", // dev Charleston Road Registry Inc. + "dhl", // dhl Deutsche Post AG + "diamonds", // diamonds John Edge, LLC + "diet", // diet Uniregistry, Corp. + "digital", // digital Dash Park, LLC + "direct", // direct Half Trail, LLC + "directory", // directory Extra Madison, LLC + "discount", // discount Holly Hill, LLC + "discover", // discover Discover Financial Services + "dish", // dish Dish DBS Corporation + "diy", // diy Lifestyle Domain Holdings, Inc. + "dnp", // dnp Dai Nippon Printing Co., Ltd. + "docs", // docs Charleston Road Registry Inc. + "doctor", // doctor Brice Trail, LLC + "dodge", // dodge FCA US LLC. + "dog", // dog Koko Mill, LLC + "doha", // doha Communications Regulatory Authority (CRA) + "domains", // domains Sugar Cross, LLC +// "doosan", // doosan Doosan Corporation (retired) + "dot", // dot Dish DBS Corporation + "download", // download dot Support Limited + "drive", // drive Charleston Road Registry Inc. + "dtv", // dtv Dish DBS Corporation + "dubai", // dubai Dubai Smart Government Department + "duck", // duck Johnson Shareholdings, Inc. + "dunlop", // dunlop The Goodyear Tire & Rubber Company + "duns", // duns The Dun & Bradstreet Corporation + "dupont", // dupont E. I. du Pont de Nemours and Company + "durban", // durban ZA Central Registry NPC trading as ZA Central Registry + "dvag", // dvag Deutsche Vermögensberatung Aktiengesellschaft DVAG + "dvr", // dvr Hughes Satellite Systems Corporation + "earth", // earth Interlink Co., Ltd. + "eat", // eat Charleston Road Registry Inc. + "eco", // eco Big Room Inc. + "edeka", // edeka EDEKA Verband kaufmännischer Genossenschaften e.V. + "edu", // edu EDUCAUSE + "education", // education Brice Way, LLC + "email", // email Spring Madison, LLC + "emerck", // emerck Merck KGaA + "energy", // energy Binky Birch, LLC + "engineer", // engineer United TLD Holdco Ltd. + "engineering", // engineering Romeo Canyon + "enterprises", // enterprises Snow Oaks, LLC + "epost", // epost Deutsche Post AG + "epson", // epson Seiko Epson Corporation + "equipment", // equipment Corn Station, LLC + "ericsson", // ericsson Telefonaktiebolaget L M Ericsson + "erni", // erni ERNI Group Holding AG + "esq", // esq Charleston Road Registry Inc. + "estate", // estate Trixy Park, LLC + "esurance", // esurance Esurance Insurance Company + "eurovision", // eurovision European Broadcasting Union (EBU) + "eus", // eus Puntueus Fundazioa + "events", // events Pioneer Maple, LLC + "everbank", // everbank EverBank + "exchange", // exchange Spring Falls, LLC + "expert", // expert Magic Pass, LLC + "exposed", // exposed Victor Beach, LLC + "express", // express Sea Sunset, LLC + "extraspace", // extraspace Extra Space Storage LLC + "fage", // fage Fage International S.A. + "fail", // fail Atomic Pipe, LLC + "fairwinds", // fairwinds FairWinds Partners, LLC + "faith", // faith dot Faith Limited + "family", // family United TLD Holdco Ltd. + "fan", // fan Asiamix Digital Ltd + "fans", // fans Asiamix Digital Limited + "farm", // farm Just Maple, LLC + "farmers", // farmers Farmers Insurance Exchange + "fashion", // fashion Top Level Domain Holdings Limited + "fast", // fast Amazon Registry Services, Inc. + "fedex", // fedex Federal Express Corporation + "feedback", // feedback Top Level Spectrum, Inc. + "ferrari", // ferrari Fiat Chrysler Automobiles N.V. + "ferrero", // ferrero Ferrero Trading Lux S.A. + "fiat", // fiat Fiat Chrysler Automobiles N.V. + "fidelity", // fidelity Fidelity Brokerage Services LLC + "fido", // fido Rogers Communications Canada Inc. + "film", // film Motion Picture Domain Registry Pty Ltd + "final", // final Núcleo de Informação e Coordenação do Ponto BR - NIC.br + "finance", // finance Cotton Cypress, LLC + "financial", // financial Just Cover, LLC + "fire", // fire Amazon Registry Services, Inc. + "firestone", // firestone Bridgestone Corporation + "firmdale", // firmdale Firmdale Holdings Limited + "fish", // fish Fox Woods, LLC + "fishing", // fishing Top Level Domain Holdings Limited + "fit", // fit Minds + Machines Group Limited + "fitness", // fitness Brice Orchard, LLC + "flickr", // flickr Yahoo! Domain Services Inc. + "flights", // flights Fox Station, LLC + "flir", // flir FLIR Systems, Inc. + "florist", // florist Half Cypress, LLC + "flowers", // flowers Uniregistry, Corp. +// "flsmidth", // flsmidth FLSmidth A/S retired 2016-07-22 + "fly", // fly Charleston Road Registry Inc. + "foo", // foo Charleston Road Registry Inc. + "food", // food Lifestyle Domain Holdings, Inc. + "foodnetwork", // foodnetwork Lifestyle Domain Holdings, Inc. + "football", // football Foggy Farms, LLC + "ford", // ford Ford Motor Company + "forex", // forex DOTFOREX REGISTRY LTD + "forsale", // forsale United TLD Holdco, LLC + "forum", // forum Fegistry, LLC + "foundation", // foundation John Dale, LLC + "fox", // fox FOX Registry, LLC + "free", // free Amazon Registry Services, Inc. + "fresenius", // fresenius Fresenius Immobilien-Verwaltungs-GmbH + "frl", // frl FRLregistry B.V. + "frogans", // frogans OP3FT + "frontdoor", // frontdoor Lifestyle Domain Holdings, Inc. + "frontier", // frontier Frontier Communications Corporation + "ftr", // ftr Frontier Communications Corporation + "fujitsu", // fujitsu Fujitsu Limited + "fujixerox", // fujixerox Xerox DNHC LLC + "fun", // fun DotSpace, Inc. + "fund", // fund John Castle, LLC + "furniture", // furniture Lone Fields, LLC + "futbol", // futbol United TLD Holdco, Ltd. + "fyi", // fyi Silver Tigers, LLC + "gal", // gal Asociación puntoGAL + "gallery", // gallery Sugar House, LLC + "gallo", // gallo Gallo Vineyards, Inc. + "gallup", // gallup Gallup, Inc. + "game", // game Uniregistry, Corp. + "games", // games United TLD Holdco Ltd. + "gap", // gap The Gap, Inc. + "garden", // garden Top Level Domain Holdings Limited + "gbiz", // gbiz Charleston Road Registry Inc. + "gdn", // gdn Joint Stock Company "Navigation-information systems" + "gea", // gea GEA Group Aktiengesellschaft + "gent", // gent COMBELL GROUP NV/SA + "genting", // genting Resorts World Inc. Pte. Ltd. + "george", // george Wal-Mart Stores, Inc. + "ggee", // ggee GMO Internet, Inc. + "gift", // gift Uniregistry, Corp. + "gifts", // gifts Goose Sky, LLC + "gives", // gives United TLD Holdco Ltd. + "giving", // giving Giving Limited + "glade", // glade Johnson Shareholdings, Inc. + "glass", // glass Black Cover, LLC + "gle", // gle Charleston Road Registry Inc. + "global", // global Dot Global Domain Registry Limited + "globo", // globo Globo Comunicação e Participações S.A + "gmail", // gmail Charleston Road Registry Inc. + "gmbh", // gmbh Extra Dynamite, LLC + "gmo", // gmo GMO Internet, Inc. + "gmx", // gmx 1&1 Mail & Media GmbH + "godaddy", // godaddy Go Daddy East, LLC + "gold", // gold June Edge, LLC + "goldpoint", // goldpoint YODOBASHI CAMERA CO.,LTD. + "golf", // golf Lone Falls, LLC + "goo", // goo NTT Resonant Inc. + "goodhands", // goodhands Allstate Fire and Casualty Insurance Company + "goodyear", // goodyear The Goodyear Tire & Rubber Company + "goog", // goog Charleston Road Registry Inc. + "google", // google Charleston Road Registry Inc. + "gop", // gop Republican State Leadership Committee, Inc. + "got", // got Amazon Registry Services, Inc. + "gov", // gov General Services Administration Attn: QTDC, 2E08 (.gov Domain Registration) + "grainger", // grainger Grainger Registry Services, LLC + "graphics", // graphics Over Madison, LLC + "gratis", // gratis Pioneer Tigers, LLC + "green", // green Afilias Limited + "gripe", // gripe Corn Sunset, LLC + "group", // group Romeo Town, LLC + "guardian", // guardian The Guardian Life Insurance Company of America + "gucci", // gucci Guccio Gucci S.p.a. + "guge", // guge Charleston Road Registry Inc. + "guide", // guide Snow Moon, LLC + "guitars", // guitars Uniregistry, Corp. + "guru", // guru Pioneer Cypress, LLC + "hair", // hair L'Oreal + "hamburg", // hamburg Hamburg Top-Level-Domain GmbH + "hangout", // hangout Charleston Road Registry Inc. + "haus", // haus United TLD Holdco, LTD. + "hbo", // hbo HBO Registry Services, Inc. + "hdfc", // hdfc HOUSING DEVELOPMENT FINANCE CORPORATION LIMITED + "hdfcbank", // hdfcbank HDFC Bank Limited + "health", // health DotHealth, LLC + "healthcare", // healthcare Silver Glen, LLC + "help", // help Uniregistry, Corp. + "helsinki", // helsinki City of Helsinki + "here", // here Charleston Road Registry Inc. + "hermes", // hermes Hermes International + "hgtv", // hgtv Lifestyle Domain Holdings, Inc. + "hiphop", // hiphop Uniregistry, Corp. + "hisamitsu", // hisamitsu Hisamitsu Pharmaceutical Co.,Inc. + "hitachi", // hitachi Hitachi, Ltd. + "hiv", // hiv dotHIV gemeinnuetziger e.V. + "hkt", // hkt PCCW-HKT DataCom Services Limited + "hockey", // hockey Half Willow, LLC + "holdings", // holdings John Madison, LLC + "holiday", // holiday Goose Woods, LLC + "homedepot", // homedepot Homer TLC, Inc. + "homegoods", // homegoods The TJX Companies, Inc. + "homes", // homes DERHomes, LLC + "homesense", // homesense The TJX Companies, Inc. + "honda", // honda Honda Motor Co., Ltd. + "honeywell", // honeywell Honeywell GTLD LLC + "horse", // horse Top Level Domain Holdings Limited + "hospital", // hospital Ruby Pike, LLC + "host", // host DotHost Inc. + "hosting", // hosting Uniregistry, Corp. + "hot", // hot Amazon Registry Services, Inc. + "hoteles", // hoteles Travel Reservations SRL + "hotmail", // hotmail Microsoft Corporation + "house", // house Sugar Park, LLC + "how", // how Charleston Road Registry Inc. + "hsbc", // hsbc HSBC Holdings PLC + "htc", // htc HTC corporation + "hughes", // hughes Hughes Satellite Systems Corporation + "hyatt", // hyatt Hyatt GTLD, L.L.C. + "hyundai", // hyundai Hyundai Motor Company + "ibm", // ibm International Business Machines Corporation + "icbc", // icbc Industrial and Commercial Bank of China Limited + "ice", // ice IntercontinentalExchange, Inc. + "icu", // icu One.com A/S + "ieee", // ieee IEEE Global LLC + "ifm", // ifm ifm electronic gmbh +// "iinet", // iinet Connect West Pty. Ltd. (Retired) + "ikano", // ikano Ikano S.A. + "imamat", // imamat Fondation Aga Khan (Aga Khan Foundation) + "imdb", // imdb Amazon Registry Services, Inc. + "immo", // immo Auburn Bloom, LLC + "immobilien", // immobilien United TLD Holdco Ltd. + "industries", // industries Outer House, LLC + "infiniti", // infiniti NISSAN MOTOR CO., LTD. + "info", // info Afilias Limited + "ing", // ing Charleston Road Registry Inc. + "ink", // ink Top Level Design, LLC + "institute", // institute Outer Maple, LLC + "insurance", // insurance fTLD Registry Services LLC + "insure", // insure Pioneer Willow, LLC + "int", // int Internet Assigned Numbers Authority + "intel", // intel Intel Corporation + "international", // international Wild Way, LLC + "intuit", // intuit Intuit Administrative Services, Inc. + "investments", // investments Holly Glen, LLC + "ipiranga", // ipiranga Ipiranga Produtos de Petroleo S.A. + "irish", // irish Dot-Irish LLC + "iselect", // iselect iSelect Ltd + "ismaili", // ismaili Fondation Aga Khan (Aga Khan Foundation) + "ist", // ist Istanbul Metropolitan Municipality + "istanbul", // istanbul Istanbul Metropolitan Municipality / Medya A.S. + "itau", // itau Itau Unibanco Holding S.A. + "itv", // itv ITV Services Limited + "iveco", // iveco CNH Industrial N.V. + "iwc", // iwc Richemont DNS Inc. + "jaguar", // jaguar Jaguar Land Rover Ltd + "java", // java Oracle Corporation + "jcb", // jcb JCB Co., Ltd. + "jcp", // jcp JCP Media, Inc. + "jeep", // jeep FCA US LLC. + "jetzt", // jetzt New TLD Company AB + "jewelry", // jewelry Wild Bloom, LLC + "jio", // jio Affinity Names, Inc. + "jlc", // jlc Richemont DNS Inc. + "jll", // jll Jones Lang LaSalle Incorporated + "jmp", // jmp Matrix IP LLC + "jnj", // jnj Johnson & Johnson Services, Inc. + "jobs", // jobs Employ Media LLC + "joburg", // joburg ZA Central Registry NPC trading as ZA Central Registry + "jot", // jot Amazon Registry Services, Inc. + "joy", // joy Amazon Registry Services, Inc. + "jpmorgan", // jpmorgan JPMorgan Chase & Co. + "jprs", // jprs Japan Registry Services Co., Ltd. + "juegos", // juegos Uniregistry, Corp. + "juniper", // juniper JUNIPER NETWORKS, INC. + "kaufen", // kaufen United TLD Holdco Ltd. + "kddi", // kddi KDDI CORPORATION + "kerryhotels", // kerryhotels Kerry Trading Co. Limited + "kerrylogistics", // kerrylogistics Kerry Trading Co. Limited + "kerryproperties", // kerryproperties Kerry Trading Co. Limited + "kfh", // kfh Kuwait Finance House + "kia", // kia KIA MOTORS CORPORATION + "kim", // kim Afilias Limited + "kinder", // kinder Ferrero Trading Lux S.A. + "kindle", // kindle Amazon Registry Services, Inc. + "kitchen", // kitchen Just Goodbye, LLC + "kiwi", // kiwi DOT KIWI LIMITED + "koeln", // koeln NetCologne Gesellschaft für Telekommunikation mbH + "komatsu", // komatsu Komatsu Ltd. + "kosher", // kosher Kosher Marketing Assets LLC + "kpmg", // kpmg KPMG International Cooperative (KPMG International Genossenschaft) + "kpn", // kpn Koninklijke KPN N.V. + "krd", // krd KRG Department of Information Technology + "kred", // kred KredTLD Pty Ltd + "kuokgroup", // kuokgroup Kerry Trading Co. Limited + "kyoto", // kyoto Academic Institution: Kyoto Jyoho Gakuen + "lacaixa", // lacaixa CAIXA D'ESTALVIS I PENSIONS DE BARCELONA + "ladbrokes", // ladbrokes LADBROKES INTERNATIONAL PLC + "lamborghini", // lamborghini Automobili Lamborghini S.p.A. + "lamer", // lamer The Estée Lauder Companies Inc. + "lancaster", // lancaster LANCASTER + "lancia", // lancia Fiat Chrysler Automobiles N.V. + "lancome", // lancome L'Oréal + "land", // land Pine Moon, LLC + "landrover", // landrover Jaguar Land Rover Ltd + "lanxess", // lanxess LANXESS Corporation + "lasalle", // lasalle Jones Lang LaSalle Incorporated + "lat", // lat ECOM-LAC Federación de Latinoamérica y el Caribe para Internet y el Comercio Electrónico + "latino", // latino Dish DBS Corporation + "latrobe", // latrobe La Trobe University + "law", // law Minds + Machines Group Limited + "lawyer", // lawyer United TLD Holdco, Ltd + "lds", // lds IRI Domain Management, LLC + "lease", // lease Victor Trail, LLC + "leclerc", // leclerc A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc + "lefrak", // lefrak LeFrak Organization, Inc. + "legal", // legal Blue Falls, LLC + "lego", // lego LEGO Juris A/S + "lexus", // lexus TOYOTA MOTOR CORPORATION + "lgbt", // lgbt Afilias Limited + "liaison", // liaison Liaison Technologies, Incorporated + "lidl", // lidl Schwarz Domains und Services GmbH & Co. KG + "life", // life Trixy Oaks, LLC + "lifeinsurance", // lifeinsurance American Council of Life Insurers + "lifestyle", // lifestyle Lifestyle Domain Holdings, Inc. + "lighting", // lighting John McCook, LLC + "like", // like Amazon Registry Services, Inc. + "lilly", // lilly Eli Lilly and Company + "limited", // limited Big Fest, LLC + "limo", // limo Hidden Frostbite, LLC + "lincoln", // lincoln Ford Motor Company + "linde", // linde Linde Aktiengesellschaft + "link", // link Uniregistry, Corp. + "lipsy", // lipsy Lipsy Ltd + "live", // live United TLD Holdco Ltd. + "living", // living Lifestyle Domain Holdings, Inc. + "lixil", // lixil LIXIL Group Corporation + "loan", // loan dot Loan Limited + "loans", // loans June Woods, LLC + "locker", // locker Dish DBS Corporation + "locus", // locus Locus Analytics LLC + "loft", // loft Annco, Inc. + "lol", // lol Uniregistry, Corp. + "london", // london Dot London Domains Limited + "lotte", // lotte Lotte Holdings Co., Ltd. + "lotto", // lotto Afilias Limited + "love", // love Merchant Law Group LLP + "lpl", // lpl LPL Holdings, Inc. + "lplfinancial", // lplfinancial LPL Holdings, Inc. + "ltd", // ltd Over Corner, LLC + "ltda", // ltda InterNetX Corp. + "lundbeck", // lundbeck H. Lundbeck A/S + "lupin", // lupin LUPIN LIMITED + "luxe", // luxe Top Level Domain Holdings Limited + "luxury", // luxury Luxury Partners LLC + "macys", // macys Macys, Inc. + "madrid", // madrid Comunidad de Madrid + "maif", // maif Mutuelle Assurance Instituteur France (MAIF) + "maison", // maison Victor Frostbite, LLC + "makeup", // makeup L'Oréal + "man", // man MAN SE + "management", // management John Goodbye, LLC + "mango", // mango PUNTO FA S.L. + "market", // market Unitied TLD Holdco, Ltd + "marketing", // marketing Fern Pass, LLC + "markets", // markets DOTMARKETS REGISTRY LTD + "marriott", // marriott Marriott Worldwide Corporation + "marshalls", // marshalls The TJX Companies, Inc. + "maserati", // maserati Fiat Chrysler Automobiles N.V. + "mattel", // mattel Mattel Sites, Inc. + "mba", // mba Lone Hollow, LLC + "mcd", // mcd McDonald’s Corporation + "mcdonalds", // mcdonalds McDonald’s Corporation + "mckinsey", // mckinsey McKinsey Holdings, Inc. + "med", // med Medistry LLC + "media", // media Grand Glen, LLC + "meet", // meet Afilias Limited + "melbourne", // melbourne The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation + "meme", // meme Charleston Road Registry Inc. + "memorial", // memorial Dog Beach, LLC + "men", // men Exclusive Registry Limited + "menu", // menu Wedding TLD2, LLC + "meo", // meo PT Comunicacoes S.A. + "metlife", // metlife MetLife Services and Solutions, LLC + "miami", // miami Top Level Domain Holdings Limited + "microsoft", // microsoft Microsoft Corporation + "mil", // mil DoD Network Information Center + "mini", // mini Bayerische Motoren Werke Aktiengesellschaft + "mint", // mint Intuit Administrative Services, Inc. + "mit", // mit Massachusetts Institute of Technology + "mitsubishi", // mitsubishi Mitsubishi Corporation + "mlb", // mlb MLB Advanced Media DH, LLC + "mls", // mls The Canadian Real Estate Association + "mma", // mma MMA IARD + "mobi", // mobi Afilias Technologies Limited dba dotMobi + "mobile", // mobile Dish DBS Corporation + "mobily", // mobily GreenTech Consultancy Company W.L.L. + "moda", // moda United TLD Holdco Ltd. + "moe", // moe Interlink Co., Ltd. + "moi", // moi Amazon Registry Services, Inc. + "mom", // mom Uniregistry, Corp. + "monash", // monash Monash University + "money", // money Outer McCook, LLC + "monster", // monster Monster Worldwide, Inc. + "montblanc", // montblanc Richemont DNS Inc. + "mopar", // mopar FCA US LLC. + "mormon", // mormon IRI Domain Management, LLC ("Applicant") + "mortgage", // mortgage United TLD Holdco, Ltd + "moscow", // moscow Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) + "moto", // moto Motorola Trademark Holdings, LLC + "motorcycles", // motorcycles DERMotorcycles, LLC + "mov", // mov Charleston Road Registry Inc. + "movie", // movie New Frostbite, LLC + "movistar", // movistar Telefónica S.A. + "msd", // msd MSD Registry Holdings, Inc. + "mtn", // mtn MTN Dubai Limited + "mtpc", // mtpc Mitsubishi Tanabe Pharma Corporation + "mtr", // mtr MTR Corporation Limited + "museum", // museum Museum Domain Management Association + "mutual", // mutual Northwestern Mutual MU TLD Registry, LLC +// "mutuelle", // mutuelle Fédération Nationale de la Mutualité Française (Retired) + "nab", // nab National Australia Bank Limited + "nadex", // nadex Nadex Domains, Inc + "nagoya", // nagoya GMO Registry, Inc. + "name", // name VeriSign Information Services, Inc. + "nationwide", // nationwide Nationwide Mutual Insurance Company + "natura", // natura NATURA COSMÉTICOS S.A. + "navy", // navy United TLD Holdco Ltd. + "nba", // nba NBA REGISTRY, LLC + "nec", // nec NEC Corporation + "net", // net VeriSign Global Registry Services + "netbank", // netbank COMMONWEALTH BANK OF AUSTRALIA + "netflix", // netflix Netflix, Inc. + "network", // network Trixy Manor, LLC + "neustar", // neustar NeuStar, Inc. + "new", // new Charleston Road Registry Inc. + "newholland", // newholland CNH Industrial N.V. + "news", // news United TLD Holdco Ltd. + "next", // next Next plc + "nextdirect", // nextdirect Next plc + "nexus", // nexus Charleston Road Registry Inc. + "nfl", // nfl NFL Reg Ops LLC + "ngo", // ngo Public Interest Registry + "nhk", // nhk Japan Broadcasting Corporation (NHK) + "nico", // nico DWANGO Co., Ltd. + "nike", // nike NIKE, Inc. + "nikon", // nikon NIKON CORPORATION + "ninja", // ninja United TLD Holdco Ltd. + "nissan", // nissan NISSAN MOTOR CO., LTD. + "nissay", // nissay Nippon Life Insurance Company + "nokia", // nokia Nokia Corporation + "northwesternmutual", // northwesternmutual Northwestern Mutual Registry, LLC + "norton", // norton Symantec Corporation + "now", // now Amazon Registry Services, Inc. + "nowruz", // nowruz Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "nowtv", // nowtv Starbucks (HK) Limited + "nra", // nra NRA Holdings Company, INC. + "nrw", // nrw Minds + Machines GmbH + "ntt", // ntt NIPPON TELEGRAPH AND TELEPHONE CORPORATION + "nyc", // nyc The City of New York by and through the New York City Department of Information Technology & Telecommunications + "obi", // obi OBI Group Holding SE & Co. KGaA + "observer", // observer Top Level Spectrum, Inc. + "off", // off Johnson Shareholdings, Inc. + "office", // office Microsoft Corporation + "okinawa", // okinawa BusinessRalliart inc. + "olayan", // olayan Crescent Holding GmbH + "olayangroup", // olayangroup Crescent Holding GmbH + "oldnavy", // oldnavy The Gap, Inc. + "ollo", // ollo Dish DBS Corporation + "omega", // omega The Swatch Group Ltd + "one", // one One.com A/S + "ong", // ong Public Interest Registry + "onl", // onl I-REGISTRY Ltd., Niederlassung Deutschland + "online", // online DotOnline Inc. + "onyourside", // onyourside Nationwide Mutual Insurance Company + "ooo", // ooo INFIBEAM INCORPORATION LIMITED + "open", // open American Express Travel Related Services Company, Inc. + "oracle", // oracle Oracle Corporation + "orange", // orange Orange Brand Services Limited + "org", // org Public Interest Registry (PIR) + "organic", // organic Afilias Limited + "orientexpress", // orientexpress Orient Express + "origins", // origins The Estée Lauder Companies Inc. + "osaka", // osaka Interlink Co., Ltd. + "otsuka", // otsuka Otsuka Holdings Co., Ltd. + "ott", // ott Dish DBS Corporation + "ovh", // ovh OVH SAS + "page", // page Charleston Road Registry Inc. + "pamperedchef", // pamperedchef The Pampered Chef, Ltd. + "panasonic", // panasonic Panasonic Corporation + "panerai", // panerai Richemont DNS Inc. + "paris", // paris City of Paris + "pars", // pars Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "partners", // partners Magic Glen, LLC + "parts", // parts Sea Goodbye, LLC + "party", // party Blue Sky Registry Limited + "passagens", // passagens Travel Reservations SRL + "pay", // pay Amazon Registry Services, Inc. + "pccw", // pccw PCCW Enterprises Limited + "pet", // pet Afilias plc + "pfizer", // pfizer Pfizer Inc. + "pharmacy", // pharmacy National Association of Boards of Pharmacy + "philips", // philips Koninklijke Philips N.V. + "phone", // phone Dish DBS Corporation + "photo", // photo Uniregistry, Corp. + "photography", // photography Sugar Glen, LLC + "photos", // photos Sea Corner, LLC + "physio", // physio PhysBiz Pty Ltd + "piaget", // piaget Richemont DNS Inc. + "pics", // pics Uniregistry, Corp. + "pictet", // pictet Pictet Europe S.A. + "pictures", // pictures Foggy Sky, LLC + "pid", // pid Top Level Spectrum, Inc. + "pin", // pin Amazon Registry Services, Inc. + "ping", // ping Ping Registry Provider, Inc. + "pink", // pink Afilias Limited + "pioneer", // pioneer Pioneer Corporation + "pizza", // pizza Foggy Moon, LLC + "place", // place Snow Galley, LLC + "play", // play Charleston Road Registry Inc. + "playstation", // playstation Sony Computer Entertainment Inc. + "plumbing", // plumbing Spring Tigers, LLC + "plus", // plus Sugar Mill, LLC + "pnc", // pnc PNC Domain Co., LLC + "pohl", // pohl Deutsche Vermögensberatung Aktiengesellschaft DVAG + "poker", // poker Afilias Domains No. 5 Limited + "politie", // politie Politie Nederland + "porn", // porn ICM Registry PN LLC + "post", // post Universal Postal Union + "pramerica", // pramerica Prudential Financial, Inc. + "praxi", // praxi Praxi S.p.A. + "press", // press DotPress Inc. + "prime", // prime Amazon Registry Services, Inc. + "pro", // pro Registry Services Corporation dba RegistryPro + "prod", // prod Charleston Road Registry Inc. + "productions", // productions Magic Birch, LLC + "prof", // prof Charleston Road Registry Inc. + "progressive", // progressive Progressive Casualty Insurance Company + "promo", // promo Afilias plc + "properties", // properties Big Pass, LLC + "property", // property Uniregistry, Corp. + "protection", // protection XYZ.COM LLC + "pru", // pru Prudential Financial, Inc. + "prudential", // prudential Prudential Financial, Inc. + "pub", // pub United TLD Holdco Ltd. + "pwc", // pwc PricewaterhouseCoopers LLP + "qpon", // qpon dotCOOL, Inc. + "quebec", // quebec PointQuébec Inc + "quest", // quest Quest ION Limited + "qvc", // qvc QVC, Inc. + "racing", // racing Premier Registry Limited + "radio", // radio European Broadcasting Union (EBU) + "raid", // raid Johnson Shareholdings, Inc. + "read", // read Amazon Registry Services, Inc. + "realestate", // realestate dotRealEstate LLC + "realtor", // realtor Real Estate Domains LLC + "realty", // realty Fegistry, LLC + "recipes", // recipes Grand Island, LLC + "red", // red Afilias Limited + "redstone", // redstone Redstone Haute Couture Co., Ltd. + "redumbrella", // redumbrella Travelers TLD, LLC + "rehab", // rehab United TLD Holdco Ltd. + "reise", // reise Foggy Way, LLC + "reisen", // reisen New Cypress, LLC + "reit", // reit National Association of Real Estate Investment Trusts, Inc. + "reliance", // reliance Reliance Industries Limited + "ren", // ren Beijing Qianxiang Wangjing Technology Development Co., Ltd. + "rent", // rent XYZ.COM LLC + "rentals", // rentals Big Hollow,LLC + "repair", // repair Lone Sunset, LLC + "report", // report Binky Glen, LLC + "republican", // republican United TLD Holdco Ltd. + "rest", // rest Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable + "restaurant", // restaurant Snow Avenue, LLC + "review", // review dot Review Limited + "reviews", // reviews United TLD Holdco, Ltd. + "rexroth", // rexroth Robert Bosch GMBH + "rich", // rich I-REGISTRY Ltd., Niederlassung Deutschland + "richardli", // richardli Pacific Century Asset Management (HK) Limited + "ricoh", // ricoh Ricoh Company, Ltd. + "rightathome", // rightathome Johnson Shareholdings, Inc. + "ril", // ril Reliance Industries Limited + "rio", // rio Empresa Municipal de Informática SA - IPLANRIO + "rip", // rip United TLD Holdco Ltd. + "rmit", // rmit Royal Melbourne Institute of Technology + "rocher", // rocher Ferrero Trading Lux S.A. + "rocks", // rocks United TLD Holdco, LTD. + "rodeo", // rodeo Top Level Domain Holdings Limited + "rogers", // rogers Rogers Communications Canada Inc. + "room", // room Amazon Registry Services, Inc. + "rsvp", // rsvp Charleston Road Registry Inc. + "ruhr", // ruhr regiodot GmbH & Co. KG + "run", // run Snow Park, LLC + "rwe", // rwe RWE AG + "ryukyu", // ryukyu BusinessRalliart inc. + "saarland", // saarland dotSaarland GmbH + "safe", // safe Amazon Registry Services, Inc. + "safety", // safety Safety Registry Services, LLC. + "sakura", // sakura SAKURA Internet Inc. + "sale", // sale United TLD Holdco, Ltd + "salon", // salon Outer Orchard, LLC + "samsclub", // samsclub Wal-Mart Stores, Inc. + "samsung", // samsung SAMSUNG SDS CO., LTD + "sandvik", // sandvik Sandvik AB + "sandvikcoromant", // sandvikcoromant Sandvik AB + "sanofi", // sanofi Sanofi + "sap", // sap SAP AG + "sapo", // sapo PT Comunicacoes S.A. + "sarl", // sarl Delta Orchard, LLC + "sas", // sas Research IP LLC + "save", // save Amazon Registry Services, Inc. + "saxo", // saxo Saxo Bank A/S + "sbi", // sbi STATE BANK OF INDIA + "sbs", // sbs SPECIAL BROADCASTING SERVICE CORPORATION + "sca", // sca SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ) + "scb", // scb The Siam Commercial Bank Public Company Limited ("SCB") + "schaeffler", // schaeffler Schaeffler Technologies AG & Co. KG + "schmidt", // schmidt SALM S.A.S. + "scholarships", // scholarships Scholarships.com, LLC + "school", // school Little Galley, LLC + "schule", // schule Outer Moon, LLC + "schwarz", // schwarz Schwarz Domains und Services GmbH & Co. KG + "science", // science dot Science Limited + "scjohnson", // scjohnson Johnson Shareholdings, Inc. + "scor", // scor SCOR SE + "scot", // scot Dot Scot Registry Limited + "seat", // seat SEAT, S.A. (Sociedad Unipersonal) + "secure", // secure Amazon Registry Services, Inc. + "security", // security XYZ.COM LLC + "seek", // seek Seek Limited + "select", // select iSelect Ltd + "sener", // sener Sener Ingeniería y Sistemas, S.A. + "services", // services Fox Castle, LLC + "ses", // ses SES + "seven", // seven Seven West Media Ltd + "sew", // sew SEW-EURODRIVE GmbH & Co KG + "sex", // sex ICM Registry SX LLC + "sexy", // sexy Uniregistry, Corp. + "sfr", // sfr Societe Francaise du Radiotelephone - SFR + "shangrila", // shangrila Shangri‐La International Hotel Management Limited + "sharp", // sharp Sharp Corporation + "shaw", // shaw Shaw Cablesystems G.P. + "shell", // shell Shell Information Technology International Inc + "shia", // shia Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "shiksha", // shiksha Afilias Limited + "shoes", // shoes Binky Galley, LLC + "shop", // shop GMO Registry, Inc. + "shopping", // shopping Over Keep, LLC + "shouji", // shouji QIHOO 360 TECHNOLOGY CO. LTD. + "show", // show Snow Beach, LLC + "showtime", // showtime CBS Domains Inc. + "shriram", // shriram Shriram Capital Ltd. + "silk", // silk Amazon Registry Services, Inc. + "sina", // sina Sina Corporation + "singles", // singles Fern Madison, LLC + "site", // site DotSite Inc. + "ski", // ski STARTING DOT LIMITED + "skin", // skin L'Oréal + "sky", // sky Sky International AG + "skype", // skype Microsoft Corporation + "sling", // sling Hughes Satellite Systems Corporation + "smart", // smart Smart Communications, Inc. (SMART) + "smile", // smile Amazon Registry Services, Inc. + "sncf", // sncf SNCF (Société Nationale des Chemins de fer Francais) + "soccer", // soccer Foggy Shadow, LLC + "social", // social United TLD Holdco Ltd. + "softbank", // softbank SoftBank Group Corp. + "software", // software United TLD Holdco, Ltd + "sohu", // sohu Sohu.com Limited + "solar", // solar Ruby Town, LLC + "solutions", // solutions Silver Cover, LLC + "song", // song Amazon Registry Services, Inc. + "sony", // sony Sony Corporation + "soy", // soy Charleston Road Registry Inc. + "space", // space DotSpace Inc. + "spiegel", // spiegel SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG + "spot", // spot Amazon Registry Services, Inc. + "spreadbetting", // spreadbetting DOTSPREADBETTING REGISTRY LTD + "srl", // srl InterNetX Corp. + "srt", // srt FCA US LLC. + "stada", // stada STADA Arzneimittel AG + "staples", // staples Staples, Inc. + "star", // star Star India Private Limited + "starhub", // starhub StarHub Limited + "statebank", // statebank STATE BANK OF INDIA + "statefarm", // statefarm State Farm Mutual Automobile Insurance Company + "statoil", // statoil Statoil ASA + "stc", // stc Saudi Telecom Company + "stcgroup", // stcgroup Saudi Telecom Company + "stockholm", // stockholm Stockholms kommun + "storage", // storage Self Storage Company LLC + "store", // store DotStore Inc. + "stream", // stream dot Stream Limited + "studio", // studio United TLD Holdco Ltd. + "study", // study OPEN UNIVERSITIES AUSTRALIA PTY LTD + "style", // style Binky Moon, LLC + "sucks", // sucks Vox Populi Registry Ltd. + "supplies", // supplies Atomic Fields, LLC + "supply", // supply Half Falls, LLC + "support", // support Grand Orchard, LLC + "surf", // surf Top Level Domain Holdings Limited + "surgery", // surgery Tin Avenue, LLC + "suzuki", // suzuki SUZUKI MOTOR CORPORATION + "swatch", // swatch The Swatch Group Ltd + "swiftcover", // swiftcover Swiftcover Insurance Services Limited + "swiss", // swiss Swiss Confederation + "sydney", // sydney State of New South Wales, Department of Premier and Cabinet + "symantec", // symantec Symantec Corporation + "systems", // systems Dash Cypress, LLC + "tab", // tab Tabcorp Holdings Limited + "taipei", // taipei Taipei City Government + "talk", // talk Amazon Registry Services, Inc. + "taobao", // taobao Alibaba Group Holding Limited + "target", // target Target Domain Holdings, LLC + "tatamotors", // tatamotors Tata Motors Ltd + "tatar", // tatar Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic" + "tattoo", // tattoo Uniregistry, Corp. + "tax", // tax Storm Orchard, LLC + "taxi", // taxi Pine Falls, LLC + "tci", // tci Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "tdk", // tdk TDK Corporation + "team", // team Atomic Lake, LLC + "tech", // tech Dot Tech LLC + "technology", // technology Auburn Falls, LLC + "tel", // tel Telnic Ltd. + "telecity", // telecity TelecityGroup International Limited + "telefonica", // telefonica Telefónica S.A. + "temasek", // temasek Temasek Holdings (Private) Limited + "tennis", // tennis Cotton Bloom, LLC + "teva", // teva Teva Pharmaceutical Industries Limited + "thd", // thd Homer TLC, Inc. + "theater", // theater Blue Tigers, LLC + "theatre", // theatre XYZ.COM LLC + "tiaa", // tiaa Teachers Insurance and Annuity Association of America + "tickets", // tickets Accent Media Limited + "tienda", // tienda Victor Manor, LLC + "tiffany", // tiffany Tiffany and Company + "tips", // tips Corn Willow, LLC + "tires", // tires Dog Edge, LLC + "tirol", // tirol punkt Tirol GmbH + "tjmaxx", // tjmaxx The TJX Companies, Inc. + "tjx", // tjx The TJX Companies, Inc. + "tkmaxx", // tkmaxx The TJX Companies, Inc. + "tmall", // tmall Alibaba Group Holding Limited + "today", // today Pearl Woods, LLC + "tokyo", // tokyo GMO Registry, Inc. + "tools", // tools Pioneer North, LLC + "top", // top Jiangsu Bangning Science & Technology Co.,Ltd. + "toray", // toray Toray Industries, Inc. + "toshiba", // toshiba TOSHIBA Corporation + "total", // total Total SA + "tours", // tours Sugar Station, LLC + "town", // town Koko Moon, LLC + "toyota", // toyota TOYOTA MOTOR CORPORATION + "toys", // toys Pioneer Orchard, LLC + "trade", // trade Elite Registry Limited + "trading", // trading DOTTRADING REGISTRY LTD + "training", // training Wild Willow, LLC + "travel", // travel Tralliance Registry Management Company, LLC. + "travelchannel", // travelchannel Lifestyle Domain Holdings, Inc. + "travelers", // travelers Travelers TLD, LLC + "travelersinsurance", // travelersinsurance Travelers TLD, LLC + "trust", // trust Artemis Internet Inc + "trv", // trv Travelers TLD, LLC + "tube", // tube Latin American Telecom LLC + "tui", // tui TUI AG + "tunes", // tunes Amazon Registry Services, Inc. + "tushu", // tushu Amazon Registry Services, Inc. + "tvs", // tvs T V SUNDRAM IYENGAR & SONS PRIVATE LIMITED + "ubank", // ubank National Australia Bank Limited + "ubs", // ubs UBS AG + "uconnect", // uconnect FCA US LLC. + "unicom", // unicom China United Network Communications Corporation Limited + "university", // university Little Station, LLC + "uno", // uno Dot Latin LLC + "uol", // uol UBN INTERNET LTDA. + "ups", // ups UPS Market Driver, Inc. + "vacations", // vacations Atomic Tigers, LLC + "vana", // vana Lifestyle Domain Holdings, Inc. + "vanguard", // vanguard The Vanguard Group, Inc. + "vegas", // vegas Dot Vegas, Inc. + "ventures", // ventures Binky Lake, LLC + "verisign", // verisign VeriSign, Inc. + "versicherung", // versicherung dotversicherung-registry GmbH + "vet", // vet United TLD Holdco, Ltd + "viajes", // viajes Black Madison, LLC + "video", // video United TLD Holdco, Ltd + "vig", // vig VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe + "viking", // viking Viking River Cruises (Bermuda) Ltd. + "villas", // villas New Sky, LLC + "vin", // vin Holly Shadow, LLC + "vip", // vip Minds + Machines Group Limited + "virgin", // virgin Virgin Enterprises Limited + "visa", // visa Visa Worldwide Pte. Limited + "vision", // vision Koko Station, LLC + "vista", // vista Vistaprint Limited + "vistaprint", // vistaprint Vistaprint Limited + "viva", // viva Saudi Telecom Company + "vivo", // vivo Telefonica Brasil S.A. + "vlaanderen", // vlaanderen DNS.be vzw + "vodka", // vodka Top Level Domain Holdings Limited + "volkswagen", // volkswagen Volkswagen Group of America Inc. + "volvo", // volvo Volvo Holding Sverige Aktiebolag + "vote", // vote Monolith Registry LLC + "voting", // voting Valuetainment Corp. + "voto", // voto Monolith Registry LLC + "voyage", // voyage Ruby House, LLC + "vuelos", // vuelos Travel Reservations SRL + "wales", // wales Nominet UK + "walmart", // walmart Wal-Mart Stores, Inc. + "walter", // walter Sandvik AB + "wang", // wang Zodiac Registry Limited + "wanggou", // wanggou Amazon Registry Services, Inc. + "warman", // warman Weir Group IP Limited + "watch", // watch Sand Shadow, LLC + "watches", // watches Richemont DNS Inc. + "weather", // weather The Weather Channel, LLC + "weatherchannel", // weatherchannel The Weather Channel, LLC + "webcam", // webcam dot Webcam Limited + "weber", // weber Saint-Gobain Weber SA + "website", // website DotWebsite Inc. + "wed", // wed Atgron, Inc. + "wedding", // wedding Top Level Domain Holdings Limited + "weibo", // weibo Sina Corporation + "weir", // weir Weir Group IP Limited + "whoswho", // whoswho Who's Who Registry + "wien", // wien punkt.wien GmbH + "wiki", // wiki Top Level Design, LLC + "williamhill", // williamhill William Hill Organization Limited + "win", // win First Registry Limited + "windows", // windows Microsoft Corporation + "wine", // wine June Station, LLC + "winners", // winners The TJX Companies, Inc. + "wme", // wme William Morris Endeavor Entertainment, LLC + "wolterskluwer", // wolterskluwer Wolters Kluwer N.V. + "woodside", // woodside Woodside Petroleum Limited + "work", // work Top Level Domain Holdings Limited + "works", // works Little Dynamite, LLC + "world", // world Bitter Fields, LLC + "wow", // wow Amazon Registry Services, Inc. + "wtc", // wtc World Trade Centers Association, Inc. + "wtf", // wtf Hidden Way, LLC + "xbox", // xbox Microsoft Corporation + "xerox", // xerox Xerox DNHC LLC + "xfinity", // xfinity Comcast IP Holdings I, LLC + "xihuan", // xihuan QIHOO 360 TECHNOLOGY CO. LTD. + "xin", // xin Elegant Leader Limited + "xn--11b4c3d", // कॉम VeriSign Sarl + "xn--1ck2e1b", // セール Amazon Registry Services, Inc. + "xn--1qqw23a", // 佛山 Guangzhou YU Wei Information Technology Co., Ltd. + "xn--30rr7y", // 慈善 Excellent First Limited + "xn--3bst00m", // 集团 Eagle Horizon Limited + "xn--3ds443g", // 在线 TLD REGISTRY LIMITED + "xn--3oq18vl8pn36a", // 大众汽车 Volkswagen (China) Investment Co., Ltd. + "xn--3pxu8k", // 点看 VeriSign Sarl + "xn--42c2d9a", // คอม VeriSign Sarl + "xn--45q11c", // 八卦 Zodiac Scorpio Limited + "xn--4gbrim", // موقع Suhub Electronic Establishment + "xn--55qw42g", // 公益 China Organizational Name Administration Center + "xn--55qx5d", // 公司 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center) + "xn--5su34j936bgsg", // 香格里拉 Shangri‐La International Hotel Management Limited + "xn--5tzm5g", // 网站 Global Website TLD Asia Limited + "xn--6frz82g", // 移动 Afilias Limited + "xn--6qq986b3xl", // 我爱你 Tycoon Treasure Limited + "xn--80adxhks", // москва Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) + "xn--80aqecdr1a", // католик Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) + "xn--80asehdb", // онлайн CORE Association + "xn--80aswg", // сайт CORE Association + "xn--8y0a063a", // 联通 China United Network Communications Corporation Limited + "xn--90ae", // бг Imena.BG Plc (NAMES.BG Plc) + "xn--9dbq2a", // קום VeriSign Sarl + "xn--9et52u", // 时尚 RISE VICTORY LIMITED + "xn--9krt00a", // 微博 Sina Corporation + "xn--b4w605ferd", // 淡马锡 Temasek Holdings (Private) Limited + "xn--bck1b9a5dre4c", // ファッション Amazon Registry Services, Inc. + "xn--c1avg", // орг Public Interest Registry + "xn--c2br7g", // नेट VeriSign Sarl + "xn--cck2b3b", // ストア Amazon Registry Services, Inc. + "xn--cg4bki", // 삼성 SAMSUNG SDS CO., LTD + "xn--czr694b", // 商标 HU YI GLOBAL INFORMATION RESOURCES(HOLDING) COMPANY.HONGKONG LIMITED + "xn--czrs0t", // 商店 Wild Island, LLC + "xn--czru2d", // 商城 Zodiac Aquarius Limited + "xn--d1acj3b", // дети The Foundation for Network Initiatives “The Smart Internet” + "xn--eckvdtc9d", // ポイント Amazon Registry Services, Inc. + "xn--efvy88h", // 新闻 Xinhua News Agency Guangdong Branch 新华通讯社广东分社 + "xn--estv75g", // 工行 Industrial and Commercial Bank of China Limited + "xn--fct429k", // 家電 Amazon Registry Services, Inc. + "xn--fhbei", // كوم VeriSign Sarl + "xn--fiq228c5hs", // 中文网 TLD REGISTRY LIMITED + "xn--fiq64b", // 中信 CITIC Group Corporation + "xn--fjq720a", // 娱乐 Will Bloom, LLC + "xn--flw351e", // 谷歌 Charleston Road Registry Inc. + "xn--fzys8d69uvgm", // 電訊盈科 PCCW Enterprises Limited + "xn--g2xx48c", // 购物 Minds + Machines Group Limited + "xn--gckr3f0f", // クラウド Amazon Registry Services, Inc. + "xn--gk3at1e", // 通販 Amazon Registry Services, Inc. + "xn--hxt814e", // 网店 Zodiac Libra Limited + "xn--i1b6b1a6a2e", // संगठन Public Interest Registry + "xn--imr513n", // 餐厅 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY. HONGKONG LIMITED + "xn--io0a7i", // 网络 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center) + "xn--j1aef", // ком VeriSign Sarl + "xn--jlq61u9w7b", // 诺基亚 Nokia Corporation + "xn--jvr189m", // 食品 Amazon Registry Services, Inc. + "xn--kcrx77d1x4a", // 飞利浦 Koninklijke Philips N.V. + "xn--kpu716f", // 手表 Richemont DNS Inc. + "xn--kput3i", // 手机 Beijing RITT-Net Technology Development Co., Ltd + "xn--mgba3a3ejt", // ارامكو Aramco Services Company + "xn--mgba7c0bbn0a", // العليان Crescent Holding GmbH + "xn--mgbab2bd", // بازار CORE Association + "xn--mgbb9fbpob", // موبايلي GreenTech Consultancy Company W.L.L. + "xn--mgbca7dzdo", // ابوظبي Abu Dhabi Systems and Information Centre + "xn--mgbi4ecexp", // كاثوليك Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) + "xn--mgbt3dhd", // همراه Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "xn--mk1bu44c", // 닷컴 VeriSign Sarl + "xn--mxtq1m", // 政府 Net-Chinese Co., Ltd. + "xn--ngbc5azd", // شبكة International Domain Registry Pty. Ltd. + "xn--ngbe9e0a", // بيتك Kuwait Finance House + "xn--nqv7f", // 机构 Public Interest Registry + "xn--nqv7fs00ema", // 组织机构 Public Interest Registry + "xn--nyqy26a", // 健康 Stable Tone Limited + "xn--p1acf", // рус Rusnames Limited + "xn--pbt977c", // 珠宝 Richemont DNS Inc. + "xn--pssy2u", // 大拿 VeriSign Sarl + "xn--q9jyb4c", // みんな Charleston Road Registry Inc. + "xn--qcka1pmc", // グーグル Charleston Road Registry Inc. + "xn--rhqv96g", // 世界 Stable Tone Limited + "xn--rovu88b", // 書籍 Amazon EU S.à r.l. + "xn--ses554g", // 网址 KNET Co., Ltd + "xn--t60b56a", // 닷넷 VeriSign Sarl + "xn--tckwe", // コム VeriSign Sarl + "xn--tiq49xqyj", // 天主教 Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) + "xn--unup4y", // 游戏 Spring Fields, LLC + "xn--vermgensberater-ctb", // VERMöGENSBERATER Deutsche Vermögensberatung Aktiengesellschaft DVAG + "xn--vermgensberatung-pwb", // VERMöGENSBERATUNG Deutsche Vermögensberatung Aktiengesellschaft DVAG + "xn--vhquv", // 企业 Dash McCook, LLC + "xn--vuq861b", // 信息 Beijing Tele-info Network Technology Co., Ltd. + "xn--w4r85el8fhu5dnra", // 嘉里大酒店 Kerry Trading Co. Limited + "xn--w4rs40l", // 嘉里 Kerry Trading Co. Limited + "xn--xhq521b", // 广东 Guangzhou YU Wei Information Technology Co., Ltd. + "xn--zfr164b", // 政务 China Organizational Name Administration Center + "xperia", // xperia Sony Mobile Communications AB + "xxx", // xxx ICM Registry LLC + "xyz", // xyz XYZ.COM LLC + "yachts", // yachts DERYachts, LLC + "yahoo", // yahoo Yahoo! Domain Services Inc. + "yamaxun", // yamaxun Amazon Registry Services, Inc. + "yandex", // yandex YANDEX, LLC + "yodobashi", // yodobashi YODOBASHI CAMERA CO.,LTD. + "yoga", // yoga Top Level Domain Holdings Limited + "yokohama", // yokohama GMO Registry, Inc. + "you", // you Amazon Registry Services, Inc. + "youtube", // youtube Charleston Road Registry Inc. + "yun", // yun QIHOO 360 TECHNOLOGY CO. LTD. + "zappos", // zappos Amazon Registry Services, Inc. + "zara", // zara Industria de Diseño Textil, S.A. (INDITEX, S.A.) + "zero", // zero Amazon Registry Services, Inc. + "zip", // zip Charleston Road Registry Inc. + "zippo", // zippo Zadco Company + "zone", // zone Outer Falls, LLC + "zuerich", // zuerich Kanton Zürich (Canton of Zurich) + }; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static final String[] COUNTRY_CODE_TLDS = new String[] { + "ac", // Ascension Island + "ad", // Andorra + "ae", // United Arab Emirates + "af", // Afghanistan + "ag", // Antigua and Barbuda + "ai", // Anguilla + "al", // Albania + "am", // Armenia +// "an", // Netherlands Antilles (retired) + "ao", // Angola + "aq", // Antarctica + "ar", // Argentina + "as", // American Samoa + "at", // Austria + "au", // Australia (includes Ashmore and Cartier Islands and Coral Sea Islands) + "aw", // Aruba + "ax", // Åland + "az", // Azerbaijan + "ba", // Bosnia and Herzegovina + "bb", // Barbados + "bd", // Bangladesh + "be", // Belgium + "bf", // Burkina Faso + "bg", // Bulgaria + "bh", // Bahrain + "bi", // Burundi + "bj", // Benin + "bm", // Bermuda + "bn", // Brunei Darussalam + "bo", // Bolivia + "br", // Brazil + "bs", // Bahamas + "bt", // Bhutan + "bv", // Bouvet Island + "bw", // Botswana + "by", // Belarus + "bz", // Belize + "ca", // Canada + "cc", // Cocos (Keeling) Islands + "cd", // Democratic Republic of the Congo (formerly Zaire) + "cf", // Central African Republic + "cg", // Republic of the Congo + "ch", // Switzerland + "ci", // Côte d'Ivoire + "ck", // Cook Islands + "cl", // Chile + "cm", // Cameroon + "cn", // China, mainland + "co", // Colombia + "cr", // Costa Rica + "cu", // Cuba + "cv", // Cape Verde + "cw", // Curaçao + "cx", // Christmas Island + "cy", // Cyprus + "cz", // Czech Republic + "de", // Germany + "dj", // Djibouti + "dk", // Denmark + "dm", // Dominica + "do", // Dominican Republic + "dz", // Algeria + "ec", // Ecuador + "ee", // Estonia + "eg", // Egypt + "er", // Eritrea + "es", // Spain + "et", // Ethiopia + "eu", // European Union + "fi", // Finland + "fj", // Fiji + "fk", // Falkland Islands + "fm", // Federated States of Micronesia + "fo", // Faroe Islands + "fr", // France + "ga", // Gabon + "gb", // Great Britain (United Kingdom) + "gd", // Grenada + "ge", // Georgia + "gf", // French Guiana + "gg", // Guernsey + "gh", // Ghana + "gi", // Gibraltar + "gl", // Greenland + "gm", // The Gambia + "gn", // Guinea + "gp", // Guadeloupe + "gq", // Equatorial Guinea + "gr", // Greece + "gs", // South Georgia and the South Sandwich Islands + "gt", // Guatemala + "gu", // Guam + "gw", // Guinea-Bissau + "gy", // Guyana + "hk", // Hong Kong + "hm", // Heard Island and McDonald Islands + "hn", // Honduras + "hr", // Croatia (Hrvatska) + "ht", // Haiti + "hu", // Hungary + "id", // Indonesia + "ie", // Ireland (Éire) + "il", // Israel + "im", // Isle of Man + "in", // India + "io", // British Indian Ocean Territory + "iq", // Iraq + "ir", // Iran + "is", // Iceland + "it", // Italy + "je", // Jersey + "jm", // Jamaica + "jo", // Jordan + "jp", // Japan + "ke", // Kenya + "kg", // Kyrgyzstan + "kh", // Cambodia (Khmer) + "ki", // Kiribati + "km", // Comoros + "kn", // Saint Kitts and Nevis + "kp", // North Korea + "kr", // South Korea + "kw", // Kuwait + "ky", // Cayman Islands + "kz", // Kazakhstan + "la", // Laos (currently being marketed as the official domain for Los Angeles) + "lb", // Lebanon + "lc", // Saint Lucia + "li", // Liechtenstein + "lk", // Sri Lanka + "lr", // Liberia + "ls", // Lesotho + "lt", // Lithuania + "lu", // Luxembourg + "lv", // Latvia + "ly", // Libya + "ma", // Morocco + "mc", // Monaco + "md", // Moldova + "me", // Montenegro + "mg", // Madagascar + "mh", // Marshall Islands + "mk", // Republic of Macedonia + "ml", // Mali + "mm", // Myanmar + "mn", // Mongolia + "mo", // Macau + "mp", // Northern Mariana Islands + "mq", // Martinique + "mr", // Mauritania + "ms", // Montserrat + "mt", // Malta + "mu", // Mauritius + "mv", // Maldives + "mw", // Malawi + "mx", // Mexico + "my", // Malaysia + "mz", // Mozambique + "na", // Namibia + "nc", // New Caledonia + "ne", // Niger + "nf", // Norfolk Island + "ng", // Nigeria + "ni", // Nicaragua + "nl", // Netherlands + "no", // Norway + "np", // Nepal + "nr", // Nauru + "nu", // Niue + "nz", // New Zealand + "om", // Oman + "pa", // Panama + "pe", // Peru + "pf", // French Polynesia With Clipperton Island + "pg", // Papua New Guinea + "ph", // Philippines + "pk", // Pakistan + "pl", // Poland + "pm", // Saint-Pierre and Miquelon + "pn", // Pitcairn Islands + "pr", // Puerto Rico + "ps", // Palestinian territories (PA-controlled West Bank and Gaza Strip) + "pt", // Portugal + "pw", // Palau + "py", // Paraguay + "qa", // Qatar + "re", // Réunion + "ro", // Romania + "rs", // Serbia + "ru", // Russia + "rw", // Rwanda + "sa", // Saudi Arabia + "sb", // Solomon Islands + "sc", // Seychelles + "sd", // Sudan + "se", // Sweden + "sg", // Singapore + "sh", // Saint Helena + "si", // Slovenia + "sj", // Svalbard and Jan Mayen Islands Not in use (Norwegian dependencies; see .no) + "sk", // Slovakia + "sl", // Sierra Leone + "sm", // San Marino + "sn", // Senegal + "so", // Somalia + "sr", // Suriname + "st", // São Tomé and Príncipe + "su", // Soviet Union (deprecated) + "sv", // El Salvador + "sx", // Sint Maarten + "sy", // Syria + "sz", // Swaziland + "tc", // Turks and Caicos Islands + "td", // Chad + "tf", // French Southern and Antarctic Lands + "tg", // Togo + "th", // Thailand + "tj", // Tajikistan + "tk", // Tokelau + "tl", // East Timor (deprecated old code) + "tm", // Turkmenistan + "tn", // Tunisia + "to", // Tonga +// "tp", // East Timor (Retired) + "tr", // Turkey + "tt", // Trinidad and Tobago + "tv", // Tuvalu + "tw", // Taiwan, Republic of China + "tz", // Tanzania + "ua", // Ukraine + "ug", // Uganda + "uk", // United Kingdom + "us", // United States of America + "uy", // Uruguay + "uz", // Uzbekistan + "va", // Vatican City State + "vc", // Saint Vincent and the Grenadines + "ve", // Venezuela + "vg", // British Virgin Islands + "vi", // U.S. Virgin Islands + "vn", // Vietnam + "vu", // Vanuatu + "wf", // Wallis and Futuna + "ws", // Samoa (formerly Western Samoa) + "xn--3e0b707e", // 한국 KISA (Korea Internet & Security Agency) + "xn--45brj9c", // ভারত National Internet Exchange of India + "xn--54b7fta0cc", // বাংলা Posts and Telecommunications Division + "xn--80ao21a", // қаз Association of IT Companies of Kazakhstan + "xn--90a3ac", // срб Serbian National Internet Domain Registry (RNIDS) + "xn--90ais", // ??? Reliable Software Inc. + "xn--clchc0ea0b2g2a9gcd", // சிங்கப்பூர் Singapore Network Information Centre (SGNIC) Pte Ltd + "xn--d1alf", // мкд Macedonian Academic Research Network Skopje + "xn--e1a4c", // ею EURid vzw/asbl + "xn--fiqs8s", // 中国 China Internet Network Information Center + "xn--fiqz9s", // 中國 China Internet Network Information Center + "xn--fpcrj9c3d", // భారత్ National Internet Exchange of India + "xn--fzc2c9e2c", // ලංකා LK Domain Registry + "xn--gecrj9c", // ભારત National Internet Exchange of India + "xn--h2brj9c", // भारत National Internet Exchange of India + "xn--j1amh", // укр Ukrainian Network Information Centre (UANIC), Inc. + "xn--j6w193g", // 香港 Hong Kong Internet Registration Corporation Ltd. + "xn--kprw13d", // 台湾 Taiwan Network Information Center (TWNIC) + "xn--kpry57d", // 台灣 Taiwan Network Information Center (TWNIC) + "xn--l1acc", // мон Datacom Co.,Ltd + "xn--lgbbat1ad8j", // الجزائر CERIST + "xn--mgb9awbf", // عمان Telecommunications Regulatory Authority (TRA) + "xn--mgba3a4f16a", // ایران Institute for Research in Fundamental Sciences (IPM) + "xn--mgbaam7a8h", // امارات Telecommunications Regulatory Authority (TRA) + "xn--mgbayh7gpa", // الاردن National Information Technology Center (NITC) + "xn--mgbbh1a71e", // بھارت National Internet Exchange of India + "xn--mgbc0a9azcg", // المغرب Agence Nationale de Réglementation des Télécommunications (ANRT) + "xn--mgberp4a5d4ar", // السعودية Communications and Information Technology Commission + "xn--mgbpl2fh", // ????? Sudan Internet Society + "xn--mgbtx2b", // عراق Communications and Media Commission (CMC) + "xn--mgbx4cd0ab", // مليسيا MYNIC Berhad + "xn--mix891f", // 澳門 Bureau of Telecommunications Regulation (DSRT) + "xn--node", // გე Information Technologies Development Center (ITDC) + "xn--o3cw4h", // ไทย Thai Network Information Center Foundation + "xn--ogbpf8fl", // سورية National Agency for Network Services (NANS) + "xn--p1ai", // рф Coordination Center for TLD RU + "xn--pgbs0dh", // تونس Agence Tunisienne d'Internet + "xn--qxam", // ελ ICS-FORTH GR + "xn--s9brj9c", // ਭਾਰਤ National Internet Exchange of India + "xn--wgbh1c", // مصر National Telecommunication Regulatory Authority - NTRA + "xn--wgbl6a", // قطر Communications Regulatory Authority + "xn--xkc2al3hye2a", // இலங்கை LK Domain Registry + "xn--xkc2dl3a5ee0h", // இந்தியா National Internet Exchange of India + "xn--y9a3aq", // ??? Internet Society + "xn--yfro4i67o", // 新加坡 Singapore Network Information Centre (SGNIC) Pte Ltd + "xn--ygbi2ammx", // فلسطين Ministry of Telecom & Information Technology (MTIT) + "ye", // Yemen + "yt", // Mayotte + "za", // South Africa + "zm", // Zambia + "zw", // Zimbabwe + }; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static final String[] LOCAL_TLDS = new String[] { + "localdomain", // Also widely used as localhost.localdomain + "localhost", // RFC2606 defined + }; + + // Additional arrays to supplement or override the built in ones. + // The PLUS arrays are valid keys, the MINUS arrays are invalid keys + + /* + * This field is used to detect whether the getInstance has been called. + * After this, the method updateTLDOverride is not allowed to be called. + * This field does not need to be volatile since it is only accessed from + * synchronized methods. + */ + private static boolean inUse = false; + + /* + * These arrays are mutable, but they don't need to be volatile. + * They can only be updated by the updateTLDOverride method, and any readers must get an instance + * using the getInstance methods which are all (now) synchronised. + */ + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static volatile String[] countryCodeTLDsPlus = EMPTY_STRING_ARRAY; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static volatile String[] genericTLDsPlus = EMPTY_STRING_ARRAY; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static volatile String[] countryCodeTLDsMinus = EMPTY_STRING_ARRAY; + + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + private static volatile String[] genericTLDsMinus = EMPTY_STRING_ARRAY; + + /** + * enum used by {@link DomainValidator#updateTLDOverride(DomainValidator.ArrayType, String[])} + * to determine which override array to update / fetch + * @since 1.5.0 + * @since 1.5.1 made public and added read-only array references + */ + public enum ArrayType { + /** Update (or get a copy of) the GENERIC_TLDS_PLUS table containing additonal generic TLDs */ + GENERIC_PLUS, + /** Update (or get a copy of) the GENERIC_TLDS_MINUS table containing deleted generic TLDs */ + GENERIC_MINUS, + /** Update (or get a copy of) the COUNTRY_CODE_TLDS_PLUS table containing additonal country code TLDs */ + COUNTRY_CODE_PLUS, + /** Update (or get a copy of) the COUNTRY_CODE_TLDS_MINUS table containing deleted country code TLDs */ + COUNTRY_CODE_MINUS, + /** Get a copy of the generic TLDS table */ + GENERIC_RO, + /** Get a copy of the country code table */ + COUNTRY_CODE_RO, + /** Get a copy of the infrastructure table */ + INFRASTRUCTURE_RO, + /** Get a copy of the local table */ + LOCAL_RO + ; + }; + + // For use by unit test code only + static synchronized void clearTLDOverrides() { + inUse = false; + countryCodeTLDsPlus = EMPTY_STRING_ARRAY; + countryCodeTLDsMinus = EMPTY_STRING_ARRAY; + genericTLDsPlus = EMPTY_STRING_ARRAY; + genericTLDsMinus = EMPTY_STRING_ARRAY; + } + /** + * Update one of the TLD override arrays. + * This must only be done at program startup, before any instances are accessed using getInstance. + *

      + * For example: + *

      + * {@code DomainValidator.updateTLDOverride(ArrayType.GENERIC_PLUS, new String[]{"apache"})} + *

      + * To clear an override array, provide an empty array. + * + * @param table the table to update, see {@link DomainValidator.ArrayType} + * Must be one of the following + *

        + *
      • COUNTRY_CODE_MINUS
      • + *
      • COUNTRY_CODE_PLUS
      • + *
      • GENERIC_MINUS
      • + *
      • GENERIC_PLUS
      • + *
      + * @param tlds the array of TLDs, must not be null + * @throws IllegalStateException if the method is called after getInstance + * @throws IllegalArgumentException if one of the read-only tables is requested + * @since 1.5.0 + */ + public static synchronized void updateTLDOverride(DomainValidator.ArrayType table, String [] tlds) { + if (inUse) { + throw new IllegalStateException("Can only invoke this method before calling getInstance"); + } + String [] copy = new String[tlds.length]; + // Comparisons are always done with lower-case entries + for (int i = 0; i < tlds.length; i++) { + copy[i] = tlds[i].toLowerCase(Locale.ENGLISH); + } + Arrays.sort(copy); + switch(table) { + case COUNTRY_CODE_MINUS: + countryCodeTLDsMinus = copy; + break; + case COUNTRY_CODE_PLUS: + countryCodeTLDsPlus = copy; + break; + case GENERIC_MINUS: + genericTLDsMinus = copy; + break; + case GENERIC_PLUS: + genericTLDsPlus = copy; + break; + case COUNTRY_CODE_RO: + case GENERIC_RO: + case INFRASTRUCTURE_RO: + case LOCAL_RO: + throw new IllegalArgumentException("Cannot update the table: " + table); + default: + throw new IllegalArgumentException("Unexpected enum value: " + table); + } + } + + /** + * Get a copy of the internal array. + * @param table the array type (any of the enum values) + * @return a copy of the array + * @throws IllegalArgumentException if the table type is unexpected (should not happen) + * @since 1.5.1 + */ + public static String [] getTLDEntries(DomainValidator.ArrayType table) { + final String array[]; + switch(table) { + case COUNTRY_CODE_MINUS: + array = countryCodeTLDsMinus; + break; + case COUNTRY_CODE_PLUS: + array = countryCodeTLDsPlus; + break; + case GENERIC_MINUS: + array = genericTLDsMinus; + break; + case GENERIC_PLUS: + array = genericTLDsPlus; + break; + case GENERIC_RO: + array = GENERIC_TLDS; + break; + case COUNTRY_CODE_RO: + array = COUNTRY_CODE_TLDS; + break; + case INFRASTRUCTURE_RO: + array = INFRASTRUCTURE_TLDS; + break; + case LOCAL_RO: + array = LOCAL_TLDS; + break; + default: + throw new IllegalArgumentException("Unexpected enum value: " + table); + } + return Arrays.copyOf(array, array.length); // clone the array + } + + /** + * Converts potentially Unicode input to punycode. + * If conversion fails, returns the original input. + * + * @param input the string to convert, not null + * @return converted input, or original input if conversion fails + */ + // Needed by UrlValidator + static String unicodeToASCII(String input) { + if (isOnlyASCII(input)) { // skip possibly expensive processing + return input; + } + try { + final String ascii = IDN.toASCII(input); + if (DomainValidator.IDNBUGHOLDER.IDN_TOASCII_PRESERVES_TRAILING_DOTS) { + return ascii; + } + final int length = input.length(); + if (length == 0) {// check there is a last character + return input; + } + // RFC3490 3.1. 1) + // Whenever dots are used as label separators, the following + // characters MUST be recognized as dots: U+002E (full stop), U+3002 + // (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61 + // (halfwidth ideographic full stop). + char lastChar = input.charAt(length-1);// fetch original last char + switch(lastChar) { + case '\u002E': // "." full stop + case '\u3002': // ideographic full stop + case '\uFF0E': // fullwidth full stop + case '\uFF61': // halfwidth ideographic full stop + return ascii + "."; // restore the missing stop + default: + return ascii; + } + } catch (IllegalArgumentException e) { // input is not valid + return input; + } + } + + private static class IDNBUGHOLDER { + private static boolean keepsTrailingDot() { + final String input = "a."; // must be a valid name + return input.equals(IDN.toASCII(input)); + } + private static final boolean IDN_TOASCII_PRESERVES_TRAILING_DOTS = keepsTrailingDot(); + } + + /* + * Check if input contains only ASCII + * Treats null as all ASCII + */ + private static boolean isOnlyASCII(String input) { + if (input == null) { + return true; + } + for(int i=0; i < input.length(); i++) { + if (input.charAt(i) > 0x7F) { // CHECKSTYLE IGNORE MagicNumber + return false; + } + } + return true; + } + + /** + * Check if a sorted array contains the specified key + * + * @param sortedArray the array to search + * @param key the key to find + * @return {@code true} if the array contains the key + */ + private static boolean arrayContains(String[] sortedArray, String key) { + return Arrays.binarySearch(sortedArray, key) >= 0; + } +} diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java new file mode 100644 index 0000000000..59ac7ceb2b --- /dev/null +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/InetAddressValidator.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +package jenkins.org.apache.commons.validator.routines; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + *

      InetAddress validation and conversion routines (java.net.InetAddress).

      + * + *

      This class provides methods to validate a candidate IP address. + * + *

      + * This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} method. + *

      + * + * @version $Revision: 1783032 $ + * @since Validator 1.4 + */ +//[PATCH] +@Restricted(NoExternalUse.class) +// end of [PATCH] +public class InetAddressValidator implements Serializable { + + private static final int IPV4_MAX_OCTET_VALUE = 255; + + private static final int MAX_UNSIGNED_SHORT = 0xffff; + + private static final int BASE_16 = 16; + + private static final long serialVersionUID = -919201640201914789L; + + private static final String IPV4_REGEX = + "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"; + + // Max number of hex groups (separated by :) in an IPV6 address + private static final int IPV6_MAX_HEX_GROUPS = 8; + + // Max hex digits in each IPv6 group + private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4; + + /** + * Singleton instance of this class. + */ + private static final InetAddressValidator VALIDATOR = new InetAddressValidator(); + + /** IPv4 RegexValidator */ + private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX); + + /** + * Returns the singleton instance of this validator. + * @return the singleton instance of this validator + */ + public static InetAddressValidator getInstance() { + return VALIDATOR; + } + + /** + * Checks if the specified string is a valid IP address. + * @param inetAddress the string to validate + * @return true if the string validates as an IP address + */ + public boolean isValid(String inetAddress) { + return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress); + } + + /** + * Validates an IPv4 address. Returns true if valid. + * @param inet4Address the IPv4 address to validate + * @return true if the argument contains a valid IPv4 address + */ + public boolean isValidInet4Address(String inet4Address) { + // verify that address conforms to generic IPv4 format + String[] groups = ipv4Validator.match(inet4Address); + + if (groups == null) { + return false; + } + + // verify that address subgroups are legal + for (String ipSegment : groups) { + if (ipSegment == null || ipSegment.length() == 0) { + return false; + } + + int iIpSegment = 0; + + try { + iIpSegment = Integer.parseInt(ipSegment); + } catch(NumberFormatException e) { + return false; + } + + if (iIpSegment > IPV4_MAX_OCTET_VALUE) { + return false; + } + + if (ipSegment.length() > 1 && ipSegment.startsWith("0")) { + return false; + } + + } + + return true; + } + + /** + * Validates an IPv6 address. Returns true if valid. + * @param inet6Address the IPv6 address to validate + * @return true if the argument contains a valid IPv6 address + * + * @since 1.4.1 + */ + public boolean isValidInet6Address(String inet6Address) { + boolean containsCompressedZeroes = inet6Address.contains("::"); + if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) { + return false; + } + if ((inet6Address.startsWith(":") && !inet6Address.startsWith("::")) + || (inet6Address.endsWith(":") && !inet6Address.endsWith("::"))) { + return false; + } + String[] octets = inet6Address.split(":"); + if (containsCompressedZeroes) { + List octetList = new ArrayList(Arrays.asList(octets)); + if (inet6Address.endsWith("::")) { + // String.split() drops ending empty segments + octetList.add(""); + } else if (inet6Address.startsWith("::") && !octetList.isEmpty()) { + octetList.remove(0); + } + octets = octetList.toArray(new String[octetList.size()]); + } + if (octets.length > IPV6_MAX_HEX_GROUPS) { + return false; + } + int validOctets = 0; + int emptyOctets = 0; // consecutive empty chunks + for (int index = 0; index < octets.length; index++) { + String octet = octets[index]; + if (octet.length() == 0) { + emptyOctets++; + if (emptyOctets > 1) { + return false; + } + } else { + emptyOctets = 0; + // Is last chunk an IPv4 address? + if (index == octets.length - 1 && octet.contains(".")) { + if (!isValidInet4Address(octet)) { + return false; + } + validOctets += 2; + continue; + } + if (octet.length() > IPV6_MAX_HEX_DIGITS_PER_GROUP) { + return false; + } + int octetInt = 0; + try { + octetInt = Integer.parseInt(octet, BASE_16); + } catch (NumberFormatException e) { + return false; + } + if (octetInt < 0 || octetInt > MAX_UNSIGNED_SHORT) { + return false; + } + } + validOctets++; + } + if (validOctets > IPV6_MAX_HEX_GROUPS || (validOctets < IPV6_MAX_HEX_GROUPS && !containsCompressedZeroes)) { + return false; + } + return true; + } +} diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java new file mode 100644 index 0000000000..eebc3747d9 --- /dev/null +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +package jenkins.org.apache.commons.validator.routines; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Regular Expression validation (using JDK 1.4+ regex support). + *

      + * Construct the validator either for a single regular expression or a set (array) of + * regular expressions. By default validation is case sensitive but constructors + * are provided to allow case in-sensitive validation. For example to create + * a validator which does case in-sensitive validation for a set of regular + * expressions: + *

      + *
      + * 
      + * String[] regexs = new String[] {...};
      + * RegexValidator validator = new RegexValidator(regexs, false);
      + * 
      + * 
      + * + *
        + *
      • Validate true or false:
      • + *
      • + *
          + *
        • boolean valid = validator.isValidRootUrl(value);
        • + *
        + *
      • + *
      • Validate returning an aggregated String of the matched groups:
      • + *
      • + *
          + *
        • String result = validator.validate(value);
        • + *
        + *
      • + *
      • Validate returning the matched groups:
      • + *
      • + *
          + *
        • String[] result = validator.match(value);
        • + *
        + *
      • + *
      + * + * Note that patterns are matched against the entire input. + * + *

      + * Cached instances pre-compile and re-use {@link Pattern}(s) - which according + * to the {@link Pattern} API are safe to use in a multi-threaded environment. + *

      + * + * @version $Revision: 1739356 $ + * @since Validator 1.4 + */ +//[PATCH] +@Restricted(NoExternalUse.class) +// end of [PATCH] +public class RegexValidator implements Serializable { + + private static final long serialVersionUID = -8832409930574867162L; + + private final Pattern[] patterns; + + /** + * Construct a case sensitive validator for a single + * regular expression. + * + * @param regex The regular expression this validator will + * validate against + */ + public RegexValidator(String regex) { + this(regex, true); + } + + /** + * Construct a validator for a single regular expression + * with the specified case sensitivity. + * + * @param regex The regular expression this validator will + * validate against + * @param caseSensitive when true matching is case + * sensitive, otherwise matching is case in-sensitive + */ + public RegexValidator(String regex, boolean caseSensitive) { + this(new String[] {regex}, caseSensitive); + } + + /** + * Construct a case sensitive validator that matches any one + * of the set of regular expressions. + * + * @param regexs The set of regular expressions this validator will + * validate against + */ + public RegexValidator(String[] regexs) { + this(regexs, true); + } + + /** + * Construct a validator that matches any one of the set of regular + * expressions with the specified case sensitivity. + * + * @param regexs The set of regular expressions this validator will + * validate against + * @param caseSensitive when true matching is case + * sensitive, otherwise matching is case in-sensitive + */ + public RegexValidator(String[] regexs, boolean caseSensitive) { + if (regexs == null || regexs.length == 0) { + throw new IllegalArgumentException("Regular expressions are missing"); + } + patterns = new Pattern[regexs.length]; + int flags = (caseSensitive ? 0: Pattern.CASE_INSENSITIVE); + for (int i = 0; i < regexs.length; i++) { + if (regexs[i] == null || regexs[i].length() == 0) { + throw new IllegalArgumentException("Regular expression[" + i + "] is missing"); + } + patterns[i] = Pattern.compile(regexs[i], flags); + } + } + + /** + * Validate a value against the set of regular expressions. + * + * @param value The value to validate. + * @return true if the value is valid + * otherwise false. + */ + public boolean isValid(String value) { + if (value == null) { + return false; + } + for (int i = 0; i < patterns.length; i++) { + if (patterns[i].matcher(value).matches()) { + return true; + } + } + return false; + } + + /** + * Validate a value against the set of regular expressions + * returning the array of matched groups. + * + * @param value The value to validate. + * @return String array of the groups matched if + * valid or null if invalid + */ + public String[] match(String value) { + if (value == null) { + return null; + } + for (int i = 0; i < patterns.length; i++) { + Matcher matcher = patterns[i].matcher(value); + if (matcher.matches()) { + int count = matcher.groupCount(); + String[] groups = new String[count]; + for (int j = 0; j < count; j++) { + groups[j] = matcher.group(j+1); + } + return groups; + } + } + return null; + } + + + /** + * Validate a value against the set of regular expressions + * returning a String value of the aggregated groups. + * + * @param value The value to validate. + * @return Aggregated String value comprised of the + * groups matched if valid or null if invalid + */ + public String validate(String value) { + if (value == null) { + return null; + } + for (int i = 0; i < patterns.length; i++) { + Matcher matcher = patterns[i].matcher(value); + if (matcher.matches()) { + int count = matcher.groupCount(); + if (count == 1) { + return matcher.group(1); + } + StringBuilder buffer = new StringBuilder(); + for (int j = 0; j < count; j++) { + String component = matcher.group(j+1); + if (component != null) { + buffer.append(component); + } + } + return buffer.toString(); + } + } + return null; + } + + /** + * Provide a String representation of this validator. + * @return A String representation of this validator + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append("RegexValidator{"); + for (int i = 0; i < patterns.length; i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(patterns[i].pattern()); + } + buffer.append("}"); + return buffer.toString(); + } + +} diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java new file mode 100644 index 0000000000..f309b177e6 --- /dev/null +++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java @@ -0,0 +1,551 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */ +package jenkins.org.apache.commons.validator.routines; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.io.Serializable; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

      URL Validation routines.

      + * Behavior of validation is modified by passing in options: + *
        + *
      • ALLOW_2_SLASHES - [FALSE] Allows double '/' characters in the path + * component.
      • + *
      • NO_FRAGMENT- [FALSE] By default fragments are allowed, if this option is + * included then fragments are flagged as illegal.
      • + *
      • ALLOW_ALL_SCHEMES - [FALSE] By default only http, https, and ftp are + * considered valid schemes. Enabling this option will let any scheme pass validation.
      • + *
      + * + *

      Originally based in on php script by Debbie Dyer, validation.php v1.2b, Date: 03/07/02, + * http://javascript.internet.com. However, this validation now bears little resemblance + * to the php original.

      + *
      + *   Example of usage:
      + *   Construct a UrlValidator with valid schemes of "http", and "https".
      + *
      + *    String[] schemes = {"http","https"}.
      + *    UrlValidator urlValidator = new UrlValidator(schemes);
      + *    if (urlValidator.isValidRootUrl("ftp://foo.bar.com/")) {
      + *       System.out.println("url is valid");
      + *    } else {
      + *       System.out.println("url is invalid");
      + *    }
      + *
      + *    prints "url is invalid"
      + *   If instead the default constructor is used.
      + *
      + *    UrlValidator urlValidator = new UrlValidator();
      + *    if (urlValidator.isValidRootUrl("ftp://foo.bar.com/")) {
      + *       System.out.println("url is valid");
      + *    } else {
      + *       System.out.println("url is invalid");
      + *    }
      + *
      + *   prints out "url is valid"
      + *  
      + * + * @see + * + * Uniform Resource Identifiers (URI): Generic Syntax + * + * + * @version $Revision: 1783203 $ + * @since Validator 1.4 + */ +//[PATCH] +@Restricted(NoExternalUse.class) +// end of [PATCH] +public class UrlValidator implements Serializable { + + private static final long serialVersionUID = 7557161713937335013L; + + private static final int MAX_UNSIGNED_16_BIT_INT = 0xFFFF; // port max + + /** + * Allows all validly formatted schemes to pass validation instead of + * supplying a set of valid schemes. + */ + public static final long ALLOW_ALL_SCHEMES = 1 << 0; + + /** + * Allow two slashes in the path component of the URL. + */ + public static final long ALLOW_2_SLASHES = 1 << 1; + + /** + * Enabling this options disallows any URL fragments. + */ + public static final long NO_FRAGMENTS = 1 << 2; + + /** + * Allow local URLs, such as http://localhost/ or http://machine/ . + * This enables a broad-brush check, for complex local machine name + * validation requirements you should create your validator with + * a {@link RegexValidator} instead ({@link #UrlValidator(RegexValidator, long)}) + */ + public static final long ALLOW_LOCAL_URLS = 1 << 3; // CHECKSTYLE IGNORE MagicNumber + + /** + * This expression derived/taken from the BNF for URI (RFC2396). + */ + private static final String URL_REGEX = + "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"; + // 12 3 4 5 6 7 8 9 + private static final Pattern URL_PATTERN = Pattern.compile(URL_REGEX); + + /** + * Schema/Protocol (ie. http:, ftp:, file:, etc). + */ + private static final int PARSE_URL_SCHEME = 2; + + /** + * Includes hostname/ip and port number. + */ + private static final int PARSE_URL_AUTHORITY = 4; + + private static final int PARSE_URL_PATH = 5; + + private static final int PARSE_URL_QUERY = 7; + + private static final int PARSE_URL_FRAGMENT = 9; + + /** + * Protocol scheme (e.g. http, ftp, https). + */ + private static final String SCHEME_REGEX = "^\\p{Alpha}[\\p{Alnum}\\+\\-\\.]*"; + private static final Pattern SCHEME_PATTERN = Pattern.compile(SCHEME_REGEX); + + // Drop numeric, and "+-." for now + // TODO does not allow for optional userinfo. + // Validation of character set is done by isValidAuthority + private static final String AUTHORITY_CHARS_REGEX = "\\p{Alnum}\\-\\."; // allows for IPV4 but not IPV6 + private static final String IPV6_REGEX = "[0-9a-fA-F:]+"; // do this as separate match because : could cause ambiguity with port prefix + + // userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + // We assume that password has the same valid chars as user info + private static final String USERINFO_CHARS_REGEX = "[a-zA-Z0-9%-._~!$&'()*+,;=]"; + // since neither ':' nor '@' are allowed chars, we don't need to use non-greedy matching + private static final String USERINFO_FIELD_REGEX = + USERINFO_CHARS_REGEX + "+" + // At least one character for the name + "(?::" + USERINFO_CHARS_REGEX + "*)?@"; // colon and password may be absent + private static final String AUTHORITY_REGEX = + "(?:\\[("+IPV6_REGEX+")\\]|(?:(?:"+USERINFO_FIELD_REGEX+")?([" + AUTHORITY_CHARS_REGEX + "]*)))(?::(\\d*))?(.*)?"; + // 1 e.g. user:pass@ 2 3 4 + private static final Pattern AUTHORITY_PATTERN = Pattern.compile(AUTHORITY_REGEX); + + private static final int PARSE_AUTHORITY_IPV6 = 1; + + private static final int PARSE_AUTHORITY_HOST_IP = 2; // excludes userinfo, if present + + private static final int PARSE_AUTHORITY_PORT = 3; // excludes leading colon + + /** + * Should always be empty. The code currently allows spaces. + */ + private static final int PARSE_AUTHORITY_EXTRA = 4; + + private static final String PATH_REGEX = "^(/[-\\w:@&?=+,.!/~*'%$_;\\(\\)]*)?$"; + private static final Pattern PATH_PATTERN = Pattern.compile(PATH_REGEX); + + private static final String QUERY_REGEX = "^(\\S*)$"; + private static final Pattern QUERY_PATTERN = Pattern.compile(QUERY_REGEX); + + /** + * Holds the set of current validation options. + */ + private final long options; + + /** + * The set of schemes that are allowed to be in a URL. + */ + private final Set allowedSchemes; // Must be lower-case + + /** + * Regular expressions used to manually validate authorities if IANA + * domain name validation isn't desired. + */ + private final RegexValidator authorityValidator; + + /** + * If no schemes are provided, default to this set. + */ + private static final String[] DEFAULT_SCHEMES = {"http", "https", "ftp"}; // Must be lower-case + + /** + * Singleton instance of this class with default schemes and options. + */ + private static final UrlValidator DEFAULT_URL_VALIDATOR = new UrlValidator(); + + /** + * Returns the singleton instance of this class with default schemes and options. + * @return singleton instance with default schemes and options + */ + public static UrlValidator getInstance() { + return DEFAULT_URL_VALIDATOR; + } + + /** + * Create a UrlValidator with default properties. + */ + public UrlValidator() { + this(null); + } + + /** + * Behavior of validation is modified by passing in several strings options: + * @param schemes Pass in one or more url schemes to consider valid, passing in + * a null will default to "http,https,ftp" being valid. + * If a non-null schemes is specified then all valid schemes must + * be specified. Setting the ALLOW_ALL_SCHEMES option will + * ignore the contents of schemes. + */ + public UrlValidator(String[] schemes) { + this(schemes, 0L); + } + + /** + * Initialize a UrlValidator with the given validation options. + * @param options The options should be set using the public constants declared in + * this class. To set multiple options you simply add them together. For example, + * ALLOW_2_SLASHES + NO_FRAGMENTS enables both of those options. + */ + public UrlValidator(long options) { + this(null, null, options); + } + + /** + * Behavior of validation is modified by passing in options: + * @param schemes The set of valid schemes. Ignored if the ALLOW_ALL_SCHEMES option is set. + * @param options The options should be set using the public constants declared in + * this class. To set multiple options you simply add them together. For example, + * ALLOW_2_SLASHES + NO_FRAGMENTS enables both of those options. + */ + public UrlValidator(String[] schemes, long options) { + this(schemes, null, options); + } + + /** + * Initialize a UrlValidator with the given validation options. + * @param authorityValidator Regular expression validator used to validate the authority part + * This allows the user to override the standard set of domains. + * @param options Validation options. Set using the public constants of this class. + * To set multiple options, simply add them together: + *

      ALLOW_2_SLASHES + NO_FRAGMENTS

      + * enables both of those options. + */ + public UrlValidator(RegexValidator authorityValidator, long options) { + this(null, authorityValidator, options); + } + + /** + * Customizable constructor. Validation behavior is modifed by passing in options. + * @param schemes the set of valid schemes. Ignored if the ALLOW_ALL_SCHEMES option is set. + * @param authorityValidator Regular expression validator used to validate the authority part + * @param options Validation options. Set using the public constants of this class. + * To set multiple options, simply add them together: + *

      ALLOW_2_SLASHES + NO_FRAGMENTS

      + * enables both of those options. + */ + public UrlValidator(String[] schemes, RegexValidator authorityValidator, long options) { + this.options = options; + + if (isOn(ALLOW_ALL_SCHEMES)) { + allowedSchemes = Collections.emptySet(); + } else { + if (schemes == null) { + schemes = DEFAULT_SCHEMES; + } + allowedSchemes = new HashSet(schemes.length); + for(int i=0; i < schemes.length; i++) { + allowedSchemes.add(schemes[i].toLowerCase(Locale.ENGLISH)); + } + } + + this.authorityValidator = authorityValidator; + } + + /** + *

      Checks if a field has a valid url address.

      + * + * Note that the method calls #isValidAuthority() + * which checks that the domain is valid. + * + * @param value The value validation is being performed on. A null + * value is considered invalid. + * @return true if the url is valid. + */ + public boolean isValid(String value) { + if (value == null) { + return false; + } + + // Check the whole url address structure + Matcher urlMatcher = URL_PATTERN.matcher(value); + if (!urlMatcher.matches()) { + return false; + } + + String scheme = urlMatcher.group(PARSE_URL_SCHEME); + if (!isValidScheme(scheme)) { + return false; + } + + String authority = urlMatcher.group(PARSE_URL_AUTHORITY); + if ("file".equals(scheme)) {// Special case - file: allows an empty authority + if (authority != null) { + if (authority.contains(":")) { // but cannot allow trailing : + return false; + } + } + // drop through to continue validation + } else { // not file: + // Validate the authority + if (!isValidAuthority(authority)) { + return false; + } + } + + if (!isValidPath(urlMatcher.group(PARSE_URL_PATH))) { + return false; + } + + if (!isValidQuery(urlMatcher.group(PARSE_URL_QUERY))) { + return false; + } + + if (!isValidFragment(urlMatcher.group(PARSE_URL_FRAGMENT))) { + return false; + } + + return true; + } + + /** + * Validate scheme. If schemes[] was initialized to a non null, + * then only those schemes are allowed. + * Otherwise the default schemes are "http", "https", "ftp". + * Matching is case-blind. + * @param scheme The scheme to validate. A null value is considered + * invalid. + * @return true if valid. + */ + protected boolean isValidScheme(String scheme) { + if (scheme == null) { + return false; + } + + // TODO could be removed if external schemes were checked in the ctor before being stored + if (!SCHEME_PATTERN.matcher(scheme).matches()) { + return false; + } + + if (isOff(ALLOW_ALL_SCHEMES) && !allowedSchemes.contains(scheme.toLowerCase(Locale.ENGLISH))) { + return false; + } + + return true; + } + + /** + * Returns true if the authority is properly formatted. An authority is the combination + * of hostname and port. A null authority value is considered invalid. + * Note: this implementation validates the domain unless a RegexValidator was provided. + * If a RegexValidator was supplied and it matches, then the authority is regarded + * as valid with no further checks, otherwise the method checks against the + * AUTHORITY_PATTERN and the DomainValidator (ALLOW_LOCAL_URLS) + * @param authority Authority value to validate, alllows IDN + * @return true if authority (hostname and port) is valid. + */ + protected boolean isValidAuthority(String authority) { + if (authority == null) { + return false; + } + + // check manual authority validation if specified + if (authorityValidator != null && authorityValidator.isValid(authority)) { + return true; + } + // convert to ASCII if possible + final String authorityASCII = DomainValidator.unicodeToASCII(authority); + + Matcher authorityMatcher = AUTHORITY_PATTERN.matcher(authorityASCII); + if (!authorityMatcher.matches()) { + return false; + } + + // We have to process IPV6 separately because that is parsed in a different group + String ipv6 = authorityMatcher.group(PARSE_AUTHORITY_IPV6); + if (ipv6 != null) { + InetAddressValidator inetAddressValidator = InetAddressValidator.getInstance(); + if (!inetAddressValidator.isValidInet6Address(ipv6)) { + return false; + } + } else { + String hostLocation = authorityMatcher.group(PARSE_AUTHORITY_HOST_IP); + // check if authority is hostname or IP address: + // try a hostname first since that's much more likely + DomainValidator domainValidator = DomainValidator.getInstance(isOn(ALLOW_LOCAL_URLS)); + if (!domainValidator.isValid(hostLocation)) { + // try an IPv4 address + InetAddressValidator inetAddressValidator = InetAddressValidator.getInstance(); + if (!inetAddressValidator.isValidInet4Address(hostLocation)) { + // isn't IPv4, so the URL is invalid + return false; + } + } + String port = authorityMatcher.group(PARSE_AUTHORITY_PORT); + if (port != null && port.length() > 0) { + try { + int iPort = Integer.parseInt(port); + if (iPort < 0 || iPort > MAX_UNSIGNED_16_BIT_INT) { + return false; + } + } catch (NumberFormatException nfe) { + return false; // this can happen for big numbers + } + } + } + + String extra = authorityMatcher.group(PARSE_AUTHORITY_EXTRA); + if (extra != null && extra.trim().length() > 0){ + return false; + } + + return true; + } + + /** + * Returns true if the path is valid. A null value is considered invalid. + * @param path Path value to validate. + * @return true if path is valid. + */ + protected boolean isValidPath(String path) { + if (path == null) { + return false; + } + + if (!PATH_PATTERN.matcher(path).matches()) { + return false; + } + + try { + URI uri = new URI(null,null,path,null); + String norm = uri.normalize().getPath(); + if (norm.startsWith("/../") // Trying to go via the parent dir + || norm.equals("/..")) { // Trying to go to the parent dir + return false; + } + } catch (URISyntaxException e) { + return false; + } + + int slash2Count = countToken("//", path); + if (isOff(ALLOW_2_SLASHES) && (slash2Count > 0)) { + return false; + } + + return true; + } + + /** + * Returns true if the query is null or it's a properly formatted query string. + * @param query Query value to validate. + * @return true if query is valid. + */ + protected boolean isValidQuery(String query) { + if (query == null) { + return true; + } + + return QUERY_PATTERN.matcher(query).matches(); + } + + /** + * Returns true if the given fragment is null or fragments are allowed. + * @param fragment Fragment value to validate. + * @return true if fragment is valid. + */ + protected boolean isValidFragment(String fragment) { + if (fragment == null) { + return true; + } + + return isOff(NO_FRAGMENTS); + } + + /** + * Returns the number of times the token appears in the target. + * @param token Token value to be counted. + * @param target Target value to count tokens in. + * @return the number of tokens. + */ + protected int countToken(String token, String target) { + int tokenIndex = 0; + int count = 0; + while (tokenIndex != -1) { + tokenIndex = target.indexOf(token, tokenIndex); + if (tokenIndex > -1) { + tokenIndex++; + count++; + } + } + return count; + } + + /** + * Tests whether the given flag is on. If the flag is not a power of 2 + * (ie. 3) this tests whether the combination of flags is on. + * + * @param flag Flag value to check. + * + * @return whether the specified flag value is on. + */ + private boolean isOn(long flag) { + return (options & flag) > 0; + } + + /** + * Tests whether the given flag is off. If the flag is not a power of 2 + * (ie. 3) this tests whether the combination of flags is off. + * + * @param flag Flag value to check. + * + * @return whether the specified flag value is off. + */ + private boolean isOff(long flag) { + return (options & flag) == 0; + } + + // Unit test access to pattern matcher + Matcher matchURL(String value) { + return URL_PATTERN.matcher(value); + } +} diff --git a/core/src/main/java/jenkins/util/UrlHelper.java b/core/src/main/java/jenkins/util/UrlHelper.java new file mode 100644 index 0000000000..21fff86a35 --- /dev/null +++ b/core/src/main/java/jenkins/util/UrlHelper.java @@ -0,0 +1,58 @@ +/* + * 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.util; + +import jenkins.org.apache.commons.validator.routines.UrlValidator; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import java.net.MalformedURLException; +import java.net.URL; + +@Restricted(NoExternalUse.class) +public class UrlHelper { + public static boolean isValidRootUrl(String url) { + String[] schemes = {"http", "https"}; + // option to accept url like http://localhost or http://SERVER_JENKINS that are often used inside company's network + UrlValidator validator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS + UrlValidator.NO_FRAGMENTS); + boolean isValid = validator.isValid(url); + if (!isValid) { + // potentially it contains _'s in hostname which seems accepted but not by the UrlValidator + // https://issues.apache.org/jira/browse/VALIDATOR-358 + try { + URL urlObject = new URL(url); + String host = urlObject.getHost(); + if (host.contains("_")) { + String hostWithoutUnderscore = host.replace("_", ""); + String modifiedUrl = url.replace(host, hostWithoutUnderscore); + isValid = validator.isValid(modifiedUrl); + } + } catch (MalformedURLException e) { + return false; + } + } + + return isValid; + } +} diff --git a/core/src/main/resources/jenkins/diagnostics/Messages.properties b/core/src/main/resources/jenkins/diagnostics/Messages.properties index 38362ae234..6eb87fc358 100644 --- a/core/src/main/resources/jenkins/diagnostics/Messages.properties +++ b/core/src/main/resources/jenkins/diagnostics/Messages.properties @@ -1,3 +1,4 @@ CompletedInitializationMonitor.DisplayName=Jenkins Initialization Monitor SecurityIsOffMonitor.DisplayName=Disabled Security URICheckEncodingMonitor.DisplayName=Check URI Encoding +RootUrlNotSetMonitor.DisplayName=Root URL not configured Monitor diff --git a/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.jelly b/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.jelly new file mode 100644 index 0000000000..f5abad1054 --- /dev/null +++ b/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.jelly @@ -0,0 +1,36 @@ + + + +
      + + + ${%urlIsNull(rootURL)} + + + ${%urlIsInvalid(rootURL)} + + +
      +
      diff --git a/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.properties b/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.properties new file mode 100644 index 0000000000..cd972c3d69 --- /dev/null +++ b/core/src/main/resources/jenkins/diagnostics/RootUrlNotSetMonitor/message.properties @@ -0,0 +1,6 @@ +urlIsNull=Jenkins root URL is empty but is required for the proper operation of many Jenkins features like email notifications, \ +PR status update, and environment variables such as BUILD_URL.
      \ +Please provide an accurate value in Jenkins configuration. +urlIsInvalid=Jenkins root URL seems to be invalid. It is required for the proper operation of many Jenkins features like email notifications, \ +PR status update, and environment variables such as BUILD_URL.
      \ +Please provide an accurate value in Jenkins configuration. diff --git a/core/src/main/resources/jenkins/install/Messages.properties b/core/src/main/resources/jenkins/install/Messages.properties new file mode 100644 index 0000000000..52b1b94367 --- /dev/null +++ b/core/src/main/resources/jenkins/install/Messages.properties @@ -0,0 +1,24 @@ +# The MIT License +# +# Copyright 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. +SetupWizard_ConfigureInstance_ValidationErrors=Some settings are invalid. See the error messages for details. +SetupWizard_ConfigureInstance_RootUrl_Empty=The URL cannot be empty +SetupWizard_ConfigureInstance_RootUrl_Invalid=The URL is invalid, please ensure you are using http:// or https:// with a valid domain. diff --git a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.jelly b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.jelly new file mode 100644 index 0000000000..0a0b2f5b69 --- /dev/null +++ b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.jelly @@ -0,0 +1,122 @@ + + + + + + + + + +
      +
      +

      ${%Instance Configuration}

      +
      + + + + + + + + +
      + +
      +
      +
      ${%jenkinsURL_help}
      +
      +
      + +
      +
      +
      +
      +
      diff --git a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.properties b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.properties new file mode 100644 index 0000000000..4d08f82ba2 --- /dev/null +++ b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance.properties @@ -0,0 +1,6 @@ +Instance\ Configuration=Instance Configuration +Jenkins\ URL=Jenkins URL +jenkinsURL_help=The Jenkins URL is used to provide the root URL for absolute links to various Jenkins resources. \ +That means this value is required for proper operation of many Jenkins features including email notifications, PR status updates, and the BUILD_URL environment variable provided to build steps.
      \ +The proposed default value shown is not saved yet and is generated from the current request, if possible. \ +The best practice is to set this value to the URL that users are expected to use. This will avoid confusion when sharing or viewing links. diff --git a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance_fr.properties b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance_fr.properties new file mode 100644 index 0000000000..7c8aae3d61 --- /dev/null +++ b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardConfigureInstance_fr.properties @@ -0,0 +1,2 @@ +Instance\ Configuration=Configuration de l''instance +Jenkins\ URL=Jenkins URL diff --git a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser.jelly b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser.jelly index 788b353a9e..96e972b9ca 100644 --- a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser.jelly +++ b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser.jelly @@ -7,7 +7,7 @@