/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch 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.elasticsearch.plugins; import org.apache.http.impl.client.HttpClients; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.Version; import org.elasticsearch.common.Base64; import org.elasticsearch.common.cli.CliTool; import org.elasticsearch.common.cli.CliTool.ExitStatus; import org.elasticsearch.common.cli.CliToolTestCase.CaptureOutputTerminal; import org.elasticsearch.common.hash.MessageDigests; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.node.internal.InternalSettingsPreparer; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.junit.annotations.Network; import org.elasticsearch.test.rest.client.http.HttpRequestBuilder; import org.elasticsearch.test.rest.client.http.HttpResponse; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.jboss.netty.handler.codec.http.DefaultHttpResponse; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpRequestDecoder; import org.jboss.netty.handler.codec.http.HttpResponseEncoder; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.jboss.netty.handler.ssl.SslContext; import org.jboss.netty.handler.ssl.SslHandler; import org.jboss.netty.handler.ssl.util.InsecureTrustManagerFactory; import org.jboss.netty.handler.ssl.util.SelfSignedCertificate; import org.junit.After; import org.junit.Before; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import java.io.BufferedWriter; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import static org.elasticsearch.common.cli.CliTool.ExitStatus.USAGE; import static org.elasticsearch.common.cli.CliToolTestCase.args; import static org.elasticsearch.common.io.FileSystemUtilsTests.assertFileContent; import static org.elasticsearch.common.settings.Settings.settingsBuilder; import static org.elasticsearch.plugins.PluginInfoTests.writeProperties; import static org.elasticsearch.test.ESIntegTestCase.Scope; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertDirectoryExists; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFileExists; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFileNotExists; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1; @ClusterScope(scope = Scope.TEST, numDataNodes = 0, transportClientRatio = 0.0) @LuceneTestCase.SuppressFileSystems("*") // TODO: clean up this test to allow extra files // TODO: jimfs is really broken here (throws wrong exception from detection method). // if its in your classpath, then do not use plugins!!!!!! public class PluginManagerIT extends ESIntegTestCase { private Environment environment; private CaptureOutputTerminal terminal = new CaptureOutputTerminal(); @Before public void setup() throws Exception { environment = buildInitialSettings(); System.setProperty("es.default.path.home", environment.settings().get("path.home")); Path binDir = environment.binFile(); if (!Files.exists(binDir)) { Files.createDirectories(binDir); } Path configDir = environment.configFile(); if (!Files.exists(configDir)) { Files.createDirectories(configDir); } } @After public void clearPathHome() { System.clearProperty("es.default.path.home"); } private void writeSha1(Path file, boolean corrupt) throws IOException { String sha1Hex = MessageDigests.toHexString(MessageDigests.sha1().digest(Files.readAllBytes(file))); try (BufferedWriter out = Files.newBufferedWriter(file.resolveSibling(file.getFileName() + ".sha1"), StandardCharsets.UTF_8)) { out.write(sha1Hex); if (corrupt) { out.write("bad"); } } } private void writeMd5(Path file, boolean corrupt) throws IOException { String md5Hex = MessageDigests.toHexString(MessageDigests.md5().digest(Files.readAllBytes(file))); try (BufferedWriter out = Files.newBufferedWriter(file.resolveSibling(file.getFileName() + ".md5"), StandardCharsets.UTF_8)) { out.write(md5Hex); if (corrupt) { out.write("bad"); } } } /** creates a plugin .zip and returns the url for testing */ private String createPlugin(final Path structure, String... properties) throws IOException { writeProperties(structure, properties); Path zip = createTempDir().resolve(structure.getFileName() + ".zip"); try (ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zip))) { Files.walkFileTree(structure, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { stream.putNextEntry(new ZipEntry(structure.relativize(file).toString())); Files.copy(file, stream); return FileVisitResult.CONTINUE; } }); } if (randomBoolean()) { writeSha1(zip, false); } else if (randomBoolean()) { writeMd5(zip, false); } return zip.toUri().toURL().toString(); } /** creates a plugin .zip and bad checksum file and returns the url for testing */ private String createPluginWithBadChecksum(final Path structure, String... properties) throws IOException { writeProperties(structure, properties); Path zip = createTempDir().resolve(structure.getFileName() + ".zip"); try (ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zip))) { Files.walkFileTree(structure, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { stream.putNextEntry(new ZipEntry(structure.relativize(file).toString())); Files.copy(file, stream); return FileVisitResult.CONTINUE; } }); } if (randomBoolean()) { writeSha1(zip, true); } else { writeMd5(zip, true); } return zip.toUri().toURL().toString(); } public void testThatPluginNameMustBeSupplied() throws IOException { Path pluginDir = createTempDir().resolve("fake-plugin"); String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", "fake-plugin", "version", "1.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "jvm", "true", "classname", "FakePlugin"); assertStatus("install", USAGE); } public void testLocalPluginInstallWithBinAndConfig() throws Exception { String pluginName = "fake-plugin"; Path pluginDir = createTempDir().resolve(pluginName); // create bin/tool and config/file Files.createDirectories(pluginDir.resolve("bin")); Files.createFile(pluginDir.resolve("bin").resolve("tool")); Files.createDirectories(pluginDir.resolve("config")); Files.createFile(pluginDir.resolve("config").resolve("file")); String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "1.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "jvm", "true", "classname", "FakePlugin"); Path binDir = environment.binFile(); Path pluginBinDir = binDir.resolve(pluginName); Path pluginConfigDir = environment.configFile().resolve(pluginName); assertStatusOk("install " + pluginUrl + " --verbose"); terminal.getTerminalOutput().clear(); assertStatusOk("list"); assertThat(terminal.getTerminalOutput(), hasItem(containsString(pluginName))); assertDirectoryExists(pluginBinDir); assertDirectoryExists(pluginConfigDir); Path toolFile = pluginBinDir.resolve("tool"); assertFileExists(toolFile); // check that the file is marked executable, without actually checking that we can execute it. PosixFileAttributeView view = Files.getFileAttributeView(toolFile, PosixFileAttributeView.class); // the view might be null, on e.g. windows, there is nothing to check there! if (view != null) { PosixFileAttributes attributes = view.readAttributes(); assertThat(attributes.permissions(), hasItem(PosixFilePermission.OWNER_EXECUTE)); assertThat(attributes.permissions(), hasItem(PosixFilePermission.OWNER_READ)); } } /** * Test for #7890 */ public void testLocalPluginInstallWithBinAndConfigInAlreadyExistingConfigDir_7890() throws Exception { String pluginName = "fake-plugin"; Path pluginDir = createTempDir().resolve(pluginName); // create config/test.txt with contents 'version1' Files.createDirectories(pluginDir.resolve("config")); Files.write(pluginDir.resolve("config").resolve("test.txt"), "version1".getBytes(StandardCharsets.UTF_8)); String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "1.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "jvm", "true", "classname", "FakePlugin"); Path pluginConfigDir = environment.configFile().resolve(pluginName); assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl)); /* First time, our plugin contains: - config/test.txt (version1) */ assertFileContent(pluginConfigDir, "test.txt", "version1"); // We now remove the plugin assertStatusOk("remove " + pluginName); // We should still have test.txt assertFileContent(pluginConfigDir, "test.txt", "version1"); // Installing a new plugin version /* Second time, our plugin contains: - config/test.txt (version2) - config/dir/testdir.txt (version1) - config/dir/subdir/testsubdir.txt (version1) */ Files.write(pluginDir.resolve("config").resolve("test.txt"), "version2".getBytes(StandardCharsets.UTF_8)); Files.createDirectories(pluginDir.resolve("config").resolve("dir").resolve("subdir")); Files.write(pluginDir.resolve("config").resolve("dir").resolve("testdir.txt"), "version1".getBytes(StandardCharsets.UTF_8)); Files.write(pluginDir.resolve("config").resolve("dir").resolve("subdir").resolve("testsubdir.txt"), "version1".getBytes(StandardCharsets.UTF_8)); pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "2.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "jvm", "true", "classname", "FakePlugin"); assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl)); assertFileContent(pluginConfigDir, "test.txt", "version1"); assertFileContent(pluginConfigDir, "test.txt.new", "version2"); assertFileContent(pluginConfigDir, "dir/testdir.txt", "version1"); assertFileContent(pluginConfigDir, "dir/subdir/testsubdir.txt", "version1"); // Removing assertStatusOk("remove " + pluginName); assertFileContent(pluginConfigDir, "test.txt", "version1"); assertFileContent(pluginConfigDir, "test.txt.new", "version2"); assertFileContent(pluginConfigDir, "dir/testdir.txt", "version1"); assertFileContent(pluginConfigDir, "dir/subdir/testsubdir.txt", "version1"); // Installing a new plugin version /* Third time, our plugin contains: - config/test.txt (version3) - config/test2.txt (version1) - config/dir/testdir.txt (version2) - config/dir/testdir2.txt (version1) - config/dir/subdir/testsubdir.txt (version2) */ Files.write(pluginDir.resolve("config").resolve("test.txt"), "version3".getBytes(StandardCharsets.UTF_8)); Files.write(pluginDir.resolve("config").resolve("test2.txt"), "version1".getBytes(StandardCharsets.UTF_8)); Files.write(pluginDir.resolve("config").resolve("dir").resolve("testdir.txt"), "version2".getBytes(StandardCharsets.UTF_8)); Files.write(pluginDir.resolve("config").resolve("dir").resolve("testdir2.txt"), "version1".getBytes(StandardCharsets.UTF_8)); Files.write(pluginDir.resolve("config").resolve("dir").resolve("subdir").resolve("testsubdir.txt"), "version2".getBytes(StandardCharsets.UTF_8)); pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "3.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "jvm", "true", "classname", "FakePlugin"); assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl)); assertFileContent(pluginConfigDir, "test.txt", "version1"); assertFileContent(pluginConfigDir, "test2.txt", "version1"); assertFileContent(pluginConfigDir, "test.txt.new", "version3"); assertFileContent(pluginConfigDir, "dir/testdir.txt", "version1"); assertFileContent(pluginConfigDir, "dir/testdir.txt.new", "version2"); assertFileContent(pluginConfigDir, "dir/testdir2.txt", "version1"); assertFileContent(pluginConfigDir, "dir/subdir/testsubdir.txt", "version1"); assertFileContent(pluginConfigDir, "dir/subdir/testsubdir.txt.new", "version2"); } // For #7152 public void testLocalPluginInstallWithBinOnly_7152() throws Exception { String pluginName = "fake-plugin"; Path pluginDir = createTempDir().resolve(pluginName); // create bin/tool Files.createDirectories(pluginDir.resolve("bin")); Files.createFile(pluginDir.resolve("bin").resolve("tool"));; String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", "fake-plugin", "version", "1.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "jvm", "true", "classname", "FakePlugin"); Path binDir = environment.binFile(); Path pluginBinDir = binDir.resolve(pluginName); assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginUrl)); assertThatPluginIsListed(pluginName); assertDirectoryExists(pluginBinDir); } public void testListInstalledEmpty() throws IOException { assertStatusOk("list"); assertThat(terminal.getTerminalOutput(), hasItem(containsString("No plugin detected"))); } public void testListInstalledEmptyWithExistingPluginDirectory() throws IOException { Files.createDirectory(environment.pluginsFile()); assertStatusOk("list"); assertThat(terminal.getTerminalOutput(), hasItem(containsString("No plugin detected"))); } public void testInstallPluginVerbose() throws IOException { String pluginName = "fake-plugin"; Path pluginDir = createTempDir().resolve(pluginName); String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "1.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "jvm", "true", "classname", "FakePlugin"); ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl + " --verbose")); assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK)); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Name: fake-plugin"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Description: fake desc"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Site: false"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Version: 1.0"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("JVM: true"))); assertThatPluginIsListed(pluginName); } public void testInstallPlugin() throws IOException { String pluginName = "fake-plugin"; Path pluginDir = createTempDir().resolve(pluginName); String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "1.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "jvm", "true", "classname", "FakePlugin"); ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl)); assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK)); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Name: fake-plugin")))); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Description:")))); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Site:")))); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Version:")))); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("JVM:")))); assertThatPluginIsListed(pluginName); } /** * @deprecated support for this is not going to stick around, seriously. */ @Deprecated public void testAlreadyInstalledNotIsolated() throws Exception { String pluginName = "fake-plugin"; Path pluginDir = createTempDir().resolve(pluginName); Files.createDirectories(pluginDir); // create a jar file in the plugin Path pluginJar = pluginDir.resolve("fake-plugin.jar"); try (ZipOutputStream out = new JarOutputStream(Files.newOutputStream(pluginJar, StandardOpenOption.CREATE))) { out.putNextEntry(new ZipEntry("foo.class")); out.closeEntry(); } String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "1.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "isolated", "false", "jvm", "true", "classname", "FakePlugin"); // install ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl)); assertEquals("unexpected exit status: output: " + terminal.getTerminalOutput(), ExitStatus.OK, status); // install again status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl)); List<String> output = terminal.getTerminalOutput(); assertEquals("unexpected exit status: output: " + output, ExitStatus.IO_ERROR, status); boolean foundExpectedMessage = false; for (String line : output) { foundExpectedMessage |= line.contains("already exists"); } assertTrue(foundExpectedMessage); } public void testInstallSitePluginVerbose() throws IOException { String pluginName = "fake-plugin"; Path pluginDir = createTempDir().resolve(pluginName); Files.createDirectories(pluginDir.resolve("_site")); Files.createFile(pluginDir.resolve("_site").resolve("somefile")); String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "1.0", "site", "true"); ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl + " --verbose")); assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK)); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Name: fake-plugin"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Description: fake desc"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Site: true"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Version: 1.0"))); assertThat(terminal.getTerminalOutput(), hasItem(containsString("JVM: false"))); assertThatPluginIsListed(pluginName); // We want to check that Plugin Manager moves content to _site assertFileExists(environment.pluginsFile().resolve(pluginName).resolve("_site")); } public void testInstallSitePlugin() throws IOException { String pluginName = "fake-plugin"; Path pluginDir = createTempDir().resolve(pluginName); Files.createDirectories(pluginDir.resolve("_site")); Files.createFile(pluginDir.resolve("_site").resolve("somefile")); String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "1.0", "site", "true"); ExitStatus status = new PluginManagerCliParser(terminal).execute(args("install " + pluginUrl)); assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(ExitStatus.OK)); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Name: fake-plugin")))); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Description:")))); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Site:")))); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("Version:")))); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("JVM:")))); assertThatPluginIsListed(pluginName); // We want to check that Plugin Manager moves content to _site assertFileExists(environment.pluginsFile().resolve(pluginName).resolve("_site")); } public void testInstallPluginWithBadChecksum() throws IOException { String pluginName = "fake-plugin"; Path pluginDir = createTempDir().resolve(pluginName); Files.createDirectories(pluginDir.resolve("_site")); Files.createFile(pluginDir.resolve("_site").resolve("somefile")); String pluginUrl = createPluginWithBadChecksum(pluginDir, "description", "fake desc", "version", "1.0", "site", "true"); assertStatus(String.format(Locale.ROOT, "install %s --verbose", pluginUrl), ExitStatus.IO_ERROR); assertThatPluginIsNotListed(pluginName); assertFileNotExists(environment.pluginsFile().resolve(pluginName).resolve("_site")); } private void singlePluginInstallAndRemove(String pluginDescriptor, String pluginName, String pluginCoordinates) throws IOException { logger.info("--> trying to download and install [{}]", pluginDescriptor); if (pluginCoordinates == null) { assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginDescriptor)); } else { assertStatusOk(String.format(Locale.ROOT, "install %s --verbose", pluginCoordinates)); } assertThatPluginIsListed(pluginName); terminal.getTerminalOutput().clear(); assertStatusOk("remove " + pluginDescriptor); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Removing " + pluginDescriptor))); // not listed anymore terminal.getTerminalOutput().clear(); assertStatusOk("list"); assertThat(terminal.getTerminalOutput(), not(hasItem(containsString(pluginName)))); } /** * We are ignoring by default these tests as they require to have an internet access * To activate the test, use -Dtests.network=true * We test regular form: username/reponame/version * It should find it in download.elasticsearch.org service */ @Network @AwaitsFix(bugUrl = "fails with jar hell failures - http://build-us-00.elastic.co/job/es_core_master_oracle_6/519/testReport/") public void testInstallPluginWithElasticsearchDownloadService() throws IOException { assumeTrue("download.elastic.co is accessible", isDownloadServiceWorking("download.elastic.co", 80, "/elasticsearch/ci-test.txt")); singlePluginInstallAndRemove("elasticsearch/elasticsearch-transport-thrift/2.4.0", "elasticsearch-transport-thrift", null); } /** * We are ignoring by default these tests as they require to have an internet access * To activate the test, use -Dtests.network=true * We test regular form: groupId/artifactId/version * It should find it in maven central service */ @Network @AwaitsFix(bugUrl = "fails with jar hell failures - http://build-us-00.elastic.co/job/es_core_master_oracle_6/519/testReport/") public void testInstallPluginWithMavenCentral() throws IOException { assumeTrue("search.maven.org is accessible", isDownloadServiceWorking("search.maven.org", 80, "/")); assumeTrue("repo1.maven.org is accessible", isDownloadServiceWorking("repo1.maven.org", 443, "/maven2/org/elasticsearch/elasticsearch-transport-thrift/2.4.0/elasticsearch-transport-thrift-2.4.0.pom")); singlePluginInstallAndRemove("org.elasticsearch/elasticsearch-transport-thrift/2.4.0", "elasticsearch-transport-thrift", null); } /** * We are ignoring by default these tests as they require to have an internet access * To activate the test, use -Dtests.network=true * We test site plugins from github: userName/repoName * It should find it on github */ @Network @AwaitsFix(bugUrl = "needs to be adapted to 2.0") public void testInstallPluginWithGithub() throws IOException { assumeTrue("github.com is accessible", isDownloadServiceWorking("github.com", 443, "/")); singlePluginInstallAndRemove("elasticsearch/kibana", "kibana", null); } private boolean isDownloadServiceWorking(String host, int port, String resource) { try { String protocol = port == 443 ? "https" : "http"; HttpResponse response = new HttpRequestBuilder(HttpClients.createDefault()).protocol(protocol).host(host).port(port).path(resource).execute(); if (response.getStatusCode() != 200) { logger.warn("[{}{}] download service is not working. Disabling current test.", host, resource); return false; } return true; } catch (Throwable t) { logger.warn("[{}{}] download service is not working. Disabling current test.", host, resource); } return false; } public void testRemovePlugin() throws Exception { String pluginName = "plugintest"; Path pluginDir = createTempDir().resolve(pluginName); String pluginUrl = createPlugin(pluginDir, "description", "fake desc", "name", pluginName, "version", "1.0.0", "elasticsearch.version", Version.CURRENT.toString(), "java.version", System.getProperty("java.specification.version"), "jvm", "true", "classname", "FakePlugin"); // We want to remove plugin with plugin short name singlePluginInstallAndRemove("plugintest", "plugintest", pluginUrl); // We want to remove plugin with groupid/artifactid/version form singlePluginInstallAndRemove("groupid/plugintest/1.0.0", "plugintest", pluginUrl); // We want to remove plugin with groupid/artifactid form singlePluginInstallAndRemove("groupid/plugintest", "plugintest", pluginUrl); } public void testRemovePlugin_NullName_ThrowsException() throws IOException { assertStatus("remove ", USAGE); } public void testRemovePluginWithURLForm() throws Exception { assertStatus("remove file://whatever", USAGE); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Illegal plugin name"))); } public void testForbiddenPluginNames() throws IOException { assertStatus("remove elasticsearch", USAGE); assertStatus("remove elasticsearch.bat", USAGE); assertStatus("remove elasticsearch.in.sh", USAGE); assertStatus("remove plugin", USAGE); assertStatus("remove plugin.bat", USAGE); assertStatus("remove service.bat", USAGE); assertStatus("remove ELASTICSEARCH", USAGE); assertStatus("remove ELASTICSEARCH.IN.SH", USAGE); } public void testOfficialPluginName_ThrowsException() throws IOException { PluginManager.checkForOfficialPlugins("analysis-icu"); PluginManager.checkForOfficialPlugins("analysis-kuromoji"); PluginManager.checkForOfficialPlugins("analysis-phonetic"); PluginManager.checkForOfficialPlugins("analysis-smartcn"); PluginManager.checkForOfficialPlugins("analysis-stempel"); PluginManager.checkForOfficialPlugins("cloud-aws"); PluginManager.checkForOfficialPlugins("cloud-azure"); PluginManager.checkForOfficialPlugins("cloud-gce"); PluginManager.checkForOfficialPlugins("delete-by-query"); PluginManager.checkForOfficialPlugins("lang-javascript"); PluginManager.checkForOfficialPlugins("lang-python"); PluginManager.checkForOfficialPlugins("mapper-attachments"); PluginManager.checkForOfficialPlugins("mapper-murmur3"); PluginManager.checkForOfficialPlugins("mapper-size"); PluginManager.checkForOfficialPlugins("discovery-multicast"); try { PluginManager.checkForOfficialPlugins("elasticsearch-mapper-attachment"); fail("elasticsearch-mapper-attachment should not be allowed"); } catch (IllegalArgumentException e) { // We expect that error } } public void testThatBasicAuthIsRejectedOnHttp() throws Exception { assertStatus(String.format(Locale.ROOT, "install http://user:pass@localhost:12345/foo.zip --verbose"), CliTool.ExitStatus.IO_ERROR); assertThat(terminal.getTerminalOutput(), hasItem(containsString("Basic auth is only supported for HTTPS!"))); } public void testThatBasicAuthIsSupportedWithHttps() throws Exception { assumeTrue("test requires security manager to be disabled", System.getSecurityManager() == null); SSLSocketFactory defaultSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); ServerBootstrap serverBootstrap = new ServerBootstrap(new NioServerSocketChannelFactory()); SelfSignedCertificate ssc = new SelfSignedCertificate("localhost"); try { // Create a trust manager that does not validate certificate chains: SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, InsecureTrustManagerFactory.INSTANCE.getTrustManagers(), null); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); final List<HttpRequest> requests = new ArrayList<>(); final SslContext sslContext = SslContext.newServerContext(ssc.certificate(), ssc.privateKey()); serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline( new SslHandler(sslContext.newEngine()), new HttpRequestDecoder(), new HttpResponseEncoder(), new LoggingServerHandler(requests) ); } }); Channel channel = serverBootstrap.bind(new InetSocketAddress(InetAddress.getByName("localhost"), 0)); int port = ((InetSocketAddress) channel.getLocalAddress()).getPort(); // IO_ERROR because there is no real file delivered... assertStatus(String.format(Locale.ROOT, "install https://user:pass@localhost:%s/foo.zip --verbose --timeout 1s", port), ExitStatus.IO_ERROR); // ensure that we did not try any other data source like download.elastic.co, in case we specified our own local URL assertThat(terminal.getTerminalOutput(), not(hasItem(containsString("download.elastic.co")))); assertThat(requests, hasSize(1)); String msg = String.format(Locale.ROOT, "Request header did not contain Authorization header, terminal output was: %s", terminal.getTerminalOutput()); assertThat(msg, requests.get(0).headers().contains("Authorization"), is(true)); assertThat(msg, requests.get(0).headers().get("Authorization"), is("Basic " + Base64.encodeBytes("user:pass".getBytes(StandardCharsets.UTF_8)))); } finally { HttpsURLConnection.setDefaultSSLSocketFactory(defaultSocketFactory); serverBootstrap.releaseExternalResources(); ssc.delete(); } } private static class LoggingServerHandler extends SimpleChannelUpstreamHandler { private List<HttpRequest> requests; public LoggingServerHandler(List<HttpRequest> requests) { this.requests = requests; } @Override public void messageReceived(final ChannelHandlerContext ctx, final MessageEvent e) throws InterruptedException { final HttpRequest request = (HttpRequest) e.getMessage(); requests.add(request); final org.jboss.netty.handler.codec.http.HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST); ctx.getChannel().write(response); } } private Environment buildInitialSettings() throws IOException { Settings settings = settingsBuilder() .put("http.enabled", true) .put("path.home", createTempDir()).build(); return InternalSettingsPreparer.prepareEnvironment(settings, null); } private void assertStatusOk(String command) { assertStatus(command, ExitStatus.OK); } private void assertStatus(String command, ExitStatus exitStatus) { ExitStatus status = new PluginManagerCliParser(terminal).execute(args(command)); assertThat("Terminal output was: " + terminal.getTerminalOutput(), status, is(exitStatus)); } private void assertThatPluginIsListed(String pluginName) { terminal.getTerminalOutput().clear(); assertStatusOk("list"); String message = String.format(Locale.ROOT, "Terminal output was: %s", terminal.getTerminalOutput()); assertThat(message, terminal.getTerminalOutput(), hasItem(containsString(pluginName))); } private void assertThatPluginIsNotListed(String pluginName) { terminal.getTerminalOutput().clear(); assertStatusOk("list"); String message = String.format(Locale.ROOT, "Terminal output was: %s", terminal.getTerminalOutput()); assertFalse(message, terminal.getTerminalOutput().contains(pluginName)); } }