Repository: oozie Updated Branches: refs/heads/master 6a731f992 -> a762991ab
OOZIE-2187 Add a way to specify a default JT/RM and NN (rkanter) Project: http://git-wip-us.apache.org/repos/asf/oozie/repo Commit: http://git-wip-us.apache.org/repos/asf/oozie/commit/a762991a Tree: http://git-wip-us.apache.org/repos/asf/oozie/tree/a762991a Diff: http://git-wip-us.apache.org/repos/asf/oozie/diff/a762991a Branch: refs/heads/master Commit: a762991ab00df5e60d5e8b7f430b89861abe0671 Parents: 6a731f9 Author: Robert Kanter <[email protected]> Authored: Tue Jul 14 16:31:43 2015 -0700 Committer: Robert Kanter <[email protected]> Committed: Tue Jul 14 16:31:43 2015 -0700 ---------------------------------------------------------------------- .../org/apache/oozie/action/ActionExecutor.java | 23 +- .../oozie/action/hadoop/JavaActionExecutor.java | 9 +- .../workflow/lite/LiteWorkflowAppParser.java | 357 ++++++++++--------- core/src/main/resources/oozie-default.xml | 21 ++ .../lite/TestLiteWorkflowAppParser.java | 270 ++++++++++++-- .../wf-schema-no-jobtracker-global.xml | 61 ++++ .../test/resources/wf-schema-no-jobtracker.xml | 57 +++ .../resources/wf-schema-no-namenode-global.xml | 61 ++++ .../test/resources/wf-schema-no-namenode.xml | 57 +++ .../resources/wf-schema-valid-global-ext.xml | 16 +- release-log.txt | 1 + 11 files changed, 738 insertions(+), 195 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/main/java/org/apache/oozie/action/ActionExecutor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/oozie/action/ActionExecutor.java b/core/src/main/java/org/apache/oozie/action/ActionExecutor.java index 07c6d26..2be4549 100644 --- a/core/src/main/java/org/apache/oozie/action/ActionExecutor.java +++ b/core/src/main/java/org/apache/oozie/action/ActionExecutor.java @@ -60,8 +60,6 @@ public abstract class ActionExecutor { * Error code used by {@link #convertException} when there is not register error information for an exception. */ public static final String ERROR_OTHER = "OTHER"; - - public boolean requiresNNJT = false; public static enum RETRYPOLICY { EXPONENTIAL, PERIODIC @@ -562,4 +560,25 @@ public abstract class ActionExecutor { */ public abstract boolean isCompleted(String externalStatus); + /** + * Returns true if this action type requires a NameNode and JobTracker. These can either be specified directly in the action + * via <name-node> and <job-tracker>, from the fields in the global section, or from their default values. If + * false, Oozie won't ensure (i.e. won't throw an Exception if non-existant) that this action type has these values. + * + * @return true if a NameNode and JobTracker are required; false if not + */ + public boolean requiresNameNodeJobTracker() { + return false; + } + + /** + * Returns true if this action type supports a Configuration and JobXML. In this case, Oozie will include the + * <configuration> and <job-xml> elements from the global section (if provided) with the action. If false, Oozie + * won't add these. + * + * @return true if the global section's Configuration and JobXML should be given; false if not + */ + public boolean supportsConfigurationJobXML() { + return false; + } } http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java b/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java index 492ceaf..6e959df 100644 --- a/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java +++ b/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java @@ -135,7 +135,6 @@ public class JavaActionExecutor extends ActionExecutor { protected JavaActionExecutor(String type) { super(type); - requiresNNJT = true; } public static List<Class> getCommonLauncherClasses() { @@ -1628,4 +1627,12 @@ public class JavaActionExecutor extends ActionExecutor { } } } + + public boolean requiresNameNodeJobTracker() { + return true; + } + + public boolean supportsConfigurationJobXML() { + return true; + } } http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java b/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java index c857011..d3a6523 100644 --- a/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java +++ b/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java @@ -93,6 +93,14 @@ public class LiteWorkflowAppParser { public static final String VALIDATE_FORK_JOIN = "oozie.validate.ForkJoin"; public static final String WF_VALIDATE_FORK_JOIN = "oozie.wf.validate.ForkJoin"; + public static final String DEFAULT_NAME_NODE = "oozie.actions.default.name-node"; + public static final String DEFAULT_JOB_TRACKER = "oozie.actions.default.job-tracker"; + + private static final String JOB_TRACKER = "job-tracker"; + private static final String NAME_NODE = "name-node"; + private static final String JOB_XML = "job-xml"; + private static final String CONFIGURATION = "configuration"; + private Schema schema; private Class<? extends ControlNodeHandler> controlNodeHandler; private Class<? extends DecisionNodeHandler> decisionHandlerClass; @@ -121,6 +129,9 @@ public class LiteWorkflowAppParser { private List<NodeAndTopDecisionParent> visitedOkNodes = new ArrayList<NodeAndTopDecisionParent>(); private List<String> visitedJoinNodes = new ArrayList<String>(); + private String defaultNameNode; + private String defaultJobTracker; + public LiteWorkflowAppParser(Schema schema, Class<? extends ControlNodeHandler> controlNodeHandler, Class<? extends DecisionNodeHandler> decisionHandlerClass, @@ -129,6 +140,21 @@ public class LiteWorkflowAppParser { this.controlNodeHandler = controlNodeHandler; this.decisionHandlerClass = decisionHandlerClass; this.actionHandlerClass = actionHandlerClass; + + defaultNameNode = ConfigurationService.get(DEFAULT_NAME_NODE); + if (defaultNameNode != null) { + defaultNameNode = defaultNameNode.trim(); + if (defaultNameNode.isEmpty()) { + defaultNameNode = null; + } + } + defaultJobTracker = ConfigurationService.get(DEFAULT_JOB_TRACKER); + if (defaultJobTracker != null) { + defaultJobTracker = defaultJobTracker.trim(); + if (defaultJobTracker.isEmpty()) { + defaultJobTracker = null; + } + } } public LiteWorkflowApp validateAndParse(Reader reader, Configuration jobConf) throws WorkflowException { @@ -391,115 +417,78 @@ public class LiteWorkflowAppParser { throws WorkflowException { Namespace ns = root.getNamespace(); LiteWorkflowApp def = null; - Element global = null; + GlobalSectionData gData = null; for (Element eNode : (List<Element>) root.getChildren()) { if (eNode.getName().equals(START_E)) { def = new LiteWorkflowApp(root.getAttributeValue(NAME_A), strDef, new StartNodeDef(controlNodeHandler, eNode.getAttributeValue(TO_A))); - } - else { - if (eNode.getName().equals(END_E)) { - def.addNode(new EndNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler)); + } else if (eNode.getName().equals(END_E)) { + def.addNode(new EndNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler)); + } else if (eNode.getName().equals(KILL_E)) { + def.addNode(new KillNodeDef(eNode.getAttributeValue(NAME_A), + eNode.getChildText(KILL_MESSAGE_E, ns), controlNodeHandler)); + } else if (eNode.getName().equals(FORK_E)) { + List<String> paths = new ArrayList<String>(); + for (Element tran : (List<Element>) eNode.getChildren(FORK_PATH_E, ns)) { + paths.add(tran.getAttributeValue(FORK_START_A)); } - else { - if (eNode.getName().equals(KILL_E)) { - def.addNode(new KillNodeDef(eNode.getAttributeValue(NAME_A), - eNode.getChildText(KILL_MESSAGE_E, ns), controlNodeHandler)); + def.addNode(new ForkNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler, paths)); + } else if (eNode.getName().equals(JOIN_E)) { + def.addNode(new JoinNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler, eNode.getAttributeValue(TO_A))); + } else if (eNode.getName().equals(DECISION_E)) { + Element eSwitch = eNode.getChild(DECISION_SWITCH_E, ns); + List<String> transitions = new ArrayList<String>(); + for (Element e : (List<Element>) eSwitch.getChildren(DECISION_CASE_E, ns)) { + transitions.add(e.getAttributeValue(TO_A)); + } + transitions.add(eSwitch.getChild(DECISION_DEFAULT_E, ns).getAttributeValue(TO_A)); + + String switchStatement = XmlUtils.prettyPrint(eSwitch).toString(); + def.addNode(new DecisionNodeDef(eNode.getAttributeValue(NAME_A), switchStatement, decisionHandlerClass, + transitions)); + } else if (ACTION_E.equals(eNode.getName())) { + String[] transitions = new String[2]; + Element eActionConf = null; + for (Element elem : (List<Element>) eNode.getChildren()) { + if (ACTION_OK_E.equals(elem.getName())) { + transitions[0] = elem.getAttributeValue(TO_A); + } else if (ACTION_ERROR_E.equals(elem.getName())) { + transitions[1] = elem.getAttributeValue(TO_A); + } else if (SLA_INFO.equals(elem.getName()) || CREDENTIALS.equals(elem.getName())) { + continue; + } else { + eActionConf = elem; + handleDefaultsAndGlobal(gData, configDefault, elem); } - else { - if (eNode.getName().equals(FORK_E)) { - List<String> paths = new ArrayList<String>(); - for (Element tran : (List<Element>) eNode.getChildren(FORK_PATH_E, ns)) { - paths.add(tran.getAttributeValue(FORK_START_A)); - } - def.addNode(new ForkNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler, paths)); - } - else { - if (eNode.getName().equals(JOIN_E)) { - def.addNode(new JoinNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler, - eNode.getAttributeValue(TO_A))); - } - else { - if (eNode.getName().equals(DECISION_E)) { - Element eSwitch = eNode.getChild(DECISION_SWITCH_E, ns); - List<String> transitions = new ArrayList<String>(); - for (Element e : (List<Element>) eSwitch.getChildren(DECISION_CASE_E, ns)) { - transitions.add(e.getAttributeValue(TO_A)); - } - transitions.add(eSwitch.getChild(DECISION_DEFAULT_E, ns).getAttributeValue(TO_A)); - - String switchStatement = XmlUtils.prettyPrint(eSwitch).toString(); - def.addNode(new DecisionNodeDef(eNode.getAttributeValue(NAME_A), switchStatement, decisionHandlerClass, - transitions)); - } - else { - if (ACTION_E.equals(eNode.getName())) { - String[] transitions = new String[2]; - Element eActionConf = null; - for (Element elem : (List<Element>) eNode.getChildren()) { - if (ACTION_OK_E.equals(elem.getName())) { - transitions[0] = elem.getAttributeValue(TO_A); - } - else { - if (ACTION_ERROR_E.equals(elem.getName())) { - transitions[1] = elem.getAttributeValue(TO_A); - } - else { - if (SLA_INFO.equals(elem.getName()) || CREDENTIALS.equals(elem.getName())) { - continue; - } - else { - eActionConf = elem; - handleGlobal(ns, global, configDefault, elem); - } - } - } - } - - String credStr = eNode.getAttributeValue(CRED_A); - String userRetryMaxStr = eNode.getAttributeValue(USER_RETRY_MAX_A); - String userRetryIntervalStr = eNode.getAttributeValue(USER_RETRY_INTERVAL_A); - try { - if (!StringUtils.isEmpty(userRetryMaxStr)) { - userRetryMaxStr = ELUtils.resolveAppName(userRetryMaxStr, jobConf); - } - if (!StringUtils.isEmpty(userRetryIntervalStr)) { - userRetryIntervalStr = ELUtils.resolveAppName(userRetryIntervalStr, - jobConf); - } - } - catch (Exception e) { - throw new WorkflowException(ErrorCode.E0703, e.getMessage()); - } - - String actionConf = XmlUtils.prettyPrint(eActionConf).toString(); - def.addNode(new ActionNodeDef(eNode.getAttributeValue(NAME_A), actionConf, actionHandlerClass, - transitions[0], transitions[1], credStr, - userRetryMaxStr, userRetryIntervalStr)); - } - else { - if (SLA_INFO.equals(eNode.getName()) || CREDENTIALS.equals(eNode.getName())) { - // No operation is required - } - else { - if (eNode.getName().equals(GLOBAL)) { - global = eNode; - } - else { - if (eNode.getName().equals(PARAMETERS)) { - // No operation is required - } - else { - throw new WorkflowException(ErrorCode.E0703, eNode.getName()); - } - } - } - } - } - } - } + } + + String credStr = eNode.getAttributeValue(CRED_A); + String userRetryMaxStr = eNode.getAttributeValue(USER_RETRY_MAX_A); + String userRetryIntervalStr = eNode.getAttributeValue(USER_RETRY_INTERVAL_A); + try { + if (!StringUtils.isEmpty(userRetryMaxStr)) { + userRetryMaxStr = ELUtils.resolveAppName(userRetryMaxStr, jobConf); } + if (!StringUtils.isEmpty(userRetryIntervalStr)) { + userRetryIntervalStr = ELUtils.resolveAppName(userRetryIntervalStr, jobConf); + } + } + catch (Exception e) { + throw new WorkflowException(ErrorCode.E0703, e.getMessage()); } + + String actionConf = XmlUtils.prettyPrint(eActionConf).toString(); + def.addNode(new ActionNodeDef(eNode.getAttributeValue(NAME_A), actionConf, actionHandlerClass, + transitions[0], transitions[1], credStr, + userRetryMaxStr, userRetryIntervalStr)); + } else if (SLA_INFO.equals(eNode.getName()) || CREDENTIALS.equals(eNode.getName())) { + // No operation is required + } else if (eNode.getName().equals(GLOBAL)) { + gData = parseGlobalSection(ns, eNode); + } else if (eNode.getName().equals(PARAMETERS)) { + // No operation is required + } else { + throw new WorkflowException(ErrorCode.E0703, eNode.getName()); } } return def; @@ -575,85 +564,138 @@ public class LiteWorkflowAppParser { traversed.put(node.getName(), VisitStatus.VISITED); } - /** - * Handle the global section - * - * @param ns - * @param global - * @param eActionConf - * @throws WorkflowException - */ - - @SuppressWarnings("unchecked") - private void handleGlobal(Namespace ns, Element global, Configuration configDefault, Element eActionConf) - throws WorkflowException { + private void addChildElement(Element parent, Namespace ns, String childName, String childValue) { + Element child = new Element(childName, ns); + child.setText(childValue); + parent.addContent(child); + } - // Use the action's namespace when getting children of the action (will - // be different than ns for extension actions) - Namespace actionNs = eActionConf.getNamespace(); + private class GlobalSectionData { + final String jobTracker; + final String nameNode; + final List<String> jobXmls; + final Configuration conf; + + public GlobalSectionData(String jobTracker, String nameNode, List<String> jobXmls, Configuration conf) { + this.jobTracker = jobTracker; + this.nameNode = nameNode; + this.jobXmls = jobXmls; + this.conf = conf; + } + } + private GlobalSectionData parseGlobalSection(Namespace ns, Element global) throws WorkflowException { + GlobalSectionData gData = null; if (global != null) { - Element globalJobTracker = global.getChild("job-tracker", ns); - Element globalNameNode = global.getChild("name-node", ns); - List<Element> globalJobXml = global.getChildren("job-xml", ns); - Element globalConfiguration = global.getChild("configuration", ns); + String globalJobTracker = null; + Element globalJobTrackerElement = global.getChild(JOB_TRACKER, ns); + if (globalJobTrackerElement != null) { + globalJobTracker = globalJobTrackerElement.getValue(); + } - if (globalJobTracker != null && eActionConf.getChild("job-tracker", actionNs) == null) { - Element jobTracker = new Element("job-tracker", actionNs); - jobTracker.setText(globalJobTracker.getText()); - eActionConf.addContent(jobTracker); + String globalNameNode = null; + Element globalNameNodeElement = global.getChild(NAME_NODE, ns); + if (globalNameNodeElement != null) { + globalNameNode = globalNameNodeElement.getValue(); } - if (globalNameNode != null && eActionConf.getChild("name-node", actionNs) == null) { - Element nameNode = new Element("name-node", actionNs); - nameNode.setText(globalNameNode.getText()); - eActionConf.addContent(nameNode); + List<String> globalJobXmls = null; + @SuppressWarnings("unchecked") + List<Element> globalJobXmlElements = global.getChildren(JOB_XML, ns); + if (!globalJobXmlElements.isEmpty()) { + globalJobXmls = new ArrayList<String>(globalJobXmlElements.size()); + for(Element jobXmlElement: globalJobXmlElements) { + globalJobXmls.add(jobXmlElement.getText()); + } } - if (!globalJobXml.isEmpty()) { - List<Element> actionJobXml = eActionConf.getChildren("job-xml", actionNs); - for(Element jobXml: globalJobXml){ + Configuration globalConf = null; + Element globalConfigurationElement = global.getChild(CONFIGURATION, ns); + if (globalConfigurationElement != null) { + try { + globalConf = new XConfiguration(new StringReader(XmlUtils.prettyPrint(globalConfigurationElement).toString())); + } catch (IOException ioe) { + throw new WorkflowException(ErrorCode.E0700, "Error while processing global section conf"); + } + } + gData = new GlobalSectionData(globalJobTracker, globalNameNode, globalJobXmls, globalConf); + } + return gData; + } + + private void handleDefaultsAndGlobal(GlobalSectionData gData, Configuration configDefault, Element actionElement) + throws WorkflowException { + ActionExecutor ae = Services.get().get(ActionService.class).getExecutor(actionElement.getName()); + if (ae == null) { + throw new WorkflowException(ErrorCode.E0723, actionElement.getName(), ActionService.class.getName()); + } + + Namespace actionNs = actionElement.getNamespace(); + + if (ae.requiresNameNodeJobTracker()) { + if (actionElement.getChild(NAME_NODE, actionNs) == null) { + if (gData != null && gData.nameNode != null) { + addChildElement(actionElement, actionNs, NAME_NODE, gData.nameNode); + } else if (defaultNameNode != null) { + addChildElement(actionElement, actionNs, NAME_NODE, defaultNameNode); + } else { + throw new WorkflowException(ErrorCode.E0701, "No " + NAME_NODE + " defined"); + } + } + if (actionElement.getChild(JOB_TRACKER, actionNs) == null) { + if (gData != null && gData.jobTracker != null) { + addChildElement(actionElement, actionNs, JOB_TRACKER, gData.jobTracker); + } else if (defaultJobTracker != null) { + addChildElement(actionElement, actionNs, JOB_TRACKER, defaultJobTracker); + } else { + throw new WorkflowException(ErrorCode.E0701, "No " + JOB_TRACKER + " defined"); + } + } + } + + if (ae.supportsConfigurationJobXML()) { + @SuppressWarnings("unchecked") + List<Element> actionJobXmls = actionElement.getChildren(JOB_XML, actionNs); + if (gData != null && gData.jobXmls != null) { + for(String gJobXml : gData.jobXmls) { boolean alreadyExists = false; - for(Element actionXml: actionJobXml){ - if(jobXml.getText().equals(actionXml.getText())){ + for (Element actionXml : actionJobXmls) { + if (gJobXml.equals(actionXml.getText())) { alreadyExists = true; break; } } - - if (!alreadyExists){ - Element ejobXml = new Element("job-xml", actionNs); - ejobXml.setText(jobXml.getText()); - eActionConf.addContent(ejobXml); + if (!alreadyExists) { + Element ejobXml = new Element(JOB_XML, actionNs); + ejobXml.setText(gJobXml); + actionElement.addContent(ejobXml); } - } } + try { XConfiguration actionConf = new XConfiguration(); if (configDefault != null) XConfiguration.copy(configDefault, actionConf); - if (globalConfiguration != null) { - Configuration globalConf = new XConfiguration(new StringReader(XmlUtils.prettyPrint( - globalConfiguration).toString())); - XConfiguration.copy(globalConf, actionConf); + if (gData != null && gData.conf != null) { + XConfiguration.copy(gData.conf, actionConf); } - Element actionConfiguration = eActionConf.getChild("configuration", actionNs); + Element actionConfiguration = actionElement.getChild(CONFIGURATION, actionNs); if (actionConfiguration != null) { //copy and override - XConfiguration.copy(new XConfiguration(new StringReader(XmlUtils.prettyPrint( - actionConfiguration).toString())), actionConf); + XConfiguration.copy(new XConfiguration(new StringReader(XmlUtils.prettyPrint(actionConfiguration).toString())), + actionConf); } - int position = eActionConf.indexOf(actionConfiguration); - eActionConf.removeContent(actionConfiguration); //replace with enhanced one + int position = actionElement.indexOf(actionConfiguration); + actionElement.removeContent(actionConfiguration); //replace with enhanced one Element eConfXml = XmlUtils.parseXml(actionConf.toXmlString(false)); eConfXml.detach(); eConfXml.setNamespace(actionNs); if (position > 0) { - eActionConf.addContent(position, eConfXml); + actionElement.addContent(position, eConfXml); } else { - eActionConf.addContent(eConfXml); + actionElement.addContent(eConfXml); } } catch (IOException e) { @@ -663,21 +705,6 @@ public class LiteWorkflowAppParser { throw new WorkflowException(ErrorCode.E0700, "Error while processing action conf"); } } - else { - ActionExecutor ae = Services.get().get(ActionService.class).getExecutor(eActionConf.getName()); - if (ae == null) { - throw new WorkflowException(ErrorCode.E0723, eActionConf.getName(), ActionService.class.getName()); - } - if (ae.requiresNNJT) { - - if (eActionConf.getChild("name-node", actionNs) == null) { - throw new WorkflowException(ErrorCode.E0701, "No name-node defined"); - } - if (eActionConf.getChild("job-tracker", actionNs) == null) { - throw new WorkflowException(ErrorCode.E0701, "No job-tracker defined"); - } - } - } } } http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/main/resources/oozie-default.xml ---------------------------------------------------------------------- diff --git a/core/src/main/resources/oozie-default.xml b/core/src/main/resources/oozie-default.xml index 4dc127e..2f44827 100644 --- a/core/src/main/resources/oozie-default.xml +++ b/core/src/main/resources/oozie-default.xml @@ -2543,4 +2543,25 @@ Set it false if there is any security concern. </description> </property> + + <property> + <name>oozie.actions.default.name-node</name> + <value> </value> + <description> + The default value to use for the <name-node> element in applicable action types. This value will be used when + neither the action itself nor the global section specifies a <name-node>. As expected, it should be of the form + "hdfs://HOST:PORT". + </description> + </property> + + <property> + <name>oozie.actions.default.job-tracker</name> + <value> </value> + <description> + The default value to use for the <job-tracker> element in applicable action types. This value will be used when + neither the action itself nor the global section specifies a <job-tracker>. As expected, it should be of the form + "HOST:PORT". + </description> + </property> + </configuration> http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java b/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java index 0eb1ee0..1fc1736 100644 --- a/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java +++ b/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Map; import org.apache.oozie.service.ActionService; +import org.apache.oozie.service.ConfigurationService; import org.apache.oozie.service.LiteWorkflowStoreService; import org.apache.oozie.service.SchemaService; import org.apache.oozie.service.Services; @@ -47,7 +48,7 @@ public class TestLiteWorkflowAppParser extends XTestCase { @Override protected void setUp() throws Exception { super.setUp(); - setSystemProperty("oozie.service.SchemaService.wf.ext.schemas", "hive-action-0.2.xsd"); + setSystemProperty("oozie.service.SchemaService.wf.ext.schemas", "hive-action-0.2.xsd,email-action-0.2.xsd"); new Services().init(); } @@ -57,6 +58,13 @@ public class TestLiteWorkflowAppParser extends XTestCase { super.tearDown(); } + private String cleanupXml(String xml) { + xml = xml.replaceAll(" xmlns=?(\"|\')(\"|\')", ""); + xml = xml.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2 + xml = xml.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1 + return xml; + } + public void testParserGlobal() throws Exception { LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, LiteWorkflowStoreService.LiteControlNodeHandler.class, @@ -79,8 +87,8 @@ public class TestLiteWorkflowAppParser extends XTestCase { " </streaming>\r\n" + " <file>/tmp</file>\r\n" + " <archive>/tmp</archive>\r\n" + - " <job-tracker>${foo}</job-tracker>\r\n" + " <name-node>bar</name-node>\r\n" + + " <job-tracker>${foo}</job-tracker>\r\n" + " <configuration>\r\n" + " <property>\r\n" + " <name>b</name>\r\n" + @@ -92,11 +100,8 @@ public class TestLiteWorkflowAppParser extends XTestCase { " </property>\r\n" + " </configuration>\r\n" + "</map-reduce>"; - d = d.replaceAll(" xmlns=?(\"|\')(\"|\')", ""); - d = d.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2 - d = d.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1 - System.out.println("\n" + d +"\n"); - assertEquals(expectedD.replaceAll(" ",""), d.replaceAll(" ", "")); + d = cleanupXml(d); + assertEquals(expectedD.replaceAll(" ", ""), d.replaceAll(" ", "")); } @@ -123,8 +128,8 @@ public class TestLiteWorkflowAppParser extends XTestCase { " <job-xml>/tmp</job-xml>\r\n" + " <file>/tmp</file>\r\n" + " <archive>/tmp</archive>\r\n" + - " <job-tracker>foo</job-tracker>\r\n" + " <name-node>bar</name-node>\r\n" + + " <job-tracker>foo</job-tracker>\r\n" + " <job-xml>/spam1</job-xml>\r\n" + " <job-xml>/spam2</job-xml>\r\n" + " <configuration>\r\n" + @@ -138,10 +143,8 @@ public class TestLiteWorkflowAppParser extends XTestCase { " </property>\r\n" + " </configuration>\r\n" + "</map-reduce>"; - d = d.replaceAll(" xmlns=?(\"|\')(\"|\')", ""); - d = d.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2 - d = d.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1 - assertEquals(expectedD.replaceAll(" ",""), d.replaceAll(" ", "")); + d = cleanupXml(d); + assertEquals(expectedD.replaceAll(" ", ""), d.replaceAll(" ", "")); } @@ -175,12 +178,10 @@ public class TestLiteWorkflowAppParser extends XTestCase { " <param>x</param>\r\n" + " <file>/tmp</file>\r\n" + " <file>/tmp</file>\r\n" + - " <job-tracker>${foo}</job-tracker>\r\n" + " <name-node>bar</name-node>\r\n" + + " <job-tracker>${foo}</job-tracker>\r\n" + "</pig>"; - e = e.replaceAll(" xmlns=?(\"|\')(\"|\')", ""); - e = e.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2 - e = e.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1 + e = cleanupXml(e); assertEquals(expectedE.replaceAll(" ", ""), e.replaceAll(" ", "")); } @@ -218,12 +219,10 @@ public class TestLiteWorkflowAppParser extends XTestCase { " <script>script.q</script>\r\n" + " <param>INPUT=/tmp/table</param>\r\n" + " <param>OUTPUT=/tmp/hive</param>\r\n" + - " <job-tracker>foo</job-tracker>\r\n" + " <name-node>bar</name-node>\r\n" + + " <job-tracker>foo</job-tracker>\r\n" + "</hive>"; - a = a.replaceAll(" xmlns=?(\"|\')(\"|\')", ""); - a = a.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2 - a = a.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1 + a = cleanupXml(a); assertEquals(expectedA.replaceAll(" ",""), a.replaceAll(" ", "")); } @@ -258,10 +257,29 @@ public class TestLiteWorkflowAppParser extends XTestCase { " <arg>/tmp/data.txt</arg>\r\n" + " <arg>/tmp2/data.txt</arg>\r\n" + "</distcp>"; - b = b.replaceAll(" xmlns=?(\"|\')(\"|\')", ""); - b = b.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2 - b = b.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1 - assertEquals(expectedB.replaceAll(" ",""), b.replaceAll(" ", "")); + b = cleanupXml(b); + assertEquals(expectedB.replaceAll(" ", ""), b.replaceAll(" ", "")); + } + + public void testParserGlobalExtensionActionsNotApplicable() throws Exception { + LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, + LiteWorkflowStoreService.LiteControlNodeHandler.class, + LiteWorkflowStoreService.LiteDecisionHandler.class, + LiteWorkflowStoreService.LiteActionHandler.class); + + // Not all actions want a JT, NN, conf, or jobxml (e.g. email action) + LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-valid-global-ext.xml", -1), + new Configuration()); + + String c1 = app.getNode("c1").getConf(); + String expectedC1 = + "<email xmlns=\"uri:oozie:email-action:0.2\">\r\n" + + " <to>[email protected]</to>\r\n" + + " <subject>foo</subject>\r\n" + + " <body>bar</body>\r\n" + + "</email>"; + c1 = cleanupXml(c1); + assertEquals(expectedC1.replaceAll(" ", ""), c1.replaceAll(" ", "")); } public void testParserGlobalExtensionActionsNoGlobal() throws Exception { @@ -288,6 +306,210 @@ public class TestLiteWorkflowAppParser extends XTestCase { } } + public void testParserDefaultNameNode() throws Exception { + ConfigurationService.set("oozie.actions.default.name-node", "default-nn"); + LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, + LiteWorkflowStoreService.LiteControlNodeHandler.class, + LiteWorkflowStoreService.LiteDecisionHandler.class, + LiteWorkflowStoreService.LiteActionHandler.class); + + LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-namenode.xml", -1), + new Configuration()); + String a = app.getNode("a").getConf(); + String expectedA = + "<hive xmlns=\"uri:oozie:hive-action:0.2\">\r\n" + + " <prepare>\r\n" + + " <delete path=\"/tmp\" />\r\n" + + " <mkdir path=\"/tmp\" />\r\n" + + " </prepare>\r\n" + + " <job-tracker>foo</job-tracker>\r\n" + + " <configuration>\r\n" + + " <property>\r\n" + + " <name>c</name>\r\n" + + " <value>C</value>\r\n" + + " </property>\r\n" + + " </configuration>\r\n" + + " <script>script.q</script>\r\n" + + " <param>INPUT=/tmp/table</param>\r\n" + + " <param>OUTPUT=/tmp/hive</param>\r\n" + + " <name-node>default-nn</name-node>\r\n" + + "</hive>"; + a = cleanupXml(a); + assertEquals(expectedA.replaceAll(" ", ""), a.replaceAll(" ", "")); + } + + public void testParserDefaultNameNodeWithGlobal() throws Exception { + ConfigurationService.set("oozie.actions.default.name-node", "default-nn"); + LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, + LiteWorkflowStoreService.LiteControlNodeHandler.class, + LiteWorkflowStoreService.LiteDecisionHandler.class, + LiteWorkflowStoreService.LiteActionHandler.class); + + LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-namenode-global.xml", -1), + new Configuration()); + String a = app.getNode("a").getConf(); + String expectedA = + "<hive xmlns=\"uri:oozie:hive-action:0.2\">\r\n" + + " <prepare>\r\n" + + " <delete path=\"/tmp\" />\r\n" + + " <mkdir path=\"/tmp\" />\r\n" + + " </prepare>\r\n" + + " <job-tracker>foo</job-tracker>\r\n" + + " <configuration>\r\n" + + " <property>\r\n" + + " <name>c</name>\r\n" + + " <value>C</value>\r\n" + + " </property>\r\n" + + " </configuration>\r\n" + + " <script>script.q</script>\r\n" + + " <param>INPUT=/tmp/table</param>\r\n" + + " <param>OUTPUT=/tmp/hive</param>\r\n" + + " <name-node>global-nn</name-node>\r\n" + + "</hive>"; + a = cleanupXml(a); + assertEquals(expectedA.replaceAll(" ", ""), a.replaceAll(" ", "")); + } + + public void testParserDefaultNameNodeNotApplicable() throws Exception { + ConfigurationService.set("oozie.actions.default.name-node", "default-nn"); + LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, + LiteWorkflowStoreService.LiteControlNodeHandler.class, + LiteWorkflowStoreService.LiteDecisionHandler.class, + LiteWorkflowStoreService.LiteActionHandler.class); + + // Not all actions want a NN (e.g. email action) + LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-namenode.xml", -1), + new Configuration()); + String b1 = app.getNode("b1").getConf(); + String expectedB1 = + "<email xmlns=\"uri:oozie:email-action:0.2\">\r\n" + + " <to>[email protected]</to>\r\n" + + " <subject>foo</subject>\r\n" + + " <body>bar</body>\r\n" + + "</email>"; + b1 = cleanupXml(b1); + assertEquals(expectedB1.replaceAll(" ", ""), b1.replaceAll(" ", "")); + } + + public void testParserDefaultNameNodeFail() throws Exception { + LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, + LiteWorkflowStoreService.LiteControlNodeHandler.class, + LiteWorkflowStoreService.LiteDecisionHandler.class, + LiteWorkflowStoreService.LiteActionHandler.class); + + // No default NN is set + try { + LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-namenode.xml", -1), + new Configuration()); + fail(); + } catch (WorkflowException e) { + assertEquals(ErrorCode.E0701, e.getErrorCode()); + assertTrue(e.getMessage().contains("No name-node defined")); + } + } + + public void testParserDefaultJobTracker() throws Exception { + ConfigurationService.set("oozie.actions.default.job-tracker", "default-jt"); + LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, + LiteWorkflowStoreService.LiteControlNodeHandler.class, + LiteWorkflowStoreService.LiteDecisionHandler.class, + LiteWorkflowStoreService.LiteActionHandler.class); + + LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-jobtracker.xml", -1), + new Configuration()); + String a = app.getNode("a").getConf(); + String expectedA = + "<hive xmlns=\"uri:oozie:hive-action:0.2\">\r\n" + + " <prepare>\r\n" + + " <delete path=\"/tmp\" />\r\n" + + " <mkdir path=\"/tmp\" />\r\n" + + " </prepare>\r\n" + + " <name-node>bar</name-node>\r\n" + + " <configuration>\r\n" + + " <property>\r\n" + + " <name>c</name>\r\n" + + " <value>C</value>\r\n" + + " </property>\r\n" + + " </configuration>\r\n" + + " <script>script.q</script>\r\n" + + " <param>INPUT=/tmp/table</param>\r\n" + + " <param>OUTPUT=/tmp/hive</param>\r\n" + + " <job-tracker>default-jt</job-tracker>\r\n" + + "</hive>"; + a = cleanupXml(a); + assertEquals(expectedA.replaceAll(" ", ""), a.replaceAll(" ", "")); + } + + public void testParserDefaultJobTrackerWithGlobal() throws Exception { + ConfigurationService.set("oozie.actions.default.job-tracker", "default-jt"); + LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, + LiteWorkflowStoreService.LiteControlNodeHandler.class, + LiteWorkflowStoreService.LiteDecisionHandler.class, + LiteWorkflowStoreService.LiteActionHandler.class); + + LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-jobtracker-global.xml", -1), + new Configuration()); + String a = app.getNode("a").getConf(); + String expectedA = + "<hive xmlns=\"uri:oozie:hive-action:0.2\">\r\n" + + " <prepare>\r\n" + + " <delete path=\"/tmp\" />\r\n" + + " <mkdir path=\"/tmp\" />\r\n" + + " </prepare>\r\n" + + " <name-node>bar</name-node>\r\n" + + " <configuration>\r\n" + + " <property>\r\n" + + " <name>c</name>\r\n" + + " <value>C</value>\r\n" + + " </property>\r\n" + + " </configuration>\r\n" + + " <script>script.q</script>\r\n" + + " <param>INPUT=/tmp/table</param>\r\n" + + " <param>OUTPUT=/tmp/hive</param>\r\n" + + " <job-tracker>global-jt</job-tracker>\r\n" + + "</hive>"; + a = cleanupXml(a); + assertEquals(expectedA.replaceAll(" ", ""), a.replaceAll(" ", "")); + } + + public void testParserDefaultJobTrackerNotApplicable() throws Exception { + ConfigurationService.set("oozie.actions.default.job-tracker", "default-jt"); + LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, + LiteWorkflowStoreService.LiteControlNodeHandler.class, + LiteWorkflowStoreService.LiteDecisionHandler.class, + LiteWorkflowStoreService.LiteActionHandler.class); + + // Not all actions want a NN (e.g. email action) + LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-jobtracker.xml", -1), + new Configuration()); + String b1 = app.getNode("b1").getConf(); + String expectedB1 = + "<email xmlns=\"uri:oozie:email-action:0.2\">\r\n" + + " <to>[email protected]</to>\r\n" + + " <subject>foo</subject>\r\n" + + " <body>bar</body>\r\n" + + "</email>"; + b1 = cleanupXml(b1); + assertEquals(expectedB1.replaceAll(" ", ""), b1.replaceAll(" ", "")); + } + + public void testParserDefaultJobTrackerFail() throws Exception { + LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, + LiteWorkflowStoreService.LiteControlNodeHandler.class, + LiteWorkflowStoreService.LiteDecisionHandler.class, + LiteWorkflowStoreService.LiteActionHandler.class); + + // No default NN is set + try { + LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-jobtracker.xml", -1), + new Configuration()); + fail(); + } catch (WorkflowException e) { + assertEquals(ErrorCode.E0701, e.getErrorCode()); + assertTrue(e.getMessage().contains("No job-tracker defined")); + } + } + public void testParser() throws Exception { LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null, LiteWorkflowStoreService.LiteControlNodeHandler.class, http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-no-jobtracker-global.xml ---------------------------------------------------------------------- diff --git a/core/src/test/resources/wf-schema-no-jobtracker-global.xml b/core/src/test/resources/wf-schema-no-jobtracker-global.xml new file mode 100644 index 0000000..433b6b8 --- /dev/null +++ b/core/src/test/resources/wf-schema-no-jobtracker-global.xml @@ -0,0 +1,61 @@ +<!-- + 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. +--> +<workflow-app xmlns="uri:oozie:workflow:0.4" name="test-wf"> + <global> + <job-tracker>global-jt</job-tracker> + </global> + + <start to="a"/> + + <action name="a"> + <hive xmlns="uri:oozie:hive-action:0.2"> + <prepare> + <delete path="/tmp"/> + <mkdir path="/tmp"/> + </prepare> + <name-node>bar</name-node> + <configuration> + <property> + <name>c</name> + <value>C</value> + </property> + </configuration> + <script>script.q</script> + <param>INPUT=/tmp/table</param> + <param>OUTPUT=/tmp/hive</param> + </hive> + <ok to="b2"/> + <error to="b1"/> + </action> + + <action name="b1"> + <email xmlns="uri:oozie:email-action:0.2"> + <to>[email protected]</to> + <subject>foo</subject> + <body>bar</body> + </email> + <ok to="b2"/> + <error to="b2"/> + </action> + + <kill name="b2"> + <message>fail</message> + </kill> + + <end name="c"/> +</workflow-app> http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-no-jobtracker.xml ---------------------------------------------------------------------- diff --git a/core/src/test/resources/wf-schema-no-jobtracker.xml b/core/src/test/resources/wf-schema-no-jobtracker.xml new file mode 100644 index 0000000..2eea734 --- /dev/null +++ b/core/src/test/resources/wf-schema-no-jobtracker.xml @@ -0,0 +1,57 @@ +<!-- + 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. +--> +<workflow-app xmlns="uri:oozie:workflow:0.4" name="test-wf"> + <start to="a"/> + + <action name="a"> + <hive xmlns="uri:oozie:hive-action:0.2"> + <prepare> + <delete path="/tmp"/> + <mkdir path="/tmp"/> + </prepare> + <name-node>bar</name-node> + <configuration> + <property> + <name>c</name> + <value>C</value> + </property> + </configuration> + <script>script.q</script> + <param>INPUT=/tmp/table</param> + <param>OUTPUT=/tmp/hive</param> + </hive> + <ok to="b2"/> + <error to="b1"/> + </action> + + <action name="b1"> + <email xmlns="uri:oozie:email-action:0.2"> + <to>[email protected]</to> + <subject>foo</subject> + <body>bar</body> + </email> + <ok to="b2"/> + <error to="b2"/> + </action> + + <kill name="b2"> + <message>fail</message> + </kill> + + <end name="c"/> +</workflow-app> http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-no-namenode-global.xml ---------------------------------------------------------------------- diff --git a/core/src/test/resources/wf-schema-no-namenode-global.xml b/core/src/test/resources/wf-schema-no-namenode-global.xml new file mode 100644 index 0000000..874ec52 --- /dev/null +++ b/core/src/test/resources/wf-schema-no-namenode-global.xml @@ -0,0 +1,61 @@ +<!-- + 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. +--> +<workflow-app xmlns="uri:oozie:workflow:0.4" name="test-wf"> + <global> + <name-node>global-nn</name-node> + </global> + + <start to="a"/> + + <action name="a"> + <hive xmlns="uri:oozie:hive-action:0.2"> + <prepare> + <delete path="/tmp"/> + <mkdir path="/tmp"/> + </prepare> + <job-tracker>foo</job-tracker> + <configuration> + <property> + <name>c</name> + <value>C</value> + </property> + </configuration> + <script>script.q</script> + <param>INPUT=/tmp/table</param> + <param>OUTPUT=/tmp/hive</param> + </hive> + <ok to="b2"/> + <error to="b1"/> + </action> + + <action name="b1"> + <email xmlns="uri:oozie:email-action:0.2"> + <to>[email protected]</to> + <subject>foo</subject> + <body>bar</body> + </email> + <ok to="b2"/> + <error to="b2"/> + </action> + + <kill name="b2"> + <message>fail</message> + </kill> + + <end name="c"/> +</workflow-app> http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-no-namenode.xml ---------------------------------------------------------------------- diff --git a/core/src/test/resources/wf-schema-no-namenode.xml b/core/src/test/resources/wf-schema-no-namenode.xml new file mode 100644 index 0000000..14ff8d4 --- /dev/null +++ b/core/src/test/resources/wf-schema-no-namenode.xml @@ -0,0 +1,57 @@ +<!-- + 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. +--> +<workflow-app xmlns="uri:oozie:workflow:0.4" name="test-wf"> + <start to="a"/> + + <action name="a"> + <hive xmlns="uri:oozie:hive-action:0.2"> + <prepare> + <delete path="/tmp"/> + <mkdir path="/tmp"/> + </prepare> + <job-tracker>foo</job-tracker> + <configuration> + <property> + <name>c</name> + <value>C</value> + </property> + </configuration> + <script>script.q</script> + <param>INPUT=/tmp/table</param> + <param>OUTPUT=/tmp/hive</param> + </hive> + <ok to="b2"/> + <error to="b1"/> + </action> + + <action name="b1"> + <email xmlns="uri:oozie:email-action:0.2"> + <to>[email protected]</to> + <subject>foo</subject> + <body>bar</body> + </email> + <ok to="b2"/> + <error to="b2"/> + </action> + + <kill name="b2"> + <message>fail</message> + </kill> + + <end name="c"/> +</workflow-app> http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-valid-global-ext.xml ---------------------------------------------------------------------- diff --git a/core/src/test/resources/wf-schema-valid-global-ext.xml b/core/src/test/resources/wf-schema-valid-global-ext.xml index 86d274d..88bf6c7 100644 --- a/core/src/test/resources/wf-schema-valid-global-ext.xml +++ b/core/src/test/resources/wf-schema-valid-global-ext.xml @@ -50,7 +50,7 @@ <param>OUTPUT=/tmp/hive</param> </hive> <ok to="b"/> - <error to="c"/> + <error to="c1"/> </action> <action name="b"> @@ -75,10 +75,20 @@ <arg>/tmp2/data.txt</arg> </distcp> <ok to="d"/> - <error to="c"/> + <error to="c1"/> </action> - <kill name="c"> + <action name="c1"> + <email xmlns="uri:oozie:email-action:0.2"> + <to>[email protected]</to> + <subject>foo</subject> + <body>bar</body> + </email> + <ok to="c2"/> + <error to="c2"/> + </action> + + <kill name="c2"> <message>fail</message> </kill> http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/release-log.txt ---------------------------------------------------------------------- diff --git a/release-log.txt b/release-log.txt index b19a913..4ffc302 100644 --- a/release-log.txt +++ b/release-log.txt @@ -1,5 +1,6 @@ -- Oozie 4.3.0 release (trunk - unreleased) +OOZIE-2187 Add a way to specify a default JT/RM and NN (rkanter) OOZIE-2272 Use Hadoop's CredentialProvider for passwords in oozie-site (rkanter) OOZIE-2287 Add support for deleting hcat partitions in fs action delete (kailongs via rohini) OOZIE-2285 Change in concurrency should trigger coord action ready command (kailongs via rohini)
