mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-06 16:45:58 +00:00
[JENKINS-34833] - Installation Wizard: Allow altering the list of suggested plugins from update sites (#2348)
* [JENKINS-34833] - Installation Wizard: Allow altering the list of succested plugins from update sites In our company we provide a custom package of Jenkins, which provides a customized set of plugins (open-source and closed-source ones). These plugins come from the custom update site. We would like to provide custom suggestions to users of our Jenkins packages when they install Jenkins. For such purpose we would like to make the plugin suggestions list overridable by UpdateSite extensions in Jenkins. * [JENKINS-34833] - Fix the JS Unit test (code from @kzantow) * [JENKINS-34833] - Unit tests for SetupWizard * [JENKINS-34833] - Address comment regarding the error propagation for the unknown version * [JENKINS-34833] - SetupWizard::getPlatformPluginList() is not a part of the public API * [JENKINS-34833] - Address comments from @orrc
This commit is contained in:
1 parent
65f2a4a406
commit
bb1f620d0a
6 files changed
+458
-79
No files matched your search
@@ -1,5 +1,6 @@
|
||||
package jenkins.install;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
@@ -26,7 +27,9 @@ import org.kohsuke.stapler.StaplerResponse;
|
||||
|
||||
import hudson.BulkChange;
|
||||
import hudson.FilePath;
|
||||
import hudson.ProxyConfiguration;
|
||||
import hudson.model.UpdateCenter;
|
||||
import hudson.model.UpdateSite;
|
||||
import hudson.model.User;
|
||||
import hudson.security.FullControlOnceLoggedInAuthorizationStrategy;
|
||||
import hudson.security.HudsonPrivateSecurityRealm;
|
||||
@@ -35,8 +38,21 @@ import hudson.security.csrf.DefaultCrumbIssuer;
|
||||
import hudson.util.HttpResponses;
|
||||
import hudson.util.PluginServletFilter;
|
||||
import hudson.util.VersionNumber;
|
||||
import java.io.File;
|
||||
import java.net.HttpRetryException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Iterator;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.s2m.AdminWhitelistRule;
|
||||
import net.sf.json.JSONArray;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import static org.apache.commons.io.FileUtils.readFileToString;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import static org.apache.commons.lang.StringUtils.defaultIfBlank;
|
||||
import org.kohsuke.accmod.restrictions.DoNotUse;
|
||||
|
||||
/**
|
||||
* A Jenkins instance used during first-run to provide a limited set of services while
|
||||
@@ -51,7 +67,7 @@ public class SetupWizard {
|
||||
*/
|
||||
public static String initialSetupAdminUserName = "admin";
|
||||
|
||||
private final Logger LOGGER = Logger.getLogger(SetupWizard.class.getName());
|
||||
private final static Logger LOGGER = Logger.getLogger(SetupWizard.class.getName());
|
||||
|
||||
private final Jenkins jenkins;
|
||||
|
||||
@@ -192,6 +208,168 @@ public class SetupWizard {
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ static void completeUpgrade(Jenkins jenkins) throws IOException {
|
||||
setCurrentLevel(Jenkins.getVersion());
|
||||
//TODO: restore when https://github.com/jenkinsci/jenkins/pull/2281 gets merged
|
||||
// jenkins.getInstallState().proceedToNextState();
|
||||
}
|
||||
|
||||
/*package*/ static void setCurrentLevel(VersionNumber v) throws IOException {
|
||||
FileUtils.writeStringToFile(getUpdateStateFile(), v.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* File that captures the state of upgrade.
|
||||
*
|
||||
* 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the version the upgrade wizard has run the last time and upgraded to?.
|
||||
* If {@link #getUpdateStateFile()} is missing, presumes the baseline is 1.0
|
||||
* @return Current baseline. {@code null} if it cannot be retrieved.
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
@CheckForNull
|
||||
public static VersionNumber getCurrentLevel() {
|
||||
VersionNumber from = new VersionNumber("1.0");
|
||||
File state = getUpdateStateFile();
|
||||
if (state.exists()) {
|
||||
try {
|
||||
from = new VersionNumber(defaultIfBlank(readFileToString(state), "1.0").trim());
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Cannot read the current version file", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initial plugin list in JSON format
|
||||
*/
|
||||
@Restricted(DoNotUse.class) // WebOnly
|
||||
public HttpResponse doPlatformPluginList() throws IOException {
|
||||
jenkins.install.SetupWizard setupWizard = Jenkins.getInstance().getSetupWizard();
|
||||
if (setupWizard != null) {
|
||||
if (InstallState.UPGRADE.equals(Jenkins.getInstance().getInstallState())) {
|
||||
JSONArray initialPluginData = getPlatformPluginUpdates();
|
||||
if(initialPluginData != null) {
|
||||
return HttpResponses.okJSON(initialPluginData);
|
||||
}
|
||||
} else {
|
||||
JSONArray initialPluginData = getPlatformPluginList();
|
||||
if(initialPluginData != null) {
|
||||
return HttpResponses.okJSON(initialPluginData);
|
||||
}
|
||||
}
|
||||
}
|
||||
return HttpResponses.okJSON();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the list of platform plugin updates from the last time
|
||||
* the upgrade was run.
|
||||
* @return {@code null} if the version range cannot be retrieved.
|
||||
*/
|
||||
@CheckForNull
|
||||
/*package*/ JSONArray getPlatformPluginUpdates() {
|
||||
final VersionNumber version = getCurrentLevel();
|
||||
if (version == null) {
|
||||
return null;
|
||||
}
|
||||
return getPlatformPluginsForUpdate(version, Jenkins.getVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the suggested plugin list from the update sites, falling back to a local version
|
||||
* @return JSON array with the categorized plugon list
|
||||
*/
|
||||
@CheckForNull
|
||||
/*package*/ JSONArray getPlatformPluginList() {
|
||||
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
|
||||
JSONArray initialPluginList = null;
|
||||
updateSiteList: for (UpdateSite updateSite : Jenkins.getInstance().getUpdateCenter().getSiteList()) {
|
||||
String updateCenterJsonUrl = updateSite.getUrl();
|
||||
String suggestedPluginUrl = updateCenterJsonUrl.replace("/update-center.json", "/platform-plugins.json");
|
||||
try {
|
||||
URLConnection connection = ProxyConfiguration.open(new URL(suggestedPluginUrl));
|
||||
|
||||
try {
|
||||
if(connection instanceof HttpURLConnection) {
|
||||
int responseCode = ((HttpURLConnection)connection).getResponseCode();
|
||||
if(HttpURLConnection.HTTP_OK != responseCode) {
|
||||
throw new HttpRetryException("Invalid response code (" + responseCode + ") from URL: " + suggestedPluginUrl, responseCode);
|
||||
}
|
||||
}
|
||||
|
||||
String initialPluginJson = IOUtils.toString(connection.getInputStream(), "utf-8");
|
||||
initialPluginList = JSONArray.fromObject(initialPluginJson);
|
||||
break updateSiteList;
|
||||
} catch(Exception e) {
|
||||
// not found or otherwise unavailable
|
||||
LOGGER.log(Level.FINE, e.getMessage(), e);
|
||||
continue updateSiteList;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
LOGGER.log(Level.FINE, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (initialPluginList == null) {
|
||||
// fall back to local file
|
||||
try {
|
||||
ClassLoader cl = getClass().getClassLoader();
|
||||
URL localPluginData = cl.getResource("jenkins/install/platform-plugins.json");
|
||||
String initialPluginJson = IOUtils.toString(localPluginData.openStream(), "utf-8");
|
||||
initialPluginList = JSONArray.fromObject(initialPluginJson);
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return initialPluginList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the platform plugins added in the version range
|
||||
*/
|
||||
/*package*/ JSONArray getPlatformPluginsForUpdate(VersionNumber from, VersionNumber to) {
|
||||
JSONArray pluginCategories = JSONArray.fromObject(getPlatformPluginList().toString());
|
||||
for (Iterator<?> categoryIterator = pluginCategories.iterator(); categoryIterator.hasNext();) {
|
||||
Object category = categoryIterator.next();
|
||||
if (category instanceof JSONObject) {
|
||||
JSONObject cat = (JSONObject)category;
|
||||
JSONArray plugins = cat.getJSONArray("plugins");
|
||||
|
||||
nextPlugin: for (Iterator<?> pluginIterator = plugins.iterator(); pluginIterator.hasNext();) {
|
||||
Object pluginData = pluginIterator.next();
|
||||
if (pluginData instanceof JSONObject) {
|
||||
JSONObject plugin = (JSONObject)pluginData;
|
||||
if (plugin.has("added")) {
|
||||
String sinceVersion = plugin.getString("added");
|
||||
if (sinceVersion != null) {
|
||||
VersionNumber v = new VersionNumber(sinceVersion);
|
||||
if(v.compareTo(to) <= 0 && v.compareTo(from) > 0) {
|
||||
plugin.put("suggested", false);
|
||||
continue nextPlugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pluginIterator.remove();
|
||||
}
|
||||
|
||||
if (plugins.isEmpty()) {
|
||||
categoryIterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return pluginCategories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file used to store the initial admin password
|
||||
*/
|
||||
@@ -211,6 +389,11 @@ public class SetupWizard {
|
||||
static void completeSetup(Jenkins jenkins) {
|
||||
jenkins.setInstallState(InstallState.INITIAL_SETUP_COMPLETED);
|
||||
InstallUtil.saveLastExecVersion();
|
||||
try {
|
||||
completeUpgrade(jenkins);
|
||||
} catch(IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Cannot update the upgrade status", ex);
|
||||
}
|
||||
// Also, clean up the setup wizard if it's completed
|
||||
jenkins.setSetupWizard(null);
|
||||
}
|
||||
|
||||
+21
-57
@@ -1,45 +1,10 @@
|
||||
//
|
||||
// TODO: Get all of this information from the Update Center via a REST API.
|
||||
//
|
||||
|
||||
//
|
||||
// TODO: Decide on what the real "recommended" plugin set is. This is just a 1st stab.
|
||||
// Also remember, the user ultimately has full control as they can easily customize
|
||||
// away from these.
|
||||
//
|
||||
exports.recommendedPlugins = [
|
||||
"ant",
|
||||
"antisamy-markup-formatter",
|
||||
"build-timeout",
|
||||
"cloudbees-folder",
|
||||
"credentials-binding",
|
||||
"email-ext",
|
||||
"git",
|
||||
"gradle",
|
||||
"ldap",
|
||||
"mailer",
|
||||
"matrix-auth",
|
||||
"pam-auth",
|
||||
"pipeline-stage-view",
|
||||
"ssh-slaves",
|
||||
"subversion",
|
||||
"timestamper",
|
||||
"workflow-aggregator",
|
||||
"github-organization-folder",
|
||||
"ws-cleanup"
|
||||
];
|
||||
|
||||
//
|
||||
// A Categorized list of the plugins offered for install in the wizard.
|
||||
// This is a community curated list.
|
||||
//
|
||||
exports.availablePlugins = [
|
||||
[
|
||||
{
|
||||
"category":"Organization and Administration",
|
||||
"plugins": [
|
||||
{ "name": "dashboard-view" },
|
||||
{ "name": "cloudbees-folder" },
|
||||
{ "name": "antisamy-markup-formatter" }
|
||||
{ "name": "cloudbees-folder", "suggested": true },
|
||||
{ "name": "antisamy-markup-formatter", "suggested": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -47,22 +12,22 @@ exports.availablePlugins = [
|
||||
"description":"Add general purpose features to your jobs",
|
||||
"plugins": [
|
||||
{ "name": "build-name-setter" },
|
||||
{ "name": "build-timeout" },
|
||||
{ "name": "build-timeout", "suggested": true },
|
||||
{ "name": "config-file-provider" },
|
||||
{ "name": "credentials-binding" },
|
||||
{ "name": "credentials-binding", "suggested": true },
|
||||
{ "name": "embeddable-build-status" },
|
||||
{ "name": "rebuild" },
|
||||
{ "name": "ssh-agent" },
|
||||
{ "name": "throttle-concurrents" },
|
||||
{ "name": "timestamper" },
|
||||
{ "name": "ws-cleanup" }
|
||||
{ "name": "timestamper", "suggested": true },
|
||||
{ "name": "ws-cleanup", "suggested": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"category":"Build Tools",
|
||||
"plugins": [
|
||||
{ "name": "ant" },
|
||||
{ "name": "gradle" },
|
||||
{ "name": "ant", "suggested": true },
|
||||
{ "name": "gradle", "suggested": true },
|
||||
{ "name": "msbuild" },
|
||||
{ "name": "nodejs" }
|
||||
]
|
||||
@@ -81,9 +46,9 @@ exports.availablePlugins = [
|
||||
{
|
||||
"category":"Pipelines and Continuous Delivery",
|
||||
"plugins": [
|
||||
{ "name": "workflow-aggregator" },
|
||||
{ "name": "github-organization-folder" },
|
||||
{ "name": "pipeline-stage-view" },
|
||||
{ "name": "workflow-aggregator", "suggested": true, "added": "2.0" },
|
||||
{ "name": "github-organization-folder", "suggested": true, "added": "2.0" },
|
||||
{ "name": "pipeline-stage-view", "suggested": true, "added": "2.0" },
|
||||
{ "name": "build-pipeline-plugin" },
|
||||
{ "name": "conditional-buildstep" },
|
||||
{ "name": "jenkins-multijob-plugin" },
|
||||
@@ -97,13 +62,13 @@ exports.availablePlugins = [
|
||||
{ "name": "bitbucket" },
|
||||
{ "name": "clearcase" },
|
||||
{ "name": "cvs" },
|
||||
{ "name": "git" },
|
||||
{ "name": "git", "suggested": true },
|
||||
{ "name": "git-parameter" },
|
||||
{ "name": "github" },
|
||||
{ "name": "gitlab-plugin" },
|
||||
{ "name": "p4" },
|
||||
{ "name": "repo" },
|
||||
{ "name": "subversion" },
|
||||
{ "name": "subversion", "suggested": true },
|
||||
{ "name": "teamconcert" },
|
||||
{ "name": "tfs" }
|
||||
]
|
||||
@@ -112,16 +77,16 @@ exports.availablePlugins = [
|
||||
"category":"Distributed Builds",
|
||||
"plugins": [
|
||||
{ "name": "matrix-project" },
|
||||
{ "name": "ssh-slaves" },
|
||||
{ "name": "ssh-slaves", "suggested": true },
|
||||
{ "name": "windows-slaves" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"category":"User Management and Security",
|
||||
"plugins": [
|
||||
{ "name": "matrix-auth" },
|
||||
{ "name": "pam-auth" },
|
||||
{ "name": "ldap" },
|
||||
{ "name": "matrix-auth", "suggested": true },
|
||||
{ "name": "pam-auth", "suggested": true },
|
||||
{ "name": "ldap", "suggested": true },
|
||||
{ "name": "role-strategy" },
|
||||
{ "name": "active-directory" }
|
||||
]
|
||||
@@ -129,12 +94,11 @@ exports.availablePlugins = [
|
||||
{
|
||||
"category":"Notifications and Publishing",
|
||||
"plugins": [
|
||||
{ "name": "email-ext" },
|
||||
{ "name": "email-ext", "suggested": true },
|
||||
{ "name": "emailext-template" },
|
||||
{ "name": "mailer" },
|
||||
{ "name": "mailer", "suggested": true },
|
||||
{ "name": "publish-over-ssh" },
|
||||
// { "name": "slack" }, // JENKINS-33571, https://github.com/jenkinsci/slack-plugin/issues/191
|
||||
{ "name": "ssh" }
|
||||
]
|
||||
}
|
||||
];
|
||||
]
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2016 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.install;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.Page;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||
import hudson.FilePath;
|
||||
import hudson.model.UpdateSite;
|
||||
import hudson.security.ACL;
|
||||
import hudson.security.AuthorizationStrategy;
|
||||
import hudson.security.SecurityRealm;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.http.auth.AuthScope;
|
||||
import org.apache.http.auth.Credentials;
|
||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||
import org.apache.http.impl.client.BasicCredentialsProvider;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.jvnet.hudson.test.JenkinsRule;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.jvnet.hudson.test.Issue;
|
||||
|
||||
/**
|
||||
* Tests of {@link SetupWizard}.
|
||||
* @author Oleg Nenashev
|
||||
*/
|
||||
public class SetupWizardTest {
|
||||
|
||||
@Rule
|
||||
public JenkinsRule j = new JenkinsRule();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tmpdir = new TemporaryFolder();
|
||||
|
||||
BasicCredentialsProvider adminCredentialsProvider;
|
||||
|
||||
@Before
|
||||
public void initSetupWizard() throws IOException, InterruptedException {
|
||||
final SetupWizard wizard = new SetupWizard(j.jenkins);
|
||||
j.jenkins.setSetupWizard(wizard);
|
||||
|
||||
// Retrieve admin credentials
|
||||
final FilePath adminPassFile = wizard.getInitialAdminPasswordFile();
|
||||
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
|
||||
adminPassFile.copyTo(ostream);
|
||||
final String password = ostream.toString();
|
||||
|
||||
adminCredentialsProvider = new BasicCredentialsProvider() {
|
||||
@Override
|
||||
public Credentials getCredentials(AuthScope authscope) {
|
||||
return new UsernamePasswordCredentials("admin", password);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnPluginListsByDefault() throws Exception {
|
||||
JenkinsRule.WebClient wc = j.createWebClient();
|
||||
// TODO: This is a hack, wc.login does not work with the form
|
||||
j.jenkins.setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
|
||||
j.jenkins.setAuthorizationStrategy(AuthorizationStrategy.UNSECURED);
|
||||
// wc.setCredentialsProvider(adminCredentialsProvider);
|
||||
// wc.login("admin");
|
||||
|
||||
String response = jsonRequest(wc, "setupWizard/platformPluginList");
|
||||
assertThat("Missing plugin is suggestions ", response, containsString("active-directory"));
|
||||
assertThat("Missing category is suggestions ", response, containsString("Pipelines and Continuous Delivery"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Issue("JENKINS-34833")
|
||||
public void shouldReturnUpdateSiteJSONIfSpecified() throws Exception {
|
||||
// Init the update site
|
||||
CustomUpdateSite us = new CustomUpdateSite(tmpdir);
|
||||
us.init();
|
||||
j.jenkins.getUpdateCenter().getSites().add(us);
|
||||
|
||||
// Prepare the connection
|
||||
JenkinsRule.WebClient wc = j.createWebClient();
|
||||
// TODO: This is a hack, wc.login does not work with the form
|
||||
j.jenkins.setSecurityRealm(SecurityRealm.NO_AUTHENTICATION);
|
||||
j.jenkins.setAuthorizationStrategy(AuthorizationStrategy.UNSECURED);
|
||||
// wc.setCredentialsProvider(adminCredentialsProvider);
|
||||
// wc.login("admin");
|
||||
|
||||
String response = jsonRequest(wc, "setupWizard/platformPluginList");
|
||||
assertThat("Missing plugin is suggestions ", response, containsString("antisamy-markup-formatter"));
|
||||
assertThat("Missing category is suggestions ", response, containsString("Organization and Administration"));
|
||||
assertThat("Missing plugin is suggestions ", response, not(containsString("active-directory")));
|
||||
assertThat("Missing category is suggestions ", response, not(containsString("Pipelines and Continuous Delivery")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldProhibitAccessToPluginListWithoutAuth() throws Exception {
|
||||
JenkinsRule.WebClient wc = j.createWebClient();
|
||||
wc.assertFails("setupWizard/platformPluginList", 403);
|
||||
wc.assertFails("setupWizard/createAdminUaser", 403);
|
||||
wc.assertFails("setupWizard/completeInstall", 403);
|
||||
}
|
||||
|
||||
private String jsonRequest(JenkinsRule.WebClient wc, String path) throws Exception {
|
||||
// Try to call the actions method to retrieve the data
|
||||
final Page res;
|
||||
try {
|
||||
res = wc.goTo(path, null);
|
||||
} catch (Exception ex) {
|
||||
ex.getMessage();
|
||||
throw new AssertionError("Cannot get a response from " + path, ex);
|
||||
}
|
||||
final String responseJSON = res.getWebResponse().getContentAsString();
|
||||
return responseJSON;
|
||||
}
|
||||
|
||||
private static final class CustomUpdateSite extends UpdateSite {
|
||||
|
||||
private final TemporaryFolder tmpdir;
|
||||
|
||||
public CustomUpdateSite(TemporaryFolder tmpdir) throws MalformedURLException {
|
||||
super("custom-uc", tmpdir.getRoot().toURI().toURL().toString() + "update-center.json");
|
||||
this.tmpdir = tmpdir;
|
||||
}
|
||||
|
||||
public void init() throws IOException {
|
||||
File newFile = tmpdir.newFile("platform-plugins.json");
|
||||
FileUtils.write(newFile, "[ { "
|
||||
+ "\"category\":\"Organization and Administration\", "
|
||||
+ "\"plugins\": [ { \"name\": \"dashboard-view\"}, { \"name\": \"antisamy-markup-formatter\" } ]"
|
||||
+ "} ]");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -7,18 +7,49 @@ var jenkins = require('../util/jenkins');
|
||||
// TODO: Get plugin info (plugins + recommended plugin list) from update center.
|
||||
// For now, we statically store them in the wizard.
|
||||
|
||||
var plugins = require('./plugins.js');
|
||||
plugins.names =[];
|
||||
for (var i = 0; i < plugins.availablePlugins.length; i++) {
|
||||
var pluginCategory = plugins.availablePlugins[i];
|
||||
var categoryPlugins = pluginCategory.plugins;
|
||||
for (var ii = 0; ii < categoryPlugins.length; ii++) {
|
||||
var pluginName = categoryPlugins[ii].name;
|
||||
if (plugins.names.indexOf(pluginName) === -1) {
|
||||
plugins.names.push(pluginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Get plugin info (plugins + recommended plugin list) from update centers.
|
||||
var plugins;
|
||||
|
||||
exports.initialPluginList = function(handler) {
|
||||
jenkins.get('/setupWizard/platformPluginList', function(response) {
|
||||
if(response.status !== 'ok') {
|
||||
handler.call({ isError: true, data: response.data });
|
||||
return;
|
||||
}
|
||||
|
||||
handler.call({ isError: false }, response.data);
|
||||
}, {
|
||||
timeout: pluginManagerErrorTimeoutMillis,
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
handler.call({ isError: true, errorMessage: errorThrown });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Call this to initialize the plugin list
|
||||
exports.init = function(handler) {
|
||||
exports.initialPluginList(function(initialPluginCategories) {
|
||||
plugins = {};
|
||||
plugins.names = [];
|
||||
plugins.recommendedPlugins = [];
|
||||
plugins.availablePlugins = initialPluginCategories;
|
||||
for (var i = 0; i < initialPluginCategories.length; i++) {
|
||||
var pluginCategory = initialPluginCategories[i];
|
||||
var categoryPlugins = pluginCategory.plugins;
|
||||
for (var ii = 0; ii < categoryPlugins.length; ii++) {
|
||||
var plugin = categoryPlugins[ii];
|
||||
var pluginName = plugin.name;
|
||||
if (plugins.names.indexOf(pluginName) === -1) {
|
||||
plugins.names.push(pluginName);
|
||||
if (plugin.suggested) {
|
||||
plugins.recommendedPlugins.push(pluginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handler();
|
||||
});
|
||||
};
|
||||
|
||||
// default 10 seconds for AJAX responses to return before triggering an error condition
|
||||
var pluginManagerErrorTimeoutMillis = 10 * 1000;
|
||||
@@ -200,3 +231,4 @@ exports.restartJenkins = function(handler) {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -136,11 +136,19 @@ var createPluginSetupWizard = function(appendTarget) {
|
||||
success.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
var pluginList;
|
||||
var allPluginNames;
|
||||
var selectedPluginNames;
|
||||
|
||||
// Initialize the plugin manager after connectivity checks
|
||||
pluginManager.init(handleGenericError(function() {
|
||||
pluginList = pluginManager.plugins();
|
||||
allPluginNames = pluginManager.pluginNames();
|
||||
selectedPluginNames = pluginManager.recommendedPluginNames();
|
||||
}));
|
||||
|
||||
// state variables for plugin data, selected plugins, etc.:
|
||||
var pluginList = pluginManager.plugins();
|
||||
var allPluginNames = pluginManager.pluginNames();
|
||||
var selectedPluginNames = pluginManager.recommendedPluginNames();
|
||||
var visibleDependencies = {};
|
||||
var categories = [];
|
||||
var availablePlugins = {};
|
||||
@@ -900,7 +908,7 @@ var createPluginSetupWizard = function(appendTarget) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// check for updates when first loaded...
|
||||
pluginManager.installStatus(handleGenericError(function(data) {
|
||||
var jobs = data.jobs;
|
||||
|
||||
@@ -9,10 +9,6 @@ var getJQuery = function() {
|
||||
return $;
|
||||
};
|
||||
|
||||
var pluginList = jsTest.requireSrcModule('api/plugins');
|
||||
|
||||
pluginList.recommendedPlugins = ['subversion'];
|
||||
|
||||
// Iterates through all responses until the end and returns the last response repeatedly
|
||||
var LastResponse = function(responses) {
|
||||
var counter = 0;
|
||||
@@ -98,8 +94,43 @@ var ajaxMocks = function(responseMappings) {
|
||||
}
|
||||
},
|
||||
'/jenkins/pluginManager/installPlugins': {
|
||||
status: 'ok',
|
||||
data: 'RANDOM_UUID_1234'
|
||||
},
|
||||
'/jenkins/setupWizard/platformPluginList': {
|
||||
status: 'ok',
|
||||
data: 'RANDOM_UUID_1234'
|
||||
data: [
|
||||
{
|
||||
"category":"Stuff",
|
||||
"plugins": [
|
||||
{ "name": "mailer" },
|
||||
{ "name": "junit" },
|
||||
{ "name": "workflow-aggregator", "added": "2.0" },
|
||||
]
|
||||
},
|
||||
{
|
||||
"category":"Source Code Management",
|
||||
"plugins": [
|
||||
{ "name": "git" },
|
||||
{ "name": "subversion", "suggested": true }
|
||||
]
|
||||
},
|
||||
{
|
||||
"category":"User Management and Security",
|
||||
"plugins": [
|
||||
{ "name": "matrix-auth" },
|
||||
{ "name": "pam-auth" },
|
||||
{ "name": "ldap" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"category":"Notifications and Publishing",
|
||||
"plugins": [
|
||||
{ "name": "email-ext" },
|
||||
{ "name": "ssh" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user