diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/clientrm/RouterClientRMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/clientrm/RouterClientRMService.java index 3d45ad207cf403..439ae40ac6a672 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/clientrm/RouterClientRMService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/clientrm/RouterClientRMService.java @@ -202,7 +202,18 @@ protected void serviceStop() throws Exception { if (this.server != null) { this.server.stop(); } - userPipelineMap.clear(); + // serviceStart() started the secret manager's ExpiredTokenRemover; stop it + // here, once the server accepts no more requests, so it does not outlive + // this service and keep using the federation state store. Server#stop does + // not wait for handlers already running, so one of those may still use the + // secret manager. Mirrors RMSecretManagerService#serviceStop. + if (this.routerDTSecretManager != null) { + this.routerDTSecretManager.stopThreads(); + } + // serviceStart() creates the map, so it is null if the service never started. + if (userPipelineMap != null) { + userPipelineMap.clear(); + } super.serviceStop(); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/clientrm/TestRouterClientRMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/clientrm/TestRouterClientRMService.java index 0c02fa1e8caae6..e60f089c987100 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/clientrm/TestRouterClientRMService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/clientrm/TestRouterClientRMService.java @@ -19,6 +19,7 @@ package org.apache.hadoop.yarn.server.router.clientrm; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -29,6 +30,7 @@ import java.util.Map; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.service.Service.STATE; import org.apache.hadoop.yarn.api.protocolrecords.GetClusterMetricsResponse; import org.apache.hadoop.yarn.api.protocolrecords.GetClusterNodeLabelsResponse; import org.apache.hadoop.yarn.api.protocolrecords.GetClusterNodesResponse; @@ -44,6 +46,7 @@ import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationResponse; import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.server.router.clientrm.RouterClientRMService.RequestInterceptorChainWrapper; +import org.apache.hadoop.yarn.server.router.security.RouterDelegationTokenSecretManager; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -271,4 +274,33 @@ public ClientRequestInterceptor run() throws Exception { assertTrue(client1.interceptor == client2.interceptor); } + /** + * Stopping the service stops the delegation token secret manager's + * ExpiredTokenRemover thread, which serviceStart started. + */ + @Test + public void testServiceStopStopsDelegationTokenSecretManager() { + MockRouterClientRMService service = getRouterClientRMService(); + RouterDelegationTokenSecretManager dtSecretManager = + service.getRouterDTSecretManager(); + assertTrue(dtSecretManager.isRunning()); + + service.stop(); + + assertFalse(dtSecretManager.isRunning()); + } + + /** + * A service that was initialized but never started can be stopped. + */ + @Test + public void testServiceStopWithoutStart() { + MockRouterClientRMService service = new MockRouterClientRMService(); + service.init(getConf()); + + service.stop(); + + assertEquals(STATE.STOPPED, service.getServiceState()); + } + }