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

[JENKINS-38370] Start defining APIs that are for the master JVM only #2556

Merged
merged 1 commit into from
Oct 24, 2016

Conversation

stephenc
Copy link
Member

See JENKINS-38370

This PR only introduces the annotation. Commented code in the annotation shows one potential path to implementing the enforcement, but that path is not currently available due to the current API contract of ClassFilter (additionally I need to check if ClassFilter applies only to classes coming from the agent and not to classes received by the agent)

I have identified a couple of classes that are clearly MasterJVMOnly there are likely more such classes in core... plugins will also want to use this annotation, e.g. CredentialsProvider is intended to be MasterJVMOnly

@stephenc stephenc added the needs-more-reviews Complex change, which would benefit from more eyes label Sep 20, 2016
@stephenc
Copy link
Member Author

@jenkinsci/code-reviewers @reviewbybees

@ghost
Copy link

ghost commented Sep 20, 2016

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.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MasterJVMOnly {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe just "Master only"? Otherwise one may think it's OK to use classes within Master Node remote invocation calls. It may be safe, but I doubt it's what we want to advertise.

Copy link
Member

Choose a reason for hiding this comment

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

🐛 It's also wrong location since plugin devs won't be unable do declare the annotation without updating the core.

Copy link
Member

Choose a reason for hiding this comment

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

If that part of core functionality - then right.


/**
* Annotation to mark classes which should not be made available to agent JVMs
*/
Copy link
Member

Choose a reason for hiding this comment

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

NIT: since

@@ -29,6 +29,7 @@
* @since 1.494
*/
@Extension @Symbol("location")
Copy link
Member

Choose a reason for hiding this comment

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

I'd suggest that all Extensions should be implicitly annotated in such way with an opt-out option

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

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

👍 for the approach, but IMHO it should be in https://github.com/kohsuke/access-modifier in order to allow declaring annotation in plugins without Jenkins core dependency update. Hence 🐛 for the PR in the current state

Copy link
Contributor

@ndeloof ndeloof left a comment

Choose a reason for hiding this comment

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

imho annotation to prevent classes from being sent to remote should be part of remoting API, not specific to jenkins-core.

@oleg-nenashev
Copy link
Member

@ndeloof

imho annotation to prevent classes from being sent to remote should be part of remoting API, not specific to jenkins-core.

Remoting is de-facto a part of Jenkins core for plugins right now. That's why I propose https://github.com/kohsuke/access-modifier, which can be easily bumped in plugin parent POM

@oleg-nenashev
Copy link
Member

Or maybe new lib-remoting-annotations, but it may be overkill even though it's more reasonable if we consider remoting as a standalone project (but we actually don't)

@stephenc
Copy link
Member Author

Access-modifiers is for annotations that are checked at compile time.

This is not a check we can do at compile time (unless we mandated plugin authors two split their code int a remote-safe jar dependency and the master-side jar... which would force a similar split on Jenkins core)... so that seems like a very poor fit to me

