/** * 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.oozie; import org.apache.commons.io.IOUtils; import org.apache.falcon.FalconException; import org.apache.falcon.entity.ClusterHelper; import org.apache.falcon.entity.EntityUtil; import org.apache.falcon.entity.v0.Entity; import org.apache.falcon.entity.v0.cluster.Cluster; import org.apache.falcon.hadoop.HadoopClientFactory; import org.apache.falcon.oozie.bundle.BUNDLEAPP; import org.apache.falcon.oozie.bundle.CONFIGURATION; import org.apache.falcon.oozie.bundle.CONFIGURATION.Property; import org.apache.falcon.oozie.bundle.COORDINATOR; import org.apache.falcon.security.CurrentUser; import org.apache.falcon.util.OozieUtils; import org.apache.falcon.workflow.engine.AbstractWorkflowEngine; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.permission.FsAction; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.oozie.client.OozieClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.transform.stream.StreamSource; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map.Entry; import java.util.Properties; /** * Base class for building oozie bundle - bundle is the entity that falcon tracks in oozie. * @param <T> */ public abstract class OozieBundleBuilder<T extends Entity> extends OozieEntityBuilder<T> { public static final Logger LOG = LoggerFactory.getLogger(OozieBundleBuilder.class); public OozieBundleBuilder(T entity) { super(entity); } @Override public Properties build(Cluster cluster, Path buildPath) throws FalconException { String clusterName = cluster.getName(); if (EntityUtil.getStartTime(entity, clusterName).compareTo(EntityUtil.getEndTime(entity, clusterName)) >= 0) { LOG.info("process validity start <= end for cluster {}. Skipping schedule", clusterName); return null; } List<Properties> coords = buildCoords(cluster, buildPath); if (coords == null || coords.isEmpty()) { return null; } BUNDLEAPP bundle = new BUNDLEAPP(); bundle.setName(EntityUtil.getWorkflowName(entity).toString()); // all the properties are set prior to bundle and coordinators creation createLogsDir(cluster, buildPath); //create logs dir for (Properties coordProps : coords) { // add the coordinator to the bundle COORDINATOR coord = new COORDINATOR(); String coordPath = coordProps.getProperty(OozieEntityBuilder.ENTITY_PATH); coord.setName(coordProps.getProperty(OozieEntityBuilder.ENTITY_NAME)); coord.setAppPath(getStoragePath(coordPath)); Properties appProps = createAppProperties(cluster, buildPath); appProps.putAll(coordProps); coord.setConfiguration(getConfig(appProps)); bundle.getCoordinator().add(coord); } Path marshalPath = marshal(cluster, bundle, buildPath); // write the bundle Properties properties = getProperties(marshalPath, entity.getName()); properties.setProperty(OozieClient.BUNDLE_APP_PATH, getStoragePath(buildPath)); properties.setProperty(AbstractWorkflowEngine.NAME_NODE, ClusterHelper.getStorageUrl(cluster)); //Add libpath String libPath = getLibPath(buildPath); if (libPath != null) { properties.put(OozieClient.LIBPATH, getStoragePath(libPath)); } return properties; } protected CONFIGURATION getConfig(Properties props) { CONFIGURATION conf = new CONFIGURATION(); for (Entry<Object, Object> prop : props.entrySet()) { Property confProp = new Property(); confProp.setName((String) prop.getKey()); confProp.setValue((String) prop.getValue()); conf.getProperty().add(confProp); } return conf; } protected Properties createAppProperties(Cluster cluster, Path buildPath) throws FalconException { Properties properties = getEntityProperties(cluster); properties.setProperty(AbstractWorkflowEngine.NAME_NODE, ClusterHelper.getStorageUrl(cluster)); properties.setProperty(AbstractWorkflowEngine.JOB_TRACKER, ClusterHelper.getMREndPoint(cluster)); properties.setProperty("colo.name", cluster.getColo()); properties.setProperty(OozieClient.USER_NAME, CurrentUser.getUser()); properties.setProperty(OozieClient.USE_SYSTEM_LIBPATH, "true"); properties.setProperty("falcon.libpath", ClusterHelper.getLocation(cluster, "working") + "/lib"); if (EntityUtil.isTableStorageType(cluster, entity)) { properties.putAll(getHiveCredentials(cluster)); } return properties; } private void createLogsDir(Cluster cluster, Path buildPath) throws FalconException { try { FileSystem fs = HadoopClientFactory.get().createFileSystem(buildPath.toUri(), ClusterHelper.getConfiguration(cluster)); Path logsDir = new Path(buildPath.getParent(), "logs"); if (!fs.mkdirs(logsDir)) { throw new FalconException("Failed to create " + logsDir); } // logs are copied with in oozie as the user in Post Processing and hence 777 permissions FsPermission permission = new FsPermission(FsAction.ALL, FsAction.ALL, FsAction.ALL); fs.setPermission(logsDir, permission); } catch (IOException e) { throw new FalconException(e); } } protected Path marshal(Cluster cluster, BUNDLEAPP bundle, Path outPath) throws FalconException { return marshal(cluster, new org.apache.falcon.oozie.bundle.ObjectFactory().createBundleApp(bundle), OozieUtils.BUNDLE_JAXB_CONTEXT, new Path(outPath, "bundle.xml")); } //Used by coordinator builders to return multiple coords //TODO Can avoid separate interface that returns list by building at lifecycle level protected abstract List<Properties> buildCoords(Cluster cluster, Path bundlePath) throws FalconException; public static BUNDLEAPP unmarshal(Cluster cluster, Path path) throws FalconException { InputStream resourceAsStream = null; try { FileSystem fs = HadoopClientFactory.get().createFileSystem(path.toUri(), ClusterHelper.getConfiguration(cluster)); Unmarshaller unmarshaller = OozieUtils.BUNDLE_JAXB_CONTEXT.createUnmarshaller(); @SuppressWarnings("unchecked") JAXBElement<BUNDLEAPP> jaxbElement = (JAXBElement<BUNDLEAPP>) unmarshaller.unmarshal(new StreamSource(fs.open(path)), BUNDLEAPP.class); return jaxbElement.getValue(); } catch (JAXBException e) { throw new FalconException(e); } catch (IOException e) { throw new FalconException(e); } finally { IOUtils.closeQuietly(resourceAsStream); } } public abstract String getLibPath(Path buildPath); }