mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-06 08:36:01 +00:00
Merge branch 'master' into JENKINS-20679-filter-out-plugins-compiled-with-more-recent-jdk
This commit is contained in:
commit
27c49bf0a1
799 files changed
+17324
-10196
No files matched your search
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user