Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read the full REST spec from a common directory #51890

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import org.elasticsearch.gradle.BwcVersions
import org.elasticsearch.gradle.Version
import org.elasticsearch.gradle.VersionProperties
import org.elasticsearch.gradle.plugin.PluginBuildPlugin
import org.elasticsearch.gradle.test.RestIntegTestTask
import org.elasticsearch.gradle.test.RestTestPlugin
import org.gradle.plugins.ide.eclipse.model.SourceFolder
import org.gradle.util.DistributionLocator
import org.gradle.util.GradleVersion
Expand All @@ -41,6 +43,7 @@ apply plugin: 'nebula.info-scm'
apply from: 'gradle/build-scan.gradle'
apply from: 'gradle/build-complete.gradle'
apply from: 'gradle/runtime-jdk-provision.gradle'
apply from: 'gradle/copy-rest-api-spec-to-global.gradle'

// common maven publishing configuration
allprojects {
Expand Down Expand Up @@ -138,6 +141,15 @@ subprojects {
precommit.dependsOn 'spotlessJavaCheck'
}
}
//copy the rest api spec to a global location and add to the test classpath
project.tasks.withType(RestIntegTestTask) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic should move to the existing RestTestPlugin rather than shove it in a withType() block. This has the side effect of creating this dependency multiple times, once for each instance of that task type. This is benign, but also unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RestTestPlugin creates a integTest task of type RestIntegTestTask task. But so does the PluginBuildPlugin (esplugin) (not sure why it explicitly creates it instead of applying the RestTestPlugin).

Additionally, many of the projects create custom tasks of type RestIntegTestTask. The requirement for the RestTestRunnerTask which is created by the RestIntegTestTask.

Actually, I think this should actually be withType(RestTestRunnerTask) since a couple project create that directly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We aren't configuring the task, we are adding a testRuntime dependency to the project. All test tasks inherit that classpath so there is no need to couple this to the existence of a RestIntegTestTask.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on your comment below

We should put the reliance on rest specs in an explicit plugin so that if you are using YAML tests and need those specs, you must apply the plugin.

I can move this to the RestTestPlugin

configurations {
copyRestApiSpecGlobal
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The consuming project doesn't need to create this configuration. When we mention this configuration below in the dependency notation, we are referring to the configuration on the root project.

}
dependencies {
testRuntime project(path: ':', configuration: 'copyRestApiSpecGlobal')
}
}
}

