mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge remote-tracking branch 'apache-github/master' into ZEPPELIN-1619-rebased
This commit is contained in:
commit
ecd925b85b
40 changed files with 356 additions and 202 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -47,6 +47,7 @@ zeppelin-web/src/fonts/google-fonts.css
|
|||
zeppelin-web/.sass-cache
|
||||
zeppelin-web/npm-debug.log
|
||||
zeppelin-web/bower_components
|
||||
zeppelin-web/yarn.lock
|
||||
**nbproject/
|
||||
**node/
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ REM set ZEPPELIN_NOTEBOOK_HOMESCREEN REM Id of notebook to be displayed in home
|
|||
REM set ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE REM hide homescreen notebook from list when this value set to "true". default "false"
|
||||
REM set ZEPPELIN_NOTEBOOK_S3_BUCKET REM Bucket where notebook saved
|
||||
REM set ZEPPELIN_NOTEBOOK_S3_USER REM User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json
|
||||
REM set ZEPPELIN_NOTEBOOK_S3_ENDPOINT REM Endpoint of the bucket
|
||||
REM set ZEPPELIN_NOTEBOOK_S3_KMS_KEY_ID REM AWS KMS key ID
|
||||
REM set ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION REM AWS KMS key region
|
||||
REM set ZEPPELIN_IDENT_STRING REM A string representing this instance of zeppelin. $USER by default.
|
||||
REM set ZEPPELIN_NICENESS REM The scheduling priority for daemons. Defaults to 0.
|
||||
REM set ZEPPELIN_INTERPRETER_LOCALREPO REM Local repository for interpreter's additional dependency loading
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
# export ZEPPELIN_NOTEBOOK_S3_BUCKET # Bucket where notebook saved
|
||||
# export ZEPPELIN_NOTEBOOK_S3_ENDPOINT # Endpoint of the bucket
|
||||
# export ZEPPELIN_NOTEBOOK_S3_USER # User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json
|
||||
# export ZEPPELIN_NOTEBOOK_S3_KMS_KEY_ID # AWS KMS key ID
|
||||
# export ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION # AWS KMS key region
|
||||
# export ZEPPELIN_IDENT_STRING # A string representing this instance of zeppelin. $USER by default.
|
||||
# export ZEPPELIN_NICENESS # The scheduling priority for daemons. Defaults to 0.
|
||||
# export ZEPPELIN_INTERPRETER_LOCALREPO # Local repository for interpreter's additional dependency loading
|
||||
|
|
|
|||
|
|
@ -108,6 +108,16 @@
|
|||
</property>
|
||||
-->
|
||||
|
||||
<!-- provide region of your KMS key -->
|
||||
<!-- See http://docs.aws.amazon.com/general/latest/gr/rande.html#kms_region for region codes names -->
|
||||
<!--
|
||||
<property>
|
||||
<name>zeppelin.notebook.s3.kmsKeyRegion</name>
|
||||
<value>us-east-1</value>
|
||||
<description>AWS KMS key region in your AWS account</description>
|
||||
</property>
|
||||
-->
|
||||
|
||||
<!-- Use a custom encryption materials provider to encrypt data -->
|
||||
<!-- No configuration is given to the provider, so you must use system properties or another means to configure -->
|
||||
<!-- See https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/model/EncryptionMaterialsProvider.html -->
|
||||
|
|
|
|||
|
|
@ -130,6 +130,23 @@ Or using the following setting in **zeppelin-site.xml**:
|
|||
</property>
|
||||
```
|
||||
|
||||
In order to set custom KMS key region, set the following environment variable in the file **zeppelin-env.sh**:
|
||||
|
||||
```
|
||||
export ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION = kms-key-region
|
||||
```
|
||||
|
||||
Or using the following setting in **zeppelin-site.xml**:
|
||||
|
||||
```
|
||||
<property>
|
||||
<name>zeppelin.notebook.s3.kmsKeyRegion</name>
|
||||
<value>target-region</value>
|
||||
<description>AWS KMS key region in your AWS account</description>
|
||||
</property>
|
||||
```
|
||||
Format of `target-region` is described in more details [here](http://docs.aws.amazon.com/general/latest/gr/rande.html#kms_region) in second `Region` column (e.g. `us-east-1`).
|
||||
|
||||
#### Custom Encryption Materials Provider class
|
||||
|
||||
You may use a custom [``EncryptionMaterialsProvider``](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/model/EncryptionMaterialsProvider.html) class as long as it is available in the classpath and able to initialize itself from system properties or another mechanism. To use this, set the following environment variable in the file **zeppelin-env.sh**:
|
||||
|
|
@ -238,4 +255,4 @@ export ZEPPELINHUB_API_TOKEN = ZeppelinHub token
|
|||
export ZEPPELINHUB_API_ADDRESS = address of ZeppelinHub service (e.g. https://www.zeppelinhub.com)
|
||||
```
|
||||
|
||||
You can get more information on generating `token` and using authentication on the corresponding [help page](http://help.zeppelinhub.com/zeppelin_integration/#add-a-new-zeppelin-instance-and-generate-a-token).
|
||||
You can get more information on generating `token` and using authentication on the corresponding [help page](http://help.zeppelinhub.com/zeppelin_integration/#add-a-new-zeppelin-instance-and-generate-a-token).
|
||||
|
|
|
|||
|
|
@ -230,5 +230,4 @@ public class SparkRInterpreter extends Interpreter {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
boolean rScriptInitialized = false;
|
||||
Integer rScriptInitializeNotifier = new Integer(0);
|
||||
|
||||
|
||||
/**
|
||||
* Request to R repl
|
||||
*/
|
||||
|
|
@ -103,8 +102,6 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
boolean rResponseError = false;
|
||||
Integer rResponseNotifier = new Integer(0);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create ZeppelinR instance
|
||||
* @param rCmdPath R repl commandline path
|
||||
|
|
@ -216,7 +213,6 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send request to r repl and return response
|
||||
* @return responseValue
|
||||
|
|
@ -257,7 +253,6 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wait until src/main/resources/R/zeppelin_sparkr.R is initialized
|
||||
* and call onScriptInitialized()
|
||||
|
|
@ -286,14 +281,11 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
if (rScriptInitialized == false) {
|
||||
throw new InterpreterException("sparkr is not responding " + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* invoked by src/main/resources/R/zeppelin_sparkr.R
|
||||
* @return
|
||||
|
|
@ -337,7 +329,6 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create R script in tmp dir
|
||||
*/
|
||||
|
|
@ -381,7 +372,6 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
return zeppelinR.get(hashcode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pass InterpreterOutput to capture the repl output
|
||||
* @param out
|
||||
|
|
@ -390,8 +380,6 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
outputStream.setInterpreterOutput(out);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onProcessComplete(int i) {
|
||||
logger.info("process complete {}", i);
|
||||
|
|
@ -403,6 +391,4 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
logger.error(e.getMessage(), e);
|
||||
rScriptRunning = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ object ZeppelinRDisplay {
|
|||
}
|
||||
|
||||
private def htmlDisplay(body: Element, imageWidth: String): RDisplay = {
|
||||
|
||||
var div = new String()
|
||||
|
||||
for (element <- body.children) {
|
||||
|
|
@ -101,7 +100,6 @@ object ZeppelinRDisplay {
|
|||
val r = (pattern findFirstIn eHtml).getOrElse("")
|
||||
|
||||
div = div + eOuterHtml.replace(r, "")
|
||||
|
||||
}
|
||||
|
||||
val content = div
|
||||
|
|
@ -115,7 +113,5 @@ object ZeppelinRDisplay {
|
|||
}
|
||||
|
||||
RDisplay(body.html, HTML, SUCCESS)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,6 +208,28 @@ public class InterpreterGroup extends ConcurrentHashMap<String, List<Interpreter
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all interpreter instances in this group
|
||||
*/
|
||||
public void shutdown() {
|
||||
LOGGER.info("Close interpreter group " + getId());
|
||||
|
||||
// make sure remote interpreter process terminates
|
||||
if (remoteInterpreterProcess != null) {
|
||||
while (remoteInterpreterProcess.referenceCount() > 0) {
|
||||
remoteInterpreterProcess.dereference();
|
||||
}
|
||||
remoteInterpreterProcess = null;
|
||||
}
|
||||
allInterpreterGroups.remove(id);
|
||||
|
||||
List<Interpreter> intpToClose = new LinkedList<>();
|
||||
for (List<Interpreter> intpGroupForSession : this.values()) {
|
||||
intpToClose.addAll(intpGroupForSession);
|
||||
}
|
||||
close(intpToClose);
|
||||
}
|
||||
|
||||
public void setResourcePool(ResourcePool resourcePool) {
|
||||
this.resourcePool = resourcePool;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,8 +187,9 @@ public class ZeppelinServer extends Application {
|
|||
LOG.info("Shutting down Zeppelin Server ... ");
|
||||
try {
|
||||
jettyWebServer.stop();
|
||||
notebook.getInterpreterFactory().close();
|
||||
notebook.getInterpreterFactory().shutdown();
|
||||
notebook.close();
|
||||
Thread.sleep(3000);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error while stopping servlet container", e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.*;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.vfs2.FileSystemException;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration;
|
||||
|
|
@ -85,8 +86,6 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.collect.Queues;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
|
|
@ -263,6 +262,9 @@ public class NotebookServer extends WebSocketServlet
|
|||
case RUN_PARAGRAPH:
|
||||
runParagraph(conn, userAndRoles, notebook, messagereceived);
|
||||
break;
|
||||
case RUN_ALL_PARAGRAPHS:
|
||||
runAllParagraphs(conn, userAndRoles, notebook, messagereceived);
|
||||
break;
|
||||
case CANCEL_PARAGRAPH:
|
||||
cancelParagraph(conn, userAndRoles, notebook, messagereceived);
|
||||
break;
|
||||
|
|
@ -1534,8 +1536,46 @@ public class NotebookServer extends WebSocketServlet
|
|||
p.abort();
|
||||
}
|
||||
|
||||
private void runParagraph(NotebookSocket conn, HashSet<String> userAndRoles, Notebook notebook,
|
||||
private void runAllParagraphs(NotebookSocket conn, HashSet<String> userAndRoles,
|
||||
Notebook notebook,
|
||||
Message fromMessage) throws IOException {
|
||||
final String noteId = (String) fromMessage.get("noteId");
|
||||
if (StringUtils.isBlank(noteId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Note note = notebook.getNote(noteId);
|
||||
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
|
||||
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
|
||||
permissionError(conn, "run all paragraphs", fromMessage.principal, userAndRoles,
|
||||
notebookAuthorization.getOwners(noteId));
|
||||
return;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> paragraphs =
|
||||
gson.fromJson(String.valueOf(fromMessage.data.get("paragraphs")),
|
||||
new TypeToken<List<Map<String, Object>>>() {}.getType());
|
||||
|
||||
for (Map<String, Object> raw : paragraphs) {
|
||||
String paragraphId = (String) raw.get("id");
|
||||
if (paragraphId == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String text = (String) raw.get("paragraph");
|
||||
String title = (String) raw.get("title");
|
||||
Map<String, Object> params = (Map<String, Object>) raw.get("params");
|
||||
Map<String, Object> config = (Map<String, Object>) raw.get("config");
|
||||
|
||||
Paragraph p = setParagraphUsingMessage(note, fromMessage,
|
||||
paragraphId, text, title, params, config);
|
||||
|
||||
persistAndExecuteSingleParagraph(conn, note, p);
|
||||
}
|
||||
}
|
||||
|
||||
private void runParagraph(NotebookSocket conn, HashSet<String> userAndRoles, Notebook notebook,
|
||||
Message fromMessage) throws IOException {
|
||||
final String paragraphId = (String) fromMessage.get("id");
|
||||
if (paragraphId == null) {
|
||||
return;
|
||||
|
|
@ -1550,30 +1590,29 @@ public class NotebookServer extends WebSocketServlet
|
|||
return;
|
||||
}
|
||||
|
||||
Paragraph p = note.getParagraph(paragraphId);
|
||||
String text = (String) fromMessage.get("paragraph");
|
||||
p.setText(text);
|
||||
p.setTitle((String) fromMessage.get("title"));
|
||||
AuthenticationInfo subject =
|
||||
new AuthenticationInfo(fromMessage.principal, fromMessage.ticket);
|
||||
p.setAuthenticationInfo(subject);
|
||||
|
||||
String title = (String) fromMessage.get("title");
|
||||
Map<String, Object> params = (Map<String, Object>) fromMessage.get("params");
|
||||
p.settings.setParams(params);
|
||||
Map<String, Object> config = (Map<String, Object>) fromMessage.get("config");
|
||||
p.setConfig(config);
|
||||
Paragraph p = setParagraphUsingMessage(note, fromMessage, paragraphId,
|
||||
text, title, params, config);
|
||||
|
||||
persistAndExecuteSingleParagraph(conn, note, p);
|
||||
}
|
||||
|
||||
private void persistAndExecuteSingleParagraph(NotebookSocket conn,
|
||||
Note note, Paragraph p) throws IOException {
|
||||
// if it's the last paragraph and empty, let's add a new one
|
||||
boolean isTheLastParagraph = note.isLastParagraph(p.getId());
|
||||
if (!(text.trim().equals(p.getMagic()) ||
|
||||
Strings.isNullOrEmpty(text)) &&
|
||||
if (!(p.getText().trim().equals(p.getMagic()) ||
|
||||
Strings.isNullOrEmpty(p.getText())) &&
|
||||
isTheLastParagraph) {
|
||||
Paragraph newPara = note.addParagraph(subject);
|
||||
Paragraph newPara = note.addParagraph(p.getAuthenticationInfo());
|
||||
broadcastNewParagraph(note, newPara);
|
||||
}
|
||||
|
||||
try {
|
||||
note.persist(subject);
|
||||
note.persist(p.getAuthenticationInfo());
|
||||
} catch (FileSystemException ex) {
|
||||
LOG.error("Exception from run", ex);
|
||||
conn.send(serializeMessage(new Message(OP.ERROR_INFO).put("info",
|
||||
|
|
@ -1584,7 +1623,7 @@ public class NotebookServer extends WebSocketServlet
|
|||
}
|
||||
|
||||
try {
|
||||
note.run(paragraphId);
|
||||
note.run(p.getId());
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Exception from run", ex);
|
||||
if (p != null) {
|
||||
|
|
@ -1595,6 +1634,21 @@ public class NotebookServer extends WebSocketServlet
|
|||
}
|
||||
}
|
||||
|
||||
private Paragraph setParagraphUsingMessage(Note note, Message fromMessage, String paragraphId,
|
||||
String text, String title, Map<String, Object> params,
|
||||
Map<String, Object> config) {
|
||||
Paragraph p = note.getParagraph(paragraphId);
|
||||
p.setText(text);
|
||||
p.setTitle(title);
|
||||
AuthenticationInfo subject =
|
||||
new AuthenticationInfo(fromMessage.principal, fromMessage.ticket);
|
||||
p.setAuthenticationInfo(subject);
|
||||
p.settings.setParams(params);
|
||||
p.setConfig(config);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private void sendAllConfigurations(NotebookSocket conn, HashSet<String> userAndRoles,
|
||||
Notebook notebook) throws IOException {
|
||||
ZeppelinConfiguration conf = notebook.getConf();
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
|
|||
driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-control-play shortcut-icon']")).isDisplayed(), CoreMatchers.equalTo(false)
|
||||
);
|
||||
|
||||
driver.findElement(By.xpath(".//*[@id='main']//button[@ng-click='runNote()']")).sendKeys(Keys.ENTER);
|
||||
driver.findElement(By.xpath(".//*[@id='main']//button[contains(@ng-click, 'runAllParagraphs')]")).sendKeys(Keys.ENTER);
|
||||
ZeppelinITUtils.sleep(1000, true);
|
||||
driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'Run all paragraphs?')]" +
|
||||
"//div[@class='modal-footer']//button[contains(.,'OK')]")).click();
|
||||
|
|
|
|||
|
|
@ -21,17 +21,18 @@ All build commands are described in [package.json](./package.json)
|
|||
|
||||
```sh
|
||||
# install required depepdencies and bower packages (only once)
|
||||
$ npm install
|
||||
$ npm install -g yarn
|
||||
$ yarn install
|
||||
|
||||
# build zeppelin-web for production
|
||||
$ npm run build
|
||||
$ yarn run build
|
||||
|
||||
# run frontend application only in dev mode (localhost:9000)
|
||||
# you need to run zeppelin backend instance also
|
||||
$ npm run dev
|
||||
$ yarn run dev
|
||||
|
||||
# execute tests
|
||||
$ npm run test
|
||||
$ yarn run test
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
|
@ -50,12 +51,12 @@ Try to add to the `.bowerrc` file the following content:
|
|||
"https-proxy" : "http://<host>:<port>"
|
||||
```
|
||||
|
||||
also try to add proxy info to npm install command:
|
||||
also try to add proxy info to yarn install command:
|
||||
```xml
|
||||
<execution>
|
||||
<id>npm install</id>
|
||||
<id>yarn install</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
<goal>yarn</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>--proxy=http://<host>:<port> --https-proxy=http://<host>:<port></arguments>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"postinstall": "bower install --silent && grunt googlefonts",
|
||||
"prebuild": "npm-run-all clean",
|
||||
"build": "grunt pre-webpack-dist && webpack && grunt post-webpack-dist",
|
||||
"predev": "npm-run-all clean && grunt pre-webpack-dev",
|
||||
"predev": "grunt pre-webpack-dev",
|
||||
"dev:server": "webpack-dev-server --hot",
|
||||
"visdev:server": "HELIUM_VIS_DEV=true webpack-dev-server --hot",
|
||||
"dev:watch": "grunt watch-webpack-dev",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
<properties>
|
||||
<node.version>v6.9.1</node.version>
|
||||
<npm.version>3.10.8</npm.version>
|
||||
<yarn.version>v0.18.1</yarn.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<!--plugin versions-->
|
||||
|
|
@ -94,6 +94,7 @@
|
|||
<exclude>bower.json</exclude>
|
||||
<exclude>**/package.json</exclude>
|
||||
<exclude>**/.npmignore</exclude>
|
||||
<exclude>yarn.lock</exclude>
|
||||
<exclude>*.md</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
|
|
@ -106,30 +107,30 @@
|
|||
<executions>
|
||||
|
||||
<execution>
|
||||
<id>install node and npm</id>
|
||||
<id>install node and yarn</id>
|
||||
<goals>
|
||||
<goal>install-node-and-npm</goal>
|
||||
<goal>install-node-and-yarn</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<nodeVersion>${node.version}</nodeVersion>
|
||||
<npmVersion>${npm.version}</npmVersion>
|
||||
<yarnVersion>${yarn.version}</yarnVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>npm install</id>
|
||||
<id>yarn install</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
<goal>yarn</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>install</arguments>
|
||||
<arguments>install --no-lockfile</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>npm build</id>
|
||||
<id>yarn build</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
<goal>yarn</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>run build</arguments>
|
||||
|
|
@ -137,9 +138,9 @@
|
|||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>npm test</id>
|
||||
<id>yarn test</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
<goal>yarn</goal>
|
||||
</goals>
|
||||
<phase>test</phase>
|
||||
<configuration>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export default class HandsonHelper {
|
|||
this.columns = columns || [];
|
||||
this.rows = rows || [];
|
||||
this.comment = comment || '';
|
||||
this._numericValidator = this._numericValidator.bind(this);
|
||||
};
|
||||
|
||||
getHandsonTableConfig(columns, columnNames, resultRows) {
|
||||
|
|
|
|||
|
|
@ -381,13 +381,13 @@ a.navbar-brand:hover {
|
|||
}
|
||||
|
||||
.zeppelin {
|
||||
background: url('/assets/images/zepLogo.png') no-repeat right;
|
||||
background: url('../assets/images/zepLogo.png') no-repeat right;
|
||||
height: 380px;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.zeppelin2 {
|
||||
background: url('/assets/images/zepLogo.png') no-repeat right;
|
||||
background: url('../assets/images/zepLogo.png') no-repeat right;
|
||||
background-position-y: 12px;
|
||||
height: 380px;
|
||||
opacity: 0.2;
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ limitations under the License.
|
|||
<div>
|
||||
<h5><a href="" data-toggle="modal" data-target="#noteImportModal" style="text-decoration: none;">
|
||||
<i style="font-size: 15px;" class="fa fa-upload"></i> Import note</a></h5>
|
||||
<h5><a href="" data-toggle="modal" data-target="#noteNameModal" style="text-decoration: none;">
|
||||
<h5 ng-controller="NotenameCtrl as notenamectrl"><a href="" data-toggle="modal" data-target="#noteNameModal" style="text-decoration: none;" ng-click="notenamectrl.getInterpreterSettings()">
|
||||
<i style="font-size: 15px;" class="icon-notebook"></i> Create new note</a></h5>
|
||||
<ul id="notebook-names">
|
||||
<li class="filter-names" ng-include="'components/filterNoteNames/filter-note-names.html'"></li>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ limitations under the License.
|
|||
<span class="labelBtn btn-group">
|
||||
<button type="button"
|
||||
class="btn btn-default btn-xs"
|
||||
ng-click="runNote()"
|
||||
ng-click="runAllParagraphs(note.id)"
|
||||
ng-class="{'disabled':isNoteRunning()}"
|
||||
tooltip-placement="bottom" tooltip="Run all paragraphs"
|
||||
ng-disabled="revisionView">
|
||||
|
|
|
|||
|
|
@ -286,16 +286,23 @@
|
|||
}
|
||||
};
|
||||
|
||||
$scope.runNote = function() {
|
||||
$scope.runAllParagraphs = function(noteId) {
|
||||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: '',
|
||||
message: 'Run all paragraphs?',
|
||||
callback: function(result) {
|
||||
if (result) {
|
||||
_.forEach($scope.note.paragraphs, function(n, key) {
|
||||
angular.element('#' + n.id + '_paragraphColumn_main').scope().runParagraph(n.text);
|
||||
const paragraphs = $scope.note.paragraphs.map(p => {
|
||||
return {
|
||||
id: p.id,
|
||||
title: p.title,
|
||||
paragraph: p.text,
|
||||
config: p.config,
|
||||
params: p.settings.params
|
||||
};
|
||||
});
|
||||
websocketMsgSrv.runAllParagraphs(noteId, paragraphs);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -582,6 +589,14 @@
|
|||
}
|
||||
websocketMsgSrv.saveInterpreterBindings($scope.note.id, selectedSettingIds);
|
||||
console.log('Interpreter bindings %o saved', selectedSettingIds);
|
||||
|
||||
_.forEach($scope.note.paragraphs, function(n, key) {
|
||||
var regExp = /^\s*%/g;
|
||||
if (n.text && !regExp.exec(n.text)) {
|
||||
$scope.$broadcast('saveInterpreterBindings', n.id);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.showSetting = false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ limitations under the License.
|
|||
-->
|
||||
|
||||
<div id="{{paragraph.id}}_runControl" class="runControl">
|
||||
<div id="{{paragraph.id}}_progress" class="progress" ng-show="paragraph.status=='RUNNING'">
|
||||
<div id="{{paragraph.id}}_progress" class="progress" ng-if="paragraph.status=='RUNNING'">
|
||||
<div ng-if="getProgress()>0 && getProgress()<100 && paragraph.status=='RUNNING'"
|
||||
class="progress-bar" role="progressbar" style="width:{{getProgress()}}%;"></div>
|
||||
<div ng-if="(getProgress()<=0 || getProgress()>=100) && (paragraph.status=='RUNNING' )"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,10 @@
|
|||
$scope.editor = null;
|
||||
|
||||
var editorSetting = {};
|
||||
// flag that is used to set editor setting on paste percent sign
|
||||
var pastePercentSign = false;
|
||||
// flag that is used to set editor setting on save interpreter bindings
|
||||
var setInterpreterBindings = false;
|
||||
var paragraphScope = $rootScope.$new(true, $rootScope);
|
||||
|
||||
// to keep backward compatibility
|
||||
|
|
@ -661,7 +664,8 @@
|
|||
if ((typeof pos === 'undefined') || (pos.row === 0 && pos.column < 30) ||
|
||||
(pos.row === 1 && pos.column === 0) || pastePercentSign) {
|
||||
// If paragraph loading, use config value if exists
|
||||
if ((typeof pos === 'undefined') && $scope.paragraph.config.editorMode) {
|
||||
if ((typeof pos === 'undefined') && $scope.paragraph.config.editorMode &&
|
||||
!setInterpreterBindings) {
|
||||
session.setMode($scope.paragraph.config.editorMode);
|
||||
} else {
|
||||
var magic = getInterpreterName(paragraphText);
|
||||
|
|
@ -676,6 +680,7 @@
|
|||
}
|
||||
}
|
||||
pastePercentSign = false;
|
||||
setInterpreterBindings = false;
|
||||
};
|
||||
|
||||
var getInterpreterName = function(paragraphText) {
|
||||
|
|
@ -1154,6 +1159,13 @@
|
|||
}
|
||||
});
|
||||
|
||||
$scope.$on('saveInterpreterBindings', function(event, paragraphId) {
|
||||
if ($scope.paragraph.id === paragraphId) {
|
||||
setInterpreterBindings = true;
|
||||
setParagraphMode($scope.editor.getSession(), $scope.editor.getSession().getValue());
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('doubleClickParagraph', function(event, paragraphId) {
|
||||
if ($scope.paragraph.id === paragraphId && $scope.paragraph.config.editorHide &&
|
||||
$scope.paragraph.config.editorSetting.editOnDblClick && $scope.revisionView !== true) {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ table.dataTable.table-condensed .sorting_desc:after {
|
|||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.plainTextContent{
|
||||
white-space:pre-wrap; /** to preserve white-space and newlines of result */
|
||||
}
|
||||
|
||||
.graphContainer {
|
||||
position: relative;
|
||||
margin-bottom: 5px;
|
||||
|
|
|
|||
|
|
@ -56,8 +56,7 @@ limitations under the License.
|
|||
ng-controller="ResultCtrl"
|
||||
ng-repeat="result in paragraph.results.msg track by $index"
|
||||
ng-init="init(result, paragraph.config.results[$index], paragraph, $index)"
|
||||
ng-include src="'app/notebook/paragraph/result/result.html'"
|
||||
>
|
||||
ng-include src="'app/notebook/paragraph/result/result.html'">
|
||||
</div>
|
||||
<div id="{{paragraph.id}}_error"
|
||||
class="error text"
|
||||
|
|
|
|||
|
|
@ -14,31 +14,33 @@ limitations under the License.
|
|||
|
||||
<div id="{{id}}_switch"
|
||||
ng-if="(type == 'TABLE' || apps.length > 0 || suggestion.available && suggestion.available.length > 0) && !asIframe && !viewOnly"
|
||||
class="btn-group result-chart-selector"
|
||||
style="margin-bottom: 10px;">
|
||||
class="result-chart-selector">
|
||||
|
||||
<button type="button" class="btn btn-default btn-sm"
|
||||
ng-if="type == 'TABLE'"
|
||||
ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
ng-class="{'active' : viz.id == graphMode && !config.helium.activeApp}"
|
||||
ng-click="switchViz(viz.id)"
|
||||
ng-bind-html="viz.icon">
|
||||
</button>
|
||||
<div ng-if="type == 'TABLE'" class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-sm"
|
||||
ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
ng-class="{'active' : viz.id == graphMode && !config.helium.activeApp}"
|
||||
ng-click="switchViz(viz.id)"
|
||||
ng-bind-html="viz.icon">
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button type="button"
|
||||
ng-if="type != 'TABLE'"
|
||||
ng-click="switchApp()"
|
||||
ng-class="{'active' : !config.helium.activeApp}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-terminal"></i>
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button type="button"
|
||||
ng-if="type != 'TABLE'"
|
||||
ng-click="switchApp()"
|
||||
ng-class="{'active' : !config.helium.activeApp}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-terminal"></i>
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
class="btn btn-default btn-sm"
|
||||
ng-repeat="app in apps"
|
||||
ng-click="switchApp(app.id)"
|
||||
ng-class="{'active' : app.id == config.helium.activeApp}"
|
||||
ng-bind-html="app.pkg.icon">
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-default btn-sm"
|
||||
ng-repeat="app in apps"
|
||||
ng-click="switchApp(app.id)"
|
||||
ng-class="{'active' : app.id == config.helium.activeApp}"
|
||||
ng-bind-html="app.pkg.icon">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="{{paragraph.id}}_helium"
|
||||
|
|
@ -47,7 +49,6 @@ limitations under the License.
|
|||
style="margin-bottom: 10px;">
|
||||
<button type="button"
|
||||
class="btn btn-default btn-sm dropdown-toggle"
|
||||
ng-if="suggestion.available && suggestion.available.length > 0"
|
||||
data-toggle="dropdown"
|
||||
style="font-weight:bold; background-color:#ffdf96; border: 1px solid #FED233">
|
||||
He
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
<div
|
||||
id="p{{id}}_resize"
|
||||
ng-if="!config.helium.activeApp"
|
||||
style="padding-bottom: 5px;"
|
||||
resize='{"allowresize": "{{!asIframe && !viewOnly}}", "graphType": "{{type}}"}'
|
||||
resizable on-resize="resize(width, height);">
|
||||
<div ng-include src="'app/notebook/paragraph/result/result-graph.html'"></div>
|
||||
|
||||
<div id="{{id}}_comment"
|
||||
class="text"
|
||||
ng-if="type == 'TABLE' && tableDataComment"
|
||||
ng-bind-html="tableDataComment">
|
||||
</div>
|
||||
|
||||
<div id="p{{id}}_text"
|
||||
ng-if="type == 'TEXT'">
|
||||
<div class="fa fa-level-down scroll-paragraph-down"
|
||||
ng-show="showScrollDownIcon(id)"
|
||||
ng-click="scrollParagraphDown(id)"
|
||||
tooltip="Follow Output"></div>
|
||||
<div id="p{{id}}_text"
|
||||
style="max-height: {{config.graph.height}}px; overflow: auto"
|
||||
class="text"></div>
|
||||
<div class="fa fa-chevron-up scroll-paragraph-up"
|
||||
ng-show="showScrollUpIcon(id)"
|
||||
ng-click="scrollParagraphUp(id)"
|
||||
tooltip="Scroll Top"></div>
|
||||
</div>
|
||||
|
||||
<div id="p{{id}}_html"
|
||||
class="resultContained"
|
||||
ng-if="type == 'HTML'">
|
||||
</div>
|
||||
|
||||
<div id="p{{id}}_angular"
|
||||
class="resultContained"
|
||||
ng-if="type == 'ANGULAR'">
|
||||
</div>
|
||||
|
||||
<img id="{{id}}_img"
|
||||
ng-if="type == 'IMG'"
|
||||
ng-src="{{getBase64ImageSrc(result.data)}}" />
|
||||
</div>
|
||||
|
||||
<div ng-repeat="app in apps">
|
||||
<div id="p{{app.id}}"
|
||||
ng-show="config.helium.activeApp == app.id">
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -316,10 +316,14 @@ import ScatterchartVisualization from '../../../visualization/builtins/visualiza
|
|||
$timeout(retryRenderer);
|
||||
};
|
||||
|
||||
var getTextEl = function (paragraphId) {
|
||||
return angular.element('#p' + $scope.id + '_text');
|
||||
}
|
||||
|
||||
var textRendererInitialized = false;
|
||||
var renderText = function() {
|
||||
var retryRenderer = function() {
|
||||
var textEl = angular.element('#p' + $scope.id + '_text');
|
||||
var textEl = getTextEl($scope.id);
|
||||
if (textEl.length) {
|
||||
// clear all lines before render
|
||||
clearTextOutput();
|
||||
|
|
@ -331,7 +335,7 @@ import ScatterchartVisualization from '../../../visualization/builtins/visualiza
|
|||
flushAppendQueue();
|
||||
}
|
||||
|
||||
angular.element('#p' + $scope.id + '_text').bind('mousewheel', function(e) {
|
||||
getTextEl($scope.id).bind('mousewheel', function(e) {
|
||||
$scope.keepScrollDown = false;
|
||||
});
|
||||
} else {
|
||||
|
|
@ -342,7 +346,7 @@ import ScatterchartVisualization from '../../../visualization/builtins/visualiza
|
|||
};
|
||||
|
||||
var clearTextOutput = function() {
|
||||
var textEl = angular.element('#p' + $scope.id + '_text');
|
||||
var textEl = getTextEl($scope.id);
|
||||
if (textEl.length) {
|
||||
textEl.children().remove();
|
||||
}
|
||||
|
|
@ -361,7 +365,7 @@ import ScatterchartVisualization from '../../../visualization/builtins/visualiza
|
|||
textAppendQueueBeforeInitialize.push(msg);
|
||||
} else {
|
||||
flushAppendQueue();
|
||||
var textEl = angular.element('#p' + $scope.id + '_text');
|
||||
var textEl = getTextEl($scope.id);
|
||||
if (textEl.length) {
|
||||
var lines = msg.split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
|
|
@ -369,7 +373,7 @@ import ScatterchartVisualization from '../../../visualization/builtins/visualiza
|
|||
}
|
||||
}
|
||||
if ($scope.keepScrollDown) {
|
||||
var doc = angular.element('#p' + $scope.id + '_text');
|
||||
var doc = getTextEl($scope.id);
|
||||
doc[0].scrollTop = doc[0].scrollHeight;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.result-chart-selector {
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.result-chart-selector button {
|
||||
width: 35px;
|
||||
height: 30px;
|
||||
|
|
|
|||
|
|
@ -21,38 +21,38 @@ limitations under the License.
|
|||
resize='{"allowresize": "{{!asIframe && !viewOnly}}", "graphType": "{{type}}"}'
|
||||
resizable on-resize="resize(width, height);">
|
||||
|
||||
<!-- graph setting -->
|
||||
<div class="option lightBold" style="overflow: visible;"
|
||||
ng-show="type=='TABLE' && graphMode!='table'
|
||||
&& config.graph.optionOpen && !asIframe && !viewOnly">
|
||||
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
id="trsetting{{id}}_{{viz.id}}"
|
||||
ng-show="graphMode == viz.id"></div>
|
||||
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
id="vizsetting{{id}}_{{viz.id}}"
|
||||
ng-show="graphMode == viz.id"></div>
|
||||
</div>
|
||||
<div ng-if="type=='TABLE'">
|
||||
<!-- graph setting -->
|
||||
<div class="option lightBold" style="overflow: visible;"
|
||||
ng-if="graphMode!='table'
|
||||
&& config.graph.optionOpen && !asIframe && !viewOnly">
|
||||
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
id="trsetting{{id}}_{{viz.id}}"
|
||||
ng-show="graphMode == viz.id"></div>
|
||||
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
id="vizsetting{{id}}_{{viz.id}}"
|
||||
ng-show="graphMode == viz.id"></div>
|
||||
</div>
|
||||
|
||||
<!-- graph -->
|
||||
<div id="p{{id}}_graph"
|
||||
class="graphContainer"
|
||||
ng-class="{'noOverflow': graphMode=='table'}"
|
||||
ng-show="type =='TABLE'"
|
||||
>
|
||||
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
id="p{{id}}_{{viz.id}}"
|
||||
ng-show="graphMode == viz.id">
|
||||
<!-- graph -->
|
||||
<div id="p{{id}}_graph"
|
||||
class="graphContainer"
|
||||
ng-class="{'noOverflow': graphMode=='table'}"
|
||||
>
|
||||
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
id="p{{id}}_{{viz.id}}"
|
||||
ng-show="graphMode == viz.id">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="{{id}}_comment"
|
||||
class="text"
|
||||
ng-if="tableDataComment"
|
||||
ng-bind-html="tableDataComment">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="{{id}}_comment"
|
||||
class="text"
|
||||
ng-if="type == 'TABLE' && tableDataComment"
|
||||
ng-bind-html="tableDataComment">
|
||||
</div>
|
||||
|
||||
<div id="p{{id}}_text"
|
||||
ng-if="type == 'TEXT'"
|
||||
<div ng-if="type == 'TEXT'"
|
||||
class="plainTextContainer">
|
||||
<div class="fa fa-level-down scroll-paragraph-down"
|
||||
ng-show="showScrollDownIcon()"
|
||||
|
|
@ -60,7 +60,7 @@ limitations under the License.
|
|||
tooltip="Follow Output"></div>
|
||||
<div id="p{{id}}_text"
|
||||
style="max-height: {{config.graph.height}}px; overflow: auto"
|
||||
class="text"></div>
|
||||
class="text plainTextContent"></div>
|
||||
<div class="fa fa-chevron-up scroll-paragraph-up"
|
||||
ng-show="showScrollUpIcon()"
|
||||
ng-click="scrollParagraphUp()"
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ limitations under the License.
|
|||
<li class="dropdown notebook-list-dropdown" dropdown>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" dropdown-toggle>Notebook <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu navbar-dropdown-maxHeight" role="menu">
|
||||
<li><a href="" data-toggle="modal" data-target="#noteNameModal"><i class="fa fa-plus"></i> Create new note</a></li>
|
||||
<li ng-controller="NotenameCtrl as notenamectrl"><a href="" data-toggle="modal" data-target="#noteNameModal" ng-click="notenamectrl.getInterpreterSettings()"><i class="fa fa-plus"></i> Create new note</a></li>
|
||||
<li class="divider"></li>
|
||||
<div id="notebook-list" class="scrollbar-container" ng-if="isDrawNavbarNoteList">
|
||||
<li class="filter-names" ng-include="'components/filterNoteNames/filter-note-names.html'"></li>
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@
|
|||
vm.getInterpreterSettings = function() {
|
||||
vm.websocketMsgSrv.getInterpreterSettings();
|
||||
};
|
||||
|
||||
|
||||
$scope.$on('interpreterSettings', function(event, data) {
|
||||
$scope.interpreterSettings = data.interpreterSettings;
|
||||
|
|
@ -108,13 +109,6 @@
|
|||
$scope.note.defaultInterpreter = data.interpreterSettings[0];
|
||||
});
|
||||
|
||||
var init = function() {
|
||||
if (!vm.clone) {
|
||||
vm.getInterpreterSettings();
|
||||
}
|
||||
};
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -173,6 +173,16 @@
|
|||
});
|
||||
},
|
||||
|
||||
runAllParagraphs: function(noteId, paragraphs) {
|
||||
websocketEvents.sendNewEvent({
|
||||
op: 'RUN_ALL_PARAGRAPHS',
|
||||
data: {
|
||||
noteId: noteId,
|
||||
paragraphs: JSON.stringify(paragraphs)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeParagraph: function(paragraphId) {
|
||||
websocketEvents.sendNewEvent({op: 'PARAGRAPH_REMOVE', data: {id: paragraphId}});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ limitations under the License.
|
|||
<!-- endbuild -->
|
||||
|
||||
<script type="text/x-mathjax-config">
|
||||
MathJax.Hub.Config({
|
||||
var config = {
|
||||
extensions: ["tex2jax.js"],
|
||||
jax: ["input/TeX", "output/HTML-CSS"],
|
||||
tex2jax: {
|
||||
|
|
@ -115,7 +115,12 @@ limitations under the License.
|
|||
},
|
||||
"HTML-CSS": { availableFonts: ["TeX"] },
|
||||
messageStyle: "none"
|
||||
});
|
||||
}
|
||||
// add root only if it's not dev mode
|
||||
if (Number(location.port) !== 9000) {
|
||||
config.root = '.';
|
||||
}
|
||||
MathJax.Hub.Config(config);
|
||||
</script>
|
||||
|
||||
<!-- build:js(.) scripts/vendor.js -->
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ describe('Controller: NotebookCtrl', function() {
|
|||
scope.note = noteMock;
|
||||
});
|
||||
|
||||
var functions = ['getCronOptionNameFromValue', 'removeNote', 'runNote', 'saveNote', 'toggleAllEditor',
|
||||
var functions = ['getCronOptionNameFromValue', 'removeNote', 'runAllParagraphs', 'saveNote', 'toggleAllEditor',
|
||||
'showAllEditor', 'hideAllEditor', 'toggleAllTable', 'hideAllTable', 'showAllTable', 'isNoteRunning',
|
||||
'killSaveTimer', 'startSaveTimer', 'setLookAndFeel', 'setCronScheduler', 'setConfig', 'updateNoteName',
|
||||
'openSetting', 'closeSetting', 'saveSetting', 'toggleSetting'];
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ module.exports = function makeWebpackConfig () {
|
|||
|
||||
// Output path from the view of the page
|
||||
// Uses webpack-dev-server in development
|
||||
publicPath: isProd ? '/' : 'http://localhost:9000/',
|
||||
publicPath: isProd ? '' : 'http://localhost:9000/',
|
||||
|
||||
// Filename for entry points
|
||||
// Only adds hash in build mode
|
||||
|
|
|
|||
|
|
@ -369,6 +369,10 @@ public class ZeppelinConfiguration extends XMLConfiguration {
|
|||
return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_KMS_KEY_ID);
|
||||
}
|
||||
|
||||
public String getS3KMSKeyRegion() {
|
||||
return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION);
|
||||
}
|
||||
|
||||
public String getS3EncryptionMaterialsProviderClass() {
|
||||
return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_EMP);
|
||||
}
|
||||
|
|
@ -579,6 +583,7 @@ public class ZeppelinConfiguration extends XMLConfiguration {
|
|||
ZEPPELIN_NOTEBOOK_S3_USER("zeppelin.notebook.s3.user", "user"),
|
||||
ZEPPELIN_NOTEBOOK_S3_EMP("zeppelin.notebook.s3.encryptionMaterialsProvider", null),
|
||||
ZEPPELIN_NOTEBOOK_S3_KMS_KEY_ID("zeppelin.notebook.s3.kmsKeyID", null),
|
||||
ZEPPELIN_NOTEBOOK_S3_KMS_KEY_REGION("zeppelin.notebook.s3.kmsKeyRegion", null),
|
||||
ZEPPELIN_NOTEBOOK_AZURE_CONNECTION_STRING("zeppelin.notebook.azure.connectionString", null),
|
||||
ZEPPELIN_NOTEBOOK_AZURE_SHARE("zeppelin.notebook.azure.share", "zeppelin"),
|
||||
ZEPPELIN_NOTEBOOK_AZURE_USER("zeppelin.notebook.azure.user", "user"),
|
||||
|
|
|
|||
|
|
@ -1049,6 +1049,30 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
List<Thread> closeThreads = new LinkedList<>();
|
||||
synchronized (interpreterSettings) {
|
||||
Collection<InterpreterSetting> intpSettings = interpreterSettings.values();
|
||||
for (final InterpreterSetting intpSetting : intpSettings) {
|
||||
Thread t = new Thread() {
|
||||
public void run() {
|
||||
intpSetting.shutdownAndRemoveAllInterpreterGroups();
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
closeThreads.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
for (Thread t : closeThreads) {
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Can't close interpreterGroup", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Interpreter createRepl(String dirName, String className, Properties property)
|
||||
throws InterpreterException {
|
||||
logger.info("Create repl {} from {}", className, dirName);
|
||||
|
|
|
|||
|
|
@ -197,6 +197,32 @@ public class InterpreterSetting {
|
|||
}
|
||||
}
|
||||
|
||||
void shutdownAndRemoveInterpreterGroup(String interpreterGroupKey) {
|
||||
String key = getInterpreterProcessKey("", interpreterGroupKey);
|
||||
|
||||
List<InterpreterGroup> groupToRemove = new LinkedList<>();
|
||||
InterpreterGroup groupItem;
|
||||
for (String intpKey : new HashSet<>(interpreterGroupRef.keySet())) {
|
||||
if (intpKey.contains(key)) {
|
||||
interpreterGroupWriteLock.lock();
|
||||
groupItem = interpreterGroupRef.remove(intpKey);
|
||||
interpreterGroupWriteLock.unlock();
|
||||
groupToRemove.add(groupItem);
|
||||
}
|
||||
}
|
||||
|
||||
for (InterpreterGroup groupToClose : groupToRemove) {
|
||||
groupToClose.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void shutdownAndRemoveAllInterpreterGroups() {
|
||||
HashSet<String> groupsToRemove = new HashSet<>(interpreterGroupRef.keySet());
|
||||
for (String interpreterGroupKey : groupsToRemove) {
|
||||
shutdownAndRemoveInterpreterGroup(interpreterGroupKey);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import java.util.Map;
|
|||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
|
||||
import org.apache.zeppelin.notebook.Note;
|
||||
|
|
@ -48,12 +49,15 @@ import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
|
|||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import com.amazonaws.services.s3.AmazonS3EncryptionClient;
|
||||
import com.amazonaws.services.s3.model.CryptoConfiguration;
|
||||
import com.amazonaws.services.s3.model.EncryptionMaterialsProvider;
|
||||
import com.amazonaws.services.s3.model.GetObjectRequest;
|
||||
import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider;
|
||||
import com.amazonaws.services.s3.model.ListObjectsRequest;
|
||||
import com.amazonaws.services.s3.model.ObjectListing;
|
||||
import com.amazonaws.services.s3.model.PutObjectRequest;
|
||||
import com.amazonaws.regions.Region;
|
||||
import com.amazonaws.regions.Regions;
|
||||
import com.amazonaws.services.s3.model.S3Object;
|
||||
import com.amazonaws.services.s3.model.S3ObjectSummary;
|
||||
import com.google.gson.Gson;
|
||||
|
|
@ -91,13 +95,24 @@ public class S3NotebookRepo implements NotebookRepo {
|
|||
|
||||
// always use the default provider chain
|
||||
AWSCredentialsProvider credentialsProvider = new DefaultAWSCredentialsProviderChain();
|
||||
CryptoConfiguration cryptoConf = null;
|
||||
String keyRegion = conf.getS3KMSKeyRegion();
|
||||
|
||||
if (StringUtils.isNotBlank(keyRegion)) {
|
||||
cryptoConf = new CryptoConfiguration();
|
||||
cryptoConf.setAwsKmsRegion(Region.getRegion(Regions.fromName(keyRegion)));
|
||||
}
|
||||
|
||||
// see if we should be encrypting data in S3
|
||||
String kmsKeyID = conf.getS3KMSKeyID();
|
||||
if (kmsKeyID != null) {
|
||||
// use the AWS KMS to encrypt data
|
||||
KMSEncryptionMaterialsProvider emp = new KMSEncryptionMaterialsProvider(kmsKeyID);
|
||||
this.s3client = new AmazonS3EncryptionClient(credentialsProvider, emp);
|
||||
if (cryptoConf != null) {
|
||||
this.s3client = new AmazonS3EncryptionClient(credentialsProvider, emp, cryptoConf);
|
||||
} else {
|
||||
this.s3client = new AmazonS3EncryptionClient(credentialsProvider, emp);
|
||||
}
|
||||
}
|
||||
else if (conf.getS3EncryptionMaterialsProviderClass() != null) {
|
||||
// use a custom encryption materials provider class
|
||||
|
|
|
|||
|
|
@ -171,7 +171,8 @@ public class Message {
|
|||
PARAGRAPH_ADDED, // [s-c] paragraph is added
|
||||
PARAGRAPH_REMOVED, // [s-c] paragraph deleted
|
||||
PARAGRAPH_MOVED, // [s-c] paragraph moved
|
||||
NOTE_UPDATED // [s-c] paragraph updated(name, config)
|
||||
NOTE_UPDATED, // [s-c] paragraph updated(name, config)
|
||||
RUN_ALL_PARAGRAPHS // [c-s] run all paragraphs
|
||||
}
|
||||
|
||||
public static final Message EMPTY = new Message(null);
|
||||
|
|
|
|||
Loading…
Reference in a new issue