PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/java/org/apache/hadoop/mapreduce/security/token/DelegationTokenRenewal.java

https://github.com/RS1999ent/hadoop-mapreduce
Java | 402 lines | 291 code | 48 blank | 63 comment | 33 complexity | a018cdd0e8071e8e31289ff2b13d6c35 MD5 | raw file
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.apache.hadoop.mapreduce.security.token;
  19. import java.io.IOException;
  20. import java.net.InetAddress;
  21. import java.net.URI;
  22. import java.security.PrivilegedExceptionAction;
  23. import java.util.Collection;
  24. import java.util.Collections;
  25. import java.util.Date;
  26. import java.util.HashSet;
  27. import java.util.Iterator;
  28. import java.util.Set;
  29. import java.util.Timer;
  30. import java.util.TimerTask;
  31. import org.apache.commons.logging.Log;
  32. import org.apache.commons.logging.LogFactory;
  33. import org.apache.hadoop.classification.InterfaceAudience;
  34. import org.apache.hadoop.classification.InterfaceStability;
  35. import org.apache.hadoop.conf.Configuration;
  36. import org.apache.hadoop.fs.FileSystem;
  37. import org.apache.hadoop.hdfs.DistributedFileSystem;
  38. import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
  39. import org.apache.hadoop.hdfs.tools.DelegationTokenFetcher;
  40. import org.apache.hadoop.io.Text;
  41. import org.apache.hadoop.mapreduce.JobID;
  42. import org.apache.hadoop.security.AccessControlException;
  43. import org.apache.hadoop.security.Credentials;
  44. import org.apache.hadoop.security.UserGroupInformation;
  45. import org.apache.hadoop.security.token.SecretManager.InvalidToken;
  46. import org.apache.hadoop.security.token.Token;
  47. import org.apache.hadoop.security.token.TokenIdentifier;
  48. import org.apache.hadoop.hdfs.DFSConfigKeys;
  49. @InterfaceAudience.Private
  50. @InterfaceStability.Unstable
  51. public class DelegationTokenRenewal {
  52. private static final Log LOG = LogFactory.getLog(DelegationTokenRenewal.class);
  53. public static final String SCHEME = "hdfs";
  54. /**
  55. * class that is used for keeping tracks of DT to renew
  56. *
  57. */
  58. private static class DelegationTokenToRenew {
  59. public final Token<DelegationTokenIdentifier> token;
  60. public final JobID jobId;
  61. public final Configuration conf;
  62. public long expirationDate;
  63. public TimerTask timerTask;
  64. public DelegationTokenToRenew(
  65. JobID jId, Token<DelegationTokenIdentifier> t,
  66. Configuration newConf, long newExpirationDate) {
  67. token = t;
  68. jobId = jId;
  69. conf = newConf;
  70. expirationDate = newExpirationDate;
  71. timerTask = null;
  72. if(token==null || jobId==null || conf==null) {
  73. throw new IllegalArgumentException("invalid params for Renew Token" +
  74. ";t="+token+";j="+jobId+";c="+conf);
  75. }
  76. }
  77. public void setTimerTask(TimerTask tTask) {
  78. timerTask = tTask;
  79. }
  80. @Override
  81. public String toString() {
  82. return token + ";exp="+expirationDate;
  83. }
  84. @Override
  85. public boolean equals (Object obj) {
  86. if (obj == this) {
  87. return true;
  88. } else if (obj == null || getClass() != obj.getClass()) {
  89. return false;
  90. } else {
  91. return token.equals(((DelegationTokenToRenew)obj).token);
  92. }
  93. }
  94. @Override
  95. public int hashCode() {
  96. return token.hashCode();
  97. }
  98. }
  99. // global single timer (daemon)
  100. private static Timer renewalTimer = new Timer(true);
  101. //managing the list of tokens using Map
  102. // jobId=>List<tokens>
  103. private static Set<DelegationTokenToRenew> delegationTokens =
  104. Collections.synchronizedSet(new HashSet<DelegationTokenToRenew>());
  105. //adding token
  106. private static void addTokenToList(DelegationTokenToRenew t) {
  107. delegationTokens.add(t);
  108. }
  109. // kind of tokens we currently renew
  110. private static final Text kindHdfs =
  111. DelegationTokenIdentifier.HDFS_DELEGATION_KIND;
  112. @SuppressWarnings("unchecked")
  113. public static synchronized void registerDelegationTokensForRenewal(
  114. JobID jobId, Credentials ts, Configuration conf) {
  115. if(ts==null)
  116. return; //nothing to add
  117. Collection <Token<? extends TokenIdentifier>> tokens = ts.getAllTokens();
  118. long now = System.currentTimeMillis();
  119. for(Token<? extends TokenIdentifier> t : tokens) {
  120. // currently we only check for HDFS delegation tokens
  121. // later we can add more different types.
  122. if(! t.getKind().equals(kindHdfs)) {
  123. continue;
  124. }
  125. Token<DelegationTokenIdentifier> dt =
  126. (Token<DelegationTokenIdentifier>)t;
  127. // first renew happens immediately
  128. DelegationTokenToRenew dtr =
  129. new DelegationTokenToRenew(jobId, dt, conf, now);
  130. addTokenToList(dtr);
  131. setTimerForTokenRenewal(dtr, true);
  132. LOG.info("registering token for renewal for service =" + dt.getService()+
  133. " and jobID = " + jobId);
  134. }
  135. }
  136. private static String getHttpAddressForToken(
  137. Token<DelegationTokenIdentifier> token, final Configuration conf)
  138. throws IOException {
  139. String[] ipaddr = token.getService().toString().split(":");
  140. InetAddress iaddr = InetAddress.getByName(ipaddr[0]);
  141. String dnsName = iaddr.getCanonicalHostName();
  142. // in case it is a different cluster it may have a different port
  143. String httpsPort = conf.get("dfs.hftp.https.port");
  144. if(httpsPort == null) {
  145. // get from this cluster
  146. httpsPort = conf.get(DFSConfigKeys.DFS_HTTPS_PORT_KEY,
  147. "" + DFSConfigKeys.DFS_HTTPS_PORT_DEFAULT);
  148. }
  149. // always use https (it is for security only)
  150. return "https://" + dnsName+":"+httpsPort;
  151. }
  152. protected static long renewDelegationTokenOverHttps(
  153. final Token<DelegationTokenIdentifier> token, final Configuration conf)
  154. throws InterruptedException, IOException{
  155. final String httpAddress = getHttpAddressForToken(token, conf);
  156. // will be chaged to debug
  157. LOG.info("address to renew=" + httpAddress + "; tok=" + token.getService());
  158. Long expDate = (Long) UserGroupInformation.getLoginUser().doAs(
  159. new PrivilegedExceptionAction<Long>() {
  160. public Long run() throws IOException {
  161. return DelegationTokenFetcher.renewDelegationToken(httpAddress, token);
  162. }
  163. });
  164. LOG.info("Renew over HTTP done. addr="+httpAddress+";res="+expDate);
  165. return expDate;
  166. }
  167. private static long renewDelegationToken(DelegationTokenToRenew dttr)
  168. throws Exception {
  169. long newExpirationDate=System.currentTimeMillis()+3600*1000;
  170. Token<DelegationTokenIdentifier> token = dttr.token;
  171. Configuration conf = dttr.conf;
  172. if(token.getKind().equals(kindHdfs)) {
  173. DistributedFileSystem dfs=null;
  174. try {
  175. // do it over rpc. For that we need DFS object
  176. dfs = getDFSForToken(token, conf);
  177. } catch (IOException e) {
  178. LOG.info("couldn't get DFS to renew. Will retry over HTTPS");
  179. dfs = null;
  180. }
  181. try {
  182. if(dfs != null)
  183. newExpirationDate = dfs.renewDelegationToken(token);
  184. else {
  185. // try HTTP
  186. newExpirationDate = renewDelegationTokenOverHttps(token, conf);
  187. }
  188. } catch (InvalidToken ite) {
  189. LOG.warn("invalid token - not scheduling for renew");
  190. removeFailedDelegationToken(dttr);
  191. throw new IOException("failed to renew token", ite);
  192. } catch (AccessControlException ioe) {
  193. LOG.warn("failed to renew token:"+token, ioe);
  194. removeFailedDelegationToken(dttr);
  195. throw new IOException("failed to renew token", ioe);
  196. } catch (Exception e) {
  197. LOG.warn("failed to renew token:"+token, e);
  198. // returns default expiration date
  199. }
  200. } else {
  201. throw new Exception("unknown token type to renew:"+token.getKind());
  202. }
  203. return newExpirationDate;
  204. }
  205. /**
  206. * Task - to renew a token
  207. *
  208. */
  209. private static class RenewalTimerTask extends TimerTask {
  210. private DelegationTokenToRenew dttr;
  211. RenewalTimerTask(DelegationTokenToRenew t) { dttr = t; }
  212. @Override
  213. public void run() {
  214. Token<DelegationTokenIdentifier> token = dttr.token;
  215. long newExpirationDate=0;
  216. try {
  217. newExpirationDate = renewDelegationToken(dttr);
  218. } catch (Exception e) {
  219. return; // message logged in renewDT method
  220. }
  221. if (LOG.isDebugEnabled())
  222. LOG.debug("renewing for:"+token.getService()+";newED=" +
  223. newExpirationDate);
  224. // new expiration date
  225. dttr.expirationDate = newExpirationDate;
  226. setTimerForTokenRenewal(dttr, false);// set the next one
  227. }
  228. }
  229. private static DistributedFileSystem getDFSForToken(
  230. Token<DelegationTokenIdentifier> token, final Configuration conf)
  231. throws Exception {
  232. DistributedFileSystem dfs = null;
  233. try {
  234. final URI uri = new URI (SCHEME + "://" + token.getService().toString());
  235. dfs =
  236. UserGroupInformation.getLoginUser().doAs(
  237. new PrivilegedExceptionAction<DistributedFileSystem>() {
  238. public DistributedFileSystem run() throws IOException {
  239. return (DistributedFileSystem) FileSystem.get(uri, conf);
  240. }
  241. });
  242. } catch (Exception e) {
  243. LOG.warn("Failed to create a dfs to renew/cancel for:" + token.getService(), e);
  244. throw e;
  245. }
  246. return dfs;
  247. }
  248. /**
  249. * find the soonest expiring token and set it for renew
  250. */
  251. private static void setTimerForTokenRenewal(
  252. DelegationTokenToRenew token, boolean firstTime) {
  253. // calculate timer time
  254. long now = System.currentTimeMillis();
  255. long renewIn;
  256. if(firstTime) {
  257. renewIn = now;
  258. } else {
  259. long expiresIn = (token.expirationDate - now);
  260. renewIn = now + expiresIn - expiresIn/10; // little before expiration
  261. }
  262. try {
  263. // need to create new timer every time
  264. TimerTask tTask = new RenewalTimerTask(token);
  265. token.setTimerTask(tTask); // keep reference to the timer
  266. renewalTimer.schedule(token.timerTask, new Date(renewIn));
  267. } catch (Exception e) {
  268. LOG.warn("failed to schedule a task, token will not renew more", e);
  269. }
  270. }
  271. /**
  272. * removing all tokens renewals
  273. */
  274. static public void close() {
  275. renewalTimer.cancel();
  276. delegationTokens.clear();
  277. }
  278. protected static void cancelDelegationTokenOverHttps(
  279. final Token<DelegationTokenIdentifier> token, final Configuration conf)
  280. throws InterruptedException, IOException{
  281. final String httpAddress = getHttpAddressForToken(token, conf);
  282. // will be chaged to debug
  283. LOG.info("address to cancel=" + httpAddress + "; tok=" + token.getService());
  284. UserGroupInformation.getLoginUser().doAs(
  285. new PrivilegedExceptionAction<Void>() {
  286. public Void run() throws IOException {
  287. DelegationTokenFetcher.cancelDelegationToken(httpAddress, token);
  288. return null;
  289. }
  290. });
  291. LOG.info("Cancel over HTTP done. addr="+httpAddress);
  292. }
  293. // cancel a token
  294. private static void cancelToken(DelegationTokenToRenew t) {
  295. Token<DelegationTokenIdentifier> token = t.token;
  296. Configuration conf = t.conf;
  297. if(token.getKind().equals(kindHdfs)) {
  298. DistributedFileSystem dfs = null;
  299. try {
  300. // do it over rpc. For that we need DFS object
  301. dfs = getDFSForToken(token, conf);
  302. } catch (Exception e) {
  303. LOG.info("couldn't get DFS to cancel. Will retry over HTTPS");
  304. dfs = null;
  305. }
  306. try {
  307. if(dfs != null) {
  308. dfs.cancelDelegationToken(token);
  309. } else {
  310. cancelDelegationTokenOverHttps(token,conf);
  311. }
  312. } catch (Exception e) {
  313. LOG.warn("Failed to cancel " + token, e);
  314. }
  315. }
  316. }
  317. /**
  318. * removing failed DT
  319. * @param jobId
  320. */
  321. private static void removeFailedDelegationToken(DelegationTokenToRenew t) {
  322. JobID jobId = t.jobId;
  323. if (LOG.isDebugEnabled())
  324. LOG.debug("removing failed delegation token for jobid=" + jobId +
  325. ";t=" + t.token.getService());
  326. delegationTokens.remove(t);
  327. // cancel the timer
  328. if(t.timerTask!=null)
  329. t.timerTask.cancel();
  330. }
  331. /**
  332. * removing DT for completed jobs
  333. * @param jobId
  334. */
  335. public static void removeDelegationTokenRenewalForJob(JobID jobId) {
  336. synchronized (delegationTokens) {
  337. Iterator<DelegationTokenToRenew> it = delegationTokens.iterator();
  338. while(it.hasNext()) {
  339. DelegationTokenToRenew dttr = it.next();
  340. if (dttr.jobId.equals(jobId)) {
  341. if (LOG.isDebugEnabled())
  342. LOG.debug("removing delegation token for jobid=" + jobId +
  343. ";t=" + dttr.token.getService());
  344. // cancel the timer
  345. if(dttr.timerTask!=null)
  346. dttr.timerTask.cancel();
  347. // cancel the token
  348. cancelToken(dttr);
  349. it.remove();
  350. }
  351. }
  352. }
  353. }
  354. }