Skip to content

Commit

Permalink
Merge remote-tracking branch 'elastic/master' into geosql
Browse files Browse the repository at this point in the history
  • Loading branch information
imotov committed Apr 1, 2019
2 parents 886830f + 1bba887 commit b4231c9
Show file tree
Hide file tree
Showing 179 changed files with 3,940 additions and 2,149 deletions.
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ plugins {
id 'com.gradle.build-scan' version '2.0.2'
id 'base'
}
if (properties.get("org.elasticsearch.acceptScanTOS", "false") == "true") {
if (Boolean.valueOf(project.findProperty('org.elasticsearch.acceptScanTOS') ?: "false")) {
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
Expand Down Expand Up @@ -162,8 +162,8 @@ task verifyVersions {
* after the backport of the backcompat code is complete.
*/

boolean bwc_tests_enabled = false
final String bwc_tests_disabled_issue = "https://github.com/elastic/elasticsearch/pull/40319" /* place a PR link here when committing bwc changes */
boolean bwc_tests_enabled = true
final String bwc_tests_disabled_issue = "" /* place a PR link here when committing bwc changes */
if (bwc_tests_enabled == false) {
if (bwc_tests_disabled_issue.isEmpty()) {
throw new GradleException("bwc_tests_disabled_issue must be set when bwc_tests_enabled == false")
Expand Down
6 changes: 6 additions & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ if (project != rootProject) {
if (isLuceneSnapshot) {
systemProperty 'test.lucene-snapshot-revision', isLuceneSnapshot[0][1]
}
String defaultParallel = System.getProperty('tests.jvms', project.rootProject.ext.defaultParallel)
if (defaultParallel == "auto") {
defaultParallel = Math.max(Runtime.getRuntime().availableProcessors(), 4)
}
maxParallelForks defaultParallel as Integer
}
check.dependsOn(integTest)

Expand All @@ -240,6 +245,7 @@ if (project != rootProject) {

forbiddenPatterns {
exclude '**/*.wav'
exclude '**/*.p12'
// the file that actually defines nocommit
exclude '**/ForbiddenPatternsTask.java'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,16 +361,8 @@ class BuildPlugin implements Plugin<Project> {
compilerJavaHome = findJavaHome(compilerJavaProperty)
}
if (compilerJavaHome == null) {
if (System.getProperty("idea.active") != null || System.getProperty("eclipse.launcher") != null) {
// IntelliJ does not set JAVA_HOME, so we use the JDK that Gradle was run with
return Jvm.current().javaHome
} else {
throw new GradleException(
"JAVA_HOME must be set to build Elasticsearch. " +
"Note that if the variable was just set you might have to run `./gradlew --stop` for " +
"it to be picked up. See https://github.com/elastic/elasticsearch/issues/31399 details."
)
}
// if JAVA_HOME does not set,so we use the JDK that Gradle was run with.
return Jvm.current().javaHome
}
return compilerJavaHome
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
* 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.gradle.http;

import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
* A utility to wait for a specific HTTP resource to be available, optionally with customized TLS trusted CAs.
* This is logically similar to using the Ant Get task to retrieve a resource, but with the difference that it can
* access resources that do not use the JRE's default trusted CAs.
*/
public class WaitForHttpResource {

private static final Logger logger = Logging.getLogger(WaitForHttpResource.class);

private Set<Integer> validResponseCodes = Collections.singleton(200);
private URL url;
private Set<File> certificateAuthorities;
private File trustStoreFile;
private String trustStorePassword;
private String username;
private String password;

public WaitForHttpResource(String protocol, String host, int numberOfNodes) throws MalformedURLException {
this(new URL(protocol + "://" + host + "/_cluster/health?wait_for_nodes=>=" + numberOfNodes + "&wait_for_status=yellow"));
}

public WaitForHttpResource(URL url) {
this.url = url;
}

public void setValidResponseCodes(int... validResponseCodes) {
this.validResponseCodes = new HashSet<>(validResponseCodes.length);
for (int rc : validResponseCodes) {
this.validResponseCodes.add(rc);
}
}

public void setCertificateAuthorities(File... certificateAuthorities) {
this.certificateAuthorities = new HashSet<>(Arrays.asList(certificateAuthorities));
}

public void setTrustStoreFile(File trustStoreFile) {
this.trustStoreFile = trustStoreFile;
}

public void setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
}

public void setUsername(String username) {
this.username = username;
}

public void setPassword(String password) {
this.password = password;
}

public boolean wait(int durationInMs) throws GeneralSecurityException, InterruptedException, IOException {
final long waitUntil = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(durationInMs);
final long sleep = Long.max(durationInMs / 10, 100);

final SSLContext ssl;
final KeyStore trustStore = buildTrustStore();
if (trustStore != null) {
ssl = createSslContext(trustStore);
} else {
ssl = null;
}
IOException failure = null;
for (; ; ) {
try {
checkResource(ssl);
return true;
} catch (IOException e) {
logger.debug("Failed to access resource [{}]", url, e);
failure = e;
}
if (System.nanoTime() < waitUntil) {
Thread.sleep(sleep);
} else {
logger.error("Failed to access url [{}]", url, failure);
return false;
}
}
}

protected void checkResource(SSLContext ssl) throws IOException {
try {
final HttpURLConnection connection = buildConnection(ssl);
connection.connect();
final Integer response = connection.getResponseCode();
if (validResponseCodes.contains(response)) {
logger.info("Got successful response [{}] from URL [{}]", response, url);
return;
} else {
throw new IOException(response + " " + connection.getResponseMessage());
}
} catch (IOException e) {
throw e;
}
}

HttpURLConnection buildConnection(SSLContext ssl) throws IOException {
final HttpURLConnection connection = (HttpURLConnection) this.url.openConnection();
configureSslContext(connection, ssl);
configureBasicAuth(connection);
connection.setRequestMethod("GET");
return connection;
}

private void configureSslContext(HttpURLConnection connection, SSLContext ssl) {
if (ssl != null) {
if (connection instanceof HttpsURLConnection) {
((HttpsURLConnection) connection).setSSLSocketFactory(ssl.getSocketFactory());
} else {
throw new IllegalStateException("SSL trust has been configured, but [" + url + "] is not a 'https' URL");
}
}
}

private void configureBasicAuth(HttpURLConnection connection) {
if (username != null) {
if (password == null) {
throw new IllegalStateException("Basic Auth user [" + username
+ "] has been set, but no password has been configured");
}
connection.setRequestProperty("Authorization",
"Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8)));
}
}

KeyStore buildTrustStore() throws GeneralSecurityException, IOException {
if (this.certificateAuthorities != null) {
if (trustStoreFile != null) {
throw new IllegalStateException("Cannot specify both truststore and CAs");
}
return buildTrustStoreFromCA();
} else if (trustStoreFile != null) {
return buildTrustStoreFromFile();
} else {
return null;
}
}

private KeyStore buildTrustStoreFromFile() throws GeneralSecurityException, IOException {
KeyStore keyStore = KeyStore.getInstance(trustStoreFile.getName().endsWith(".jks") ? "JKS" : "PKCS12");
try (InputStream input = new FileInputStream(trustStoreFile)) {
keyStore.load(input, trustStorePassword == null ? null : trustStorePassword.toCharArray());
}
return keyStore;
}

private KeyStore buildTrustStoreFromCA() throws GeneralSecurityException, IOException {
final KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(null, null);
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
int counter = 0;
for (File ca : certificateAuthorities) {
try (InputStream input = new FileInputStream(ca)) {
for (Certificate certificate : certFactory.generateCertificates(input)) {
store.setCertificateEntry("cert-" + counter, certificate);
counter++;
}
}
}
return store;
}

private SSLContext createSslContext(KeyStore trustStore) throws GeneralSecurityException {
checkForTrustEntry(trustStore);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(new KeyManager[0], tmf.getTrustManagers(), new SecureRandom());
return sslContext;
}

private void checkForTrustEntry(KeyStore trustStore) throws KeyStoreException {
Enumeration<String> enumeration = trustStore.aliases();
while (enumeration.hasMoreElements()) {
if (trustStore.isCertificateEntry(enumeration.nextElement())) {
// found trusted cert entry
return;
}
}
throw new IllegalStateException("Trust-store does not contain any trusted certificate entries");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.gradle.api.Task;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.TaskContainer;

import java.lang.reflect.InvocationTargetException;
Expand Down Expand Up @@ -72,6 +71,8 @@ public void apply(Project project) {
pullFixture.setEnabled(false);
return;
}
preProcessFixture.onlyIf(spec -> buildFixture.getEnabled());
postProcessFixture.onlyIf(spec -> buildFixture.getEnabled());

project.apply(spec -> spec.plugin(BasePlugin.class));
project.apply(spec -> spec.plugin(DockerComposePlugin.class));
Expand All @@ -95,20 +96,26 @@ public void apply(Project project) {
(name, port) -> postProcessFixture.getExtensions()
.getByType(ExtraPropertiesExtension.class).set(name, port)
);
extension.fixtures.add(project);
}

extension.fixtures.all(fixtureProject -> project.evaluationDependsOn(fixtureProject.getPath()));
extension.fixtures
.matching(fixtureProject -> fixtureProject.equals(project) == false)
.all(fixtureProject -> project.evaluationDependsOn(fixtureProject.getPath()));

conditionTaskByType(tasks, extension, getTaskClass("com.carrotsearch.gradle.junit4.RandomizedTestingTask"));
conditionTaskByType(tasks, extension, getTaskClass("org.elasticsearch.gradle.test.RestIntegTestTask"));
conditionTaskByType(tasks, extension, TestingConventionsTasks.class);
conditionTaskByType(tasks, extension, ComposeUp.class);

if (dockerComposeSupported(project) == false) {
project.getLogger().warn(
"Tests for {} require docker-compose at /usr/local/bin/docker-compose or /usr/bin/docker-compose " +
"but none could be found so these will be skipped", project.getPath()
);
disableTaskByType(tasks, getTaskClass("com.carrotsearch.gradle.junit4.RandomizedTestingTask"));
// conventions are not honored when the tasks are disabled
disableTaskByType(tasks, TestingConventionsTasks.class);
disableTaskByType(tasks, ComposeUp.class);
return;
}

tasks.withType(getTaskClass("com.carrotsearch.gradle.junit4.RandomizedTestingTask"), task ->
extension.fixtures.all(fixtureProject -> {
fixtureProject.getTasks().matching(it -> it.getName().equals("buildFixture")).all(buildFixture ->
Expand All @@ -122,11 +129,22 @@ public void apply(Project project) {
fixtureProject,
(name, port) -> setSystemProperty(task, name, port)
);
task.dependsOn(fixtureProject.getTasks().getByName("postProcessFixture"));
})
);

}

private void conditionTaskByType(TaskContainer tasks, TestFixtureExtension extension, Class<? extends DefaultTask> taskClass) {
tasks.withType(
taskClass,
task -> task.onlyIf(spec ->
extension.fixtures.stream()
.anyMatch(fixtureProject -> fixtureProject.getTasks().getByName("buildFixture").getEnabled() == false) == false
)
);
}

private void configureServiceInfoForTask(Task task, Project fixtureProject, BiConsumer<String, Integer> consumer) {
// Configure ports for the tests as system properties.
// We only know these at execution time so we need to do it in doFirst
Expand Down Expand Up @@ -155,7 +173,6 @@ private void configureServiceInfoForTask(Task task, Project fixtureProject, BiCo
);
}

@Input
public boolean dockerComposeSupported(Project project) {
if (OS.current().equals(OS.WINDOWS)) {
return false;
Expand Down
Loading

0 comments on commit b4231c9

Please sign in to comment.