org.apache.hadoop.hdfs.tools.DelegationTokenFetcher.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hdfs.tools.DelegationTokenFetcher.java

Source

/**
 * 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.hdfs.tools;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URL;
import java.net.URLConnection;
import java.security.PrivilegedExceptionAction;
import java.util.Collection;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.HftpFileSystem;
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
import org.apache.hadoop.hdfs.server.namenode.CancelDelegationTokenServlet;
import org.apache.hadoop.hdfs.server.namenode.GetDelegationTokenServlet;
import org.apache.hadoop.hdfs.server.namenode.RenewDelegationTokenServlet;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.Credentials;
import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.util.GenericOptionsParser;

/**
 * Fetch a DelegationToken from the current Namenode and store it in the
 * specified file.
 */
public class DelegationTokenFetcher {

    static {
        Configuration.addDefaultResource("hdfs-default.xml");
        Configuration.addDefaultResource("hdfs-site.xml");
    }

    private static final Log LOG = LogFactory.getLog(DelegationTokenFetcher.class);
    private static final String WEBSERVICE = "webservice";
    private static final String CANCEL = "cancel";
    private static final String RENEW = "renew";

    static {
        // Enable Kerberos sockets
        System.setProperty("https.cipherSuites", "TLS_KRB5_WITH_3DES_EDE_CBC_SHA");
    }

    private static void printUsage(PrintStream err) throws IOException {
        err.println("fetchdt retrieves delegation tokens from the NameNode");
        err.println();
        err.println("fetchdt <opts> <token file>");
        err.println("Options:");
        err.println("  --webservice <url>  Url to contact NN on");
        err.println("  --cancel            Cancel the delegation token");
        err.println("  --renew             Renew the delegation token");
        err.println();
        GenericOptionsParser.printGenericCommandUsage(err);
        System.exit(1);
    }

    private static Collection<Token<?>> readTokens(Path file, Configuration conf) throws IOException {
        Credentials creds = Credentials.readTokenStorageFile(file, conf);
        return creds.getAllTokens();
    }

