001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.action.hadoop; 020 021import org.apache.commons.lang.StringUtils; 022import org.apache.hadoop.conf.Configuration; 023import org.apache.hadoop.io.Text; 024import org.apache.hadoop.mapreduce.v2.api.HSClientProtocol; 025import org.apache.hadoop.mapreduce.v2.api.MRClientProtocol; 026import org.apache.hadoop.mapreduce.v2.api.protocolrecords.GetDelegationTokenRequest; 027import org.apache.hadoop.mapreduce.v2.jobhistory.JHAdminConfig; 028import org.apache.hadoop.net.NetUtils; 029import org.apache.hadoop.security.Credentials; 030import org.apache.hadoop.security.SecurityUtil; 031import org.apache.hadoop.security.UserGroupInformation; 032import org.apache.hadoop.security.token.Token; 033import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider; 034import org.apache.hadoop.yarn.ipc.YarnRPC; 035import org.apache.hadoop.yarn.util.ConverterUtils; 036import org.apache.oozie.ErrorCode; 037import org.apache.oozie.action.ActionExecutor; 038import org.apache.oozie.service.Services; 039import org.apache.oozie.service.UserGroupInformationService; 040import org.apache.oozie.util.XLog; 041 042import java.io.IOException; 043import java.security.PrivilegedAction; 044 045public class JHSCredentials implements CredentialsProvider { 046 protected XLog LOG = XLog.getLog(getClass()); 047 048 049 /** 050 * Add an MR_DELEGATION_TOKEN to the {@link Credentials} provided. 051 * @param credentials the credentials object which is updated 052 * @param config launcher AM configuration 053 * @param props properties for getting credential token or certificate 054 * @param context workflow context 055 * @throws Exception thrown if failed 056 */ 057 @Override 058 public void updateCredentials(Credentials credentials, Configuration config, CredentialsProperties props, 059 ActionExecutor.Context context) throws Exception { 060 try { 061 LOG.debug("Instantiating JHS Proxy"); 062 MRClientProtocol hsProxy = instantiateHistoryProxy(config, context); 063 Text hsService = SecurityUtil.buildTokenService(hsProxy.getConnectAddress()); 064 LOG.debug("Getting delegation token for {0}", hsService.toString()); 065 Token<?> jhsToken = getDelegationTokenFromJHS(hsProxy, new HadoopTokenHelper().getServerPrincipal(config)); 066 LOG.debug("Acquired token {0}", jhsToken); 067 credentials.addToken(hsService, jhsToken); 068 } catch (IOException | InterruptedException ex) { 069 LOG.debug("exception in updateCredentials", ex); 070 throw new CredentialException(ErrorCode.E0512, ex.getMessage(), ex); 071 } 072 } 073 074 /** 075 * Get a Delegation token from the JHS. 076 * Copied over from YARNRunner in Hadoop. 077 * @param hsProxy protcol used to get the token 078 * @return The RM_DELEGATION_TOKEN that can be used to talk to JHS 079 * @throws IOException 080 * @throws InterruptedException 081 */ 082 private Token<?> getDelegationTokenFromJHS(final MRClientProtocol hsProxy, final String renewer) 083 throws IOException, InterruptedException { 084 GetDelegationTokenRequest request = RecordFactoryProvider 085 .getRecordFactory(null).newRecordInstance(GetDelegationTokenRequest.class); 086 LOG.debug("Creating requsest to JHS using renewer [{0}]", renewer); 087 request.setRenewer(renewer); 088 org.apache.hadoop.yarn.api.records.Token mrDelegationToken = hsProxy.getDelegationToken(request) 089 .getDelegationToken(); 090 LOG.debug("Got token to JHS : {0}. Converting token.", mrDelegationToken); 091 return ConverterUtils.convertFromYarn(mrDelegationToken, hsProxy.getConnectAddress()); 092 } 093 094 /** 095 * Create an MRClientProtocol to the JHS 096 * Copied over from ClientCache in Hadoop. 097 * @return the protocol that can be used to get a token with 098 * @throws IOException 099 */ 100 private MRClientProtocol instantiateHistoryProxy(final Configuration configuration, final ActionExecutor.Context context) 101 throws IOException { 102 final String serviceAddr = configuration.get(JHAdminConfig.MR_HISTORY_ADDRESS); 103 if (StringUtils.isEmpty(serviceAddr)) { 104 return null; 105 } 106 LOG.debug("Connecting to JHS at: " + serviceAddr); 107 final YarnRPC rpc = YarnRPC.create(configuration); 108 LOG.debug("Connected to JHS at: " + serviceAddr); 109 UserGroupInformation currentUser = Services.get().get(UserGroupInformationService.class) 110 .getProxyUser(context.getWorkflow().getUser()); 111 return currentUser.doAs(new PrivilegedAction<MRClientProtocol>() { 112 @Override 113 public MRClientProtocol run() { 114 return (MRClientProtocol) rpc.getProxy(HSClientProtocol.class, 115 NetUtils.createSocketAddr(serviceAddr), configuration); 116 } 117 }); 118 } 119}