Skip to content

Commit

Permalink
Merge branch 'master' into compat_rest_api
Browse files Browse the repository at this point in the history
  • Loading branch information
pgomulka committed Apr 23, 2020
2 parents 3c517b8 + 85a160f commit 144dd89
Show file tree
Hide file tree
Showing 416 changed files with 8,792 additions and 5,206 deletions.
1 change: 1 addition & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ dependencies {
compile 'de.thetaphi:forbiddenapis:2.7'
compile 'com.avast.gradle:gradle-docker-compose-plugin:0.8.12'
compile 'org.apache.maven:maven-model:3.6.2'
compile 'com.networknt:json-schema-validator:1.0.36'
compileOnly "com.puppycrawl.tools:checkstyle:${props.getProperty('checkstyle')}"
testCompile "com.puppycrawl.tools:checkstyle:${props.getProperty('checkstyle')}"
testCompile "junit:junit:${props.getProperty('junit')}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,8 @@ class BuildPlugin implements Plugin<Project> {
// we use 'temp' relative to CWD since this is per JVM and tests are forbidden from writing to CWD
nonInputProperties.systemProperty('java.io.tmpdir', test.workingDir.toPath().resolve('temp'))

nonInputProperties.systemProperty('compiler.java', "${-> BuildParams.compilerJavaVersion.majorVersion}")
nonInputProperties.systemProperty('compiler.java', BuildParams.compilerJavaVersion.majorVersion)
nonInputProperties.systemProperty('runtime.java', BuildParams.runtimeJavaVersion.majorVersion)

// TODO: remove setting logging level via system property
test.systemProperty 'tests.logger.level', 'WARN'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* 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.precommit;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaException;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SchemaValidatorsConfig;
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;
import org.gradle.api.DefaultTask;
import org.gradle.api.UncheckedIOException;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import org.gradle.work.ChangeType;
import org.gradle.work.Incremental;
import org.gradle.work.InputChanges;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.StreamSupport;

/**
* Incremental task to validate a set of JSON files against against a schema.
*/
public class ValidateJsonAgainstSchemaTask extends DefaultTask {

private final ObjectMapper mapper = new ObjectMapper();
private Set<String> ignore = new HashSet<>();
private File jsonSchema;
private FileCollection inputFiles;

@Incremental
@InputFiles
public FileCollection getInputFiles() {
return inputFiles;
}

public void setInputFiles(FileCollection inputFiles) {
this.inputFiles = inputFiles;
}

@InputFile
public File getJsonSchema() {
return jsonSchema;
}

public void setJsonSchema(File jsonSchema) {
this.jsonSchema = jsonSchema;
}

@Input
@Optional
public Set<String> getIgnore() {
return ignore;
}

public void ignore(String... ignore) {
this.ignore.addAll(Arrays.asList(ignore));
}

@OutputFile
public File getReport() {
return new File(getProject().getBuildDir(), "reports/validateJson.txt");
}

@TaskAction
public void validate(InputChanges inputChanges) throws IOException {
File jsonSchemaOnDisk = getJsonSchema();
getLogger().debug("JSON schema : [{}]", jsonSchemaOnDisk.getAbsolutePath());
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
JsonSchema jsonSchema = factory.getSchema(mapper.readTree(jsonSchemaOnDisk), config);
Map<File, Set<String>> errors = new LinkedHashMap<>();
// incrementally evaluate input files
StreamSupport.stream(inputChanges.getFileChanges(getInputFiles()).spliterator(), false)
.filter(f -> f.getChangeType() != ChangeType.REMOVED)
.forEach(fileChange -> {
File file = fileChange.getFile();
if (ignore.contains(file.getName())) {
getLogger().debug("Ignoring file [{}] due to configuration", file.getName());
} else if (file.isDirectory() == false) {
// validate all files and hold on to errors for a complete report if there are failures
getLogger().debug("Validating JSON [{}]", file.getName());
try {
Set<ValidationMessage> validationMessages = jsonSchema.validate(mapper.readTree(file));
maybeLogAndCollectError(validationMessages, errors, file);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});
if (errors.isEmpty()) {
Files.writeString(getReport().toPath(), "Success! No validation errors found.", StandardOpenOption.CREATE);
} else {
try (PrintWriter printWriter = new PrintWriter(getReport())) {
printWriter.printf("Schema: %s%n", jsonSchemaOnDisk);
printWriter.println("----------Validation Errors-----------");
errors.values().stream().flatMap(Collection::stream).forEach(printWriter::println);
}
StringBuilder sb = new StringBuilder();
sb.append("Error validating JSON. See the report at: ");
sb.append(getReport().toURI().toASCIIString());
sb.append(System.lineSeparator());
sb.append(
String.format("JSON validation failed: %d files contained %d violations", errors.keySet().size(), errors.values().size())
);
throw new JsonSchemaException(sb.toString());
}
}

private void maybeLogAndCollectError(Set<ValidationMessage> messages, Map<File, Set<String>> errors, File file) {
for (ValidationMessage message : messages) {
getLogger().error("[validate JSON][ERROR][{}][{}]", file.getName(), message.toString());
errors.computeIfAbsent(file, k -> new LinkedHashSet<>())
.add(String.format("%s: %s", file.getAbsolutePath(), message.toString()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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.precommit;

import org.elasticsearch.gradle.util.Util;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.provider.Provider;

import java.io.File;

public class ValidateRestSpecPlugin implements Plugin<Project> {
private static final String DOUBLE_STAR = "**"; // checkstyle thinks these are javadocs :(

@Override
public void apply(Project project) {
Provider<ValidateJsonAgainstSchemaTask> validateRestSpecTask = project.getTasks()
.register("validateRestSpec", ValidateJsonAgainstSchemaTask.class, task -> {
task.setInputFiles(Util.getJavaTestAndMainSourceResources(project, filter -> {
filter.include(DOUBLE_STAR + "/rest-api-spec/api/" + DOUBLE_STAR + "/*.json");
filter.exclude(DOUBLE_STAR + "/_common.json");
}));
task.setJsonSchema(new File(project.getRootDir(), "rest-api-spec/src/main/resources/schema.json"));
});
project.getTasks().named("precommit").configure(t -> t.dependsOn(validateRestSpecTask));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1049,44 +1049,49 @@ private void syncWithLinks(Path sourceRoot, Path destinationRoot) {

private void createConfiguration() {
String nodeName = nameCustomization.apply(safeName(name));
Map<String, String> baseConfig = new HashMap<>(defaultConfig);
if (nodeName != null) {
defaultConfig.put("node.name", nodeName);
}
defaultConfig.put("path.repo", confPathRepo.toAbsolutePath().toString());
defaultConfig.put("path.data", confPathData.toAbsolutePath().toString());
defaultConfig.put("path.logs", confPathLogs.toAbsolutePath().toString());
defaultConfig.put("path.shared_data", workingDir.resolve("sharedData").toString());
defaultConfig.put("node.attr.testattr", "test");
defaultConfig.put("node.portsfile", "true");
defaultConfig.put("http.port", httpPort);
baseConfig.put("node.name", nodeName);
}
baseConfig.put("path.repo", confPathRepo.toAbsolutePath().toString());
baseConfig.put("path.data", confPathData.toAbsolutePath().toString());
baseConfig.put("path.logs", confPathLogs.toAbsolutePath().toString());
baseConfig.put("path.shared_data", workingDir.resolve("sharedData").toString());
baseConfig.put("node.attr.testattr", "test");
baseConfig.put("node.portsfile", "true");
baseConfig.put("http.port", httpPort);
if (getVersion().onOrAfter(Version.fromString("6.7.0"))) {
defaultConfig.put("transport.port", transportPort);
baseConfig.put("transport.port", transportPort);
} else {
defaultConfig.put("transport.tcp.port", transportPort);
baseConfig.put("transport.tcp.port", transportPort);
}
// Default the watermarks to absurdly low to prevent the tests from failing on nodes without enough disk space
defaultConfig.put("cluster.routing.allocation.disk.watermark.low", "1b");
defaultConfig.put("cluster.routing.allocation.disk.watermark.high", "1b");
baseConfig.put("cluster.routing.allocation.disk.watermark.low", "1b");
baseConfig.put("cluster.routing.allocation.disk.watermark.high", "1b");
// increase script compilation limit since tests can rapid-fire script compilations
defaultConfig.put("script.max_compilations_rate", "2048/1m");
if (getVersion().getMajor() >= 8) {
baseConfig.put("script.disable_max_compilations_rate", "true");
} else {
baseConfig.put("script.max_compilations_rate", "2048/1m");
}
if (getVersion().getMajor() >= 6) {
defaultConfig.put("cluster.routing.allocation.disk.watermark.flood_stage", "1b");
baseConfig.put("cluster.routing.allocation.disk.watermark.flood_stage", "1b");
}
// Temporarily disable the real memory usage circuit breaker. It depends on real memory usage which we have no full control
// over and the REST client will not retry on circuit breaking exceptions yet (see #31986 for details). Once the REST client
// can retry on circuit breaking exceptions, we can revert again to the default configuration.
if (getVersion().getMajor() >= 7) {
defaultConfig.put("indices.breaker.total.use_real_memory", "false");
baseConfig.put("indices.breaker.total.use_real_memory", "false");
}
// Don't wait for state, just start up quickly. This will also allow new and old nodes in the BWC case to become the master
defaultConfig.put("discovery.initial_state_timeout", "0s");
baseConfig.put("discovery.initial_state_timeout", "0s");

if (getVersion().getMajor() >= 8) {
defaultConfig.put("cluster.service.slow_task_logging_threshold", "5s");
defaultConfig.put("cluster.service.slow_master_task_logging_threshold", "5s");
baseConfig.put("cluster.service.slow_task_logging_threshold", "5s");
baseConfig.put("cluster.service.slow_master_task_logging_threshold", "5s");
}

HashSet<String> overriden = new HashSet<>(defaultConfig.keySet());
HashSet<String> overriden = new HashSet<>(baseConfig.keySet());
overriden.retainAll(settings.keySet());
overriden.removeAll(OVERRIDABLE_SETTINGS);
if (overriden.isEmpty() == false) {
Expand All @@ -1095,12 +1100,12 @@ private void createConfiguration() {
);
}
// Make sure no duplicate config keys
settings.keySet().stream().filter(OVERRIDABLE_SETTINGS::contains).forEach(defaultConfig::remove);
settings.keySet().stream().filter(OVERRIDABLE_SETTINGS::contains).forEach(baseConfig::remove);

try {
Files.write(
configFile,
Stream.concat(settings.entrySet().stream(), defaultConfig.entrySet().stream())
Stream.concat(settings.entrySet().stream(), baseConfig.entrySet().stream())
.map(entry -> entry.getKey() + ": " + entry.getValue())
.collect(Collectors.joining("\n"))
.getBytes(StandardCharsets.UTF_8),
Expand Down
69 changes: 69 additions & 0 deletions buildSrc/src/main/java/org/elasticsearch/gradle/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,23 @@
package org.elasticsearch.gradle.util;

import org.elasticsearch.gradle.info.GlobalBuildInfoPlugin;
import org.gradle.api.Action;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.file.FileTree;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.util.PatternFilterable;

import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import java.util.Optional;

public class Util {

Expand Down Expand Up @@ -75,4 +83,65 @@ public static URI getBuildSrcCodeSource() {
throw new GradleException("Error determining build tools JAR location", e);
}
}

/**
* @param project The project to look for resources.
* @param filter Optional filter function to filter the returned resources
* @return Returns the {@link FileTree} for main resources from Java projects. Returns null if no files exist.
*/
@Nullable
public static FileTree getJavaMainSourceResources(Project project, Action<? super PatternFilterable> filter) {
final Optional<FileTree> mainFileTree = getJavaMainSourceSet(project).map(SourceSet::getResources).map(FileTree::getAsFileTree);
return mainFileTree.map(files -> files.matching(filter)).orElse(null);
}

/**
* @param project The project to look for resources.
* @param filter Optional filter function to filter the returned resources
* @return Returns the {@link FileTree} for test resources from Java projects. Returns null if no files exist.
*/
@Nullable
public static FileTree getJavaTestSourceResources(Project project, Action<? super PatternFilterable> filter) {
final Optional<FileTree> testFileTree = getJavaTestSourceSet(project).map(SourceSet::getResources).map(FileTree::getAsFileTree);
return testFileTree.map(files -> files.matching(filter)).orElse(null);
}

/**
* @param project The project to look for resources.
* @param filter Optional filter function to filter the returned resources
* @return Returns the combined {@link FileTree} for test and main resources from Java projects. Returns null if no files exist.
*/
@Nullable
public static FileTree getJavaTestAndMainSourceResources(Project project, Action<? super PatternFilterable> filter) {
final Optional<FileTree> testFileTree = getJavaTestSourceSet(project).map(SourceSet::getResources).map(FileTree::getAsFileTree);
final Optional<FileTree> mainFileTree = getJavaMainSourceSet(project).map(SourceSet::getResources).map(FileTree::getAsFileTree);
if (testFileTree.isPresent() && mainFileTree.isPresent()) {
return testFileTree.get().plus(mainFileTree.get()).matching(filter);
} else if (mainFileTree.isPresent()) {
return mainFileTree.get().matching(filter);
} else if (testFileTree.isPresent()) {
return testFileTree.get().matching(filter);
}
return null;
}

/**
* @param project The project to look for test Java resources.
* @return An Optional that contains the Java test SourceSet if it exists.
*/
public static Optional<SourceSet> getJavaTestSourceSet(Project project) {
return project.getConvention().findPlugin(JavaPluginConvention.class) == null
? Optional.empty()
: Optional.ofNullable(GradleUtils.getJavaSourceSets(project).findByName(SourceSet.TEST_SOURCE_SET_NAME));
}

/**
* @param project The project to look for main Java resources.
* @return An Optional that contains the Java main SourceSet if it exists.
*/
public static Optional<SourceSet> getJavaMainSourceSet(Project project) {
return project.getConvention().findPlugin(JavaPluginConvention.class) == null
? Optional.empty()
: Optional.ofNullable(GradleUtils.getJavaSourceSets(project).findByName(SourceSet.MAIN_SOURCE_SET_NAME));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
implementation-class=org.elasticsearch.gradle.precommit.ValidateRestSpecPlugin
Loading

0 comments on commit 144dd89

Please sign in to comment.