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

Add gradle plugin for downloading jdk #41461

Merged
merged 19 commits into from
May 8, 2019
Merged

Conversation

rjernst
Copy link
Member

@rjernst rjernst commented Apr 23, 2019

We currently download 3 variants of the same version of the jdk for
bundling into the distributions. Additionally, the vagrant images do
their own downloading. This commit moves the jdk downloading into a
utility gradle plugin. This will be used in a future PR by the packaging
tests.

The new plugin exposes a "usesJdk" method on tasks. Once the jdk version
and platform are set for a task, a "jdkHome" property exists which may
be used to get the extracted jdk directory at runtime.

We currently download 3 variants of the same version of the jdk for
bundling into the distributions. Additionally, the vagrant images do
their own downloading. This commit moves the jdk downloading into a
utility gradle plugin. This will be used in a future PR by the packaging
tests.

The new plugin exposes a "usesJdk" method on tasks. Once the jdk version
and platform are set for a task, a "jdkHome" property exists which may
be used to get the extracted jdk directory at runtime.
@rjernst rjernst added >enhancement :Delivery/Build Build or test infrastructure :Delivery/Packaging RPM and deb packaging, tar and zip archives, shell and batch scripts v8.0.0 v7.2.0 labels Apr 23, 2019
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-core-infra

@rjernst rjernst requested a review from alpar-t April 24, 2019 15:02
if (ALLOWED_PLATFORMS.contains(platform) == false) {
throw new IllegalArgumentException("platform must be one of " + ALLOWED_PLATFORMS);
}
ExtraPropertiesExtension extraProperties = task.getExtensions().findByType(ExtraPropertiesExtension.class);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: could be moved up so it's repeated on line 60

}

// ensure a root level jdk download task exists
setupRootJdkDownload(project.getRootProject(), platform, version);
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we set up a single task to download all as we do for test clusters rather than one for each version ? Having fewer tasks will make configuration faster and I find it easier to follow what's going on.
It could also be a lazily created task that's always set up and then have the tasks that require it depend on it

Copy link
Contributor

Choose a reason for hiding this comment

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

I might be missing it, but is task made dependent on this ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I do not think we should have a single configuration for all jdks we download. That means resolving the configuration triggers download of all of them. Additionally, the way this works is to be able to resolve a configuration to a single file.

is task made dependent on this ?

The root project does the extraction, and makes the extracted dir available as an artifact of the root project. The tasks in subprojects add a normal dependency on that "artifact", through a separate configuration in the subproject, and the task then depends on that local subproject configuration.

Copy link
Contributor

Choose a reason for hiding this comment

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

I wasn't referring to having a single configuration, we actually need those to be separate to be able to have different versions, but we can extract all in the same task, having multiple tasks for it doesn't buy us anything.

Copy link
Member Author

Choose a reason for hiding this comment

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

Having a single task would mean the task depends on all those configurations, thus still resolving all the artifacts at the same time. We need a 1-1 mapping to ensure one extraction is done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is that all that bad? The configurations are cached, and the task has up to date checks . For most things this all the JDKs will be needed anyway due to the number of tests that depend on the JDK.

Copy link
Member Author

Choose a reason for hiding this comment

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

For most things this all the JDKs will be needed anyway

I don't think that is true. When I run a packaging test for a single distro, I need one jdk, not many, and certainly not the ones from non linux.

ivyRepo.setName(repoName);
ivyRepo.setUrl("https://download.java.net");
ivyRepo.patternLayout(layout -> {
layout.artifact("java/GA/jdk" + jdkMajor + "/" + jdkBuild + "/GPL/openjdk-[revision]_[module]-x64_bin.[ext]");
Copy link
Contributor

@alpar-t alpar-t Apr 24, 2019

Choose a reason for hiding this comment

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

we could add something like:

spec.content(c-> c.includeGroup("org.elasticsearch.jdk-downloader"));

And use "org.elasticsearch.jdk-downloader" as a group when downloading the dependency.
It's a good practice for repositories confirmed by plugins.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry I don't follow. The group is completely arbitrary and internal since this is a fake ivy repo.

Copy link
Contributor

Choose a reason for hiding this comment

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

But the repository is not, so all dependencies could potentially be checked against it.
I just suggested org.elasticsearch.jdk-downloader to make it more specific, or make it "ours" since it's not really used in the pattern layout. It would work the same way with "jdk".

@rjernst
Copy link
Member Author

rjernst commented Apr 25, 2019

@atorok I wasn't happy with usesJdk on every task, so I rethought this and have pushed an alternative approach. The new code has a jdks container extension on the project where each jdk used has a name. Then depending on the jdk in a task is as simple as dependsOn jdks.myjdk, and using it eg for cli args is args jdks.myjdk.homeDir and to copy the files from jdks.myjdk.files.

@rjernst
Copy link
Member Author

rjernst commented Apr 25, 2019

Note that I need to update the tests with the new approach, but wanted to get this up here for you to look at if you have time today.

@rjernst
Copy link
Member Author

rjernst commented Apr 26, 2019

@elasticmachine run elasticsearch-ci/1

Copy link
Contributor

@mark-vieira mark-vieira left a comment

Choose a reason for hiding this comment

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

Some misc comments, otherwise this looks great.

import java.util.List;
import java.util.regex.Pattern;

public class Jdk implements Buildable {
Copy link
Contributor

Choose a reason for hiding this comment

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

To be more explicit we could also implement Named here.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm happy to add this. Can you explain what benefits it would give us here?

Copy link
Contributor

@mark-vieira mark-vieira May 6, 2019

Choose a reason for hiding this comment

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

It's just a bit more explicit (getName() will no be an @Override method) and I believe I recall some Gradle APIs that handle a Named explicitly.

public class Jdk implements Buildable {

static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)(\\.\\d+\\.\\d+)?\\+(\\d+)");
private static final List<String> ALLOWED_PLATFORMS = Collections.unmodifiableList(Arrays.asList("linux", "windows", "darwin"));
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use an enum then?

Copy link
Contributor

Choose a reason for hiding this comment

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

We have something similar: org.elasticsearch.gradle.OS

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think an enum has any advantages here. In fact, it would require more unnecessary code to convert between the capitalized enum entities and the lowercase platform names we need here.

import java.io.File;
import java.util.function.Supplier;

public class JdkDownloadExtension {
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks to be unused. Seems we went the domain objection container route instead.

buildSrc/src/main/java/org/elasticsearch/gradle/Jdk.java Outdated Show resolved Hide resolved
return configuration;
}

public FileCollection getFiles() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rename this getFileCollection(). as FileCollection itself already has a method called getFiles() which returns a Set<File>.

if (rootProject.getRepositories().findByName(repoName) == null) {
rootProject.getRepositories().ivy(ivyRepo -> {
ivyRepo.setName(repoName);
ivyRepo.setUrl("https://download.java.net");
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't we use jvm-catalog.elastic.co instead? It has a much simpler URL pattern that would allow us to avoid having to create a repo per version as well. If we use a number of versions we are going to make a lot of HEAD requests that return a 404 with this approach.

Copy link
Member Author

Choose a reason for hiding this comment

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

This would add a considerable load to our infrastructure. We may want to still consider it, but I think it should be separate from this PR, which is just to consolidate existing code, which already uses this url.

Copy link
Contributor

Choose a reason for hiding this comment

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

I still have a bit of a concern with this approach of registering so many repositories. Is there anyway perhaps that we could construct the dependency notation so that we can avoid creating a repo per version? I'm not sure this is possible, or if what I see here even works. For example, the download link I see here includes the artifact hash in the download URL.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think it is possible here. The reason we embed this information in the pattern is we only have so many elements we can substitute via the maven/ivy coordinates.

The hash included in the download link is something that changed very recently. There are 2 versions registered here because versions before 12.0.1 still use the old pattern. I could definitely decide which one to register based on the version, but in this PR I'm only trying to move the logic we already have in the build for jdk downloading. We can certainly look at improving this either through reducing repos or moving to using the jvm catalog, but I think these should be discussed separately as followups to this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since we're only registering all these repos to the root project it's probably ok, as it won't affect dependency solution of subprojects. Let's make sure we create an issue to follow up here. I assume if the recent version download links have changed this is going to be a problem for fetching newer JDK versions, no?

Copy link
Member Author

Choose a reason for hiding this comment

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

We register 2 ivy repositories, one with the older pattern, and one with the hash. Are you sure you are looking at the latest commit?

Copy link
Contributor

Choose a reason for hiding this comment

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

I definitely was not. It seems GitHub was filtering out the latest commits for me. Thanks GH!

Copy link
Contributor

Choose a reason for hiding this comment

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

Given that we know the version when registering the repo can we be smart about it and avoid registering both for all versions?

Copy link
Contributor

Choose a reason for hiding this comment

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

Or simpler yet, if the artifact version notation includes a hash, use the hash repo, otherwise use the legacy one.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, I will do that in a followup.

int rootNdx = platform.equals("darwin") ? 2 : 1;
Action<CopySpec> removeRootDir = copy -> {
// remove extra unnecessary directory levels
copy.eachFile((FileCopyDetails details) -> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this cast to FileCopyDetails necessary?


String fakeJdkVersion = Objects.requireNonNull(System.getProperty('tests.jdk_version'))
jdks {
linux_jdk {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: could we remove _jdk from the name to avoid redundancy ? And keep that as the naming format in general.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is just an example in a test, but I removed it.

@alpar-t
Copy link
Contributor

alpar-t commented May 2, 2019

@atorok I wasn't happy with usesJdk on every task, so I rethought this and have pushed an alternative approach. The new code has a jdks container extension on the project where each jdk used has a name. Then depending on the jdk in a task is as simple as dependsOn jdks.myjdk, and using it eg for cli args is args jdks.myjdk.homeDir and to copy the files from jdks.myjdk.files.

@rjernst ++ I like the approach very much !

You might be able to add some additional magic with Object#asType(Class).
I'm not 100% sure if that helps or just creates more confusion, but you would be able to always pass just jdks.myjdk.

@rjernst
Copy link
Member Author

rjernst commented May 6, 2019

You might be able to add some additional magic with Object#asType(Class).

Thanks for the idea! While I didn't use asType, I was able to eliminate homeDir and fileCollection by making Jdk implement toString and Iterable.

@rjernst
Copy link
Member Author

rjernst commented May 7, 2019

@atorok @mark-vieira I believe I have addressed all your feedback.

Copy link
Contributor

@alpar-t alpar-t left a comment

Choose a reason for hiding this comment

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

LGTM

We might still need an asType(File) to be able to pass a JDK to an API that expects a File, but we can add that whenever we need it.

@mark-vieira
Copy link
Contributor

Yeah, I'm on the fence on using asType(). It still would require you to do something like myJdk as File which I'm not certain is significantly better than myJdk.file. I'm generally in favor of using the least amount of Groovy magic as possible unless it makes the DSL significantly cleaner.

jaymode and others added 3 commits May 7, 2019 10:24
* Update TLS ciphers and protocols for JDK 11 (elastic#41385)

This commit updates the default ciphers and TLS protocols that are used
after the minimum supported JDK is JDK 11. The conditionals around
TLSv1.3 and 256-bit cipher support have been removed. JDK 11 no longer
requires an unlimited JCE policy file for 256 bit cipher support and
TLSv1.3 is supported in JDK 11+. New cipher support has been introduced
in the newer JDK versions as well. The ciphers are ordered with PFS
ciphers being most preferred, then AEAD ciphers, and finally those with
mainstream hardware support.

* Fixes for TLSv1.3 on JDK11

* fix for JDK-8212885
…tic#41893)

The class was called PatternSeenEventExcpectation. This commit
is a straight class rename to correct the spelling.
@rjernst
Copy link
Member Author

rjernst commented May 7, 2019

@elasticmachine run elasticsearch-ci/1

@rjernst
Copy link
Member Author

rjernst commented May 7, 2019

@elasticmachine run elasticsearch-ci/packaging

@rjernst
Copy link
Member Author

rjernst commented May 8, 2019

@elasticmachine run elasticsearch-ci/1

@rjernst rjernst merged commit c7db902 into elastic:master May 8, 2019
@rjernst rjernst deleted the jdk_download branch May 8, 2019 14:16
rjernst added a commit that referenced this pull request May 8, 2019
We currently download 3 variants of the same version of the jdk for
bundling into the distributions. Additionally, the vagrant images do
their own downloading. This commit moves the jdk downloading into a
utility gradle plugin. This will be used in a future PR by the packaging
tests.

The new plugin exposes a "jdks" project extension which allows creating
named jdks. Once the jdk version and platform are set for a named jdk,
the jdk object may be used as a lazy String for the jdk home path, or a
file collection for copying.
gurkankaymak pushed a commit to gurkankaymak/elasticsearch that referenced this pull request May 27, 2019
We currently download 3 variants of the same version of the jdk for
bundling into the distributions. Additionally, the vagrant images do
their own downloading. This commit moves the jdk downloading into a
utility gradle plugin. This will be used in a future PR by the packaging
tests.

The new plugin exposes a "jdks" project extension which allows creating
named jdks. Once the jdk version and platform are set for a named jdk,
the jdk object may be used as a lazy String for the jdk home path, or a
file collection for copying.
@mark-vieira mark-vieira added the Team:Delivery Meta label for Delivery team label Nov 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Delivery/Build Build or test infrastructure :Delivery/Packaging RPM and deb packaging, tar and zip archives, shell and batch scripts >enhancement Team:Delivery Meta label for Delivery team v7.2.0 v8.0.0-alpha1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants