Merge branch 'master' into JENKINS-20679-filter-out-plugins-compiled-with-more-recent-jdk

This commit is contained in:
Baptiste Mathus committed 2019-01-07 11:11:38 +01:00
commit 27c49bf0a1
799 files changed
+17324 -10196

No files matched your search

+174 -47
View File
@@ -25,9 +25,6 @@ package hudson;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.security.ACLContext;
import jenkins.ExtensionRefreshException;
import jenkins.util.SystemProperties;
import hudson.PluginWrapper.Dependency;
import hudson.init.InitMilestone;
import hudson.init.InitStrategy;
@@ -37,22 +34,27 @@ import hudson.model.AbstractModelObject;
import hudson.model.AdministrativeMonitor;
import hudson.model.Api;
import hudson.model.Descriptor;
import hudson.model.DownloadService;
import hudson.model.Failure;
import hudson.model.ItemGroupMixIn;
import hudson.model.UpdateCenter;
import hudson.model.UpdateSite;
import hudson.model.UpdateCenter.DownloadJob;
import hudson.model.UpdateCenter.InstallationJob;
import hudson.model.UpdateSite;
import hudson.security.ACL;
import hudson.security.ACLContext;
import hudson.security.Permission;
import hudson.security.PermissionScope;
import hudson.util.CyclicGraphDetector;
import hudson.util.CyclicGraphDetector.CycleDetectedException;
import hudson.util.FormValidation;
import hudson.util.PersistedList;
import hudson.util.Retrier;
import hudson.util.Service;
import hudson.util.VersionNumber;
import hudson.util.XStream2;
import jenkins.ClassLoaderReflectionToolkit;
import jenkins.ExtensionRefreshException;
import jenkins.InitReactorRunner;
import jenkins.MissingDependencyException;
import jenkins.RestartRequiredException;
@@ -60,12 +62,12 @@ import jenkins.YesNoMaybe;
import jenkins.install.InstallState;
import jenkins.install.InstallUtil;
import jenkins.model.Jenkins;
import jenkins.security.CustomClassFilter;
import jenkins.util.SystemProperties;
import jenkins.util.io.OnMaster;
import jenkins.util.xml.RestrictiveEntityResolver;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.acegisecurity.Authentication;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
@@ -83,7 +85,9 @@ import org.jvnet.hudson.reactor.Reactor;
import org.jvnet.hudson.reactor.ReactorException;
import org.jvnet.hudson.reactor.TaskBuilder;
import org.jvnet.hudson.reactor.TaskGraphBuilder;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
@@ -95,6 +99,10 @@ import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
@@ -102,6 +110,7 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FilenameFilter;
@@ -109,10 +118,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -123,6 +134,7 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
@@ -130,32 +142,15 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.function.Supplier;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import static hudson.init.InitMilestone.*;
import hudson.model.DownloadService;
import hudson.util.FormValidation;
import java.io.ByteArrayInputStream;
import java.net.JarURLConnection;
import java.net.URLConnection;
import java.util.ServiceLoader;
import java.util.function.Supplier;
import java.util.jar.JarEntry;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;
import jenkins.security.CustomClassFilter;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import static java.util.logging.Level.*;
/**
* Manages {@link PluginWrapper}s.
@@ -183,6 +178,31 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
/** Custom plugin manager system property or context param. */
public static final String CUSTOM_PLUGIN_MANAGER = PluginManager.class.getName() + ".className";
private static final Logger LOGGER = Logger.getLogger(PluginManager.class.getName());
/**
* Time elapsed between retries to check the updates sites. It's kind of constant, but let it so for tests
*/
/* private final */ static int CHECK_UPDATE_SLEEP_TIME_MILLIS;
/**
* Number of attempts to check the updates sites. It's kind of constant, but let it so for tests
*/
/* private final */ static int CHECK_UPDATE_ATTEMPTS;
static {
try {
// Secure initialization
CHECK_UPDATE_SLEEP_TIME_MILLIS = SystemProperties.getInteger(PluginManager.class.getName() + ".checkUpdateSleepTimeMillis", 1000);
CHECK_UPDATE_ATTEMPTS = SystemProperties.getInteger(PluginManager.class.getName() + ".checkUpdateAttempts", 1);
} catch(Exception e) {
LOGGER.warning(String.format("There was an error initializing the PluginManager. Exception: %s", e));
} finally {
CHECK_UPDATE_ATTEMPTS = CHECK_UPDATE_ATTEMPTS > 0 ? CHECK_UPDATE_ATTEMPTS : 1;
CHECK_UPDATE_SLEEP_TIME_MILLIS = CHECK_UPDATE_SLEEP_TIME_MILLIS > 0 ? CHECK_UPDATE_SLEEP_TIME_MILLIS : 1000;
}
}
/** Accepted constructors for custom plugin manager, in the order they are tried. */
private enum PMConstructor {
JENKINS {
@@ -272,6 +292,12 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
*/
public final File rootDir;
/**
* Hold the status of the last try to check update centers. Consumed from the check.jelly to show an
* error message if the last attempt failed.
*/
private String lastErrorCheckUpdateCenters = null;
/**
* If non-null, the base directory for all exploded .hpi/.jpi plugins. Controlled by the system property / servlet
* context parameter {@literal hudson.PluginManager.workDir}.
@@ -935,8 +961,15 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
@Restricted(NoExternalUse.class)
public synchronized void resolveDependantPlugins() {
for (PluginWrapper plugin : plugins) {
// Set of optional dependants plugins of plugin
Set<String> optionalDependants = new HashSet<>();
Set<String> dependants = new HashSet<>();
for (PluginWrapper possibleDependant : plugins) {
// No need to check if plugin is dependant of itself
if(possibleDependant.getShortName().equals(plugin.getShortName())) {
continue;
}
// The plugin could have just been deleted. If so, it doesn't
// count as a dependant.
if (possibleDependant.isDeleted()) {
@@ -946,10 +979,20 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
for (Dependency dependency : dependencies) {
if (dependency.shortName.equals(plugin.getShortName())) {
dependants.add(possibleDependant.getShortName());
// If, in addition, the dependency is optional, add to the optionalDependants list
if (dependency.optional) {
optionalDependants.add(possibleDependant.getShortName());
}
// already know possibleDependant depends on plugin, no need to continue with the rest of
// dependencies. We continue with the next possibleDependant
break;
}
}
}
plugin.setDependants(dependants);
plugin.setOptionalDependants(optionalDependants);
}
}
@@ -1216,7 +1259,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
/**
* Discover all the service provider implementations of the given class,
* via <tt>META-INF/services</tt>.
* via {@code META-INF/services}.
* @deprecated Use {@link ServiceLoader} instead, or (more commonly) {@link ExtensionList}.
*/
@Deprecated
@@ -1493,7 +1536,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
updateCenter.persistInstallStatus();
if(!failures) {
try (ACLContext _ = ACL.as(currentAuth)) {
try (ACLContext acl = ACL.as(currentAuth)) {
InstallUtil.proceedToNextStateFrom(InstallState.INITIAL_PLUGINS_INSTALLING);
}
}
@@ -1643,24 +1686,86 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
@Restricted(NoExternalUse.class)
@RequirePOST public HttpResponse doCheckUpdatesServer() throws IOException {
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
// We'll check the update servers with a try-retry mechanism. The retrier is built with a builder
Retrier<FormValidation> updateServerRetrier = new Retrier.Builder<>(
// the action to perform
this::checkUpdatesServer,
// the way we know whether this attempt was right or wrong
(currentAttempt, result) -> result.kind == FormValidation.Kind.OK,
// the action name we are trying to perform
"check updates server")
// the number of attempts to try
.withAttempts(CHECK_UPDATE_ATTEMPTS)
// the delay between attempts
.withDelay(CHECK_UPDATE_SLEEP_TIME_MILLIS)
// whatever exception raised is considered as a fail attempt (all exceptions), not a failure
.withDuringActionExceptions(new Class[] {Exception.class})
// what we do with a failed attempt due to an allowed exception, return an FormValidation.error with the message
.withDuringActionExceptionListener( (attempt, e) -> FormValidation.errorWithMarkup(e.getClass().getSimpleName() + ": " + e.getLocalizedMessage()))
// lets get our retrier object
.build();
try {
for (UpdateSite site : Jenkins.getInstance().getUpdateCenter().getSites()) {
FormValidation v = site.updateDirectlyNow(DownloadService.signatureCheck);
if (v.kind != FormValidation.Kind.OK) {
// TODO crude but enough for now
return v;
// Begin the process
FormValidation result = updateServerRetrier.start();
// Check how it went
if (!FormValidation.Kind.OK.equals(result.kind)) {
LOGGER.log(Level.SEVERE, Messages.PluginManager_UpdateSiteError(CHECK_UPDATE_ATTEMPTS, result.getMessage()));
if (CHECK_UPDATE_ATTEMPTS > 1 && !Logger.getLogger(Retrier.class.getName()).isLoggable(Level.WARNING)) {
LOGGER.log(Level.SEVERE, Messages.PluginManager_UpdateSiteChangeLogLevel(Retrier.class.getName()));
}
lastErrorCheckUpdateCenters = Messages.PluginManager_CheckUpdateServerError(result.getMessage());
} else {
lastErrorCheckUpdateCenters = null;
}
for (DownloadService.Downloadable d : DownloadService.Downloadable.all()) {
FormValidation v = d.updateNow();
if (v.kind != FormValidation.Kind.OK) {
return v;
}
}
return HttpResponses.forwardToPreviousPage();
} catch(RuntimeException ex) {
throw new IOException("Unhandled exception during updates server check", ex);
} catch (Exception e) {
// It's never going to be reached because we declared all Exceptions in the withDuringActionExceptions, so
// whatever exception is considered a expected failed attempt and the retries continue
LOGGER.log(Level.WARNING, Messages.PluginManager_UnexpectedException(), e);
// In order to leave this method as it was, rethrow as IOException
throw new IOException(e);
}
// Stay in the same page in any case
return HttpResponses.forwardToPreviousPage();
}
private FormValidation checkUpdatesServer() throws Exception {
for (UpdateSite site : Jenkins.get().getUpdateCenter().getSites()) {
FormValidation v = site.updateDirectlyNow(DownloadService.signatureCheck);
if (v.kind != FormValidation.Kind.OK) {
// Stop with an error
return v;
}
}
for (DownloadService.Downloadable d : DownloadService.Downloadable.all()) {
FormValidation v = d.updateNow();
if (v.kind != FormValidation.Kind.OK) {
// Stop with an error
return v;
}
}
return FormValidation.ok();
}
/**
* Returns the last error raised during the update sites checking.
* @return the last error message
*/
public String getLastErrorCheckUpdateCenters() {
return lastErrorCheckUpdateCenters;
}
protected String identifyPluginShortName(File t) {
@@ -1708,7 +1813,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
for (Map.Entry<String,VersionNumber> requestedPlugin : parseRequestedPlugins(configXml).entrySet()) {
PluginWrapper pw = getPlugin(requestedPlugin.getKey());
if (pw == null) { // install new
UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey());
UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey(), requestedPlugin.getValue());
if (toInstall == null) {
LOGGER.log(WARNING, "No such plugin {0} to install", requestedPlugin.getKey());
continue;
@@ -1724,7 +1829,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
jobs.add(toInstall.deploy(true));
} else if (pw.isOlderThan(requestedPlugin.getValue())) { // upgrade
UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey());
UpdateSite.Plugin toInstall = uc.getPlugin(requestedPlugin.getKey(), requestedPlugin.getValue());
if (toInstall == null) {
LOGGER.log(WARNING, "No such plugin {0} to upgrade", requestedPlugin.getKey());
continue;
@@ -1835,6 +1940,31 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
return new MetadataCache();
}
/**
* Disable a list of plugins using a strategy for their dependants plugins.
* @param strategy the strategy regarding how the dependant plugins are processed
* @param plugins the list of plugins
* @return the list of results for every plugin and their dependant plugins.
* @throws IOException see {@link PluginWrapper#disable()}
*/
public @NonNull List<PluginWrapper.PluginDisableResult> disablePlugins(@NonNull PluginWrapper.PluginDisableStrategy strategy, @NonNull List<String> plugins) throws IOException {
// Where we store the results of each plugin disablement
List<PluginWrapper.PluginDisableResult> results = new ArrayList<>(plugins.size());
// Disable all plugins passed
for (String pluginName : plugins) {
PluginWrapper plugin = this.getPlugin(pluginName);
if (plugin == null) {
results.add(new PluginWrapper.PluginDisableResult(pluginName, PluginWrapper.PluginDisableStatus.NO_SUCH_PLUGIN, Messages.PluginWrapper_NoSuchPlugin(pluginName)));
} else {
results.add(plugin.disable(strategy));
}
}
return results;
}
@Restricted(NoExternalUse.class) // table.jelly
public static final class MetadataCache {
private final Map<String, Object> data = new HashMap<>();
@@ -1960,9 +2090,6 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
return "classLoader " + getClass().getName();
}
}
private static final Logger LOGGER = Logger.getLogger(PluginManager.class.getName());
public static boolean FAST_LOOKUP = !SystemProperties.getBoolean(PluginManager.class.getName()+".noFastLookup");
public static final Permission UPLOAD_PLUGINS = new Permission(Jenkins.PERMISSIONS, "UploadPlugins", Messages._PluginManager_UploadPluginsPermission_Description(),Jenkins.ADMINISTER,PermissionScope.JENKINS);