From 87ce20fa5d6fce73a03d040571114038313d3e7b Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Sun, 12 Nov 2017 09:18:41 +0800 Subject: [PATCH] ZEPPELIN-3085. Introduce generic ConfInterpreter for more fine-grained control of interpreter setting --- .../img/screenshots/conf_interpreter.png | Bin 0 -> 39388 bytes docs/usage/interpreter/overview.md | 13 +++ .../rest/ZeppelinSparkClusterTest.java | 30 ++++++ .../zeppelin/interpreter/ConfInterpreter.java | 92 ++++++++++++++++ .../interpreter/InterpreterSetting.java | 43 ++++++-- .../interpreter/ManagedInterpreterGroup.java | 8 +- .../interpreter/remote/RemoteInterpreter.java | 7 +- .../apache/zeppelin/notebook/Paragraph.java | 1 + .../interpreter/ConfInterpreterTest.java | 102 ++++++++++++++++++ .../ManagedInterpreterGroupTest.java | 4 +- 10 files changed, 287 insertions(+), 13 deletions(-) create mode 100644 docs/assets/themes/zeppelin/img/screenshots/conf_interpreter.png create mode 100644 zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/ConfInterpreter.java create mode 100644 zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/ConfInterpreterTest.java diff --git a/docs/assets/themes/zeppelin/img/screenshots/conf_interpreter.png b/docs/assets/themes/zeppelin/img/screenshots/conf_interpreter.png new file mode 100644 index 0000000000000000000000000000000000000000..156c3575c9ba289702b2e0cf05725f5615f730a0 GIT binary patch literal 39388 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�eg;EyN`fxMNpp~OZs)-4^ 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 uRou~@jre&=6DdUq1} + diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java index 56056d65cc6..615675544ec 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java @@ -535,6 +535,8 @@ public void testSparkZeppelinContextDynamicForms() throws IOException { assertEquals("1", result[1]); assertEquals("items: Seq[Object] = Buffer(2)", result[2]); assertEquals("2", result[3]); + + ZeppelinServer.notebook.removeNote(note.getId(), anonymous); } @Test @@ -568,5 +570,33 @@ public void testPySparkZeppelinContextDynamicForms() throws IOException { assertEquals("default_name", result[0]); assertEquals("1", result[1]); assertEquals("2", result[2]); + + ZeppelinServer.notebook.removeNote(note.getId(), anonymous); + } + + @Test + public void testConfInterpreter() throws IOException { + Note note = ZeppelinServer.notebook.createNote(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); + Map config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + p.setText("%spark.conf spark.jars.packages\tcom.databricks:spark-csv_2.11:1.2.0"); + p.setAuthenticationInfo(anonymous); + note.run(p.getId()); + waitForFinish(p); + assertEquals(Status.FINISHED, p.getStatus()); + + Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); + p1.setConfig(config); + p1.setText("%spark\nimport com.databricks.spark.csv._"); + p1.setAuthenticationInfo(anonymous); + note.run(p1.getId()); + + waitForFinish(p1); + assertEquals(Status.FINISHED, p1.getStatus()); + + ZeppelinServer.notebook.removeNote(note.getId(), anonymous); + } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/ConfInterpreter.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/ConfInterpreter.java new file mode 100644 index 00000000000..d50c57b4da5 --- /dev/null +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/ConfInterpreter.java @@ -0,0 +1,92 @@ +/* + * 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.zeppelin.interpreter; + +import org.apache.commons.lang.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Properties; + +/** + * Special Interpreter for Interpreter Configuration customization. It is attached to each + * InterpreterGroup implicitly by Zeppelin. + */ +public class ConfInterpreter extends Interpreter { + + private static Logger LOGGER = LoggerFactory.getLogger(ConfInterpreter.class); + + private String interpreterGroupId; + private InterpreterSetting interpreterSetting; + + + public ConfInterpreter(Properties properties, + String interpreterGroupId, + InterpreterSetting interpreterSetting) { + super(properties); + this.interpreterGroupId = interpreterGroupId; + this.interpreterSetting = interpreterSetting; + } + + @Override + public void open() throws InterpreterException { + + } + + @Override + public void close() throws InterpreterException { + + } + + @Override + public InterpreterResult interpret(String st, InterpreterContext context) + throws InterpreterException { + + try { + Properties finalProperties = new Properties(); + finalProperties.putAll(getProperties()); + Properties newProperties = new Properties(); + newProperties.load(new StringReader(st)); + finalProperties.putAll(newProperties); + LOGGER.debug("Properties for InterpreterGroup: " + interpreterGroupId + " is " + + finalProperties); + interpreterSetting.setInterpreterGroupProperties(interpreterGroupId, finalProperties); + return new InterpreterResult(InterpreterResult.Code.SUCCESS); + } catch (IOException e) { + LOGGER.error("Fail to update interpreter setting", e); + return new InterpreterResult(InterpreterResult.Code.ERROR, ExceptionUtils.getStackTrace(e)); + } + } + + @Override + public void cancel(InterpreterContext context) throws InterpreterException { + + } + + @Override + public FormType getFormType() throws InterpreterException { + return null; + } + + @Override + public int getProgress(InterpreterContext context) throws InterpreterException { + return 0; + } +} diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterSetting.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterSetting.java index 26fcd8e93d4..d5ff947ad0c 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterSetting.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterSetting.java @@ -138,9 +138,11 @@ public class InterpreterSetting { // launcher in future when we have other launcher implementation. e.g. third party launcher // service like livy private transient InterpreterLauncher launcher; - /////////////////////////////////////////////////////////////////////////////////////////// private transient LifecycleManager lifecycleManager; + /////////////////////////////////////////////////////////////////////////////////////////// + + /** * Builder class for InterpreterSetting @@ -648,12 +650,11 @@ public void clearNoteIdAndParaMap() { /////////////////////////////////////////////////////////////////////////////////////// // This is the only place to create interpreters. For now we always create multiple interpreter // together (one session). We don't support to create single interpreter yet. - List createInterpreters(String user, String sessionId) { + List createInterpreters(String user, String interpreterGroupId, String sessionId) { List interpreters = new ArrayList<>(); List interpreterInfos = getInterpreterInfos(); for (InterpreterInfo info : interpreterInfos) { - Interpreter interpreter = null; - interpreter = new RemoteInterpreter(getJavaProperties(), sessionId, + Interpreter interpreter = new RemoteInterpreter(getJavaProperties(), sessionId, info.getClassName(), user, lifecycleManager); if (info.isDefaultInterpreter()) { interpreters.add(0, interpreter); @@ -663,15 +664,17 @@ List createInterpreters(String user, String sessionId) { LOGGER.info("Interpreter {} created for user: {}, sessionId: {}", interpreter.getClassName(), user, sessionId); } + interpreters.add(new ConfInterpreter(getJavaProperties(), interpreterGroupId, this)); return interpreters; } - synchronized RemoteInterpreterProcess createInterpreterProcess() throws IOException { + synchronized RemoteInterpreterProcess createInterpreterProcess(Properties properties) + throws IOException { if (launcher == null) { createLauncher(); } InterpreterLaunchContext launchContext = new - InterpreterLaunchContext(getJavaProperties(), option, interpreterRunner, id, group, name); + InterpreterLaunchContext(properties, option, interpreterRunner, id, group, name); RemoteInterpreterProcess process = (RemoteInterpreterProcess) launcher.launch(launchContext); process.setRemoteInterpreterEventPoller( new RemoteInterpreterEventPoller(remoteInterpreterProcessListener, appEventListener)); @@ -716,6 +719,11 @@ private String getInterpreterClassFromInterpreterSetting(String replName) { return info.getClassName(); } } + //TODO(zjffdu) It requires user can not create interpreter with name `conf`, + // conf is a reserved word of interpreter name + if (replName.equals("conf")) { + return ConfInterpreter.class.getName(); + } return null; } @@ -728,6 +736,29 @@ private ManagedInterpreterGroup createInterpreterGroup(String groupId) { return interpreterGroup; } + /** + * Throw exception when interpreter process has already launched + * + * @param interpreterGroupId + * @param properties + * @throws IOException + */ + public void setInterpreterGroupProperties(String interpreterGroupId, Properties properties) + throws IOException { + ManagedInterpreterGroup interpreterGroup = this.interpreterGroups.get(interpreterGroupId); + for (List session : interpreterGroup.sessions.values()) { + for (Interpreter intp : session) { + if (!intp.getProperties().equals(properties) && + interpreterGroup.getRemoteInterpreterProcess() != null && + interpreterGroup.getRemoteInterpreterProcess().isRunning()) { + throw new IOException("Can not change interpreter properties when interpreter process " + + "has already been launched"); + } + intp.setProperties(properties); + } + } + } + private void loadInterpreterDependencies() { setStatus(Status.DOWNLOADING_DEPENDENCIES); setErrorReason(null); diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/ManagedInterpreterGroup.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/ManagedInterpreterGroup.java index 219204f0417..2378f140daa 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/ManagedInterpreterGroup.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/ManagedInterpreterGroup.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.util.Collection; import java.util.List; +import java.util.Properties; /** * ManagedInterpreterGroup runs under zeppelin server @@ -54,10 +55,11 @@ public InterpreterSetting getInterpreterSetting() { return interpreterSetting; } - public synchronized RemoteInterpreterProcess getOrCreateInterpreterProcess() throws IOException { + public synchronized RemoteInterpreterProcess getOrCreateInterpreterProcess(Properties properties) + throws IOException { if (remoteInterpreterProcess == null) { LOGGER.info("Create InterpreterProcess for InterpreterGroup: " + getId()); - remoteInterpreterProcess = interpreterSetting.createInterpreterProcess(); + remoteInterpreterProcess = interpreterSetting.createInterpreterProcess(properties); } return remoteInterpreterProcess; } @@ -131,7 +133,7 @@ public synchronized List getOrCreateSession(String user, String ses if (sessions.containsKey(sessionId)) { return sessions.get(sessionId); } else { - List interpreters = interpreterSetting.createInterpreters(user, sessionId); + List interpreters = interpreterSetting.createInterpreters(user, id, sessionId); for (Interpreter interpreter : interpreters) { interpreter.setInterpreterGroup(this); } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java index 4ad36cf1b28..6defd9ba825 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java @@ -25,6 +25,7 @@ import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.GUI; import org.apache.zeppelin.display.Input; +import org.apache.zeppelin.interpreter.ConfInterpreter; import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterContextRunner; @@ -101,7 +102,7 @@ public synchronized RemoteInterpreterProcess getOrCreateInterpreterProcess() thr return this.interpreterProcess; } ManagedInterpreterGroup intpGroup = getInterpreterGroup(); - this.interpreterProcess = intpGroup.getOrCreateInterpreterProcess(); + this.interpreterProcess = intpGroup.getOrCreateInterpreterProcess(properties); synchronized (interpreterProcess) { if (!interpreterProcess.isRunning()) { interpreterProcess.start(this.getUserName(), false); @@ -130,7 +131,9 @@ public void open() throws InterpreterException { for (Interpreter interpreter : getInterpreterGroup() .getOrCreateSession(this.getUserName(), sessionId)) { try { - ((RemoteInterpreter) interpreter).internal_create(); + if (!(interpreter instanceof ConfInterpreter)) { + ((RemoteInterpreter) interpreter).internal_create(); + } } catch (IOException e) { throw new InterpreterException(e); } 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 5ec132931f6..32b9b73263c 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 @@ -351,6 +351,7 @@ public boolean execute(boolean blocking) { setStatus(Job.Status.ERROR); throw intpException; } + setStatus(Status.READY); if (getConfig().get("enabled") == null || (Boolean) getConfig().get("enabled")) { setAuthenticationInfo(getAuthenticationInfo()); interpreter.getScheduler().submit(this); diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/ConfInterpreterTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/ConfInterpreterTest.java new file mode 100644 index 00000000000..4d74c7cbfb5 --- /dev/null +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/ConfInterpreterTest.java @@ -0,0 +1,102 @@ +/* + * 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.zeppelin.interpreter; + +import com.sun.net.httpserver.Authenticator; +import org.apache.zeppelin.display.GUI; +import org.apache.zeppelin.interpreter.remote.RemoteInterpreter; +import org.apache.zeppelin.user.AuthenticationInfo; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class ConfInterpreterTest extends AbstractInterpreterTest { + + @Test + public void testCorrectConf() throws IOException, InterpreterException { + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getSettingIds()); + assertTrue(interpreterFactory.getInterpreter("user1", "note1", "test.conf") instanceof ConfInterpreter); + ConfInterpreter confInterpreter = (ConfInterpreter) interpreterFactory.getInterpreter("user1", "note1", "test.conf"); + + InterpreterContext context = new InterpreterContext("noteId", "paragraphId", "repl", + "title", "text", AuthenticationInfo.ANONYMOUS, new HashMap(), new GUI(), new GUI(), + null, null, new ArrayList(), null); + InterpreterResult result = confInterpreter.interpret("property_1\tnew_value\nnew_property\tdummy_value", context); + assertEquals(InterpreterResult.Code.SUCCESS, result.code); + + assertTrue(interpreterFactory.getInterpreter("user1", "note1", "test") instanceof RemoteInterpreter); + RemoteInterpreter remoteInterpreter = (RemoteInterpreter) interpreterFactory.getInterpreter("user1", "note1", "test"); + remoteInterpreter.interpret("hello world", context); + assertEquals(7, remoteInterpreter.getProperties().size()); + assertEquals("new_value", remoteInterpreter.getProperty("property_1")); + assertEquals("dummy_value", remoteInterpreter.getProperty("new_property")); + assertEquals("value_3", remoteInterpreter.getProperty("property_3")); + + // rerun the paragraph with the same properties would result in SUCCESS + result = confInterpreter.interpret("property_1\tnew_value\nnew_property\tdummy_value", context); + assertEquals(InterpreterResult.Code.SUCCESS, result.code); + + // run the paragraph with the same properties would result in ERROR + result = confInterpreter.interpret("property_1\tnew_value_2\nnew_property\tdummy_value", context); + assertEquals(InterpreterResult.Code.ERROR, result.code); + } + + @Test + public void testEmptyConf() throws IOException, InterpreterException { + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getSettingIds()); + assertTrue(interpreterFactory.getInterpreter("user1", "note1", "test.conf") instanceof ConfInterpreter); + ConfInterpreter confInterpreter = (ConfInterpreter) interpreterFactory.getInterpreter("user1", "note1", "test.conf"); + + InterpreterContext context = new InterpreterContext("noteId", "paragraphId", "repl", + "title", "text", AuthenticationInfo.ANONYMOUS, new HashMap(), new GUI(), new GUI(), + null, null, new ArrayList(), null); + InterpreterResult result = confInterpreter.interpret("", context); + assertEquals(InterpreterResult.Code.SUCCESS, result.code); + + assertTrue(interpreterFactory.getInterpreter("user1", "note1", "test") instanceof RemoteInterpreter); + RemoteInterpreter remoteInterpreter = (RemoteInterpreter) interpreterFactory.getInterpreter("user1", "note1", "test"); + assertEquals(6, remoteInterpreter.getProperties().size()); + assertEquals("value_1", remoteInterpreter.getProperty("property_1")); + assertEquals("value_3", remoteInterpreter.getProperty("property_3")); + } + + + @Test + public void testRunningAfterOtherInterpreter() throws IOException, InterpreterException { + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getSettingIds()); + assertTrue(interpreterFactory.getInterpreter("user1", "note1", "test.conf") instanceof ConfInterpreter); + ConfInterpreter confInterpreter = (ConfInterpreter) interpreterFactory.getInterpreter("user1", "note1", "test.conf"); + + InterpreterContext context = new InterpreterContext("noteId", "paragraphId", "repl", + "title", "text", AuthenticationInfo.ANONYMOUS, new HashMap(), new GUI(), new GUI(), + null, null, new ArrayList(), null); + RemoteInterpreter remoteInterpreter = (RemoteInterpreter) interpreterFactory.getInterpreter("user1", "note1", "test"); + InterpreterResult result = remoteInterpreter.interpret("hello world", context); + assertEquals(InterpreterResult.Code.SUCCESS, result.code); + + result = confInterpreter.interpret("property_1\tnew_value\nnew_property\tdummy_value", context); + assertEquals(InterpreterResult.Code.ERROR, result.code); + } + +} diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/ManagedInterpreterGroupTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/ManagedInterpreterGroupTest.java index 74bd2010855..aa7374991b2 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/ManagedInterpreterGroupTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/ManagedInterpreterGroupTest.java @@ -62,7 +62,7 @@ public void testInterpreterGroup() { // create session_1 List interpreters = interpreterGroup.getOrCreateSession("user1", "session_1"); - assertEquals(2, interpreters.size()); + assertEquals(3, interpreters.size()); assertEquals(EchoInterpreter.class.getName(), interpreters.get(0).getClassName()); assertEquals(DoubleEchoInterpreter.class.getName(), interpreters.get(1).getClassName()); assertEquals(1, interpreterGroup.getSessionNum()); @@ -73,7 +73,7 @@ public void testInterpreterGroup() { // create session_2 List interpreters2 = interpreterGroup.getOrCreateSession("user1", "session_2"); - assertEquals(2, interpreters2.size()); + assertEquals(3, interpreters2.size()); assertEquals(EchoInterpreter.class.getName(), interpreters2.get(0).getClassName()); assertEquals(DoubleEchoInterpreter.class.getName(), interpreters2.get(1).getClassName()); assertEquals(2, interpreterGroup.getSessionNum());