Remoting does not have the concept of a master... and there are things other than Jenkins that use remoting (few and far between, but still) as such, baking the concept of Master and Agent into the remoting API seems wrong (e.g. close source we have the master-to-master remoting channel in which case baking the restriction into remoting seems wrong.

That leaves the option of a new library Jar... for what is basically just one annotation... certainly seems like overkill... it will likely be at least 18-24 months before we can even consider turning enforcement on by default (on say 10% of masters)... as such I consider core the perfect fit.

@oleg-nenashev @ndeloof

@ndeloof
Copy link
Contributor

ndeloof commented Sep 20, 2016

@stephenc I don't ask to introduce support for master/agent into remoting, but to define there annotation to mark some class as "Never send on channel". one way or the other doesn't matter

@olivergondza
Copy link
Member

How about java.util.io.OnMaster interface? It has been there for a while so many plugin can start using it right away...

@ndeloof
Copy link
Contributor

ndeloof commented Sep 20, 2016

@olivergondza +1 I didn't know this one, seems like it has been designed for this exact usage

@ydubreuil
Copy link
Contributor

+1 for OnMaster, it's already used by some core classes. I'll try if it prevents to load SystemProperties in #2551

@jglick
Copy link
Member

jglick commented Sep 20, 2016

Remoting does not have the concept of a master

It does, it just does not enforce restrictions itself. See jenkins.security.Roles.

@jglick
Copy link
Member

jglick commented Sep 20, 2016

OnMaster works. BTW Job implements it but I suspect that should be generalized to Item. I suspect all Descriptors should be so marked as well. Not sure about other @Extensions—cannot see a reason why these would be passed over but there might be some weird exceptions.

@jglick
Copy link
Member

jglick commented Sep 20, 2016

I need to check if ClassFilter applies only to classes coming from the agent and not to classes received by the agent

AFAICT it is currently applied to all channels unless you opt out, which core does not seem to do.

@jglick
Copy link
Member

jglick commented Sep 20, 2016

that path is not currently available due to the current API contract of ClassFilter

Right, we need to either make isBlacklisted overloads public, or introduce a

public static ClassFilter compose(final ClassFilter... delegates) {
    return new ClassFilter() {
        @Override
        protected boolean isBlacklisted(String name) {
            for (ClassFilter delegate : delegates) {
                if (delegate.isBlacklisted(name)) {
                    return true;
                }
            }
            return false;
        }
        @Override
        protected boolean isBlacklisted(Class c) {
            for (ClassFilter delegate : delegates) {
                if (delegate.isBlacklisted(c)) {
                    return true;
                }
            }
            return false;
        }
    };
}

//
// @Override
// protected boolean isBlacklisted(String name) {
// return blacklist.contains(name) || delegate.isBlacklisted(name);
Copy link
Member

Choose a reason for hiding this comment

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

Actually you might be able to work around this without a remoting bump:

if (blacklist.contains(name)) {
    return true;
}
delegate.check(name);
return false;

@stephenc
Copy link
Member Author

OnMaster is an interface whereas we really want an annotation as you cannot remove an interface from a class signature without breaking binary compatibility

@ndeloof
Copy link
Contributor

ndeloof commented Sep 20, 2016

As a marker interface removing would have no realistic impact on binary compatibility.

@stephenc
Copy link
Member Author

If everyone is happy with the market interface, fine by me... but the annotation seems a better fit to my mind

@ydubreuil
Copy link
Contributor

I'm not sure OnMaster is actually enforced. I added OnMaster to SystemProperties and wrote a test with a remote agent which is able to use SystemProperties on the remote side. Did I miss something?

@KostyaSha
Copy link
Member

KostyaSha commented Sep 20, 2016

On some level it sounds like anti-Serializable invention. Btw inner-Serializable issues could be detected with findbugs. Imho Serializable can allow replace remoting in future by some other library.

@stephenc
Copy link
Member Author

@ydubreuil there is no enforcement yet... only market interface where IMHO an annotation may be a better alignment (though the interface is available now... so we have the choice: available now but imperfect vs available in the future but perhaps a better fit)

@stephenc
Copy link
Member Author

@KostyaSha not quite. This is not about banning instances from being serialised to an agent rather about banning the class from even being loaded on the remote agent.

I am less convinced that a findbugs rule would catch such issues... for sure we can spot "quick wins" in classes that extend the Callable<V,T extends Throwable> interface, catching every object that could get caught up there or indeed catching every object type that could be returned from an interface that is Channel.export(...) would seem very hard to achieve.

In part, this is why I suspect it may be some effort to turn on enforcement, but an important effort none the less

@stephenc
Copy link
Member Author

@ndeloof iirc I had linkage errors when a marker interface was removed... i'll try some tests again to see if that was something else...

(An interface may actually be a good fit as a child class should never be remote safe if the parent is not remote safe, so the only question is what breaks if we remove a marker interface)

@olivergondza
Copy link
Member

The only situation I can imagine removing the marker interface can cause problems (as there are no methods or fields to depend on) is when someone refers to an object using the marker interface and after the update it will no longer implement that interface. This is something we should document and, if we feel strongly, we can even enforce it (plugins must not use the type outside of implements clause). I do not think people would do that a lot and we would probably not remove the interface from classes often either.

@daniel-beck
Copy link
Member

Won't enforcing this result in similar issues as the 2.4 null instance problem?

@stephenc
Copy link
Member Author

@daniel-beck well yeah, if we turned it on now... what I want to do is turn on enforcement for hpi:run (or anyone who chooses to opt in)

After 12-24 months we turn it on for say 10% of installations... (have usage stats report installations state of the AB test) if that stays at 10% then we can ramp it up to 50% and switch over to 100% enforcement... when the usage stats report a good % actually using the enforcement we can decide it safe to remove the disable enforcement option.

The current question is really:

  • annotation; or
  • marker interface

@KostyaSha
Copy link
Member

I would recommend turning it on more aggressively, since I think the problems will show up promptly in functional tests, and what do you think is actually going happen in the first 12mo if it is not on for anyone?

Maybe enable on dev versions and disable for LTS?

@jglick
Copy link
Member

jglick commented Sep 22, 2016

Maybe enable on dev versions and disable for LTS?

Yeah I was thinking the same thing.

@oleg-nenashev
Copy link
Member

I would recommend turning it on more aggressively, since I think the problems will show up promptly in functional tests, and what do you think is actually going happen in the first 12mo if it is not on for anyone

Well, IMHO it requires some additional clarification on the Website, etc. Even after the migration to the "Weekly" term we do not explicitly say that its users act as testers. But generally I support the idea.

@stephenc
Copy link
Member Author

Ok, so I have reworked this PR to now use the OnMaster interface.

The only bit I feel may be "controversial" is making ExtensionPoint implement OnMaster... there may be some plugins that have been architected in such a way that they reuse their ExtensionPoint from the agent side... that practice is probably the cause of issues... but it may be risky to start enforcing that one...

One option we could do is introduce an IdeallyOnMaster marker interface that is for core only and is extended by OnMaster and use that to provide two layers of enforcement

@KostyaSha
Copy link
Member

One option we could do is introduce an IdeallyOnMaster marker interface that is for core only and is extended by OnMaster and use that to provide two layers of enforcement

If you want introduce something, why not annotation then?

@stephenc
Copy link
Member Author

@KostyaSha said:

If you want introduce something, why not annotation then?

I am not sure we need to introduce something. My belief is that we should just ban the loading of ExtensionPoint types on agents. This may well mean refactoring how some things are done, e.g. there are some cases where a plugin will define an extension and then send it over to a remote agent... in every case I can think of that kind of API design is incorrect and will lead to a multitude of issues... but it there are a couple of places where it might be blocking to turn on enforcement of OnMaster if ExtensionPoint extends OnMaster... so what I was thinking is that we temporarily introduce @Restricted(NoExternalUse.class) interface IdeallyOnMaster and have OnMaster extend IdeallyOnMaster then ExtensionPoint extends IdeallyOnMaster and we can turn on enforcement and start educating plugin authors on how to refactor and resolve their incorrect API design... there is no need to have plugins use the IdeallyOnMaster interface, so we can just continue with OnMaster for any cases that plugins have a need to mark things as blacklisted from agents

@@ -323,7 +324,7 @@
@ExportedBean
public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLevelItemGroup, StaplerProxy, StaplerFallback,
ModifiableViewGroup, AccessControlled, DescriptorByNameOwner,
ModelObjectWithContextMenu, ModelObjectWithChildren {
ModelObjectWithContextMenu, ModelObjectWithChildren, OnMaster {
Copy link
Member

@daniel-beck daniel-beck Oct 11, 2016

Choose a reason for hiding this comment

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

Node implements it already.

Copy link
Member

Choose a reason for hiding this comment

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

Well, it never hurts to make explicit for a better Javadoc visiblity

@@ -16,4 +16,36 @@
* @since 1.475
*/
public interface OnMaster {
// TODO uncomment once we can have a delegating ClassFilter, also add SystemProperty to toggle feature
Copy link
Member

Choose a reason for hiding this comment

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

As noted in an earlier revision:

if (blacklist.contains(name)) {
    return true;
}
delegate.check(name);
return false;

Copy link
Member

Choose a reason for hiding this comment

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

Would be great to have references to the issue in this TODO

@jglick
Copy link
Member

jglick commented Oct 11, 2016

I notice that Run is not included—accident?

But in fact including it would break new code, never mind compatibility. Consider this snippet in jenkinsci/workflow-support-plugin#15, which is designed to run on an agent. It does not transfer a Run instance (that would never work) but it does need to load Run.class. There is no way around that except by defining a fresh core API that avoids referring to builds, which would be rather disruptive.

So perhaps we need to scale back the ambition here, from banning classes which we think have no legitimate use on an agent (but might, if the code is written carefully), to classes which we know will actively cause harm—such as types in the Servlet API, the original motivation for JENKINS-26677.

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

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

🐝 for the cuent scope, but I agree with @jglick that other classes could be annotated as well.
Regarding extension points, I'm not 100% sure it's going to fly in general. I'd bug since ProcessKiller is being executed on slaves (https://github.com/jenkinsci/cygwin-process-killer-plugin/blob/master/src/main/java/com/synopsys/arc/jenkinsci/plugins/cygwinprocesskiller/CygwinProcessKiller.java).

@@ -323,7 +324,7 @@
@ExportedBean
public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLevelItemGroup, StaplerProxy, StaplerFallback,
ModifiableViewGroup, AccessControlled, DescriptorByNameOwner,
ModelObjectWithContextMenu, ModelObjectWithChildren {
ModelObjectWithContextMenu, ModelObjectWithChildren, OnMaster {
Copy link
Member

Choose a reason for hiding this comment

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

Well, it never hurts to make explicit for a better Javadoc visiblity

@oleg-nenashev
Copy link
Member

UPD: *regarding banning ALL extension points

@rsandell
Copy link
Member

I think that an annotation would be better than an interface since iirc an annotation is more clearly marked in the javadoc than interfaces are, especially when something is implementing a lot of them or it is far down in the inheritance tree.

@oleg-nenashev
Copy link
Member

@reviewbybees done
@rsandell are you fine with the current state? Having an annotation would be great, but this interface is also fine (especially if we write a FindBugs detector for it)

@daniel-beck
Copy link
Member

@rsandell Well, the interface already exists. Seems wasteful to deprecate it and replace with an annotation that does the same thing.

@oleg-nenashev oleg-nenashev added ready-for-merge The PR is ready to go, and it will be merged soon if there is no negative feedback and removed needs-more-reviews Complex change, which would benefit from more eyes labels Oct 23, 2016
@oleg-nenashev
Copy link
Member

Marked as ready-for-merge since there is no negative feedback.
@KostyaSha @rsandell @olivergondza Please respond if you actually disagree with the change

@olivergondza
Copy link
Member

👍

@oleg-nenashev oleg-nenashev merged commit 37c4736 into jenkinsci:master Oct 24, 2016
@stephenc stephenc deleted the master-jvm-only branch October 24, 2016 20:03
@stephenc
Copy link
Member Author

@oleg-nenashev I believe you may have merged this too soon.

sad panda2

The real concern is with ExtensionPoint as some of the ExtensionPoint classes are used on both sides and this change marks them as master only. I think we need to at least revert the changes to ExtensionPoint

@oleg-nenashev
Copy link
Member

@stephenc Well, I just lost the context about this ExtensionPoint conversation. The PR has not been labeled properly, hence I've merged it after addressing the process requirements. But labeling as WiP is a responsibility of the PR requester, so I'll need to send you the Sad Panda's head in order to share it with you :(

Anyway, I agree that we need to revert a particular annotation for Extension points ASAP.

@stephenc
Copy link
Member Author

@oleg-nenashev well you removed the labels in the first place

screen shot 2016-10-24 at 21 15 56

screen shot 2016-10-24 at 21 15 39

So that sad panda is all yours

@stephenc
Copy link
Member Author

@oleg-nenashev in future, if the author adds a "needs-review" label, perhaps check with the author before removing it

@stephenc
Copy link
Member Author

It was not WiP because we still had not established whether to change it or whether it was fully baked... the outcome of the review would have been a decision on whether it is WIP or ready for merge

@oleg-nenashev
Copy link
Member

well. For such cases we can use the "on-hold" label now. "needs-review" and all other such guys are not documented, hence I agree that may be misreadings. I'll keep the Sad Panda's head for myself since unlikely Irish customs accepts such parcels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready-for-merge The PR is ready to go, and it will be merged soon if there is no negative feedback
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants