From ab231f5e1c54c5d9a70e6d1d0cb274ffffe49bf5 Mon Sep 17 00:00:00 2001 From: Jason Lowe Date: Wed, 5 Apr 2017 12:36:26 -0500 Subject: [PATCH] YARN-6403. Invalid local resource request can raise NPE and make NM exit. Contributed by Tao Yang --- .../impl/pb/ContainerLaunchContextPBImpl.java | 13 ++++ .../TestApplicationClientProtocolRecords.java | 61 +++++++++++++++++++ .../ContainerManagerImpl.java | 10 +++ .../TestContainerManagerWithLCE.java | 11 ++++ .../TestContainerManager.java | 45 ++++++++++++++ 5 files changed, 140 insertions(+) create mode 100644 hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/api/records/impl/pb/TestApplicationClientProtocolRecords.java diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/ContainerLaunchContextPBImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/ContainerLaunchContextPBImpl.java index 12dcfcd9f8f..3d1c08e2b88 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/ContainerLaunchContextPBImpl.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/ContainerLaunchContextPBImpl.java @@ -197,10 +197,23 @@ extends ContainerLaunchContext { final Map localResources) { if (localResources == null) return; + checkLocalResources(localResources); initLocalResources(); this.localResources.clear(); this.localResources.putAll(localResources); } + + private void checkLocalResources(Map localResources) { + for (Map.Entry rsrcEntry : localResources + .entrySet()) { + if (rsrcEntry.getValue() == null + || rsrcEntry.getValue().getResource() == null) { + throw new NullPointerException( + "Null resource URL for local resource " + rsrcEntry.getKey() + " : " + + rsrcEntry.getValue()); + } + } + } private void addLocalResourcesToProto() { maybeInitBuilder(); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/api/records/impl/pb/TestApplicationClientProtocolRecords.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/api/records/impl/pb/TestApplicationClientProtocolRecords.java new file mode 100644 index 00000000000..36102635e86 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/api/records/impl/pb/TestApplicationClientProtocolRecords.java @@ -0,0 +1,61 @@ +/** +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package org.apache.hadoop.yarn.api.records.impl.pb; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; +import org.apache.hadoop.yarn.api.records.LocalResource; +import org.apache.hadoop.yarn.api.records.LocalResourceType; +import org.apache.hadoop.yarn.api.records.LocalResourceVisibility; +import org.apache.hadoop.yarn.factories.RecordFactory; +import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider; +import org.junit.Assert; +import org.junit.Test; + +public class TestApplicationClientProtocolRecords { + + /* + * This test validates the scenario in which the client sets a null value for + * local resource URL. + */ + @Test + public void testCLCPBImplNullResourceURL() throws IOException { + RecordFactory recordFactory = RecordFactoryProvider.getRecordFactory(null); + // Throw NPE if resource URL is null + try { + LocalResource rsrc_alpha = recordFactory.newRecordInstance(LocalResource.class); + rsrc_alpha.setResource(null); + rsrc_alpha.setSize(-1); + rsrc_alpha.setVisibility(LocalResourceVisibility.APPLICATION); + rsrc_alpha.setType(LocalResourceType.FILE); + rsrc_alpha.setTimestamp(System.currentTimeMillis()); + Map localResources = + new HashMap(); + localResources.put("null_url_resource", rsrc_alpha); + ContainerLaunchContext containerLaunchContext = recordFactory.newRecordInstance(ContainerLaunchContext.class); + containerLaunchContext.setLocalResources(localResources); + Assert.fail("Setting an invalid local resource should be an error!"); + } catch (NullPointerException e) { + Assert.assertTrue(e.getMessage().contains("Null resource URL for local resource")); + } + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/ContainerManagerImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/ContainerManagerImpl.java index cca73acc504..92575b294a5 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/ContainerManagerImpl.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/ContainerManagerImpl.java @@ -75,6 +75,7 @@ import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; import org.apache.hadoop.yarn.api.records.ContainerState; import org.apache.hadoop.yarn.api.records.ContainerStatus; +import org.apache.hadoop.yarn.api.records.LocalResource; import org.apache.hadoop.yarn.api.records.LogAggregationContext; import org.apache.hadoop.yarn.api.records.NodeId; import org.apache.hadoop.yarn.api.records.Resource; @@ -898,6 +899,15 @@ public class ContainerManagerImpl extends CompositeService implements } } + // Sanity check for local resources + for (Map.Entry rsrc : launchContext + .getLocalResources().entrySet()) { + if (rsrc.getValue() == null || rsrc.getValue().getResource() == null) { + throw new YarnException( + "Null resource URL for local resource " + rsrc.getKey() + " : " + rsrc.getValue()); + } + } + Credentials credentials = YarnServerSecurityUtils.parseCredentials(launchContext); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestContainerManagerWithLCE.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestContainerManagerWithLCE.java index 47024c73896..2ae18a3216e 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestContainerManagerWithLCE.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestContainerManagerWithLCE.java @@ -269,6 +269,17 @@ public class TestContainerManagerWithLCE extends TestContainerManager { super.testForcefulShutdownSignal(); } + @Override + public void testStartContainerFailureWithInvalidLocalResource() throws Exception { + // Don't run the test if the binary is not available. + if (!shouldRunTest()) { + LOG.info("LCE binary path is not passed. Not running the test"); + return; + } + LOG.info("Running testStartContainerFailureWithInvalidLocalResource"); + super.testStartContainerFailureWithInvalidLocalResource(); + } + private boolean shouldRunTest() { return System .getProperty(YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH) != null; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/TestContainerManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/TestContainerManager.java index 68a1b00201e..d2e8f7e8212 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/TestContainerManager.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/TestContainerManager.java @@ -1334,4 +1334,49 @@ public class TestContainerManager extends BaseContainerManagerTest { Assert.assertEquals(signal, signalContext.getSignal()); } } + + @Test + public void testStartContainerFailureWithInvalidLocalResource() + throws Exception { + containerManager.start(); + LocalResource rsrc_alpha = + recordFactory.newRecordInstance(LocalResource.class); + rsrc_alpha.setResource(null); + rsrc_alpha.setSize(-1); + rsrc_alpha.setVisibility(LocalResourceVisibility.APPLICATION); + rsrc_alpha.setType(LocalResourceType.FILE); + rsrc_alpha.setTimestamp(System.currentTimeMillis()); + Map localResources = + new HashMap(); + localResources.put("invalid_resource", rsrc_alpha); + ContainerLaunchContext containerLaunchContext = + recordFactory.newRecordInstance(ContainerLaunchContext.class); + ContainerLaunchContext spyContainerLaunchContext = + Mockito.spy(containerLaunchContext); + Mockito.when(spyContainerLaunchContext.getLocalResources()) + .thenReturn(localResources); + + ContainerId cId = createContainerId(0); + String user = "start_container_fail"; + Token containerToken = + createContainerToken(cId, DUMMY_RM_IDENTIFIER, context.getNodeId(), + user, context.getContainerTokenSecretManager()); + StartContainerRequest request = StartContainerRequest + .newInstance(spyContainerLaunchContext, containerToken); + + // start containers + List startRequest = + new ArrayList(); + startRequest.add(request); + StartContainersRequest requestList = + StartContainersRequest.newInstance(startRequest); + + StartContainersResponse response = + containerManager.startContainers(requestList); + Assert.assertTrue(response.getFailedRequests().size() == 1); + Assert.assertTrue(response.getSuccessfullyStartedContainers().size() == 0); + Assert.assertTrue(response.getFailedRequests().containsKey(cId)); + Assert.assertTrue(response.getFailedRequests().get(cId).getMessage() + .contains("Null resource URL for local resource")); + } }