org.apache.hadoop.hive.llap.daemon.impl.LlapProtocolServerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hive.llap.daemon.impl.LlapProtocolServerImpl.java

Source

/*
 * Licensed 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.hive.llap.daemon.impl;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.PrivilegedAction;
import java.util.concurrent.atomic.AtomicReference;

import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.google.protobuf.BlockingService;
import com.google.protobuf.ByteString;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hadoop.hive.llap.DaemonId;
import org.apache.hadoop.hive.llap.LlapUtil;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.GetTokenRequestProto;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.GetTokenResponseProto;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.QueryCompleteRequestProto;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.QueryCompleteResponseProto;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.SourceStateUpdatedRequestProto;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.SourceStateUpdatedResponseProto;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.SubmitWorkRequestProto;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.SubmitWorkResponseProto;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.TerminateFragmentRequestProto;
import org.apache.hadoop.hive.llap.daemon.rpc.LlapDaemonProtocolProtos.TerminateFragmentResponseProto;
import org.apache.hadoop.ipc.ProtobufRpcEngine;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authorize.AccessControlList;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.hive.llap.security.LlapTokenIdentifier;
import org.apache.hadoop.hive.llap.security.SecretManager;
import org.apache.hadoop.service.AbstractService;
import org.apache.hadoop.hive.llap.daemon.ContainerRunner;
import org.apache.hadoop.hive.llap.protocol.LlapProtocolBlockingPB;
import org.apache.hadoop.hive.llap.protocol.LlapManagementProtocolPB;
import org.apache.hadoop.hive.llap.security.LlapDaemonPolicyProvider;

public class LlapProtocolServerImpl extends AbstractService
        implements LlapProtocolBlockingPB, LlapManagementProtocolPB {

    private static final Logger LOG = LoggerFactory.getLogger(LlapProtocolServerImpl.class);

    private enum TokenRequiresSigning {
        TRUE, FALSE, EXCEPT_OWNER
    }

    private final int numHandlers;
    private final ContainerRunner containerRunner;
    private final int srvPort, mngPort;
    private RPC.Server server, mngServer;
    private final AtomicReference<InetSocketAddress> srvAddress, mngAddress;
    private final SecretManager secretManager;
    private String clusterUser = null;
    private boolean isRestrictedToClusterUser = false;
    private final DaemonId daemonId;
    private TokenRequiresSigning isSigningRequiredConfig = TokenRequiresSigning.TRUE;

    public LlapProtocolServerImpl(SecretManager secretManager, int numHandlers, ContainerRunner containerRunner,
            AtomicReference<InetSocketAddress> srvAddress, AtomicReference<InetSocketAddress> mngAddress,
            int srvPort, int mngPort, DaemonId daemonId) {
        super("LlapDaemonProtocolServerImpl");
        this.numHandlers = numHandlers;
        this.containerRunner = containerRunner;
        this.secretManager = secretManager;
        this.srvAddress = srvAddress;
        this.srvPort = srvPort;
        this.mngAddress = mngAddress;
        this.mngPort = mngPort;
        this.daemonId = daemonId;
        LOG.info("Creating: " + LlapProtocolServerImpl.class.getSimpleName() + " with port configured to: "
                + srvPort);
    }

    @Override
    public SubmitWorkResponseProto submitWork(RpcController controller, SubmitWorkRequestProto request)
            throws ServiceException {
        try {
            return containerRunner.submitWork(request);
        } catch (IOException e) {
            throw new ServiceException(e);
        }
    }

    @Override
    public SourceStateUpdatedResponseProto sourceStateUpdated(RpcController controller,
            SourceStateUpdatedRequestProto request) throws ServiceException {
        try {
            return containerRunner.sourceStateUpdated(request);
        } catch (IOException e) {
            throw new ServiceException(e);
        }
    }

    @Override
    public QueryCompleteResponseProto queryComplete(RpcController controller, QueryCompleteRequestProto request)
            throws ServiceException {
        try {
            return containerRunner.queryComplete(request);
        } catch (IOException e) {
            throw new ServiceException(e);
        }
    }

    @Override
    public TerminateFragmentResponseProto terminateFragment(RpcController controller,
            TerminateFragmentRequestProto request) throws ServiceException {
        try {
            return containerRunner.terminateFragment(request);
        } catch (IOException e) {
            throw new ServiceException(e);
        }
    }

    @Override
    public void serviceStart() {
        final Configuration conf = getConfig();
        isSigningRequiredConfig = getSigningConfig(conf);
        final BlockingService daemonImpl = LlapDaemonProtocolProtos.LlapDaemonProtocol
                .newReflectiveBlockingService(this);
        final BlockingService managementImpl = LlapDaemonProtocolProtos.LlapManagementProtocol
                .newReflectiveBlockingService(this);
        if (!UserGroupInformation.isSecurityEnabled()) {
            startProtocolServers(conf, daemonImpl, managementImpl);
            return;
        }
        try {
            this.clusterUser = UserGroupInformation.getCurrentUser().getShortUserName();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if (isPermissiveManagementAcl(conf)) {
            LOG.warn("Management protocol has a '*' ACL.");
            isRestrictedToClusterUser = true;
        }
        String llapPrincipal = HiveConf.getVar(conf, ConfVars.LLAP_KERBEROS_PRINCIPAL),
                llapKeytab = HiveConf.getVar(conf, ConfVars.LLAP_KERBEROS_KEYTAB_FILE);

        // Start the protocol server after properly authenticating with daemon keytab.
        UserGroupInformation daemonUgi = null;
        try {
            daemonUgi = LlapUtil.loginWithKerberos(llapPrincipal, llapKeytab);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        daemonUgi.doAs(new PrivilegedAction<Void>() {
            @Override
            public Void run() {
                startProtocolServers(conf, daemonImpl, managementImpl);
                return null;
            }
        });
    }

    private static TokenRequiresSigning getSigningConfig(final Configuration conf) {
        String signSetting = HiveConf.getVar(conf, ConfVars.LLAP_REMOTE_TOKEN_REQUIRES_SIGNING).toLowerCase();
        switch (signSetting) {
        case "true":
            return TokenRequiresSigning.TRUE;
        case "except_llap_owner":
            return TokenRequiresSigning.EXCEPT_OWNER;
        case "false":
            return TokenRequiresSigning.FALSE;
        default: {
            throw new RuntimeException("Invalid value for " + ConfVars.LLAP_REMOTE_TOKEN_REQUIRES_SIGNING.varname
                    + ": " + signSetting);
        }
        }
    }

    private static boolean isPermissiveManagementAcl(Configuration conf) {
        return HiveConf.getBoolVar(conf, ConfVars.LLAP_VALIDATE_ACLS)
                && AccessControlList.WILDCARD_ACL_VALUE.equals(HiveConf.getVar(conf, ConfVars.LLAP_MANAGEMENT_ACL))
                && "".equals(HiveConf.getVar(conf, ConfVars.LLAP_MANAGEMENT_ACL_DENY));
    }

    private void startProtocolServers(Configuration conf, BlockingService daemonImpl,
            BlockingService managementImpl) {
        server = startProtocolServer(srvPort, numHandlers, srvAddress, conf, daemonImpl,
                LlapProtocolBlockingPB.class, ConfVars.LLAP_SECURITY_ACL, ConfVars.LLAP_SECURITY_ACL_DENY);
        mngServer = startProtocolServer(mngPort, 2, mngAddress, conf, managementImpl,
                LlapManagementProtocolPB.class, ConfVars.LLAP_MANAGEMENT_ACL, ConfVars.LLAP_MANAGEMENT_ACL_DENY);
    }

    private RPC.Server startProtocolServer(int srvPort, int numHandlers,
            AtomicReference<InetSocketAddress> bindAddress, Configuration conf, BlockingService impl,
            Class<?> protocolClass, ConfVars... aclVars) {
        InetSocketAddress addr = new InetSocketAddress(srvPort);
        RPC.Server server;
        try {
            server = createServer(protocolClass, addr, conf, numHandlers, impl, aclVars);
            server.start();
        } catch (IOException e) {
            LOG.error("Failed to run RPC Server on port: " + srvPort, e);
            throw new RuntimeException(e);
        }

        InetSocketAddress serverBindAddress = NetUtils.getConnectAddress(server);
        bindAddress.set(NetUtils.createSocketAddrForHost(serverBindAddress.getAddress().getCanonicalHostName(),
                serverBindAddress.getPort()));
        LOG.info("Instantiated " + protocolClass.getSimpleName() + " at " + bindAddress);
        return server;
    }

    @Override
    public void serviceStop() {
        if (server != null) {
            server.stop();
        }
        if (mngServer != null) {
            mngServer.stop();
        }
    }

    @InterfaceAudience.Private
    InetSocketAddress getBindAddress() {
        return srvAddress.get();
    }

    @InterfaceAudience.Private
    InetSocketAddress getManagementBindAddress() {
        return mngAddress.get();
    }

    private RPC.Server createServer(Class<?> pbProtocol, InetSocketAddress addr, Configuration conf,
            int numHandlers, BlockingService blockingService, ConfVars... aclVars) throws IOException {
        Configuration serverConf = conf;
        boolean isSecurityEnabled = conf.getBoolean(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHORIZATION,
                false);
        if (isSecurityEnabled) {
            // Enforce Hive defaults.
            for (ConfVars acl : aclVars) {
                if (conf.get(acl.varname) != null)
                    continue; // Some value is set.
                if (serverConf == conf) {
                    serverConf = new Configuration(conf);
                }
                serverConf.set(acl.varname, HiveConf.getVar(serverConf, acl)); // Set the default.
            }
        }
        RPC.setProtocolEngine(serverConf, pbProtocol, ProtobufRpcEngine.class);
        RPC.Builder builder = new RPC.Builder(serverConf).setProtocol(pbProtocol).setInstance(blockingService)
                .setBindAddress(addr.getHostName()).setPort(addr.getPort()).setNumHandlers(numHandlers);
        if (secretManager != null) {
            builder = builder.setSecretManager(secretManager);
        }
        RPC.Server server = builder.build();
        if (isSecurityEnabled) {
            server.refreshServiceAcl(serverConf, new LlapDaemonPolicyProvider());
        }
        return server;
    }

    @Override
    public GetTokenResponseProto getDelegationToken(RpcController controller, GetTokenRequestProto request)
            throws ServiceException {
        if (secretManager == null) {
            throw new ServiceException("Operation not supported on unsecure cluster");
        }
        UserGroupInformation callingUser = null;
        Token<LlapTokenIdentifier> token = null;
        try {
            callingUser = UserGroupInformation.getCurrentUser();
            // Determine if the user would need to sign fragments.
            boolean isSigningRequired = determineIfSigningIsRequired(callingUser);
            token = secretManager.createLlapToken(request.hasAppId() ? request.getAppId() : null, null,
                    isSigningRequired);
        } catch (IOException e) {
            throw new ServiceException(e);
        }
        if (isRestrictedToClusterUser && !clusterUser.equals(callingUser.getShortUserName())) {
            throw new ServiceException("Management protocol ACL is too permissive. The access has been"
                    + " automatically restricted to " + clusterUser + "; " + callingUser.getShortUserName()
                    + " is denied acccess. Please set " + ConfVars.LLAP_VALIDATE_ACLS.varname + " to false,"
                    + " or adjust " + ConfVars.LLAP_MANAGEMENT_ACL.varname + " and "
                    + ConfVars.LLAP_MANAGEMENT_ACL_DENY.varname + " to a more restrictive ACL.");
        }

        ByteArrayDataOutput out = ByteStreams.newDataOutput();
        try {
            token.write(out);
        } catch (IOException e) {
            throw new ServiceException(e);
        }
        ByteString bs = ByteString.copyFrom(out.toByteArray());
        GetTokenResponseProto response = GetTokenResponseProto.newBuilder().setToken(bs).build();
        return response;
    }

    private boolean determineIfSigningIsRequired(UserGroupInformation callingUser) {
        switch (isSigningRequiredConfig) {
        case FALSE:
            return false;
        case TRUE:
            return true;
        // Note that this uses short user name without consideration for Kerberos realm.
        // This seems to be the common approach (e.g. for HDFS permissions), but it may be
        // better to consider the realm (although not the host, so not the full name).
        case EXCEPT_OWNER:
            return !clusterUser.equals(callingUser.getShortUserName());
        default:
            throw new AssertionError("Unknown value " + isSigningRequiredConfig);
        }
    }
}