YARN-6150. TestContainerManagerSecurity tests for Yarn Server are flakey. Contributed by Daniel Sturman and Ray Chiang.

This commit is contained in:
Akira Ajisaka 2017-07-25 15:11:21 +09:00
parent f2921e51f0
commit 218b1b33ff
No known key found for this signature in database
GPG Key ID: C1EDBB9CA400FD50

View File

@ -67,6 +67,7 @@
import org.apache.hadoop.yarn.server.nodemanager.Context; import org.apache.hadoop.yarn.server.nodemanager.Context;
import org.apache.hadoop.yarn.server.nodemanager.NodeManager; import org.apache.hadoop.yarn.server.nodemanager.NodeManager;
import org.apache.hadoop.yarn.server.nodemanager.security.NMTokenSecretManagerInNM; import org.apache.hadoop.yarn.server.nodemanager.security.NMTokenSecretManagerInNM;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.MockRMApp; import org.apache.hadoop.yarn.server.resourcemanager.rmapp.MockRMApp;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppState; import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppState;
import org.apache.hadoop.yarn.server.resourcemanager.security.NMTokenSecretManagerInRM; import org.apache.hadoop.yarn.server.resourcemanager.security.NMTokenSecretManagerInRM;
@ -122,7 +123,13 @@ public void tearDown() {
testRootDir.delete(); testRootDir.delete();
} }
@Parameters /*
* Run two tests: one with no security ("simple") and one with "Secure"
* The first parameter is just the test name to make it easier to debug
* and to give details in say an IDE. The second is the configuraiton
* object to use.
*/
@Parameters(name = "{0}")
public static Collection<Object[]> configs() { public static Collection<Object[]> configs() {
Configuration configurationWithoutSecurity = new Configuration(); Configuration configurationWithoutSecurity = new Configuration();
configurationWithoutSecurity.set( configurationWithoutSecurity.set(
@ -142,16 +149,18 @@ public static Collection<Object[]> configs() {
YarnConfiguration.NM_WEBAPP_SPNEGO_KEYTAB_FILE_KEY, YarnConfiguration.NM_WEBAPP_SPNEGO_KEYTAB_FILE_KEY,
httpSpnegoKeytabFile.getAbsolutePath()); httpSpnegoKeytabFile.getAbsolutePath());
return Arrays.asList(new Object[][] { { configurationWithoutSecurity }, return Arrays.asList(new Object[][] {
{ configurationWithSecurity } }); {"Simple", configurationWithoutSecurity},
{"Secure", configurationWithSecurity}});
} }
public TestContainerManagerSecurity(Configuration conf) { public TestContainerManagerSecurity(String name, Configuration conf) {
LOG.info("RUNNING TEST " + name);
conf.setLong(YarnConfiguration.RM_AM_EXPIRY_INTERVAL_MS, 100000L); conf.setLong(YarnConfiguration.RM_AM_EXPIRY_INTERVAL_MS, 100000L);
this.conf = conf; this.conf = conf;
} }
@Test (timeout = 120000) @Test
public void testContainerManager() throws Exception { public void testContainerManager() throws Exception {
// TestNMTokens. // TestNMTokens.
@ -165,7 +174,11 @@ public void testContainerManager() throws Exception {
} }
private void testNMTokens(Configuration conf) throws Exception { /**
* Run a series of tests using different NMTokens. A configuration is
* provided for managing creating of the tokens and rpc.
*/
private void testNMTokens(Configuration testConf) throws Exception {
NMTokenSecretManagerInRM nmTokenSecretManagerRM = NMTokenSecretManagerInRM nmTokenSecretManagerRM =
yarnCluster.getResourceManager().getRMContext() yarnCluster.getResourceManager().getRMContext()
.getNMTokenSecretManager(); .getNMTokenSecretManager();
@ -201,7 +214,7 @@ private void testNMTokens(Configuration conf) throws Exception {
* nmToken error. (This can be best tested if we roll over NMToken * nmToken error. (This can be best tested if we roll over NMToken
* master key twice). * master key twice).
*/ */
YarnRPC rpc = YarnRPC.create(conf); YarnRPC rpc = YarnRPC.create(testConf);
String user = "test"; String user = "test";
Resource r = Resource.newInstance(1024, 1); Resource r = Resource.newInstance(1024, 1);
@ -233,7 +246,7 @@ private void testNMTokens(Configuration conf) throws Exception {
StringBuilder sb; StringBuilder sb;
// testInvalidNMToken ... creating NMToken using different secret manager. // testInvalidNMToken ... creating NMToken using different secret manager.
NMTokenSecretManagerInRM tempManager = new NMTokenSecretManagerInRM(conf); NMTokenSecretManagerInRM tempManager = new NMTokenSecretManagerInRM(testConf);
tempManager.rollMasterKey(); tempManager.rollMasterKey();
do { do {
tempManager.rollMasterKey(); tempManager.rollMasterKey();
@ -252,7 +265,9 @@ private void testNMTokens(Configuration conf) throws Exception {
} }
String errorMsg = testStartContainer(rpc, validAppAttemptId, validNode, String errorMsg = testStartContainer(rpc, validAppAttemptId, validNode,
validContainerToken, null, true); validContainerToken, null, true);
Assert.assertTrue(errorMsg.contains(sb.toString())); Assert.assertTrue("In calling " + validNode + " exception was '"
+ errorMsg + "' but doesn't contain '"
+ sb.toString() + "'", errorMsg.contains(sb.toString()));
org.apache.hadoop.yarn.api.records.Token invalidNMToken = org.apache.hadoop.yarn.api.records.Token invalidNMToken =
tempManager.createNMToken(validAppAttemptId, validNode, user); tempManager.createNMToken(validAppAttemptId, validNode, user);
@ -277,7 +292,7 @@ private void testNMTokens(Configuration conf) throws Exception {
validContainerToken, invalidNMToken, true))); validContainerToken, invalidNMToken, true)));
// using correct tokens. nmtoken for app attempt should get saved. // using correct tokens. nmtoken for app attempt should get saved.
conf.setInt(YarnConfiguration.RM_CONTAINER_ALLOC_EXPIRY_INTERVAL_MS, testConf.setInt(YarnConfiguration.RM_CONTAINER_ALLOC_EXPIRY_INTERVAL_MS,
4 * 60 * 1000); 4 * 60 * 1000);
validContainerToken = validContainerToken =
containerTokenSecretManager.createContainerToken(validContainerId, containerTokenSecretManager.createContainerToken(validContainerId,
@ -375,8 +390,8 @@ private void testNMTokens(Configuration conf) throws Exception {
Assert.assertTrue(testGetContainer(rpc, validAppAttemptId, validNode, Assert.assertTrue(testGetContainer(rpc, validAppAttemptId, validNode,
validContainerId, validNMToken, false).contains(sb.toString())); validContainerId, validNMToken, false).contains(sb.toString()));
// using appAttempt-1 NMtoken for launching container for appAttempt-2 should // using appAttempt-1 NMtoken for launching container for appAttempt-2
// succeed. // should succeed.
ApplicationAttemptId attempt2 = ApplicationAttemptId.newInstance(appId, 2); ApplicationAttemptId attempt2 = ApplicationAttemptId.newInstance(appId, 2);
Token attempt1NMToken = Token attempt1NMToken =
nmTokenSecretManagerRM nmTokenSecretManagerRM
@ -390,13 +405,20 @@ private void testNMTokens(Configuration conf) throws Exception {
} }
private void waitForContainerToFinishOnNM(ContainerId containerId) { private void waitForContainerToFinishOnNM(ContainerId containerId) {
Context nmContet = yarnCluster.getNodeManager(0).getNMContext(); Context nmContext = yarnCluster.getNodeManager(0).getNMContext();
int interval = 4 * 60; // Max time for container token to expire. int interval = 4 * 60; // Max time for container token to expire.
Assert.assertNotNull(nmContet.getContainers().containsKey(containerId));
Assert.assertNotNull(nmContext.getContainers().containsKey(containerId));
// Get the container first, as it may be removed from the Context
// by asynchronous calls.
// This was leading to a flakey test as otherwise the container could
// be removed and end up null.
Container waitContainer = nmContext.getContainers().get(containerId);
while ((interval-- > 0) while ((interval-- > 0)
&& !nmContet.getContainers().get(containerId) && !waitContainer.cloneAndGetContainerStatus()
.cloneAndGetContainerStatus().getState() .getState().equals(ContainerState.COMPLETE)) {
.equals(ContainerState.COMPLETE)) {
try { try {
LOG.info("Waiting for " + containerId + " to complete."); LOG.info("Waiting for " + containerId + " to complete.");
Thread.sleep(1000); Thread.sleep(1000);
@ -407,7 +429,8 @@ private void waitForContainerToFinishOnNM(ContainerId containerId) {
// explicitly acked by RM. Now, manually remove it for testing. // explicitly acked by RM. Now, manually remove it for testing.
yarnCluster.getNodeManager(0).getNodeStatusUpdater() yarnCluster.getNodeManager(0).getNodeStatusUpdater()
.addCompletedContainer(containerId); .addCompletedContainer(containerId);
nmContet.getContainers().remove(containerId); LOG.info("Removing container from NMContext, containerID = " + containerId);
nmContext.getContainers().remove(containerId);
} }
protected void waitForNMToReceiveNMTokenKey( protected void waitForNMToReceiveNMTokenKey(
@ -439,7 +462,7 @@ private String testStopContainer(YarnRPC rpc,
ContainerId containerId, Token nmToken, boolean isExceptionExpected) { ContainerId containerId, Token nmToken, boolean isExceptionExpected) {
try { try {
stopContainer(rpc, nmToken, stopContainer(rpc, nmToken,
Arrays.asList(new ContainerId[] { containerId }), appAttemptId, Arrays.asList(new ContainerId[] {containerId}), appAttemptId,
nodeId); nodeId);
if (isExceptionExpected) { if (isExceptionExpected) {
fail("Exception was expected!!"); fail("Exception was expected!!");
@ -525,7 +548,8 @@ private void stopContainer(YarnRPC rpc, Token nmToken,
proxy = proxy =
getContainerManagementProtocolProxy(rpc, nmToken, nodeId, getContainerManagementProtocolProxy(rpc, nmToken, nodeId,
appAttemptId.toString()); appAttemptId.toString());
GetContainerStatusesResponse statuses = proxy.getContainerStatuses(request); GetContainerStatusesResponse statuses
= proxy.getContainerStatuses(request);
if (statuses.getFailedRequests() != null if (statuses.getFailedRequests() != null
&& statuses.getFailedRequests().containsKey(containerId)) { && statuses.getFailedRequests().containsKey(containerId)) {
parseAndThrowException(statuses.getFailedRequests().get(containerId) parseAndThrowException(statuses.getFailedRequests().get(containerId)
@ -546,7 +570,7 @@ private void startContainer(final YarnRPC rpc,
ContainerLaunchContext context = ContainerLaunchContext context =
Records.newRecord(ContainerLaunchContext.class); Records.newRecord(ContainerLaunchContext.class);
StartContainerRequest scRequest = StartContainerRequest scRequest =
StartContainerRequest.newInstance(context,containerToken); StartContainerRequest.newInstance(context, containerToken);
List<StartContainerRequest> list = new ArrayList<StartContainerRequest>(); List<StartContainerRequest> list = new ArrayList<StartContainerRequest>();
list.add(scRequest); list.add(scRequest);
StartContainersRequest allRequests = StartContainersRequest allRequests =
@ -582,7 +606,7 @@ protected ContainerManagementProtocol getContainerManagementProtocolProxy(
ContainerManagementProtocol proxy; ContainerManagementProtocol proxy;
UserGroupInformation ugi = UserGroupInformation.createRemoteUser(user); UserGroupInformation ugi = UserGroupInformation.createRemoteUser(user);
final InetSocketAddress addr = final InetSocketAddress addr =
NetUtils.createSocketAddr(nodeId.getHost(), nodeId.getPort()); new InetSocketAddress(nodeId.getHost(), nodeId.getPort());
if (nmToken != null) { if (nmToken != null) {
ugi.addToken(ConverterUtils.convertFromYarn(nmToken, addr)); ugi.addToken(ConverterUtils.convertFromYarn(nmToken, addr));
} }
@ -645,9 +669,11 @@ private void testContainerToken(Configuration conf) throws IOException,
ContainerTokenIdentifier containerTokenIdentifier = ContainerTokenIdentifier containerTokenIdentifier =
getContainerTokenIdentifierFromToken(containerToken); getContainerTokenIdentifierFromToken(containerToken);
// Verify new compatible version ContainerTokenIdentifier can work successfully. // Verify new compatible version ContainerTokenIdentifier
// can work successfully.
ContainerTokenIdentifierForTest newVersionTokenIdentifier = ContainerTokenIdentifierForTest newVersionTokenIdentifier =
new ContainerTokenIdentifierForTest(containerTokenIdentifier, "message"); new ContainerTokenIdentifierForTest(containerTokenIdentifier,
"message");
byte[] password = byte[] password =
containerTokenSecretManager.createPassword(newVersionTokenIdentifier); containerTokenSecretManager.createPassword(newVersionTokenIdentifier);