org.apache.falcon.entity.DatasourceHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.falcon.entity.DatasourceHelper.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.falcon.entity;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.falcon.FalconException;
import org.apache.falcon.entity.store.ConfigurationStore;
import org.apache.falcon.entity.v0.EntityType;
import org.apache.falcon.entity.v0.datasource.Credential;
import org.apache.falcon.entity.v0.datasource.Credentialtype;
import org.apache.falcon.entity.v0.datasource.Datasource;
import org.apache.falcon.entity.v0.datasource.DatasourceType;
import org.apache.falcon.entity.v0.datasource.Driver;
import org.apache.falcon.entity.v0.datasource.Interface;
import org.apache.falcon.entity.v0.datasource.Interfaces;
import org.apache.falcon.entity.v0.datasource.Interfacetype;
import org.apache.falcon.entity.v0.datasource.PasswordAliasType;
import org.apache.falcon.entity.v0.datasource.Property;
import org.apache.falcon.security.CurrentUser;
import org.apache.hadoop.conf.Configuration;
import org.apache.falcon.hadoop.HadoopClientFactory;
import org.apache.falcon.security.CredentialProviderHelper;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.UserGroupInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URI;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Map;

/**
 * DataSource entity helper methods.
 */

public final class DatasourceHelper {

    public static final String HADOOP_CREDENTIAL_PROVIDER_FILEPATH = "hadoop.security.credential.provider.path";

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

    private static final ConfigurationStore STORE = ConfigurationStore.get();

    public static DatasourceType getDatasourceType(String datasourceName) throws FalconException {
        return getDatasource(datasourceName).getType();
    }

    private DatasourceHelper() {
    }

    public static Datasource getDatasource(String datasourceName) throws FalconException {
        return STORE.get(EntityType.DATASOURCE, datasourceName);
    }

    public static String getReadOnlyEndpoint(Datasource datasource) {
        return getInterfaceEndpoint(datasource, Interfacetype.READONLY);
    }

    public static String getWriteEndpoint(Datasource datasource) {
        return getInterfaceEndpoint(datasource, Interfacetype.WRITE);
    }

    /**
     * Returns user name and password pair as it is specified in the XML. If the credential type is
     * password-file, the path name is returned.
     *
     * @param db
     * @return Credential
     * @throws FalconException
     */

    public static Credential getReadPasswordInfo(Datasource db) throws FalconException {
        for (Interface ifs : db.getInterfaces().getInterfaces()) {
            if ((ifs.getType() == Interfacetype.READONLY) && (ifs.getCredential() != null)) {
                return ifs.getCredential();
            }
        }
        return getDefaultPasswordInfo(db.getInterfaces());
    }

    public static Credential getWritePasswordInfo(Datasource db) throws FalconException {
        for (Interface ifs : db.getInterfaces().getInterfaces()) {
            if ((ifs.getType() == Interfacetype.WRITE) && (ifs.getCredential() != null)) {
                return ifs.getCredential();
            }
        }
        return getDefaultPasswordInfo(db.getInterfaces());
    }

    /**
     * Returns user name and actual password pair. If the credential type is password-file, then the
     * password is read from the HDFS file. If the credential type is password-text, the clear text
     * password is returned.
     *
     * @param db
     * @return
     * @throws FalconException
     */
    public static java.util.Properties fetchReadPasswordInfo(Datasource db) throws FalconException {
        Credential cred = getReadPasswordInfo(db);
        return fetchPasswordInfo(cred);
    }

    public static java.util.Properties fetchWritePasswordInfo(Datasource db) throws FalconException {
        Credential cred = getWritePasswordInfo(db);
        return fetchPasswordInfo(cred);
    }

    public static java.util.Properties fetchPasswordInfo(Credential cred) throws FalconException {
        java.util.Properties p = new java.util.Properties();
        p.put("user", cred.getUserName());
        if (cred.getType() == Credentialtype.PASSWORD_TEXT) {
            p.put("password", cred.getPasswordText());
        } else if (cred.getType() == Credentialtype.PASSWORD_FILE) {
            String actualPasswd = fetchPasswordInfoFromFile(cred.getPasswordFile());
            p.put("password", actualPasswd);
        } else if (cred.getType() == Credentialtype.PASSWORD_ALIAS) {
            String actualPasswd = fetchPasswordInfoFromCredentialStore(cred.getPasswordAlias());
            p.put("password", actualPasswd);
        }
        return p;
    }

    public static String buildJceksProviderPath(URI credURI) {
        StringBuilder sb = new StringBuilder();
        final String credProviderPath = sb.append("jceks:").append("//").append(credURI.getScheme()).append("@")
                .append(credURI.getHost()).append(credURI.getPath()).toString();
        return credProviderPath;
    }

