Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIXED JENKINS-41196] allow plugins to override core components #2754

Closed
wants to merge 45 commits into from

Conversation

kohsuke
Copy link
Member

@kohsuke kohsuke commented Feb 17, 2017

See this example for the actual example of a plugin that overrides core component.

  • war file now contains very small bootstrap in WEB-INF/lib
  • bootstrap builds "core classloader" from WEB-INF/jars which is where most of the jar files now live
  • plugins who want to override core components have to have a manifest entry.
  • bootstrap considers that in building the classpath.
  • version consistency check is performed on every jar that's getting overridden. Any attempt to "downgrade" a component from what's in core will result in the plugin getting rejected.

Still more to do:

  • NAH: If a core override is rejected, that plugin should be marked as failed. Currently, the error message just gets logged and the plugin will load like a normal plugin. This is necessary for some plugins (like BO) to require a newer version of Stapler.
  • do not double-load jar files in plugin classloader so as not to have two copies of the same jar (which trips up META-INF/services lookup for example)

Otherwise we end up using that class before it gets initialized
Implemented the bootstrap logic and necessary packaging change to load
the core from a separate classloader.
Jelly's <invokeStatic> tries to load a class from context classloader,
so we need it to be core loader, not bootstrap loader.
@kohsuke kohsuke added the work-in-progress The PR is under active development, not ready to the final review label Feb 17, 2017
Conflicts:
	core/src/main/java/hudson/WebAppMain.java
	war/src/main/webapp/WEB-INF/web.xml
@kohsuke kohsuke closed this Feb 17, 2017
@kohsuke kohsuke reopened this Feb 17, 2017
@kohsuke
Copy link
Member Author

kohsuke commented Feb 17, 2017

If a core override is rejected, that plugin should be marked as failed. Currently, the error message just gets logged and the plugin will load like a normal plugin. This is necessary for some plugins (like BO) to require a newer version of Stapler.

On further thought I think this is wrong, because if BO specifies a specific version of stapler-plugin to be a dependency and if core is newer than that, then we want BO to run normally, instead of failing. So I think the current behavior is actually sufficient.

@kohsuke kohsuke added needs-more-reviews Complex change, which would benefit from more eyes and removed work-in-progress The PR is under active development, not ready to the final review labels Feb 17, 2017
@kohsuke
Copy link
Member Author

kohsuke commented Feb 17, 2017

@reviewbybees

@jglick jglick self-assigned this Feb 17, 2017
If someone is enumerating resources, it can end up twice
If the bootstrap fails, we want "java -jar jenkins.war" to terminate.
<!--
The MIT License

Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smells like an outdated header

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (and the one below you commented) is because I copied this file from core/pom.xml. I don't know how much of pom is copyrightable, but I think I'll just leave it in.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have modified the Pom you should at least update the date range (unclear if this was personal time or work time project so adding employer is your personal due diligence)

<description>Hands off the execution to jenkins-core</description>

<properties>
<!-- TODO: Actually many issues are being filtered by src/findbugs/findbugs-excludes.xml -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, FindBugs takes only high-severity issues. IIRC we have about 400 of the medium-priority. I have started working on cleaning up/filtering them in #2519 , but have not finished this PR yet. Hope to revisit once I have some time for it.

FTR https://issues.jenkins-ci.org/browse/JENKINS-36716

<dependencies>
<dependency>
<groupId>org.jenkins-ci</groupId>
<artifactId>version-number</artifactId>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it could be moved to the parent POM

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See 3377523. I think it's error prone to have this dependency in multiple places.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

* Bootstrap code uses {@link ServiceLoader} to look for subtypes of this marker interface
* and invokes {@link #contextInitialized(ServletContextEvent)} and {@link #contextDestroyed(ServletContextEvent)}
*
* @author Kohsuke Kawaguchi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since Javadocs for new API classes would be useful (since we really want to implement them). Or @Restricted otherwise

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has to wait until the code gets merged since we don't know which version this will go live.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We commonly use @since TODO in such case

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, 8bdeb05

/**
* JENKINS_HOME
*/
private File home;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Maybe makes sense to use Path and the java.io.Files API with exceptions for the new code. Otherwise error handling may be painful (and FindBugs will grumble)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change that should be handled separately.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

}