/* Introspect all versions of ES that may be tested against for backwards
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.elasticsearch.gradle.test

import org.elasticsearch.gradle.VersionProperties
import org.elasticsearch.gradle.info.BuildParams
import org.elasticsearch.gradle.testclusters.RestTestRunnerTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.Copy

/**
* Copies the core REST specs to the local resource directory for the REST YAML tests.
*/
class CopyRestApiSpecPlugin implements Plugin<Project> {
Copy link
Contributor Author

@jakelandis jakelandis Feb 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still have a few places where we need the specs copied to the local resources. Notably the examples so that they can rely on the release jar. There are few others projects where it is easier to copy them locally then run from the global dir. However, I believe all (but 1) of the modules, plugins, and x-pack plugins REST Yaml tests run from the global dir.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me what the difference is between just putting them on the test runtime classpath, and copying them to the resources output directory. Either way, we are just throwing these files on the classpath.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I may have mis-attributed "easier", I will re-visit the usage of this. However, this is still required for the examples project.


@Override
void apply(Project project) {
project.with {
configurations.create('restSpec')
dependencies.add(
'restSpec',
BuildParams.internal ? project.project(':rest-api-spec') :
"org.elasticsearch:rest-api-spec:${VersionProperties.elasticsearch}"
)

tasks.create("copyCoreRestSpecLocal", Copy) {
dependsOn project.configurations.restSpec
into(project.sourceSets.test.output.resourcesDir)
from({ project.zipTree(project.configurations.restSpec.singleFile) }) {
includeEmptyDirs = false
include 'rest-api-spec/api/**'
}
}

tasks.withType(RestTestRunnerTask).each { Task t ->
t.dependsOn('copyCoreRestSpecLocal')
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,20 @@
*/
package org.elasticsearch.gradle.test

import org.elasticsearch.gradle.VersionProperties
import org.elasticsearch.gradle.info.BuildParams

import org.elasticsearch.gradle.testclusters.ElasticsearchCluster
import org.elasticsearch.gradle.testclusters.RestTestRunnerTask
import org.elasticsearch.gradle.tool.Boilerplate
import org.gradle.api.DefaultTask
import org.gradle.api.Task
import org.gradle.api.file.FileCopyDetails
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.testing.Test
import org.gradle.plugins.ide.idea.IdeaPlugin

/**
* A wrapper task around setting up a cluster and running rest tests.
*/
class RestIntegTestTask extends DefaultTask {

protected Test runner

/** Flag indicating whether the rest tests in the rest spec should be run. */
@Input
Boolean includePackaged = false

RestIntegTestTask() {
runner = project.tasks.create("${name}Runner", RestTestRunnerTask.class)
super.dependsOn(runner)
Expand Down Expand Up @@ -69,10 +60,6 @@ class RestIntegTestTask extends DefaultTask {
runner.systemProperty('test.clustername', System.getProperty("tests.clustername"))
}

// copy the rest spec/tests onto the test classpath
Copy copyRestSpec = createCopyRestSpecTask()
project.sourceSets.test.output.builtBy(copyRestSpec)

// this must run after all projects have been configured, so we know any project
// references can be accessed as a fully configured
project.gradle.projectsEvaluated {
Expand All @@ -83,12 +70,6 @@ class RestIntegTestTask extends DefaultTask {
}
}

/** Sets the includePackaged property */
public void includePackaged(boolean include) {
includePackaged = include
}


@Override
public Task dependsOn(Object... dependencies) {
runner.dependsOn(dependencies)
Expand All @@ -113,38 +94,4 @@ class RestIntegTestTask extends DefaultTask {
public void runner(Closure configure) {
project.tasks.getByName("${name}Runner").configure(configure)
}

Copy createCopyRestSpecTask() {
Boilerplate.maybeCreate(project.configurations, 'restSpec') {
project.dependencies.add(
'restSpec',
BuildParams.internal ? project.project(':rest-api-spec') :
"org.elasticsearch:rest-api-spec:${VersionProperties.elasticsearch}"
)
}

return Boilerplate.maybeCreate(project.tasks, 'copyRestSpec', Copy) { Copy copy ->
copy.dependsOn project.configurations.restSpec
copy.into(project.sourceSets.test.output.resourcesDir)
copy.from({ project.zipTree(project.configurations.restSpec.singleFile) }) {
includeEmptyDirs = false
include 'rest-api-spec/**'
filesMatching('rest-api-spec/test/**') { FileCopyDetails details ->
if (includePackaged == false) {
details.exclude()
}
}
}

if (project.plugins.hasPlugin(IdeaPlugin)) {
project.idea {
module {
if (scopes.TEST != null) {
scopes.TEST.plus.add(project.configurations.restSpec)
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#
# 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.
#

implementation-class=org.elasticsearch.gradle.test.CopyRestApiSpecPlugin
21 changes: 2 additions & 19 deletions client/rest-high-level/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ apply plugin: 'elasticsearch.rest-test'
apply plugin: 'nebula.maven-base-publish'
apply plugin: 'nebula.maven-scm'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'elasticsearch.copy-rest-api-spec'

group = 'org.elasticsearch.client'
archivesBaseName = 'elasticsearch-rest-high-level-client'
Expand All @@ -36,18 +37,6 @@ publishing {
}
}

configurations {
restSpec
}

idea {
module {
if (scopes.TEST != null) {
scopes.TEST.plus.add(project.configurations.restSpec)
}
}
}

dependencies {
compile project(':modules:mapper-extras')
compile project(':modules:parent-join')
Expand All @@ -70,17 +59,11 @@ dependencies {
testCompile(project(':x-pack:plugin:core')) {
exclude group: 'org.elasticsearch', module: 'elasticsearch-rest-high-level-client'
}

restSpec project(':rest-api-spec')
}

//we need to copy the yaml spec so we can check naming (see RestHighlevelClientTests#testApiNamingConventions)
processTestResources {
dependsOn configurations.restSpec // so that configurations resolve
from({ zipTree(configurations.restSpec.singleFile) }) {
include 'rest-api-spec/api/**'
}
from(project(':client:rest-high-level').file('src/test/resources'))
dependsOn copyCoreRestSpecLocal
}

dependencyLicenses {
Expand Down
6 changes: 5 additions & 1 deletion distribution/archives/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -305,17 +305,21 @@ subprojects {
configure(subprojects.findAll { it.name == 'integ-test-zip' }) {
apply plugin: 'elasticsearch.standalone-rest-test'
apply plugin: 'elasticsearch.rest-test'
apply plugin: 'elasticsearch.copy-rest-api-spec'

group = "org.elasticsearch.distribution.integ-test-zip"

integTest {
dependsOn assemble
includePackaged = true
}

processTestResources {
from({ zipTree(configurations.restSpec.singleFile) }) {
include 'rest-api-spec/test/**'
}
inputs.properties(project(':distribution').restTestExpansions)
MavenFilteringHack.filter(it, project(':distribution').restTestExpansions)
dependsOn configurations.restSpec
}


Expand Down
8 changes: 2 additions & 6 deletions distribution/docker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@ import org.elasticsearch.gradle.testfixtures.TestFixturesPlugin

apply plugin: 'elasticsearch.standalone-rest-test'
apply plugin: 'elasticsearch.test.fixtures'
apply plugin: 'elasticsearch.copy-rest-api-spec'

testFixtures.useFixture()

configurations {
dockerPlugins
dockerSource
ossDockerSource
restSpec
}

dependencies {
dockerSource project(path: ":distribution:archives:linux-tar")
ossDockerSource project(path: ":distribution:archives:oss-linux-tar")
restSpec project(':rest-api-spec')
}

ext.expansions = { oss, local ->
Expand Down Expand Up @@ -128,12 +127,9 @@ preProcessFixture {
}

processTestResources {
from({ zipTree(configurations.restSpec.singleFile) }) {
include 'rest-api-spec/api/**'
}
from project(':x-pack:plugin:core')
.file('src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks')
dependsOn configurations.restSpec
dependsOn copyCoreRestSpecLocal
}

task integTest(type: Test) {
Expand Down
61 changes: 61 additions & 0 deletions gradle/copy-rest-api-spec-to-global.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

/**
* Copies all of the REST api specs (including core, modules, plugins, and x-pack) to common location for the REST YAML test.
* This plugin is expected to only be applied to applied to the root project.
*/
File rootBuildDir = rootProject.buildDir
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so I think we decided against centralizing all specs because I came late to the party with the whole cache hit issue. Sorry about that. Given that then the only common specs are those in :rest-api-spec, let's just add this logic to that project, exposing it's specs as a build artifact. Kinda what we already do ☹️


tasks.create("copyCoreRestSpecGlobal", Copy) {
from project.findProject(':rest-api-spec').projectDir
into rootBuildDir
includeEmptyDirs = false
include '**/src/**/rest-api-spec/api/**'
eachFile {
path = new File("rest-api-spec/api", name)
}
}

tasks.create("copyModulesRestSpecGlobal", Copy) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So essentially this is the stuff you ripped out of RestIntegTestTask. I think that's still the right thing to do, but let's instead move it to RestTestPlugin. So the plugin sets up the specs on the classpath instead of the task constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, there are many usages of RestIntegTestTask outside of the RestTestPlugin , so I can't move this to RestTestPlugin. I'll look into moving this back to the RestIntegTestTask , but not sure if that will just put us back to where we started.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all RestIntegTestTasks require the rest specs though, that's the difference. We use those task for Java tests as well. We should put the reliance on rest specs in an explicit plugin so that if you are using YAML tests and need those specs, you must apply the plugin. Otherwise we are adding the core rest specs to the classpath unnecessarily for many tasks, again, negatively affecting cacheability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense... However, I would like to also remove the integTest from the esplugin (here) to help enforce this. I haven't checked yet, but for any esplugin that has REST tests, but does not have YAML based rest tests would need to include the specs unnecessarily or explicitly create the integTest task. I think this is pretty rare, so either option probably not much of an issue.

Thoughts on removing integTest from the esplugin to enforce the inclusion of RestTestPlugin for projects that have Rest tests ? This is technically a breaking change for plugin devs. (I am only targeting 8.0 anyway)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I am only targeting 8.0 anyway)

We'll want to backport this to 7.x so I don't think we can safely make this change.

I haven't checked yet, but for any esplugin that has REST tests, but does not have YAML based rest tests would need to include the specs unnecessarily or explicitly create the integTest task. I think this is pretty rare, so either option probably not much of an issue.

I don't think so. There are a number of plugins that don't use YAML tests.

I think the problem is when we say "rest tests" we actually mean a few things. This includes both Java-based tests that target an external cluster and YAML tests. We only need the specs for the latter, but we group them together as a single thing. We need to tease these apart.

Not all projects that execute tests against an external cluster (i.e. have a RestIntegTesttask) utilize YAML tests. Pulling the copy rest spec stuff out of that task constructor is the first start. The question remains, where do we now put that logic, such that it get's applied to the appropriate projects. Moving it to RestTestPlugin actually wouldn't be correct, as this would apply again, to all projects. I suggest we create a new YamlRestTestPlugin that does all the spec copying and wiring and we apply this plugin explicitly to projects that leverage YAML tests.

from project.findProject(':modules').projectDir
into rootBuildDir
includeEmptyDirs = false
include '**/src/**/rest-api-spec/api/**'
eachFile {
path = new File("rest-api-spec/api", name)
}
}

tasks.create("copyPluginsRestSpecGlobal", Copy) {
from project.findProject(':plugins').projectDir
into rootBuildDir
includeEmptyDirs = false
include '**/src/**/rest-api-spec/api/**'
exclude '**/examples/**'
eachFile {
path = new File("rest-api-spec/api", name)
}
}

tasks.create("copyXpackPluginsRestSpecGlobal", Copy) {
from project.findProject(':x-pack:plugin').projectDir
into rootBuildDir
includeEmptyDirs = false
include '**/src/**/rest-api-spec/api/**'
eachFile {
path = new File("rest-api-spec/api", name)
}
}

tasks.create("copyAllRestSpecsGlobal") {
dependsOn copyCoreRestSpecGlobal
dependsOn copyModulesRestSpecGlobal
dependsOn copyPluginsRestSpecGlobal
dependsOn copyXpackPluginsRestSpecGlobal
}

configurations {
copyRestApiSpecGlobal
}
artifacts {
copyRestApiSpecGlobal(file(rootBuildDir)) { builtBy copyAllRestSpecsGlobal }
}
2 changes: 2 additions & 0 deletions modules/lang-painless/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ esplugin {
description 'An easy, safe and fast scripting language for Elasticsearch'
classname 'org.elasticsearch.painless.PainlessPlugin'
}
//since there are local api specs, we need to copy the core spec locally too
apply plugin: 'elasticsearch.copy-rest-api-spec'

testClusters.integTest {
module file(project(':modules:mapper-extras').tasks.bundlePlugin.archiveFile)
Expand Down
7 changes: 5 additions & 2 deletions plugins/examples/rest-handler/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import org.elasticsearch.gradle.info.BuildParams

/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
Expand All @@ -18,8 +16,11 @@ import org.elasticsearch.gradle.info.BuildParams
* specific language governing permissions and limitations
* under the License.
*/
import org.elasticsearch.gradle.info.BuildParams

apply plugin: 'elasticsearch.testclusters'
apply plugin: 'elasticsearch.esplugin'
apply plugin: 'elasticsearch.copy-rest-api-spec'

esplugin {
name 'rest-handler'
Expand Down Expand Up @@ -51,3 +52,5 @@ testingConventions.naming {
baseClass 'org.elasticsearch.test.ESTestCase'
}
}


Loading