    /**
     * checks if two datasource interfaces are same.
     *
     * @param oldEntity old datasource entity
     * @param newEntity new datasource entity
     * @param ifacetype type of interface
     * @return true if same else false
     */
    public static boolean isSameInterface(Datasource oldEntity, Datasource newEntity, Interfacetype ifacetype) {
        LOG.debug("Verifying if Interfaces match for Datasource {} : Old - {}, New - {}", oldEntity, newEntity);
        Interface oIface = getInterface(oldEntity, ifacetype);
        Interface nIface = getInterface(newEntity, ifacetype);
        if ((oIface == null) && (nIface == null)) {
            return true;
        }
        if ((oIface == null) || (nIface == null)) {
            return false;
        }

        return (StringUtils.equals(oIface.getEndpoint(), nIface.getEndpoint())
                && isSameDriverClazz(oIface.getDriver(), nIface.getDriver())
                && isSameCredentials(oIface.getCredential(), nIface.getCredential()));

    }

    /**
     * check if datasource driver is same.
     * @param oldEntity
     * @param newEntity
     * @return true if same or false
     */

    public static boolean isSameDriverClazz(Driver oldEntity, Driver newEntity) {
        if ((oldEntity == null) && (newEntity == null)) {
            return true;
        }
        if ((oldEntity == null) || (newEntity == null)) {
            return false;
        }
        return StringUtils.equals(oldEntity.getClazz(), newEntity.getClazz());
    }

    /**
     * checks if data source properties are same.
     * @param oldEntity
     * @param newEntity
     * @return true if same else false
     */

    public static boolean isSameProperties(Datasource oldEntity, Datasource newEntity) {
        Map<String, String> oldProps = getDatasourceProperties(oldEntity);
        Map<String, String> newProps = getDatasourceProperties(newEntity);
        return oldProps.equals(newProps);
    }

    /**
     * checks if data source credentials are same.
     * @param oCred
     * @param nCred
     * @return true true
     */
    public static boolean isSameCredentials(Credential oCred, Credential nCred) {
        if ((oCred == null) && (nCred == null)) {
            return true;
        }
        if ((oCred == null) || (nCred == null)) {
            return true;
        }
        if (StringUtils.equals(oCred.getUserName(), nCred.getUserName())) {
            if (oCred.getType() == nCred.getType()) {
                if (oCred.getType() == Credentialtype.PASSWORD_TEXT) {
                    return StringUtils.equals(oCred.getPasswordText(), nCred.getPasswordText());
                } else if (oCred.getType() == Credentialtype.PASSWORD_FILE) {
                    return StringUtils.equals(oCred.getPasswordFile(), nCred.getPasswordFile());
                } else if (oCred.getType() == Credentialtype.PASSWORD_ALIAS) {
                    return (StringUtils.equals(oCred.getPasswordAlias().getAlias(),
                            nCred.getPasswordAlias().getAlias())
                            && StringUtils.equals(oCred.getPasswordAlias().getProviderPath(),
                                    nCred.getPasswordAlias().getProviderPath()));
                }
            } else {
                return false;
            }
        }
        return false;
    }

    public static Credential getCredential(Datasource db) {
        return getCredential(db, null);
    }

    public static Credential getCredential(Datasource db, Interfacetype interfaceType) {
        if (interfaceType == null) {
            return db.getInterfaces().getCredential();
        } else {
            for (Interface iface : db.getInterfaces().getInterfaces()) {
                if (iface.getType() == interfaceType) {
                    return iface.getCredential();
                }
            }
        }
        return null;
    }

    public static void validateCredential(Credential cred) throws FalconException {
        if (cred == null) {
            return;
        }
        switch (cred.getType()) {
        case PASSWORD_TEXT:
            if (StringUtils.isBlank(cred.getUserName()) || StringUtils.isBlank(cred.getPasswordText())) {
                throw new FalconException(String.format("Credential type '%s' missing tags '%s' or '%s'",
                        cred.getType().value(), "userName", "passwordText"));
            }
            break;
        case PASSWORD_FILE:
            if (StringUtils.isBlank(cred.getUserName()) || StringUtils.isBlank(cred.getPasswordFile())) {
                throw new FalconException(String.format("Credential type '%s' missing tags '%s' or '%s'",
                        cred.getType().value(), "userName", "passwordFile"));
            }
            break;
        case PASSWORD_ALIAS:
            if (StringUtils.isBlank(cred.getUserName()) || (cred.getPasswordAlias() == null)
                    || StringUtils.isBlank(cred.getPasswordAlias().getAlias())
                    || StringUtils.isBlank(cred.getPasswordAlias().getProviderPath())) {
                throw new FalconException(String.format("Credential type '%s' missing tags '%s' or '%s' or %s'",
                        cred.getType().value(), "userName", "alias", "providerPath"));
            }
            break;
        default:
            throw new FalconException(String.format("Unknown Credential type '%s'", cred.getType().value()));
        }
    }