private String getString(String key) {
String value = System.getProperty(key); // keep passing on any exceptions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SystemProperties (?) Just to support web.xml overrides & Co

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. IMHO this method duplicates SystemProperties logic. Likely we should move it somewhere to a shared lib (required for the stuff like remoting anyway)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to bring a large class like that into bootstrap, and it's not like there's a lot of duplicate here, so I think this is OK.


private static final Logger LOGGER = Logger.getLogger(Bootstrap.class.getName());

private static final String[] HOME_NAMES = {"JENKINS_HOME","HUDSON_HOME"};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we follow the Oracle codestyle and keep these definitions in the beginning of the file? I do not care much, but we declare this codestyle somewhere

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's funny. I've worked for Sun Microsystems for close to 10 years and our coding convention has always been to put static fields & methods in the end. I just checked the coding convention PDF and it does say static fields before instance fields and no mention of static variables.

public DependenciesTxt(InputStream i) throws IOException {
List<Dependency> list = new ArrayList<>();

try (Reader r = new InputStreamReader(i,"UTF-8");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we fine with hardcoding UTF? IIRC @jtnord has bugged one of my FindBugs PRs due to it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice this file contents is within the ASCII charset, so frankly it's not worth our time to discuss this one further.


// line that we care about has 5 components: groupId:artifactId:packaging[:classifier]:version:scope
String line;
while ((line=br.readLine())!=null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readLine() may cause hanging of the logic. I am not sure we want to have it for external files. Would be better to define a timeout

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There must be misunderstanding. This is just to read dependencies.txt from a jar file.

/**
* Attempts to reverse map a jar file name back to {@link Dependency}
*/
public Dependency fromFileName(String jarFileName) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know what @stephenc may say, but annotations would not hurt here. NIT

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well when I see no annotations I assume that means Nullable. Iow the caller is responsible for knowing when null is appropriate.

If the caller is abdicated of that responsible then CheckForNull is useful to indicate the method is taking the responsibility for the caller.

NonNull is saying never give me a null.

As the default is Nullable, I do not see adding annotations as being ever a blocking issue with a PR review.

That said, there are times where adding annotations is very useful, and gentle reminders are ok. For example when defining an API / SPI I would see annotations as almost mandatory. But when creating an implementation of the api/spi or when writing code that is not intended to be used elsewhere I see them as very optional.

That's my €0.02 anyway

jars in the bootstrap override jars in the core, so duplicating it is
not only verbose but also error prone.
@daniel-beck
Copy link
Member

@jenkinsci/code-reviewers

 ... but I guess I've never actually bothered to read it!
@jglick
Copy link
Member

jglick commented Sep 5, 2017

Besides https://github.com/kohsuke/remoting-plugin there also seems to be https://github.com/ndeloof/stapler-plugin as example implementations. Do we expect these to become reactor modules of https://github.com/jenkinsci/remoting and https://github.com/stapler/stapler resp., so that we automatically get plugin releases every time we cut a component release?

@jglick
Copy link
Member

jglick commented Sep 5, 2017

Unclear what CI failures are about, but needs to be cleared up.

@daniel-beck
Copy link
Member

that we automatically get plugin releases every time we cut a component release?

Please, no. We should at least wait the LTS soaking period, preferably longer, before such a plugin is released.

@kohsuke
Copy link
Member Author

kohsuke commented Sep 5, 2017

It looks like CLIActionTest gets into an infinite loop, puts stress to the test machine, and eats all the available disk space. I suspect this resulted in strange CI failures.

@kohsuke
Copy link
Member Author

kohsuke commented Sep 5, 2017

After investigation and conversation with Jesse, we believe we hit the same test problem that blocked #2959 from getting integrated.

The problem is that CLIActionTest.interleavedStdio() runs into a tight loop in PlainCLIProtocol.Reader.run() when it catches TimeoutException wrapped in IOException. Newer version of Jetty used in newer JTH makes this condition fatal and not retryable.

The result is that the test produce ungodly amount of log output, eating up all the disk space and preventing the successful completion of the tests.

Jesse said he'll incorporate a fix to this problem in #2959 and merge that first, which should unblock this PR.

@jglick
Copy link
Member

jglick commented Sep 6, 2017

We should at least wait the LTS soaking period, preferably longer, before such a plugin is released.

Why? Consider that if I cut a release of, say, script-security-plugin, that goes to the public update center in a matter of an hour or so. A mistake in that plugin is just as likely to cause catastrophic harm as a mistake in Stapler or Remoting, and I see no reason to think it is less likely either. Why are these core components any different? If you want a stable installation, do not update any plugins, or only update plugins to versions that have been pretested by some third party.

@daniel-beck
Copy link
Member

daniel-beck commented Sep 6, 2017

A mistake in that plugin is just as likely to cause catastrophic harm as a mistake in Stapler

The history of terrible Stapler releases over the last year indicates that the likelihood of such a bug is greater. Notably, release of the plugin would precede the core PR to update the component (and the history of process irregularities surrounding merges without code review for Stapler also gives me no confidence that a "snapshot PR first" process would actually be followed). Which is when we repeatedly caught regressions.

@jglick jglick dismissed their stale review September 6, 2017 02:25

Last review was about test results.

@jglick jglick self-requested a review September 6, 2017 02:25
@stephenc
Copy link
Member

stephenc commented Sep 6, 2017

Notably, release of the plugin would precede the core PR to update the component (and the history of process irregularities surrounding merges without code review for Stapler also gives me no confidence that a "snapshot PR first" process would actually be followed). Which is when we repeatedly caught regressions.

Though having the CI for stapler automatically produce the plugin with every PR would enable easier testing of the side-effects before release. Whereas at the moment you need to make a custom build of jenkins to upgrade these.

I think a better approach would be to set the update center to blacklist new versions of these plugins from the non-experimental update centres until they have been vetted, but have remoting and stapler produce their plugins with every release... thus easier testing of upgrades and less risk to users

@jglick
Copy link
Member

jglick commented Sep 6, 2017

at the moment you need to make a custom build of jenkins to upgrade these

You would anyway, because those components have only unit tests, and by far the most extensive tests of their functionality are in jenkinsci/jenkins/test/. Having them be plugins would only be convenient for manual sanity testing. To run the automated test suites, you need to have jenkinsci/jenkins/core/pom.xml specify the snapshot dependency.

Of course we can and should have a daily CI build which picks up master branches of components like these, so that integration test failures would be apparent before anyone considered cutting a release.

The history of terrible Stapler releases over the last year indicates that the likelihood of such a bug is greater.

Have you not been following script-security changelogs? :-)

@jglick
Copy link
Member

jglick commented Sep 6, 2017

stapler-plugin seems to be bundling only org.kohsuke.stapler:stapler, i.e., Stapler core. But jenkins-core also includes stapler-groovy, stapler-jrebel, and stapler-jelly. (I am not sure what picks up stapler-jrubyruby plugin does not appear to—and I think stapler-jsp is unused.) We need a ready-to-release proposed repository or submodule for this component to consider the core component fully up for review, as this was a critical use case.

@jglick
Copy link
Member

jglick commented Sep 6, 2017

set the update center to blacklist new versions of these plugins from the non-experimental update centres until they have been vetted, but have remoting and stapler produce their plugins with every release

Looks like this should be possible with minor changes to update-center2. I am going to play with it. Certainly much easier to deal with than having override plugins living in separate repositories.

@jglick
Copy link
Member

jglick commented Sep 6, 2017

Finally have a clean CI build, at least. A fair amount of manual testing remains to be done.

@ghost
Copy link

ghost commented Sep 15, 2017

This pull request originates from a CloudBees employee. At CloudBees, we require that all pull requests be reviewed by other CloudBees employees before we seek to have the change accepted. If you want to learn more about our process please see this explanation.

Copy link
Member

@jglick jglick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think where this stands is that excepting some minor technical details, the change itself was fine, but the advent of Essentials and general changes in workflow and policy mean we do not need this much any more and it is perhaps not worth the added complexity. TBD.

@batmat batmat added the proposed-for-close There is no consensus about feasibility of this PR, and maintainers do not see a way forward for it label Jan 10, 2019
@batmat
Copy link
Member

batmat commented Jan 10, 2019

Given this is conflicted and stalled, I'm proposing to close this.

I'll do it in the next days in nobody objects.

Thanks!

@jglick
Copy link
Member

jglick commented Jan 11, 2019

I'm proposing to close this.

I was just about to do that myself. Please close the PR and JENKINS-41196 as well. There are also downstream PRs that should be closed: jenkinsci/jenkins-test-harness#48, jenkinsci/maven-hpi-plugin#73, and jenkinsci/acceptance-test-harness#356.

@batmat
Copy link
Member

batmat commented Jan 17, 2019

@jglick done.

Also closing here as per my last comment.

Thanks everyone!

@batmat batmat closed this Jan 17, 2019
@batmat batmat deleted the JENKINS-41196 branch January 17, 2019 22:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-more-reviews Complex change, which would benefit from more eyes needs-testcase Test automation is required for this pull request on-hold This pull request depends on another event/release, and it cannot be merged right now proposed-for-close There is no consensus about feasibility of this PR, and maintainers do not see a way forward for it unresolved-merge-conflict There is a merge conflict with the target branch.
Projects
None yet
7 participants