YARN-6177. Yarn client should exit with an informative error message if an incompatible Jersey library is used at client. Contributed by Weiwei Yang.
(cherry picked from commit 5d339c46f5
)
This commit is contained in:
parent
f07b52dc5a
commit
8fc67e5973
|
@ -376,6 +376,16 @@ public class YarnClientImpl extends YarnClient {
|
|||
return null;
|
||||
}
|
||||
throw e;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
NoClassDefFoundError wrappedError = new NoClassDefFoundError(
|
||||
e.getMessage() + ". It appears that the timeline client "
|
||||
+ "failed to initiate because an incompatible dependency "
|
||||
+ "in classpath. If timeline service is optional to this "
|
||||
+ "client, try to work around by setting "
|
||||
+ YarnConfiguration.TIMELINE_SERVICE_ENABLED
|
||||
+ " to false in client configuration.");
|
||||
wrappedError.setStackTrace(e.getStackTrace());
|
||||
throw wrappedError;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -155,26 +155,6 @@ public class TestYarnClient {
|
|||
rm.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartWithTimelineV15Failure() throws Exception{
|
||||
Configuration conf = new Configuration();
|
||||
conf.setBoolean(YarnConfiguration.TIMELINE_SERVICE_ENABLED, true);
|
||||
conf.setFloat(YarnConfiguration.TIMELINE_SERVICE_VERSION, 1.5f);
|
||||
conf.setBoolean(YarnConfiguration.TIMELINE_SERVICE_CLIENT_BEST_EFFORT,
|
||||
true);
|
||||
YarnClient client = YarnClient.createYarnClient();
|
||||
if(client instanceof YarnClientImpl) {
|
||||
YarnClientImpl impl = (YarnClientImpl) client;
|
||||
YarnClientImpl spyClient = spy(impl);
|
||||
when(spyClient.createTimelineClient()).thenThrow(
|
||||
new IOException("ATS v1.5 client initialization failed. "));
|
||||
spyClient.init(conf);
|
||||
spyClient.start();
|
||||
spyClient.getTimelineDelegationToken();
|
||||
spyClient.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartWithTimelineV15() throws Exception {
|
||||
Configuration conf = new Configuration();
|
||||
|
@ -186,6 +166,89 @@ public class TestYarnClient {
|
|||
client.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartTimelineClientWithErrors()
|
||||
throws Exception {
|
||||
// If timeline client failed to init with a NoClassDefFoundError
|
||||
// it should be wrapped with an informative error message
|
||||
testCreateTimelineClientWithError(
|
||||
1.5f,
|
||||
true,
|
||||
false,
|
||||
new NoClassDefFoundError("Mock a NoClassDefFoundError"),
|
||||
new CreateTimelineClientErrorVerifier(1) {
|
||||
@Override
|
||||
public void verifyError(Throwable e) {
|
||||
Assert.assertTrue(e instanceof NoClassDefFoundError);
|
||||
Assert.assertTrue(e.getMessage() != null &&
|
||||
e.getMessage().contains(
|
||||
YarnConfiguration.TIMELINE_SERVICE_ENABLED));
|
||||
}
|
||||
});
|
||||
|
||||
// Disable timeline service for this client,
|
||||
// yarn client will not fail when such error happens
|
||||
testCreateTimelineClientWithError(
|
||||
1.5f,
|
||||
false,
|
||||
false,
|
||||
new NoClassDefFoundError("Mock a NoClassDefFoundError"),
|
||||
new CreateTimelineClientErrorVerifier(0) {
|
||||
@Override public void verifyError(Throwable e) {
|
||||
Assert.fail("NoClassDefFoundError while creating timeline client"
|
||||
+ "should be tolerated when timeline service is disabled.");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Set best-effort to true, verify an error is still fatal
|
||||
testCreateTimelineClientWithError(
|
||||
1.5f,
|
||||
true,
|
||||
true,
|
||||
new NoClassDefFoundError("Mock a NoClassDefFoundError"),
|
||||
new CreateTimelineClientErrorVerifier(1) {
|
||||
@Override public void verifyError(Throwable e) {
|
||||
Assert.assertTrue(e instanceof NoClassDefFoundError);
|
||||
Assert.assertTrue(e.getMessage() != null &&
|
||||
e.getMessage().contains(
|
||||
YarnConfiguration.TIMELINE_SERVICE_ENABLED));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Set best-effort to false, verify that an exception
|
||||
// causes the client to fail
|
||||
testCreateTimelineClientWithError(
|
||||
1.5f,
|
||||
true,
|
||||
false,
|
||||
new IOException("ATS v1.5 client initialization failed."),
|
||||
new CreateTimelineClientErrorVerifier(1) {
|
||||
@Override
|
||||
public void verifyError(Throwable e) {
|
||||
Assert.assertTrue(e instanceof IOException);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Set best-effort to true, verify that an normal exception
|
||||
// won't fail the entire client
|
||||
testCreateTimelineClientWithError(
|
||||
1.5f,
|
||||
true,
|
||||
true,
|
||||
new IOException("ATS v1.5 client initialization failed."),
|
||||
new CreateTimelineClientErrorVerifier(0) {
|
||||
@Override
|
||||
public void verifyError(Throwable e) {
|
||||
Assert.fail("IOException while creating timeline client"
|
||||
+ "should be tolerated when best effort is true");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test (timeout = 30000)
|
||||
public void testSubmitApplication() throws Exception {
|
||||
|
@ -1680,4 +1743,66 @@ public class TestYarnClient {
|
|||
Assert.assertEquals(containerId, request.getContainerId());
|
||||
Assert.assertEquals(command, request.getCommand());
|
||||
}
|
||||
|
||||
private void testCreateTimelineClientWithError(
|
||||
float timelineVersion,
|
||||
boolean timelineServiceEnabled,
|
||||
boolean timelineClientBestEffort,
|
||||
Throwable mockErr,
|
||||
CreateTimelineClientErrorVerifier errVerifier) throws Exception {
|
||||
Configuration conf = new Configuration();
|
||||
conf.setBoolean(YarnConfiguration.TIMELINE_SERVICE_ENABLED,
|
||||
timelineServiceEnabled);
|
||||
conf.setBoolean(YarnConfiguration.TIMELINE_SERVICE_CLIENT_BEST_EFFORT,
|
||||
timelineClientBestEffort);
|
||||
conf.setFloat(YarnConfiguration.TIMELINE_SERVICE_VERSION,
|
||||
timelineVersion);
|
||||
YarnClient client = new MockYarnClient();
|
||||
if (client instanceof YarnClientImpl) {
|
||||
YarnClientImpl impl = (YarnClientImpl) client;
|
||||
YarnClientImpl spyClient = spy(impl);
|
||||
when(spyClient.createTimelineClient()).thenThrow(mockErr);
|
||||
CreateTimelineClientErrorVerifier verifier = spy(errVerifier);
|
||||
spyClient.init(conf);
|
||||
spyClient.start();
|
||||
|
||||
ApplicationSubmissionContext context =
|
||||
mock(ApplicationSubmissionContext.class);
|
||||
ContainerLaunchContext containerContext =
|
||||
mock(ContainerLaunchContext.class);
|
||||
ApplicationId applicationId =
|
||||
ApplicationId.newInstance(System.currentTimeMillis(), 1);
|
||||
when(containerContext.getTokens()).thenReturn(null);
|
||||
when(context.getApplicationId()).thenReturn(applicationId);
|
||||
when(spyClient.isSecurityEnabled()).thenReturn(true);
|
||||
when(context.getAMContainerSpec()).thenReturn(containerContext);
|
||||
|
||||
try {
|
||||
spyClient.submitApplication(context);
|
||||
} catch (Throwable e) {
|
||||
verifier.verifyError(e);
|
||||
} finally {
|
||||
// Make sure the verifier runs with expected times
|
||||
// This is required because in case throwable is swallowed
|
||||
// and verifyError never gets the chance to run
|
||||
verify(verifier, times(verifier.getExpectedTimes()))
|
||||
.verifyError(any(Throwable.class));
|
||||
spyClient.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class CreateTimelineClientErrorVerifier {
|
||||
// Verify verifyError gets executed with expected times
|
||||
private int times = 0;
|
||||
protected CreateTimelineClientErrorVerifier(int times) {
|
||||
this.times = times;
|
||||
}
|
||||
public int getExpectedTimes() {
|
||||
return this.times;
|
||||
}
|
||||
// Verification a throwable is in desired state
|
||||
// E.g verify type and error message
|
||||
public abstract void verifyError(Throwable e);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue