YARN-1867. Fixed a bug in ResourceManager that was causing invalid ACL checks in the web-services after fail-over. Contributed by Vinod Kumar Vavilapalli.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1581662 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Vinod Kumar Vavilapalli 2014-03-26 03:03:17 +00:00
parent dce1d20383
commit 8a9ae9e3ec
6 changed files with 89 additions and 35 deletions

View File

@ -577,6 +577,9 @@ Release 2.4.0 - UNRELEASED
YARN-1866. Fixed an issue with renewal of RM-delegation tokens on restart or YARN-1866. Fixed an issue with renewal of RM-delegation tokens on restart or
fail-over. (Jian He via vinodkv) fail-over. (Jian He via vinodkv)
YARN-1867. Fixed a bug in ResourceManager that was causing invalid ACL checks
in the web-services after fail-over. (Vinod Kumar Vavilapalli)
Release 2.3.1 - UNRELEASED Release 2.3.1 - UNRELEASED
INCOMPATIBLE CHANGES INCOMPATIBLE CHANGES

View File

@ -81,6 +81,7 @@ public class RMContextImpl implements RMContext {
private ApplicationMasterService applicationMasterService; private ApplicationMasterService applicationMasterService;
private RMApplicationHistoryWriter rmApplicationHistoryWriter; private RMApplicationHistoryWriter rmApplicationHistoryWriter;
private ConfigurationProvider configurationProvider; private ConfigurationProvider configurationProvider;
/** /**
* Default constructor. To be used in conjunction with setter methods for * Default constructor. To be used in conjunction with setter methods for
* individual fields. * individual fields.

View File

@ -141,19 +141,14 @@ public class ResourceManager extends CompositeService implements Recoverable {
protected ResourceScheduler scheduler; protected ResourceScheduler scheduler;
private ClientRMService clientRM; private ClientRMService clientRM;
protected ApplicationMasterService masterService; protected ApplicationMasterService masterService;
private ApplicationMasterLauncher applicationMasterLauncher;
private ContainerAllocationExpirer containerAllocationExpirer;
protected NMLivelinessMonitor nmLivelinessMonitor; protected NMLivelinessMonitor nmLivelinessMonitor;
protected NodesListManager nodesListManager; protected NodesListManager nodesListManager;
private EventHandler<SchedulerEvent> schedulerDispatcher;
protected RMAppManager rmAppManager; protected RMAppManager rmAppManager;
protected ApplicationACLsManager applicationACLsManager; protected ApplicationACLsManager applicationACLsManager;
protected QueueACLsManager queueACLsManager; protected QueueACLsManager queueACLsManager;
private DelegationTokenRenewer delegationTokenRenewer;
private WebApp webApp; private WebApp webApp;
private AppReportFetcher fetcher = null; private AppReportFetcher fetcher = null;
protected ResourceTrackerService resourceTracker; protected ResourceTrackerService resourceTracker;
private boolean recoveryEnabled;
private String webAppAddress; private String webAppAddress;
private ConfigurationProvider configurationProvider = null; private ConfigurationProvider configurationProvider = null;
@ -333,6 +328,14 @@ protected static void validateConfigs(Configuration conf) {
*/ */
@Private @Private
class RMActiveServices extends CompositeService { class RMActiveServices extends CompositeService {
private DelegationTokenRenewer delegationTokenRenewer;
private EventHandler<SchedulerEvent> schedulerDispatcher;
private ApplicationMasterLauncher applicationMasterLauncher;
private ContainerAllocationExpirer containerAllocationExpirer;
private boolean recoveryEnabled;
RMActiveServices() { RMActiveServices() {
super("RMActiveServices"); super("RMActiveServices");
} }
@ -1009,6 +1012,11 @@ public QueueACLsManager getQueueACLsManager() {
return this.queueACLsManager; return this.queueACLsManager;
} }
@Private
WebApp getWebapp() {
return this.webApp;
}
@Override @Override
public void recover(RMState state) throws Exception { public void recover(RMState state) throws Exception {
// recover RMdelegationTokenSecretManager // recover RMdelegationTokenSecretManager
@ -1055,16 +1063,14 @@ private void resetDispatcher() {
rmContext.setDispatcher(rmDispatcher); rmContext.setDispatcher(rmDispatcher);
} }
/** /**
* Retrieve RM bind address from configuration * Retrieve RM bind address from configuration
* *
* @param conf * @param conf
* @return InetSocketAddress * @return InetSocketAddress
*/ */
public static InetSocketAddress getBindAddress(Configuration conf) { public static InetSocketAddress getBindAddress(Configuration conf) {
return conf.getSocketAddr(YarnConfiguration.RM_ADDRESS, return conf.getSocketAddr(YarnConfiguration.RM_ADDRESS,
YarnConfiguration.DEFAULT_RM_ADDRESS, YarnConfiguration.DEFAULT_RM_ADDRESS, YarnConfiguration.DEFAULT_RM_PORT);
YarnConfiguration.DEFAULT_RM_PORT);
} }
} }

View File

@ -101,19 +101,12 @@ public class RMWebServices {
private final ResourceManager rm; private final ResourceManager rm;
private static RecordFactory recordFactory = RecordFactoryProvider private static RecordFactory recordFactory = RecordFactoryProvider
.getRecordFactory(null); .getRecordFactory(null);
private final ApplicationACLsManager aclsManager;
private final QueueACLsManager queueACLsManager;
private final Configuration conf; private final Configuration conf;
private @Context HttpServletResponse response; private @Context HttpServletResponse response;
@Inject @Inject
public RMWebServices(final ResourceManager rm, public RMWebServices(final ResourceManager rm, Configuration conf) {
final ApplicationACLsManager aclsManager,
final QueueACLsManager queueACLsManager,
Configuration conf) {
this.rm = rm; this.rm = rm;
this.aclsManager = aclsManager;
this.queueACLsManager = queueACLsManager;
this.conf = conf; this.conf = conf;
} }
@ -125,10 +118,11 @@ protected Boolean hasAccess(RMApp app, HttpServletRequest hsr) {
callerUGI = UserGroupInformation.createRemoteUser(remoteUser); callerUGI = UserGroupInformation.createRemoteUser(remoteUser);
} }
if (callerUGI != null if (callerUGI != null
&& !(this.aclsManager.checkAccess(callerUGI, && !(this.rm.getApplicationACLsManager().checkAccess(callerUGI,
ApplicationAccessType.VIEW_APP, app.getUser(), ApplicationAccessType.VIEW_APP, app.getUser(),
app.getApplicationId()) || this.queueACLsManager.checkAccess( app.getApplicationId()) ||
callerUGI, QueueACL.ADMINISTER_QUEUE, app.getQueue()))) { this.rm.getQueueACLsManager().checkAccess(callerUGI,
QueueACL.ADMINISTER_QUEUE, app.getQueue()))) {
return false; return false;
} }
return true; return true;

View File

@ -80,6 +80,8 @@
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class MockRM extends ResourceManager { public class MockRM extends ResourceManager {
static final String ENABLE_WEBAPP = "mockrm.webapp.enabled";
public MockRM() { public MockRM() {
this(new YarnConfiguration()); this(new YarnConfiguration());
} }
@ -494,7 +496,12 @@ public RMAppManager getRMAppManager() {
@Override @Override
protected void startWepApp() { protected void startWepApp() {
// override to disable webapp if (getConfig().getBoolean(ENABLE_WEBAPP, false)) {
super.startWepApp();
return;
}
// Disable webapp
} }
public static void finishAMAndVerifyAppState(RMApp rmApp, MockRM rm, MockNM nm, public static void finishAMAndVerifyAppState(RMApp rmApp, MockRM rm, MockNM nm,

View File

@ -18,6 +18,18 @@
package org.apache.hadoop.yarn.server.resourcemanager; package org.apache.hadoop.yarn.server.resourcemanager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.net.InetSocketAddress;
import javax.ws.rs.core.MediaType;
import junit.framework.Assert;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
@ -25,10 +37,11 @@
import org.apache.hadoop.ha.HAServiceProtocol.HAServiceState; import org.apache.hadoop.ha.HAServiceProtocol.HAServiceState;
import org.apache.hadoop.ha.HAServiceProtocol.StateChangeRequestInfo; import org.apache.hadoop.ha.HAServiceProtocol.StateChangeRequestInfo;
import org.apache.hadoop.ha.HealthCheckFailedException; import org.apache.hadoop.ha.HealthCheckFailedException;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.service.AbstractService; import org.apache.hadoop.service.AbstractService;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.conf.HAUtil; import org.apache.hadoop.yarn.conf.HAUtil;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.event.Dispatcher; import org.apache.hadoop.yarn.event.Dispatcher;
import org.apache.hadoop.yarn.event.EventHandler; import org.apache.hadoop.yarn.event.EventHandler;
import org.apache.hadoop.yarn.exceptions.YarnRuntimeException; import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
@ -36,17 +49,15 @@
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt; import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptState; import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptState;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.QueueMetrics; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.QueueMetrics;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.io.IOException; import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import org.junit.Assert; import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TestRMHA { public class TestRMHA {
private Log LOG = LogFactory.getLog(TestRMHA.class); private Log LOG = LogFactory.getLog(TestRMHA.class);
@ -77,6 +88,10 @@ public void setUp() throws Exception {
configuration.set(HAUtil.addSuffix(confKey, RM2_NODE_ID), RM2_ADDRESS); configuration.set(HAUtil.addSuffix(confKey, RM2_NODE_ID), RM2_ADDRESS);
configuration.set(HAUtil.addSuffix(confKey, RM3_NODE_ID), RM3_ADDRESS); configuration.set(HAUtil.addSuffix(confKey, RM3_NODE_ID), RM3_ADDRESS);
} }
// Enable webapp to test web-services also
configuration.setBoolean(MockRM.ENABLE_WEBAPP, true);
configuration.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, true);
} }
private void checkMonitorHealth() throws IOException { private void checkMonitorHealth() throws IOException {
@ -97,7 +112,7 @@ private void checkStandbyRMFunctionality() throws IOException {
rm.adminService.getServiceStatus().isReadyToBecomeActive()); rm.adminService.getServiceStatus().isReadyToBecomeActive());
} }
private void checkActiveRMFunctionality() throws IOException { private void checkActiveRMFunctionality() throws Exception {
assertEquals(STATE_ERR, HAServiceState.ACTIVE, assertEquals(STATE_ERR, HAServiceState.ACTIVE,
rm.adminService.getServiceStatus().getState()); rm.adminService.getServiceStatus().getState());
assertTrue("Active RM services aren't started", assertTrue("Active RM services aren't started",
@ -115,6 +130,33 @@ private void checkActiveRMFunctionality() throws IOException {
fail("Unable to perform Active RM functions"); fail("Unable to perform Active RM functions");
LOG.error("ActiveRM check failed", e); LOG.error("ActiveRM check failed", e);
} }
checkActiveRMWebServices();
}
// Do some sanity testing of the web-services after fail-over.
private void checkActiveRMWebServices() throws JSONException {
// Validate web-service
Client webServiceClient = Client.create(new DefaultClientConfig());
InetSocketAddress rmWebappAddr =
NetUtils.getConnectAddress(rm.getWebapp().getListenerAddress());
String webappURL =
"http://" + rmWebappAddr.getHostName() + ":" + rmWebappAddr.getPort();
WebResource webResource = webServiceClient.resource(webappURL);
String path = app.getApplicationId().toString();
ClientResponse response =
webResource.path("ws").path("v1").path("cluster").path("apps")
.path(path).accept(MediaType.APPLICATION_JSON)
.get(ClientResponse.class);
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
JSONObject json = response.getEntity(JSONObject.class);
assertEquals("incorrect number of elements", 1, json.length());
JSONObject appJson = json.getJSONObject("app");
assertEquals("ACCEPTED", appJson.getString("state"));
// Other stuff is verified in the regular web-services related tests
} }
/** /**
@ -129,9 +171,10 @@ private void checkActiveRMFunctionality() throws IOException {
* become Active * become Active
*/ */
@Test (timeout = 30000) @Test (timeout = 30000)
public void testStartAndTransitions() throws Exception { public void testFailoverAndTransitions() throws Exception {
configuration.setBoolean(YarnConfiguration.AUTO_FAILOVER_ENABLED, false); configuration.setBoolean(YarnConfiguration.AUTO_FAILOVER_ENABLED, false);
Configuration conf = new YarnConfiguration(configuration); Configuration conf = new YarnConfiguration(configuration);
rm = new MockRM(conf); rm = new MockRM(conf);
rm.init(conf); rm.init(conf);
StateChangeRequestInfo requestInfo = new StateChangeRequestInfo( StateChangeRequestInfo requestInfo = new StateChangeRequestInfo(
@ -191,7 +234,7 @@ public void testStartAndTransitions() throws Exception {
} }
@Test @Test
public void testTransitionsWhenAutomaticFailoverEnabled() throws IOException { public void testTransitionsWhenAutomaticFailoverEnabled() throws Exception {
final String ERR_UNFORCED_REQUEST = "User request succeeded even when " + final String ERR_UNFORCED_REQUEST = "User request succeeded even when " +
"automatic failover is enabled"; "automatic failover is enabled";