    /**
     * Command-line interface
     */
    public static void main(final String[] args) throws Exception {
        final Configuration conf = new Configuration();
        Options fetcherOptions = new Options();
        fetcherOptions.addOption(WEBSERVICE, true, "HTTPS url to reach the NameNode at");
        fetcherOptions.addOption(CANCEL, false, "cancel the token");
        fetcherOptions.addOption(RENEW, false, "renew the token");
        GenericOptionsParser parser = new GenericOptionsParser(conf, fetcherOptions, args);
        CommandLine cmd = parser.getCommandLine();

        // get options
        final String webUrl = cmd.hasOption(WEBSERVICE) ? cmd.getOptionValue(WEBSERVICE) : null;
        final boolean cancel = cmd.hasOption(CANCEL);
        final boolean renew = cmd.hasOption(RENEW);
        String[] remaining = parser.getRemainingArgs();

        // check option validity
        if (cancel && renew) {
            System.err.println("ERROR: Only specify cancel or renew.");
            printUsage(System.err);
        }
        if (remaining.length != 1 || remaining[0].charAt(0) == '-') {
            System.err.println("ERROR: Must specify exactly one token file");
            printUsage(System.err);
        }
        // default to using the local file system
        FileSystem local = FileSystem.getLocal(conf);
        final Path tokenFile = new Path(local.getWorkingDirectory(), remaining[0]);

        // Login the current user
        final UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
        ugi.doAs(new PrivilegedExceptionAction<Object>() {
            @Override
            public Object run() throws Exception {

                if (cancel) {
                    for (Token<?> token : readTokens(tokenFile, conf)) {
                        if (token.isManaged()) {
                            token.cancel(conf);
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("Cancelled token for " + token.getService());
                            }
                        }
                    }
                } else if (renew) {
                    for (Token<?> token : readTokens(tokenFile, conf)) {
                        if (token.isManaged()) {
                            token.renew(conf);
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("Renewed token for " + token.getService());
                            }
                        }
                    }
                } else {
                    if (webUrl != null) {
                        getDTfromRemote(webUrl, null).writeTokenStorageFile(tokenFile, conf);
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Fetched token via http for " + webUrl);
                        }
                    } else {
                        FileSystem fs = FileSystem.get(conf);
                        Token<?> token = fs.getDelegationToken(ugi.getShortUserName());
                        Credentials cred = new Credentials();
                        cred.addToken(token.getService(), token);
                        cred.writeTokenStorageFile(tokenFile, conf);
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Fetched token for " + fs.getUri() + " into " + tokenFile);
                        }
                    }
                }
                return null;
            }
        });

    }

    /**
     * Utility method to obtain a delegation token over http
     * @param nnHttpAddr Namenode http addr, such as http://namenode:50070
     */
    static public Credentials getDTfromRemote(String nnAddr, String renewer) throws IOException {
        DataInputStream dis = null;
        InetSocketAddress serviceAddr = NetUtils.createSocketAddr(nnAddr);

        try {
            StringBuffer url = new StringBuffer();
            if (renewer != null) {
                url.append(nnAddr).append(GetDelegationTokenServlet.PATH_SPEC).append("?")
                        .append(GetDelegationTokenServlet.RENEWER).append("=").append(renewer);
            } else {
                url.append(nnAddr).append(GetDelegationTokenServlet.PATH_SPEC);
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("Retrieving token from: " + url);
            }
            URL remoteURL = new URL(url.toString());
            SecurityUtil.fetchServiceTicket(remoteURL);
            URLConnection connection = remoteURL.openConnection();

            InputStream in = connection.getInputStream();
            Credentials ts = new Credentials();
            dis = new DataInputStream(in);
            ts.readFields(dis);
            for (Token<?> token : ts.getAllTokens()) {
                token.setKind(HftpFileSystem.TOKEN_KIND);
                SecurityUtil.setTokenService(token, serviceAddr);
            }
            return ts;
        } catch (Exception e) {
            throw new IOException("Unable to obtain remote token", e);
        } finally {
            if (dis != null)
                dis.close();
        }
    }

    /**
     * Renew a Delegation Token.
     * @param nnAddr the NameNode's address
     * @param tok the token to renew
     * @return the Date that the token will expire next.
     * @throws IOException
     */
    static public long renewDelegationToken(String nnAddr, Token<DelegationTokenIdentifier> tok)
            throws IOException {
        StringBuilder buf = new StringBuilder();
        buf.append(nnAddr);
        buf.append(RenewDelegationTokenServlet.PATH_SPEC);
        buf.append("?");
        buf.append(RenewDelegationTokenServlet.TOKEN);
        buf.append("=");
        buf.append(tok.encodeToUrlString());
        BufferedReader in = null;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(buf.toString());
            SecurityUtil.fetchServiceTicket(url);
            connection = (HttpURLConnection) url.openConnection();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            long result = Long.parseLong(in.readLine());
            in.close();
            return result;
        } catch (IOException ie) {
            LOG.info("error in renew over HTTP", ie);
            IOException e = null;
            if (connection != null) {
                String resp = connection.getResponseMessage();
                e = getExceptionFromResponse(resp);
            }

            IOUtils.cleanup(LOG, in);
            if (e != null) {
                LOG.info("rethrowing exception from HTTP request: " + e.getLocalizedMessage());
                throw e;
            }
            throw ie;
        }
    }

    static private IOException getExceptionFromResponse(String resp) {
        String exceptionClass = "", exceptionMsg = "";
        if (resp != null && !resp.isEmpty()) {
            String[] rs = resp.split(";");
            exceptionClass = rs[0];
            exceptionMsg = rs[1];
        }
        LOG.info("Error response from HTTP request=" + resp + ";ec=" + exceptionClass + ";em=" + exceptionMsg);
        IOException e = null;
        if (exceptionClass != null && !exceptionClass.isEmpty()) {
            if (exceptionClass.contains("InvalidToken")) {
                e = new org.apache.hadoop.security.token.SecretManager.InvalidToken(exceptionMsg);
                e.setStackTrace(new StackTraceElement[0]); // stack is not relevant
            } else if (exceptionClass.contains("AccessControlException")) {
                e = new org.apache.hadoop.security.AccessControlException(exceptionMsg);
                e.setStackTrace(new StackTraceElement[0]); // stack is not relevant
            }
        }
        LOG.info("Exception from HTTP response=" + e.getLocalizedMessage());
        return e;
    }

    /**
     * Cancel a Delegation Token.
     * @param nnAddr the NameNode's address
     * @param tok the token to cancel
     * @throws IOException
     */
    static public void cancelDelegationToken(String nnAddr, Token<DelegationTokenIdentifier> tok)
            throws IOException {
        StringBuilder buf = new StringBuilder();
        buf.append(nnAddr);
        buf.append(CancelDelegationTokenServlet.PATH_SPEC);
        buf.append("?");
        buf.append(CancelDelegationTokenServlet.TOKEN);
        buf.append("=");
        buf.append(tok.encodeToUrlString());
        BufferedReader in = null;
        try {
            URL url = new URL(buf.toString());
            SecurityUtil.fetchServiceTicket(url);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                throw new IOException("Error cancelling token:" + connection.getResponseMessage());
            }
        } catch (IOException ie) {
            IOUtils.cleanup(LOG, in);
            throw ie;
        }
    }
}