YARN-6177. Yarn client should exit with an informative error message if an incompatible Jersey library is used at client. Contributed by Weiwei Yang.
This commit is contained in:
parent
4fa1afdb88
commit
5d339c46f5
|
@ -381,6 +381,16 @@ public class YarnClientImpl extends YarnClient {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
throw e;
|
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();
|
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
|
@Test
|
||||||
public void testStartWithTimelineV15() throws Exception {
|
public void testStartWithTimelineV15() throws Exception {
|
||||||
Configuration conf = new Configuration();
|
Configuration conf = new Configuration();
|
||||||
|
@ -186,6 +166,89 @@ public class TestYarnClient {
|
||||||
client.stop();
|
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")
|
@SuppressWarnings("deprecation")
|
||||||
@Test (timeout = 30000)
|
@Test (timeout = 30000)
|
||||||
public void testSubmitApplication() throws Exception {
|
public void testSubmitApplication() throws Exception {
|
||||||
|
@ -1680,4 +1743,66 @@ public class TestYarnClient {
|
||||||
Assert.assertEquals(containerId, request.getContainerId());
|
Assert.assertEquals(containerId, request.getContainerId());
|
||||||
Assert.assertEquals(command, request.getCommand());
|
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