Author: rkanter
Date: Thu Nov 14 23:08:09 2013
New Revision: 1542114
URL: http://svn.apache.org/r1542114
Log:
OOZIE-1550 Create a safeguard to kill errant recursive workflows before they
bring down oozie (rkanter)
Modified:
oozie/trunk/core/src/main/java/org/apache/oozie/action/oozie/SubWorkflowActionExecutor.java
oozie/trunk/core/src/main/resources/oozie-default.xml
oozie/trunk/core/src/test/java/org/apache/oozie/action/hadoop/ActionExecutorTestCase.java
oozie/trunk/core/src/test/java/org/apache/oozie/action/oozie/TestSubWorkflowActionExecutor.java
oozie/trunk/docs/src/site/twiki/WorkflowFunctionalSpec.twiki
oozie/trunk/release-log.txt
Modified:
oozie/trunk/core/src/main/java/org/apache/oozie/action/oozie/SubWorkflowActionExecutor.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/main/java/org/apache/oozie/action/oozie/SubWorkflowActionExecutor.java?rev=1542114&r1=1542113&r2=1542114&view=diff
==============================================================================
---
oozie/trunk/core/src/main/java/org/apache/oozie/action/oozie/SubWorkflowActionExecutor.java
(original)
+++
oozie/trunk/core/src/main/java/org/apache/oozie/action/oozie/SubWorkflowActionExecutor.java
Thu Nov 14 23:08:09 2013
@@ -48,6 +48,8 @@ public class SubWorkflowActionExecutor e
public static final String ACTION_TYPE = "sub-workflow";
public static final String LOCAL = "local";
public static final String PARENT_ID = "oozie.wf.parent.id";
+ public static final String SUBWORKFLOW_MAX_DEPTH =
"oozie.action.subworkflow.max.depth";
+ private static final String SUBWORKFLOW_DEPTH =
"oozie.action.subworkflow.depth";
private static final Set<String> DISALLOWED_DEFAULT_PROPERTIES = new
HashSet<String>();
@@ -119,6 +121,16 @@ public class SubWorkflowActionExecutor e
conf.set(PARENT_ID, parentId);
}
+ protected void verifyAndInjectSubworkflowDepth(Configuration parentConf,
Configuration conf) throws ActionExecutorException {
+ int depth = conf.getInt(SUBWORKFLOW_DEPTH, 0);
+ int maxDepth = Services.get().getConf().getInt(SUBWORKFLOW_MAX_DEPTH,
50);
+ if (depth >= maxDepth) {
+ throw new
ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "SUBWF001",
+ "Depth [{0}] cannot exceed maximum subworkflow depth
[{1}]", (depth + 1), maxDepth);
+ }
+ conf.setInt(SUBWORKFLOW_DEPTH, depth + 1);
+ }
+
protected String checkIfRunning(OozieClient oozieClient, String extId)
throws OozieClientException {
String jobId = oozieClient.getJobId(extId);
if (jobId.equals("")) {
@@ -161,6 +173,7 @@ public class SubWorkflowActionExecutor e
injectCallback(context, subWorkflowConf);
injectRecovery(extId, subWorkflowConf);
injectParent(context.getWorkflow().getId(), subWorkflowConf);
+ verifyAndInjectSubworkflowDepth(parentConf, subWorkflowConf);
//TODO: this has to be refactored later to be done in a single
place for REST calls and this
JobUtils.normalizeAppPath(context.getWorkflow().getUser(),
context.getWorkflow().getGroup(),
Modified: oozie/trunk/core/src/main/resources/oozie-default.xml
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/main/resources/oozie-default.xml?rev=1542114&r1=1542113&r2=1542114&view=diff
==============================================================================
--- oozie/trunk/core/src/main/resources/oozie-default.xml (original)
+++ oozie/trunk/core/src/main/resources/oozie-default.xml Thu Nov 14 23:08:09
2013
@@ -1519,6 +1519,18 @@
</description>
</property>
+ <!-- SubworkflowActionExecutor -->
+
+ <property>
+ <name>oozie.action.subworkflow.max.depth</name>
+ <value>50</value>
+ <description>
+ The maximum depth for subworkflows. For example, if set to 3,
then a workflow can start subwf1, which can start subwf2,
+ which can start subwf3; but if subwf3 tries to start subwf4, then
the action will fail. This is helpful in preventing
+ errant workflows from starting infintely recursive subworkflows.
+ </description>
+ </property>
+
<!-- HadoopAccessorService -->
<property>
Modified:
oozie/trunk/core/src/test/java/org/apache/oozie/action/hadoop/ActionExecutorTestCase.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/action/hadoop/ActionExecutorTestCase.java?rev=1542114&r1=1542113&r2=1542114&view=diff
==============================================================================
---
oozie/trunk/core/src/test/java/org/apache/oozie/action/hadoop/ActionExecutorTestCase.java
(original)
+++
oozie/trunk/core/src/test/java/org/apache/oozie/action/hadoop/ActionExecutorTestCase.java
Thu Nov 14 23:08:09 2013
@@ -71,7 +71,9 @@ public abstract class ActionExecutorTest
@Override
protected void tearDown() throws Exception {
- Services.get().destroy();
+ if (Services.get() != null) {
+ Services.get().destroy();
+ }
super.tearDown();
}
Modified:
oozie/trunk/core/src/test/java/org/apache/oozie/action/oozie/TestSubWorkflowActionExecutor.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/action/oozie/TestSubWorkflowActionExecutor.java?rev=1542114&r1=1542113&r2=1542114&view=diff
==============================================================================
---
oozie/trunk/core/src/test/java/org/apache/oozie/action/oozie/TestSubWorkflowActionExecutor.java
(original)
+++
oozie/trunk/core/src/test/java/org/apache/oozie/action/oozie/TestSubWorkflowActionExecutor.java
Thu Nov 14 23:08:09 2013
@@ -33,6 +33,9 @@ import java.io.File;
import java.io.StringReader;
import java.io.Writer;
import java.io.OutputStreamWriter;
+import java.util.Properties;
+import org.apache.oozie.local.LocalOozie;
+import org.apache.oozie.service.XLogService;
public class TestSubWorkflowActionExecutor extends ActionExecutorTestCase {
private static final int JOB_TIMEOUT = 100 * 1000;
@@ -406,4 +409,62 @@ public class TestSubWorkflowActionExecut
childConf = wps.createProtoActionConf(childConf, true);
assertEquals(childConf.get(WorkflowAppService.APP_LIB_PATH_LIST),
subwfLibJar.toString());
}
+
+ public void testSubworkflowDepth() throws Exception {
+ Path subWorkflowAppPath = getFsTestCaseDir();
+ FileSystem fs = getFileSystem();
+ Writer writer = new OutputStreamWriter(fs.create(new
Path(subWorkflowAppPath, "workflow.xml")));
+ // Infinitly recursive workflow
+ String appStr = "<workflow-app xmlns=\"uri:oozie:workflow:0.4\"
name=\"workflow\">" +
+ "<start to=\"subwf\"/>" +
+ "<action name=\"subwf\">" +
+ " <sub-workflow xmlns='uri:oozie:workflow:0.4'>" +
+ " <app-path>" + subWorkflowAppPath.toString() +
"</app-path>" +
+ " </sub-workflow>" +
+ " <ok to=\"end\"/>" +
+ " <error to=\"fail\"/>" +
+ "</action>" +
+ "<kill name=\"fail\">" +
+ " <message>Sub workflow failed, error
message[${wf:errorMessage(wf:lastErrorNode())}]</message>" +
+ "</kill>" +
+ "<end name=\"end\"/>" +
+ "</workflow-app>";
+ writer.write(appStr);
+ writer.close();
+
+ try {
+ Services.get().destroy();
+ setSystemProperty(XLogService.LOG4J_FILE,
"oozie-log4j.properties");
+ LocalOozie.start();
+ // Set the max depth to 3
+
Services.get().getConf().setInt("oozie.action.subworkflow.max.depth", 3);
+ final OozieClient wfClient = LocalOozie.getClient();
+ Properties conf = wfClient.createConfiguration();
+ conf.setProperty(OozieClient.APP_PATH,
subWorkflowAppPath.toString());
+ conf.setProperty(OozieClient.USER_NAME, getTestUser());
+ final String jobId0 = wfClient.submit(conf);
+ wfClient.start(jobId0);
+
+ waitFor(20 * 1000, new Predicate() {
+ @Override
+ public boolean evaluate() throws Exception {
+ return wfClient.getJobInfo(jobId0).getStatus() ==
WorkflowJob.Status.KILLED;
+ }
+ });
+ // All should be KILLED because our infinitly recusrive workflow
hit the max
+ assertEquals(WorkflowJob.Status.KILLED,
wfClient.getJobInfo(jobId0).getStatus());
+ String jobId1 =
wfClient.getJobInfo(jobId0).getActions().get(1).getExternalId();
+ assertEquals(WorkflowJob.Status.KILLED,
wfClient.getJobInfo(jobId1).getStatus());
+ String jobId2 =
wfClient.getJobInfo(jobId1).getActions().get(1).getExternalId();
+ assertEquals(WorkflowJob.Status.KILLED,
wfClient.getJobInfo(jobId2).getStatus());
+ String jobId3 =
wfClient.getJobInfo(jobId2).getActions().get(1).getExternalId();
+ assertEquals(WorkflowJob.Status.KILLED,
wfClient.getJobInfo(jobId3).getStatus());
+ String jobId4 =
wfClient.getJobInfo(jobId3).getActions().get(1).getExternalId();
+ // A fourth subworkflow shouldn't have been created because we set
the max to 3
+ assertNull(jobId4);
+ }
+ finally {
+ LocalOozie.stop();
+ }
+ }
}
Modified: oozie/trunk/docs/src/site/twiki/WorkflowFunctionalSpec.twiki
URL:
http://svn.apache.org/viewvc/oozie/trunk/docs/src/site/twiki/WorkflowFunctionalSpec.twiki?rev=1542114&r1=1542113&r2=1542114&view=diff
==============================================================================
--- oozie/trunk/docs/src/site/twiki/WorkflowFunctionalSpec.twiki (original)
+++ oozie/trunk/docs/src/site/twiki/WorkflowFunctionalSpec.twiki Thu Nov 14
23:08:09 2013
@@ -1311,6 +1311,9 @@ in oozie-site.xml or on a per-job basis
If both are specified, =oozie.wf.subworkflow.classpath.inheritance= has
priority. If the subworkflow and the parent have
conflicting jars, the subworkflow's jar has priority. By default,
=oozie.wf.subworkflow.classpath.inheritance= is set to false.
+To prevent errant workflows from starting infinitely recursive subworkflows,
=oozie.action.subworkflow.max.depth= can be specified
+in oozie-site.xml to set the maximum depth of subworkflow calls. For example,
if set to 3, then a workflow can start subwf1, which
+can start subwf2, which can start subwf3; but if subwf3 tries to start subwf4,
then the action will fail. The default is 50.
#JavaAction
---++++ 3.2.7 Java Action
Modified: oozie/trunk/release-log.txt
URL:
http://svn.apache.org/viewvc/oozie/trunk/release-log.txt?rev=1542114&r1=1542113&r2=1542114&view=diff
==============================================================================
--- oozie/trunk/release-log.txt (original)
+++ oozie/trunk/release-log.txt Thu Nov 14 23:08:09 2013
@@ -1,5 +1,6 @@
-- Oozie 4.1.0 release (trunk - unreleased)
+OOZIE-1550 Create a safeguard to kill errant recursive workflows before they
bring down oozie (rkanter)
OOZIE-1314 IllegalArgumentException: wfId cannot be empty (shwethags via virag)
OOZIE-1606 Update Curator to 2.3.0 and fix some misc minor ZK related things
(rkanter)
OOZIE-1544 Support variables for coord data-in/data-out dataset (puru via
rohini)