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

[LIVY-494] Add thriftserver to Livy server #107

Closed
wants to merge 5 commits into from

Conversation

mgaido91
Copy link
Contributor

What changes were proposed in this pull request?

The PR adds a configuration parameter in order to startup also the thriftserver when starting Livy server.

Apart from this trivial change, other 3 main things were needed and are present in this PR:

  • Add the thriftserver JARs to the assembly and the livy-server script;
  • A small refactor in order to enforce impersonation in the *Session classes, instead of in the *Servlet ones, so that it is picked up by the thriftserver module too (this change is not strictly needed, but I consider it a better option that duplicating this logic in the thriftserver module too);
  • Creating a UGI from the configured keytab. This is needed because the thriftserver requires the UGI to be created from a keytab in order to work properly and previously Livy was using a UGI generated from the cached TGT (created by the kinit command).

How was this patch tested?

Manual test: starting the server and having it up for more than 9 days.

@mgaido91
Copy link
Contributor Author

cc @jerryshao @vanzin

@mgaido91
Copy link
Contributor Author

kindly ping @jerryshao @vanzin

@@ -115,6 +117,15 @@ class LivyServer extends Logging {
error("Failed to run kinit, stopping the server.")
sys.exit(1)
}
// This is and should be the only place where a login() on the UGI is performed. Any
Copy link

Choose a reason for hiding this comment

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

The comment you have in the PR description, that the current Hive Thrift code needs this, is way better here, since otherwise it's unclear why you need this login at all.

The rest of the comment is basically a long way of explaining the assert you added later in this class.

@@ -134,6 +145,14 @@ class LivyServer extends Logging {
server = new WebServer(livyConf, host, port)
server.context.setResourceBase("src/main/org/apache/livy/server")

def invokeOnStaticThriftserver(method: String, args: (Class[_], Object)*): Object = {
Copy link

Choose a reason for hiding this comment

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

Instead of reflection hacks I'd use an interface to define a factory method to be called. You could use a java.util.ServiceLoader to load the class too, but with a single one, it's fine to use Class.forName.

target.orElse(Option(remoteUser(req)))
} else {
None
protected def withHaltOnForbiddenAction(f: => S): S = {
Copy link

Choose a reason for hiding this comment

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

Not a fan of the name; just haltOnForbiddenAction sounds better.

Also, it's been a long time since I looked at scalatra... but could you do this instead in the error handler of SessionServlet, and map the exception to the http error? Or is the "halt" required for some reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it is doable. I'll try and do that. Thanks.

accessManager: AccessManager): Option[String] = {
if (livyConf.getBoolean(LivyConf.IMPERSONATION_ENABLED)) {
if (!target.forall(hasSuperAccess(_, requestUser, accessManager))) {
throw new InvalidParameterException(
Copy link

Choose a reason for hiding this comment

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

AccessControlException makes more sense here.


object ThriftServerFactory {
def getInstance: ThriftServerFactory = {
Class.forName("org.apache.livy.thriftserver.LivyThriftServer$").getField("MODULE$").get(null)
Copy link

Choose a reason for hiding this comment

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

Instead of this, have a ThriftServerFactoryImpl class that implements the start method, and instantiate it with Class.forName(blah).newInstance(). That's closer to what ServiceLoader does.

Copy link

@vanzin vanzin left a comment

Choose a reason for hiding this comment

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

Looks ok, just left two minor comments.

* request's user - which may not be defined - otherwise, or `None` if impersonation is
* disabled.
*/
def checkImpersonation(
Copy link

Choose a reason for hiding this comment

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

Since this method requires an accessManager, wouldn't it be better as a method in the AccessManager class itself? (Same for hasSuperAccess.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure, I agree. I'll move hasViewAccess and hasModifyAccess too for coherency. Seems also that the AccessManager is the right place for putting the logic of these methods IMHO. Thanks.

@@ -115,6 +117,16 @@ class LivyServer extends Logging {
error("Failed to run kinit, stopping the server.")
sys.exit(1)
}
// This is and should be the only place where a login() on the UGI is performed.
Copy link

Choose a reason for hiding this comment

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

This should be pretty easy to check with a scalastyle rule that disallows calls to UserGroupInformation.login* (and disabling the check around this call). Not foolproof, but should catch inadvertent use at least.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually this wouldn't really work as - for instance - UserGroupInformation.loginUserFromKeytabAndReturnUGI is ok to be called, as it doesn't perform a UserGroupInformation.login(). We can list all the APIs which we should not call, but I am not sure it is worth.

@mgaido91
Copy link
Contributor Author

Travis failure is intermittent, the branch build passed: https://travis-ci.org/mgaido91/incubator-livy/builds/432380763

@vanzin
Copy link

vanzin commented Sep 24, 2018

Merging to master.

@asfgit asfgit closed this in 134713d Sep 24, 2018
mgaido91 added a commit to mgaido91/incubator-livy that referenced this pull request Sep 25, 2018
## What changes were proposed in this pull request?

The PR adds a configuration parameter in order to startup also the thriftserver when starting Livy server.

Apart from this trivial change, other 3 main things were needed and are present in this PR:

 - Add the thriftserver JARs to the assembly and the livy-server script;
 - A small refactor in order to enforce impersonation in the `*Session` classes, instead of in the `*Servlet` ones, so that it is picked up by the thriftserver module too (this change is not strictly needed, but I consider it a better option that duplicating this logic in the thriftserver module too);
 - Creating a UGI from the configured keytab. This is needed because the thriftserver requires the UGI to be created from a keytab in order to work properly and previously Livy was using a UGI generated from the cached TGT (created by the `kinit` command).

## How was this patch tested?

Manual test: starting the server and having it up for more than 9 days.

Author: Marco Gaido <mgaido@hortonworks.com>

Closes apache#107 from mgaido91/LIVY-494.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants