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

Introduce packaging tests for Docker #46599

Merged
merged 18 commits into from
Oct 5, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
105 changes: 103 additions & 2 deletions Vagrantfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# vim: ft=ruby ts=2 sw=2 sts=2 et:

# This Vagrantfile exists to test packaging. Read more about its use in the
# vagrant section in TESTING.asciidoc.
Expand Down Expand Up @@ -63,6 +63,7 @@ Vagrant.configure(2) do |config|
# Install Jayatana so we can work around it being present.
[ -f /usr/share/java/jayatanaag.jar ] || install jayatana
SHELL
ubuntu_docker config
end
end
'ubuntu-1804'.tap do |box|
Expand All @@ -72,6 +73,7 @@ Vagrant.configure(2) do |config|
# Install Jayatana so we can work around it being present.
[ -f /usr/share/java/jayatanaag.jar ] || install jayatana
SHELL
ubuntu_docker config
end
end
'debian-8'.tap do |box|
Expand All @@ -87,6 +89,7 @@ Vagrant.configure(2) do |config|
config.vm.define box, define_opts do |config|
config.vm.box = 'elastic/debian-9-x86_64'
deb_common config, box
deb_docker config
end
end
'centos-6'.tap do |box|
Expand All @@ -99,6 +102,7 @@ Vagrant.configure(2) do |config|
config.vm.define box, define_opts do |config|
config.vm.box = 'elastic/centos-7-x86_64'
rpm_common config, box
rpm_docker config
end
end
'oel-6'.tap do |box|
Expand All @@ -117,12 +121,14 @@ Vagrant.configure(2) do |config|
config.vm.define box, define_opts do |config|
config.vm.box = 'elastic/fedora-28-x86_64'
dnf_common config, box
dnf_docker config
end
end
'fedora-29'.tap do |box|
config.vm.define box, define_opts do |config|
config.vm.box = 'elastic/fedora-28-x86_64'
dnf_common config, box
dnf_docker config
end
end
'opensuse-42'.tap do |box|
Expand Down Expand Up @@ -185,6 +191,63 @@ def deb_common(config, name, extra: '')
)
end

def ubuntu_docker(config)
config.vm.provision 'install Docker using apt', type: 'shell', inline: <<-SHELL
# Install packages to allow apt to use a repository over HTTPS
apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-common
# Add Docker’s official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
# Set up the stable Docker repository
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
# Install Docker. Unlike Fedora and CentOS, this also start the daemon.
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io
# Add vagrant to the Docker group, so that it can run commands
usermod -aG docker vagrant
SHELL
end


def deb_docker(config)
config.vm.provision 'install Docker using apt', type: 'shell', inline: <<-SHELL
# Install packages to allow apt to use a repository over HTTPS
apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-common
# Add Docker’s official GPG key
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
# Set up the stable Docker repository
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
# Install Docker. Unlike Fedora and CentOS, this also start the daemon.
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io
# Add vagrant to the Docker group, so that it can run commands
usermod -aG docker vagrant
SHELL
end

def rpm_common(config, name)
linux_common(
config,
Expand All @@ -195,6 +258,25 @@ def rpm_common(config, name)
)
end

def rpm_docker(config)
config.vm.provision 'install Docker using yum', type: 'shell', inline: <<-SHELL
# Install prerequisites
yum install -y yum-utils device-mapper-persistent-data lvm2
# Add repository
yum-config-manager -y --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Install Docker
yum install -y docker-ce docker-ce-cli containerd.io
# Start Docker
systemctl enable --now docker
# Add vagrant to the Docker group, so that it can run commands
usermod -aG docker vagrant
SHELL
end

def dnf_common(config, name)
# Autodetect doesn't work....
if Vagrant.has_plugin?('vagrant-cachier')
Expand All @@ -211,6 +293,25 @@ def dnf_common(config, name)
)
end

def dnf_docker(config)
config.vm.provision 'install Docker using dnf', type: 'shell', inline: <<-SHELL
# Install prerequisites
dnf -y install dnf-plugins-core
# Add repository
dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
# Install Docker
dnf install -y docker-ce docker-ce-cli containerd.io
# Start Docker
systemctl enable --now docker
# Add vagrant to the Docker group, so that it can run commands
usermod -aG docker vagrant
SHELL
end

def suse_common(config, name, extra: '')
linux_common(
config,
Expand Down Expand Up @@ -268,7 +369,7 @@ def linux_common(config,

# This prevents leftovers from previous tests using the
# same VM from messing up the current test
config.vm.provision 'clean es installs in tmp', run: 'always', type: 'shell', inline: <<-SHELL
config.vm.provision 'clean es installs in tmp', type: 'shell', inline: <<-SHELL
rm -rf /tmp/elasticsearch*
SHELL

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ private List<ElasticsearchDistribution> configureDistributions(Project project,
List<ElasticsearchDistribution> currentDistros = new ArrayList<>();
List<ElasticsearchDistribution> upgradeDistros = new ArrayList<>();

for (Type type : Arrays.asList(Type.DEB, Type.RPM)) {
for (Type type : Arrays.asList(Type.DEB, Type.RPM, Type.DOCKER)) {
for (Flavor flavor : Flavor.values()) {
for (boolean bundledJdk : Arrays.asList(true, false)) {
addDistro(distributions, type, null, flavor, bundledJdk, VersionProperties.getElasticsearch(), currentDistros);
Expand Down Expand Up @@ -370,7 +370,8 @@ private static void addDistro(NamedDomainObjectContainer<ElasticsearchDistributi
if (type == Type.ARCHIVE) {
d.setPlatform(platform);
}
d.setBundledJdk(bundledJdk);
// We don't test Docker images with a non-bundled JDK
d.setBundledJdk(type == Type.DOCKER || bundledJdk);
Copy link
Member

Choose a reason for hiding this comment

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

This should be handled outside of addDistro, otherwise we are adding the same distribution twice? ie around line 325 where this method is called

d.setVersion(version);
});
container.add(distro);
Expand All @@ -382,10 +383,17 @@ private static boolean isWindows(Project project) {
}

private static String distroId(Type type, Platform platform, Flavor flavor, boolean bundledJdk) {
return flavor + "-" + (type == Type.ARCHIVE ? platform + "-" : "") + type + (bundledJdk ? "" : "-no-jdk");
// We don't test Docker images with a non-bundled JDK
return flavor + "-" + (type == Type.ARCHIVE ? platform + "-" : "") + type + (bundledJdk || type == Type.DOCKER ? "" : "-no-jdk");
}

private static String destructiveDistroTestTaskName(ElasticsearchDistribution distro) {
return "destructiveDistroTest." + distroId(distro.getType(), distro.getPlatform(), distro.getFlavor(), distro.getBundledJdk());
Type type = distro.getType();
return "destructiveDistroTest." + distroId(
type,
distro.getPlatform(),
distro.getFlavor(),
// We don't test Docker images with a non-bundled JDK
type == Type.DOCKER || distro.getBundledJdk());
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be necessary, see my previous comment about where we add the distros. We should never add a docker distro with bundledJdk=false

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ void setupDistributions(Project project) {
// for the distribution as a file, just depend on the artifact directly
dependencies.add(distribution.configuration.getName(), dependencyNotation(project, distribution));

// no extraction allowed for rpm or deb
if (distribution.getType() != Type.RPM && distribution.getType() != Type.DEB) {
// no extraction allowed for rpm, deb or docker
if (distribution.getType().shouldExtract()) {
// for the distribution extracted, add a root level task that does the extraction, and depend on that
// extracted configuration as an artifact consisting of the extracted distribution directory
dependencies.add(distribution.getExtracted().configuration.getName(),
Expand Down Expand Up @@ -221,7 +221,6 @@ private Object dependencyNotation(Project project, ElasticsearchDistribution dis
}

private static Dependency projectDependency(Project project, String projectPath, String projectConfig) {

if (project.findProject(projectPath) == null) {
throw new GradleException("no project [" + projectPath + "], project names: " + project.getRootProject().getAllprojects());
}
Expand All @@ -233,11 +232,20 @@ private static Dependency projectDependency(Project project, String projectPath,

private static String distributionProjectPath(ElasticsearchDistribution distribution) {
String projectPath = ":distribution";
if (distribution.getType() == Type.INTEG_TEST_ZIP) {
projectPath += ":archives:integ-test-zip";
} else {
projectPath += distribution.getType() == Type.ARCHIVE ? ":archives:" : ":packages:";
projectPath += distributionProjectName(distribution);
switch (distribution.getType()) {
case INTEG_TEST_ZIP:
projectPath += ":archives:integ-test-zip";
break;

case DOCKER:
Copy link
Contributor

Choose a reason for hiding this comment

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

@pugnascotia the download distribution plugin is meant to be used outside of our build,
@rjernst is working on adding support to download some of the artifacts that are not already downloadable. I think in the case of docker, this could be set up differently to make that work easier.
For example, ensureImageIsLoaded from the test should be part of this plugin, since docker is distributed trough the registry, so there will be nothing to do here for the released versions.

Alternatively @rjernst, it might be more straight forward and easier to maintain to split the download plugin into two. One that deals exclusively with versions that are an actual download, and restrict that to some distribution types and one that deals with unreleased versions, with the lather being applied only as part of our build.

I don't necessarily see this as reason not to merge this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I feel that we should merge this and come back to some of these issues. At least we'll have something build upon.

projectPath += ":docker:";
projectPath += distributionProjectName(distribution);
break;

default:
projectPath += distribution.getType() == Type.ARCHIVE ? ":archives:" : ":packages:";
projectPath += distributionProjectName(distribution);
break;
}
return projectPath;
}
Expand All @@ -250,9 +258,12 @@ private static String distributionProjectName(ElasticsearchDistribution distribu
if (distribution.getBundledJdk() == false) {
projectName += "no-jdk-";
}

if (distribution.getType() == Type.ARCHIVE) {
Platform platform = distribution.getPlatform();
projectName += platform.toString() + (platform == Platform.WINDOWS ? "-zip" : "-tar");
} else if (distribution.getType() == Type.DOCKER) {
projectName += "docker-export";
} else {
projectName += distribution.getType();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,25 @@ public enum Type {
INTEG_TEST_ZIP,
ARCHIVE,
RPM,
DEB;
DEB,
DOCKER;

@Override
public String toString() {
return super.toString().toLowerCase(Locale.ROOT);
}

public boolean shouldExtract() {
switch (this) {
case DEB:
case DOCKER:
case RPM:
return false;

default:
return true;
}
}
}

public enum Flavor {
Expand Down Expand Up @@ -171,11 +184,16 @@ public String toString() {
}

public Extracted getExtracted() {
if (getType() == Type.RPM || getType() == Type.DEB) {
throw new UnsupportedOperationException("distribution type [" + getType() + "] for " +
"elasticsearch distribution [" + name + "] cannot be extracted");
switch (getType()) {
case DEB:
case DOCKER:
case RPM:
throw new UnsupportedOperationException("distribution type [" + getType() + "] for " +
"elasticsearch distribution [" + name + "] cannot be extracted");

default:
return extracted;
}
return extracted;
}

@Override
Expand Down Expand Up @@ -217,7 +235,7 @@ void finalizeValues() {
if (platform.isPresent() == false) {
platform.set(CURRENT_PLATFORM);
}
} else { // rpm or deb
} else { // rpm, deb or docker
if (platform.isPresent()) {
throw new IllegalArgumentException("platform not allowed for elasticsearch distribution ["
+ name + "] of type [" + getType() + "]");
Expand Down
34 changes: 34 additions & 0 deletions distribution/docker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,37 @@ assemble.dependsOn "buildDockerImage"
if (tasks.findByName("composePull")) {
tasks.composePull.enabled = false
}

/*
* The export subprojects write out the generated Docker images to disk, so
* that they can be easily reloaded, for example into a VM.
*/
subprojects { Project subProject ->
if (subProject.name.contains('docker-export')) {
apply plugin: 'distribution'

def oss = subProject.name.startsWith('oss')
Copy link
Member

Choose a reason for hiding this comment

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

Please use static types. It makes the code easier to reason about, eg String here.


def exportTaskName = taskName("export", oss, "DockerImage")
def buildTaskName = taskName("build", oss, "DockerImage")
def tarFile = "${parent.projectDir}/build/elasticsearch${oss ? '-oss' : ''}_test.${VersionProperties.elasticsearch}.docker.tar"

final Task exportDockerImageTask = task(exportTaskName, type: LoggedExec) {
executable 'docker'
args "save",
"-o",
tarFile,
"elasticsearch${oss ? '-oss' : ''}:test"
}

exportDockerImageTask.dependsOn(parent.tasks.getByName(buildTaskName))

artifacts.add('default', file(tarFile)) {
type 'tar'
name "elasticsearch${oss ? '-oss' : ''}"
builtBy exportTaskName
}

assemble.dependsOn exportTaskName
}
}
2 changes: 2 additions & 0 deletions distribution/docker/docker-export/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// This file is intentionally blank. All configuration of the
// export is done in the parent project.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need a project for the extraction ? Could it just be a task on parent ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's to do with DistributionDownloadPlugin - as far as I can tell, it's written to create depdencies between ES distributions and the default config on a project. I'm new to Gradle, but it looks like it doesn't depend on a task because the plugin needs to be able to locate the built archives. I'd be very happy to be pointed at better ways of doing this. I had to spend a while deciphering what was going on between DistroTestPlugin and DistributionDownloadPlugin.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, the same pattern is used in :distribution:packages.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, it's a bit strange that this we have projects for export only here, but I'll defer to @rjernst

2 changes: 2 additions & 0 deletions distribution/docker/oss-docker-export/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// This file is intentionally blank. All configuration of the
// export is done in the parent project.
Loading