diff --git a/zeppelin-server/conf/notebook-authorization.json b/zeppelin-server/conf/notebook-authorization.json new file mode 100644 index 00000000000..1e3767cb6f2 --- /dev/null +++ b/zeppelin-server/conf/notebook-authorization.json @@ -0,0 +1,16 @@ +{ + "authInfo": { + "2F8GB7HN7": { + "readers": [], + "owners": [], + "writers": [], + "runners": [] + }, + "2F9W6X5GY": { + "readers": [], + "owners": [], + "writers": [], + "runners": [] + } + } +} \ No newline at end of file diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java index fa8e6b8c5b2..d41ebb1a1be 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java @@ -287,11 +287,15 @@ private static Thread shutdown(ZeppelinConfiguration conf) { () -> { LOG.info("Shutting down Zeppelin Server ... "); try { - jettyWebServer.stop(); - if (!conf.isRecoveryEnabled()) { - sharedServiceLocator.getService(InterpreterSettingManager.class).close(); + if (jettyWebServer != null) { + jettyWebServer.stop(); + } + if (sharedServiceLocator != null) { + if (!conf.isRecoveryEnabled()) { + sharedServiceLocator.getService(InterpreterSettingManager.class).close(); + } + sharedServiceLocator.getService(Notebook.class).close(); } - sharedServiceLocator.getService(Notebook.class).close(); Thread.sleep(3000); } catch (Exception e) { LOG.error("Error while stopping servlet container", e); diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java b/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java index 9901586d0fd..bc7788b293a 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java @@ -279,6 +279,23 @@ public Note importNote(String notePath, } } + /** + * Executes given paragraph with passed paragraph info like noteId, paragraphId, title, text and etc. + * + * @param noteId + * @param paragraphId + * @param title + * @param text + * @param params + * @param config + * @param failIfDisabled + * @param blocking + * @param context + * @param callback + * @return return true only when paragraph execution finished, it could end with succeed or error due to user code. + * return false when paragraph execution fails due to zeppelin internal issue. + * @throws IOException + */ public boolean runParagraph(String noteId, String paragraphId, String title, @@ -334,9 +351,9 @@ public boolean runParagraph(String noteId, try { notebook.saveNote(note, context.getAutheInfo()); - boolean result = note.run(p.getId(), blocking, context.getAutheInfo().getUser()); + note.run(p.getId(), blocking, context.getAutheInfo().getUser()); callback.onSuccess(p, context); - return result; + return true; } catch (Exception ex) { LOGGER.error("Exception from run", ex); p.setReturn(new InterpreterResult(InterpreterResult.Code.ERROR, ex.getMessage()), ex); diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java index 7498822867e..328a1b2f895 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertThat; import com.google.gson.Gson; +import com.google.gson.internal.StringMap; import com.google.gson.reflect.TypeToken; import org.apache.commons.httpclient.methods.GetMethod; @@ -40,6 +41,7 @@ import org.junit.runners.MethodSorters; import java.io.IOException; +import java.util.List; import java.util.Map; import java.util.Set; @@ -160,6 +162,28 @@ public void testRunParagraphSynchronously() throws IOException { // Check if the paragraph is emptied assertEquals(title, p.getTitle()); assertEquals(text, p.getText()); + + // run invalid code + text = "%sh\n invalid_cmd"; + p.setTitle(title); + p.setText(text); + + post = httpPost("/notebook/run/" + note1.getId() + "/" + p.getId(), ""); + assertEquals(500, post.getStatusCode()); + resp = gson.fromJson(post.getResponseBodyAsString(), + new TypeToken>() {}.getType()); + assertEquals("INTERNAL_SERVER_ERROR", resp.get("status")); + StringMap stringMap = (StringMap) resp.get("body"); + assertEquals("ERROR", stringMap.get("code")); + List interpreterResults = (List) stringMap.get("msg"); + assertTrue(interpreterResults.get(0).toString(), + interpreterResults.get(0).get("data").toString().contains("invalid_cmd: command not found")); + post.releaseConnection(); + assertNotEquals(p.getStatus(), Job.Status.READY); + + // Check if the paragraph is emptied + assertEquals(title, p.getTitle()); + assertEquals(text, p.getText()); } finally { // cleanup if (null != note1) { diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/service/NotebookServiceTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/service/NotebookServiceTest.java index 3b7e2f2d035..a463617806d 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/service/NotebookServiceTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/service/NotebookServiceTest.java @@ -388,7 +388,7 @@ public void testParagraphOperations() throws IOException { reset(callback); runStatus = notebookService.runParagraph(note1.getId(), p.getId(), "my_title", "invalid_code", new HashMap<>(), new HashMap<>(), false, true, context, callback); - assertFalse(runStatus); + assertTrue(runStatus); // TODO(zjffdu) Enable it after ZEPPELIN-3699 // assertNotNull(p.getResult()); verify(callback).onSuccess(p, context); diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java index 0af557c789d..ce7049109a0 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java @@ -780,7 +780,7 @@ public boolean run(String paragraphId, boolean blocking) { } /** - * Run a single paragraph + * Run a single paragraph. Return true only when paragraph run successfully. * * @param paragraphId * @param blocking diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java index da4c7c63b28..0ff95c3faf2 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java @@ -347,6 +347,11 @@ public boolean shouldSkipRunParagraph() { return checkEmptyConfig && Strings.isNullOrEmpty(scriptText) && localProperties.isEmpty(); } + /** + * Return true only when paragraph run successfully with state of FINISHED. + * @param blocking + * @return + */ public boolean execute(boolean blocking) { try { this.interpreter = getBindedInterpreter();