This is an automated email from the ASF dual-hosted git repository. jongyoul pushed a commit to branch branch-0.12 in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/branch-0.12 by this push: new b5c54d684c [ZEPPELIN-6163] HBase interpreter supports hbase-2.x b5c54d684c is described below commit b5c54d684cb8b0ff30c90aa81b5cc8006d27527e Author: Paul Zhang <xzhang...@126.com> AuthorDate: Tue Apr 22 14:42:13 2025 +0800 [ZEPPELIN-6163] HBase interpreter supports hbase-2.x ### What is this PR for? Currently in Zeppelin HBase interpreter does not support HBase 2.x ruby syntax. This patch added hbase-2.x support. This closes ZEPPELIN-6163 ### What type of PR is it? Improvement ### Todos ### What is the Jira issue? * Open an issue on Jira https://issues.apache.org/jira/browse/ZEPPELIN/ * Put link here, and add [ZEPPELIN-*Jira number*] in PR title, eg. [ZEPPELIN-533] [ZEPPELIN-6163](https://issues.apache.org/jira/browse/ZEPPELIN-6163) ### How should this be tested? * Strongly recommended: add automated unit tests for any new or changed behavior * Outline any manual steps to test the PR here. Compiled and tested manually. ### Screenshots (if appropriate) ### Questions: * Does the license files need to update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Closes #4908 from paul8263/hbase-2.x. Signed-off-by: Jongyoul Lee <jongy...@gmail.com> (cherry picked from commit 52ea80f312b7a19dcce865a6ca8249a2f5fb49cd) Signed-off-by: Jongyoul Lee <jongy...@gmail.com> --- docs/interpreter/hbase.md | 24 +-- hbase/pom.xml | 10 +- .../apache/zeppelin/hbase/HbaseInterpreter.java | 206 +++++++++++---------- hbase/src/main/resources/interpreter-setting.json | 12 -- .../zeppelin/hbase/HbaseInterpreterTest.java | 36 +--- .../zeppelin/integration/HBaseIntegrationTest.java | 91 +++++++++ .../integration/HBaseIntegrationTest172.java | 36 ++++ .../integration/HBaseIntegrationTest2418.java | 36 ++++ .../org/apache/zeppelin/test/DownloadUtils.java | 47 ++++- .../apache/zeppelin/test/DownloadUtilsTest.java | 10 +- 10 files changed, 339 insertions(+), 169 deletions(-) diff --git a/docs/interpreter/hbase.md b/docs/interpreter/hbase.md index 50228407c9..0afbc726fa 100644 --- a/docs/interpreter/hbase.md +++ b/docs/interpreter/hbase.md @@ -28,19 +28,7 @@ limitations under the License. To get start with HBase, please see [HBase Quickstart](https://hbase.apache.org/book.html#quickstart). ## HBase release supported -By default, Zeppelin is built against HBase 1.0.x releases. To work with HBase 1.1.x releases, use the following build command: - -```bash -# HBase 1.1.4 -./mvnw clean package -DskipTests -Phadoop-2.6 -Dhadoop.version=2.6.0 -P build-distr -Dhbase.hbase.version=1.1.4 -Dhbase.hadoop.version=2.6.0 -``` - -To work with HBase 1.2.0+, use the following build command: - -```bash -# HBase 1.2.0 -./mvnw clean package -DskipTests -Phadoop-2.6 -Dhadoop.version=2.6.0 -P build-distr -Dhbase.hbase.version=1.2.0 -Dhbase.hadoop.version=2.6.0 -``` +Zeppelin is built against HBase 1.x and 2.x releases. ## Configuration @@ -55,16 +43,6 @@ To work with HBase 1.2.0+, use the following build command: <td>/usr/lib/hbase</td> <td>Installation directory of HBase, defaults to HBASE_HOME in environment</td> </tr> - <tr> - <td>hbase.ruby.sources</td> - <td>lib/ruby</td> - <td>Path to Ruby scripts relative to 'hbase.home'</td> - </tr> - <tr> - <td>zeppelin.hbase.test.mode</td> - <td>false</td> - <td>Disable checks for unit and manual tests</td> - </tr> </table> If you want to connect to HBase running on a cluster, you'll need to follow the next step. diff --git a/hbase/pom.xml b/hbase/pom.xml index 4c33d68c1e..b468216e6f 100644 --- a/hbase/pom.xml +++ b/hbase/pom.xml @@ -33,14 +33,16 @@ <properties> <!--library versions--> <interpreter.name>hbase</interpreter.name> - <jruby.version>1.6.8</jruby.version> </properties> <dependencies> <dependency> - <groupId>org.jruby</groupId> - <artifactId>jruby-complete</artifactId> - <version>${jruby.version}</version> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> </dependency> </dependencies> diff --git a/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java b/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java index 6400fae479..fd8952ba65 100644 --- a/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java +++ b/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java @@ -14,118 +14,135 @@ package org.apache.zeppelin.hbase; -import org.jruby.embed.LocalContextScope; -import org.jruby.embed.ScriptingContainer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.StringWriter; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; +import org.apache.commons.exec.CommandLine; +import org.apache.commons.exec.DefaultExecutor; +import org.apache.commons.exec.ExecuteException; +import org.apache.commons.exec.ExecuteWatchdog; +import org.apache.commons.exec.Executor; +import org.apache.commons.exec.PumpStreamHandler; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterException; import org.apache.zeppelin.interpreter.InterpreterResult; -import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Support for HBase Shell. All the commands documented here - * http://hbase.apache.org/book.html#shell is supported. - * - * Requirements: - * HBase Shell should be installed on the same machine. To be more specific, the following dir. - * should be available: https://github.com/apache/hbase/tree/master/hbase-shell/src/main/ruby - * HBase Shell should be able to connect to the HBase cluster from terminal. This makes sure - * that the client is configured properly. - * - * The interpreter takes 3 config parameters: - * hbase.home: Root directory where HBase is installed. Default is /usr/lib/hbase/ - * hbase.ruby.sources: Dir where shell ruby code is installed. - * Path is relative to hbase.home. Default: lib/ruby - * zeppelin.hbase.test.mode: (Testing only) Disable checks for unit and manual tests. Default: false + * HBase interpreter. It uses the hbase shell to interpret the commands. */ public class HbaseInterpreter extends Interpreter { + private static final Logger LOGGER = LoggerFactory.getLogger(HbaseInterpreter.class); + public static final String HBASE_HOME = "hbase.home"; - public static final String HBASE_RUBY_SRC = "hbase.ruby.sources"; - public static final String HBASE_TEST_MODE = "zeppelin.hbase.test.mode"; - private static final Logger LOGGER = LoggerFactory.getLogger(HbaseInterpreter.class); - private ScriptingContainer scriptingContainer; + private static final Path TEMP_FOLDER = Paths.get(System.getProperty("java.io.tmpdir"), + "zeppelin-hbase-scripts"); - private StringWriter writer; + private Map<String, Executor> runningProcesses = new HashMap<>(); - public HbaseInterpreter(Properties property) { - super(property); + private Map<String, File> tempFiles = new HashMap<>(); + + private static final int SIGTERM_CODE = 143; + + private long commandTimeout = 60000; + + public HbaseInterpreter(Properties properties) { + super(properties); } @Override public void open() throws InterpreterException { - this.scriptingContainer = new ScriptingContainer(LocalContextScope.SINGLETON); - this.writer = new StringWriter(); - scriptingContainer.setOutput(this.writer); - - if (!Boolean.parseBoolean(getProperty(HBASE_TEST_MODE))) { - String hbaseHome = getProperty(HBASE_HOME); - String rubySrc = getProperty(HBASE_RUBY_SRC); - Path absRubySrc = Paths.get(hbaseHome, rubySrc).toAbsolutePath(); - - LOGGER.info("Home:" + hbaseHome); - LOGGER.info("Ruby Src:" + rubySrc); - - File f = absRubySrc.toFile(); - if (!f.exists() || !f.isDirectory()) { - throw new InterpreterException("HBase ruby sources is not available at '" + absRubySrc - + "'"); - } - - LOGGER.info("Absolute Ruby Source:" + absRubySrc.toString()); - // hirb.rb:41 requires the following system properties to be set. - Properties sysProps = System.getProperties(); - sysProps.setProperty(HBASE_RUBY_SRC, absRubySrc.toString()); - - Path absHirbPath = Paths.get(hbaseHome, "bin/hirb.rb"); - try { - FileInputStream fis = new FileInputStream(absHirbPath.toFile()); - this.scriptingContainer.runScriptlet(fis, "hirb.rb"); - fis.close(); - } catch (IOException e) { - throw new InterpreterException(e.getCause()); - } - } + // Do nothing } @Override public void close() { - if (this.scriptingContainer != null) { - this.scriptingContainer.terminate(); - } + runningProcesses.clear(); + runningProcesses = null; + tempFiles.clear(); + tempFiles = null; } @Override - public InterpreterResult interpret(String cmd, InterpreterContext interpreterContext) { + public InterpreterResult interpret(String st, InterpreterContext context) { + LOGGER.debug("Run HBase shell script: {}", st); + + if (StringUtils.isEmpty(st)) { + return new InterpreterResult(InterpreterResult.Code.SUCCESS); + } + + String paragraphId = context.getParagraphId(); + final File scriptFile; + try { + // Write script in a temporary file + // The script is enriched with extensions + scriptFile = createTempFile(paragraphId); + FileUtils.write(scriptFile, st + "\nexit"); + } catch (IOException e) { + LOGGER.error("Can not write script in temp file", e); + return new InterpreterResult(InterpreterResult.Code.ERROR, e.getMessage()); + } + + InterpreterResult result = new InterpreterResult(InterpreterResult.Code.SUCCESS); + + final DefaultExecutor executor = new DefaultExecutor(); + final ByteArrayOutputStream errorStream = new ByteArrayOutputStream(); + + executor.setStreamHandler(new PumpStreamHandler(context.out, errorStream)); + executor.setWatchdog(new ExecuteWatchdog(commandTimeout)); + + String hbaseCmdPath = Paths.get(getProperty(HBASE_HOME), "bin", "hbase").toString(); + final CommandLine cmdLine = CommandLine.parse(hbaseCmdPath); + cmdLine.addArgument("shell", false); + cmdLine.addArgument(scriptFile.getAbsolutePath(), false); + try { - LOGGER.info(cmd); - this.writer.getBuffer().setLength(0); - this.scriptingContainer.runScriptlet(cmd); - this.writer.flush(); - LOGGER.debug(writer.toString()); - return new InterpreterResult(InterpreterResult.Code.SUCCESS, writer.getBuffer().toString()); - } catch (Throwable t) { - LOGGER.error("Can not run '" + cmd + "'", t); - return new InterpreterResult(InterpreterResult.Code.ERROR, t.getMessage()); + executor.execute(cmdLine); + runningProcesses.put(paragraphId, executor); + } catch (ExecuteException e) { + LOGGER.error("Can not run script in paragraph {}", paragraphId, e); + + final int exitValue = e.getExitValue(); + InterpreterResult.Code code = InterpreterResult.Code.ERROR; + String msg = errorStream.toString(); + + if (exitValue == SIGTERM_CODE) { + code = InterpreterResult.Code.INCOMPLETE; + msg = msg + "Paragraph received a SIGTERM.\n"; + LOGGER.info("The paragraph {} stopped executing: {}", paragraphId, msg); + } + + msg += "ExitValue: " + exitValue; + result = new InterpreterResult(code, msg); + } catch (IOException e) { + LOGGER.error("Can not run script in paragraph {}", paragraphId, e); + result = new InterpreterResult(InterpreterResult.Code.ERROR, e.getMessage()); + } finally { + deleteTempFile(paragraphId); + stopProcess(paragraphId); } + return result; } @Override - public void cancel(InterpreterContext context) {} + public void cancel(InterpreterContext context) { + stopProcess(context.getParagraphId()); + deleteTempFile(context.getParagraphId()); + } @Override public FormType getFormType() { @@ -143,30 +160,27 @@ public class HbaseInterpreter extends Interpreter { HbaseInterpreter.class.getName() + this.hashCode()); } - @Override - public List<InterpreterCompletion> completion(String buf, int cursor, - InterpreterContext interpreterContext) { - return null; + private void stopProcess(String paragraphId) { + Executor executor = runningProcesses.remove(paragraphId); + if (null != executor) { + final ExecuteWatchdog watchdog = executor.getWatchdog(); + watchdog.destroyProcess(); + } } - private static String getSystemDefault( - String envName, - String propertyName, - String defaultValue) { - - if (envName != null && !envName.isEmpty()) { - String envValue = System.getenv().get(envName); - if (envValue != null) { - return envValue; - } + private File createTempFile(String paragraphId) throws IOException { + if (!Files.exists(TEMP_FOLDER)) { + Files.createDirectory(TEMP_FOLDER); } + File temp = Files.createTempFile(TEMP_FOLDER, paragraphId, ".txt").toFile(); + tempFiles.put(paragraphId, temp); + return temp; + } - if (propertyName != null && !propertyName.isEmpty()) { - String propValue = System.getProperty(propertyName); - if (propValue != null) { - return propValue; - } + private void deleteTempFile(String paragraphId) { + File tmpFile = tempFiles.remove(paragraphId); + if (null != tmpFile) { + FileUtils.deleteQuietly(tmpFile); } - return defaultValue; } } diff --git a/hbase/src/main/resources/interpreter-setting.json b/hbase/src/main/resources/interpreter-setting.json index c5d89f083d..6d7a79c185 100644 --- a/hbase/src/main/resources/interpreter-setting.json +++ b/hbase/src/main/resources/interpreter-setting.json @@ -10,18 +10,6 @@ "defaultValue": "/usr/lib/hbase/", "description": "Installation directory of HBase", "type": "string" - }, - "hbase.ruby.sources": { - "propertyName": "hbase.ruby.sources", - "defaultValue": "lib/ruby", - "description": "Path to Ruby scripts relative to 'hbase.home'", - "type": "string" - }, - "zeppelin.hbase.test.mode": { - "propertyName": "zeppelin.hbase.test.mode", - "defaultValue": false, - "description": "Disable checks for unit and manual tests", - "type": "checkbox" } }, "editor": { diff --git a/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java b/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java index 5af152243b..460fc1f061 100644 --- a/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java +++ b/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java @@ -14,16 +14,14 @@ package org.apache.zeppelin.hbase; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.util.Properties; - import org.apache.zeppelin.interpreter.InterpreterException; -import org.apache.zeppelin.interpreter.InterpreterResult; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + /** * Tests for HBase Interpreter. */ @@ -34,8 +32,6 @@ public class HbaseInterpreterTest { public static void setUp() throws NullPointerException, InterpreterException { Properties properties = new Properties(); properties.put("hbase.home", ""); - properties.put("hbase.ruby.sources", ""); - properties.put("zeppelin.hbase.test.mode", "true"); hbaseInterpreter = new HbaseInterpreter(properties); hbaseInterpreter.open(); @@ -45,28 +41,4 @@ public class HbaseInterpreterTest { void newObject() { assertNotNull(hbaseInterpreter); } - - @Test - void putsTest() { - InterpreterResult result = hbaseInterpreter.interpret("puts \"Hello World\"", null); - assertEquals(InterpreterResult.Code.SUCCESS, result.code()); - assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType()); - assertEquals("Hello World\n", result.message().get(0).getData()); - } - - public void putsLoadPath() { - InterpreterResult result = hbaseInterpreter.interpret( - "require 'two_power'; puts twoToThePowerOf(4)", null); - assertEquals(InterpreterResult.Code.SUCCESS, result.code()); - assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType()); - assertEquals("16\n", result.message().get(0).getData()); - } - - @Test - void testException() { - InterpreterResult result = hbaseInterpreter.interpret("plot practical joke", null); - assertEquals(InterpreterResult.Code.ERROR, result.code()); - assertEquals("(NameError) undefined local variable or method `joke' for main:Object", - result.message().get(0).getData()); - } } diff --git a/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest.java b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest.java new file mode 100644 index 0000000000..b8e7e18693 --- /dev/null +++ b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest.java @@ -0,0 +1,91 @@ +/* + * 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.integration; + + +import org.apache.zeppelin.test.DownloadUtils; +import org.apache.zeppelin.MiniZeppelinServer; +import org.apache.zeppelin.interpreter.ExecutionContext; +import org.apache.zeppelin.interpreter.Interpreter; +import org.apache.zeppelin.interpreter.InterpreterContext; +import org.apache.zeppelin.interpreter.InterpreterFactory; +import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.InterpreterSetting; +import org.apache.zeppelin.interpreter.InterpreterSettingManager; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public abstract class HBaseIntegrationTest { + private static final Logger LOGGER = LoggerFactory.getLogger(HBaseIntegrationTest.class); + + private static InterpreterFactory interpreterFactory; + protected static InterpreterSettingManager interpreterSettingManager; + + private String hbaseHome; + + private static MiniZeppelinServer zepServer; + + public void prepareHBase(String hbaseVersion) { + LOGGER.info("Testing HBase Version: " + hbaseVersion); + this.hbaseHome = DownloadUtils.downloadHBase(hbaseVersion); + } + + @BeforeAll + static void init() throws Exception { + zepServer = new MiniZeppelinServer(HBaseIntegrationTest.class.getSimpleName()); + zepServer.addInterpreter("hbase"); + zepServer.copyBinDir(); + zepServer.copyLogProperties(); + zepServer.start(); + } + + @AfterAll + public static void tearDown() throws Exception { + zepServer.destroy(); + } + + @BeforeEach + void setup() { + interpreterSettingManager = zepServer.getService(InterpreterSettingManager.class); + interpreterFactory = new InterpreterFactory(interpreterSettingManager); + } + + @Test + public void testHBaseInterpreter() throws Exception { + InterpreterSetting hbaseInterpreterSetting = interpreterSettingManager.getInterpreterSettingByName("hbase"); + hbaseInterpreterSetting.setProperty("hbase.home", hbaseHome); + + Interpreter hbaseInterpreter = interpreterFactory.getInterpreter("hbase", new ExecutionContext("user1", "note1", "hbase")); + + InterpreterContext context = new InterpreterContext.Builder().setNoteId("note1").setParagraphId("paragraph_1").build(); + InterpreterResult interpreterResult = hbaseInterpreter.interpret("puts 'hello'", context); + + assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code(), interpreterResult.toString()); + assertTrue(interpreterResult.message().get(0).getData().contains("hello")); + + interpreterSettingManager.close(); + } + +} diff --git a/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest172.java b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest172.java new file mode 100644 index 0000000000..2979959869 --- /dev/null +++ b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest172.java @@ -0,0 +1,36 @@ +/* + * 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.integration; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; + +public class HBaseIntegrationTest172 { + + @Nested + @DisplayName("HBase 1.x") + public class HBase172 extends HBaseIntegrationTest { + + @BeforeEach + public void downloadHBase() { + prepareHBase("1.7.2"); + } + } + +} diff --git a/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest2418.java b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest2418.java new file mode 100644 index 0000000000..d495d73a6c --- /dev/null +++ b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest2418.java @@ -0,0 +1,36 @@ +/* + * 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.integration; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; + +public class HBaseIntegrationTest2418 { + + @Nested + @DisplayName("HBase 2.x") + public class HBase2418 extends HBaseIntegrationTest { + + @BeforeEach + public void downloadHBase() { + prepareHBase("2.4.18"); + } + } + +} diff --git a/zeppelin-test/src/main/java/org/apache/zeppelin/test/DownloadUtils.java b/zeppelin-test/src/main/java/org/apache/zeppelin/test/DownloadUtils.java index 37332b6c22..684b740de9 100644 --- a/zeppelin-test/src/main/java/org/apache/zeppelin/test/DownloadUtils.java +++ b/zeppelin-test/src/main/java/org/apache/zeppelin/test/DownloadUtils.java @@ -198,7 +198,6 @@ public class DownloadUtils { return targetSparkHomeFolder.getAbsolutePath(); } - public static void download(String url, int retries, File dst) throws IOException { download(new URL(url), retries, dst); } @@ -626,4 +625,50 @@ public class DownloadUtils { } return "incubator/livy/" + livyVersion + "/apache-livy-" + livyVersion + "-bin.zip"; } + + /** + * Download of a HBase distribution + * + * @param version HBase version + * @return home of HBase installation + */ + public static String downloadHBase(String version) { + File hbaseDownloadFolder = new File(downloadFolder, "hbase"); + hbaseDownloadFolder.mkdir(); + File targetHBaseHomeFolder = new File(hbaseDownloadFolder, "hbase-" + version); + if (targetHBaseHomeFolder.exists()) { + LOGGER.info("Skip to download HBase {} as it is already downloaded.", version); + return targetHBaseHomeFolder.getAbsolutePath(); + } + File hbaseTGZ = new File(hbaseDownloadFolder, "hbase-" + version + ".tar.gz"); + try { + URL mirrorURL = new URL(MIRROR_URL + generateHBaseDownloadUrl(version)); + URL archiveURL = new URL(ARCHIVE_URL + generateHBaseDownloadUrl(version)); + LOGGER.info("Download HBase {}", version); + download(new DownloadRequest(mirrorURL, archiveURL), hbaseTGZ); + ProgressBarBuilder pbb = new ProgressBarBuilder() + .setTaskName("Unarchive") + .setUnit("MiB", 1048576) // setting the progress bar to use MiB as the unit + .setStyle(ProgressBarStyle.ASCII) + .setUpdateIntervalMillis(1000) + .setConsumer(new DelegatingProgressBarConsumer(LOGGER::info)); + try ( + InputStream fis = Files.newInputStream(hbaseTGZ.toPath()); + InputStream pbis = ProgressBar.wrap(fis, pbb); + InputStream bis = new BufferedInputStream(pbis); + InputStream gzis = new GzipCompressorInputStream(bis); + ArchiveInputStream<TarArchiveEntry> o = new TarArchiveInputStream(gzis)) { + LOGGER.info("Unarchive HBase {} to {}", version, targetHBaseHomeFolder); + unarchive(o, targetHBaseHomeFolder, 1); + LOGGER.info("Unarchive HBase {} done", version); + } + } catch (IOException e) { + throw new RuntimeException("Unable to download HBase"); + } + return targetHBaseHomeFolder.getAbsolutePath(); + } + + private static String generateHBaseDownloadUrl(String hbaseVersion) { + return "hbase/" + hbaseVersion + "/hbase-" + hbaseVersion + "-bin.tar.gz"; + } } diff --git a/zeppelin-test/src/test/java/org/apache/zeppelin/test/DownloadUtilsTest.java b/zeppelin-test/src/test/java/org/apache/zeppelin/test/DownloadUtilsTest.java index 6bed71683c..ffe357729f 100644 --- a/zeppelin-test/src/test/java/org/apache/zeppelin/test/DownloadUtilsTest.java +++ b/zeppelin-test/src/test/java/org/apache/zeppelin/test/DownloadUtilsTest.java @@ -76,5 +76,13 @@ class DownloadUtilsTest { assertTrue(sparkHomePath.toFile().exists()); assertTrue(sparkHomePath.toFile().isDirectory()); } - + + @Test + void downloadHBase() { + String hbaseHome = DownloadUtils.downloadHBase("2.14.8"); + Path hbaseHomePath = Paths.get(hbaseHome); + assertTrue(hbaseHomePath.toFile().exists()); + assertTrue(hbaseHomePath.toFile().isDirectory()); + } + }