    /**
     * Return the Interface endpoint for the interface type specified in the argument.
     *
     * @param db
     * @param type - can be read-only or write
     * @return
     */
    private static String getInterfaceEndpoint(Datasource db, Interfacetype type) {
        if (getInterface(db, type) != null) {
            return getInterface(db, type).getEndpoint();
        } else {
            return null;
        }
    }

    private static Interface getInterface(Datasource db, Interfacetype type) {
        for (Interface ifs : db.getInterfaces().getInterfaces()) {
            if (ifs.getType() == type) {
                return ifs;
            }
        }
        return null;
    }

    private static Credential getDefaultPasswordInfo(Interfaces ifs) throws FalconException {

        if (ifs.getCredential() != null) {
            return ifs.getCredential();
        } else {
            throw new FalconException("Missing Interfaces default credential");
        }
    }

    /**
     * fetch password from the corresponding store.
     * @param c
     * @return actual password
     * @throws FalconException
     */
    private static String fetchPasswordInfoFromCredentialStore(final PasswordAliasType c) throws FalconException {
        try {
            final String credPath = c.getProviderPath();
            final URI credURI = new URI(credPath);
            if (StringUtils.isBlank(credURI.getScheme()) || StringUtils.isBlank(credURI.getHost())
                    || StringUtils.isBlank(credURI.getPath())) {
                throw new FalconException("Password alias jceks provider HDFS path is incorrect.");
            }
            final String alias = c.getAlias();
            if (StringUtils.isBlank(alias)) {
                throw new FalconException("Password alias is empty.");
            }

            final String credProviderPath = buildJceksProviderPath(credURI);
            LOG.info("Credential provider HDFS path : " + credProviderPath);

            if (CredentialProviderHelper.isProviderAvailable()) {
                UserGroupInformation ugi = CurrentUser.getProxyUGI();
                String password = ugi.doAs(new PrivilegedExceptionAction<String>() {
                    public String run() throws Exception {
                        final Configuration conf = new Configuration();
                        conf.set(HadoopClientFactory.FS_DEFAULT_NAME_KEY, credPath);
                        conf.set(CredentialProviderHelper.CREDENTIAL_PROVIDER_PATH, credProviderPath);
                        FileSystem fs = FileSystem.get(credURI, conf);
                        if (!fs.exists(new Path(credPath))) {
                            String msg = String.format(
                                    "Credential provider hdfs path [%s] does not " + "exist or access denied!",
                                    credPath);
                            LOG.error(msg);
                            throw new FalconException(msg);
                        }
                        return CredentialProviderHelper.resolveAlias(conf, alias);
                    }
                });
                return password;
            } else {
                throw new FalconException("Credential Provider is not initialized");
            }
        } catch (Exception ioe) {
            String msg = "Exception while trying to fetch credential alias";
            LOG.error(msg, ioe);
            throw new FalconException(msg, ioe);
        }
    }

    /**
     * fetch the password from file.
     *
     * @param passwordFilePath
     * @return
     * @throws FalconException
     */

    private static String fetchPasswordInfoFromFile(String passwordFilePath) throws FalconException {
        try {
            Path path = new Path(passwordFilePath);
            FileSystem fs = HadoopClientFactory.get().createProxiedFileSystem(path.toUri());
            if (!fs.exists(path)) {
                throw new IOException("The password file does not exist! ");
            }

            if (!fs.isFile(path)) {
                throw new IOException("The password file cannot be a directory! ");
            }

            InputStream is = fs.open(path);
            StringWriter writer = new StringWriter();
            try {
                IOUtils.copy(is, writer);
                return writer.toString();
            } finally {
                IOUtils.closeQuietly(is);
                IOUtils.closeQuietly(writer);
                fs.close();
            }
        } catch (IOException ioe) {
            LOG.error("Error reading password file from HDFS : " + ioe);
            throw new FalconException(ioe);
        }
    }

    /*
     returns data store properties
     */

    public static Map<String, String> getDatasourceProperties(final Datasource datasource) {
        Map<String, String> returnProps = new HashMap<String, String>();
        if (datasource.getProperties() != null) {
            for (Property prop : datasource.getProperties().getProperties()) {
                returnProps.put(prop.getName(), prop.getValue());
            }
        }
        return returnProps;
    }

}