Replace fixtures with docker-compose (#35651)

Creates a new plugin to manage docker-compose based test fixtures. 
Convert the smb-fixture as a first example.
This commit is contained in:
Alpar Torok 2018-11-29 09:43:16 +02:00 committed by GitHub
parent 609f742e5f
commit 45db829039
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 244 additions and 101 deletions

View File

@ -16,4 +16,4 @@ while [ -h "$SCRIPT" ] ; do
done
source $(dirname "${SCRIPT}")/java-versions.properties
JAVA_HOME="${HOME}"/.java/${ES_BUILD_JAVA} ./gradlew resolveAllDependencies --parallel
JAVA_HOME="${HOME}"/.java/${ES_BUILD_JAVA} ./gradlew --parallel resolveAllDependencies composePull

View File

@ -251,6 +251,9 @@ If you want to just run the precommit checks:
./gradlew precommit
---------------------------------------------------------------------------
Some of these checks will require `docker-compose` installed for bringing up
test fixtures. If it's not present those checks will be skipped automatically.
== Testing the REST layer
The available integration tests make use of the java API to communicate with

View File

@ -123,6 +123,7 @@ dependencies {
compile "org.elasticsearch:jna:4.5.1"
compile 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
compile 'de.thetaphi:forbiddenapis:2.6'
compile 'com.avast.gradle:docker-compose-gradle-plugin:0.4.5'
testCompile "junit:junit:${props.getProperty('junit')}"
}

View File

@ -0,0 +1,48 @@
/*
* 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.testfixtures;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Project;
public class TestFixtureExtension {
private final Project project;
final NamedDomainObjectContainer<Project> fixtures;
public TestFixtureExtension(Project project) {
this.project = project;
this.fixtures = project.container(Project.class);
}
public void useFixture(String path) {
Project fixtureProject = this.project.findProject(path);
if (fixtureProject == null) {
throw new IllegalArgumentException("Could not find test fixture " + fixtureProject);
}
if (fixtureProject.file(TestFixturesPlugin.DOCKER_COMPOSE_YML).exists() == false) {
throw new IllegalArgumentException(
"Project " + path + " is not a valid test fixture: missing " + TestFixturesPlugin.DOCKER_COMPOSE_YML
);
}
fixtures.add(fixtureProject);
}
}

View File

@ -0,0 +1,133 @@
/*
* 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.testfixtures;
import com.avast.gradle.dockercompose.ComposeExtension;
import com.avast.gradle.dockercompose.DockerComposePlugin;
import org.elasticsearch.gradle.precommit.JarHellTask;
import org.elasticsearch.gradle.precommit.ThirdPartyAuditTask;
import org.gradle.api.DefaultTask;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.tasks.TaskContainer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
public class TestFixturesPlugin implements Plugin<Project> {
static final String DOCKER_COMPOSE_YML = "docker-compose.yml";
@Override
public void apply(Project project) {
TaskContainer tasks = project.getTasks();
TestFixtureExtension extension = project.getExtensions().create(
"testFixtures", TestFixtureExtension.class, project
);
// Don't look for docker-compose on the PATH yet that would pick up on Windows as well
if (project.file("/usr/local/bin/docker-compose").exists() == false &&
project.file("/usr/bin/docker-compose").exists() == false
) {
project.getLogger().warn(
"Tests require docker-compose at /usr/local/bin/docker-compose or /usr/bin/docker-compose " +
"but none could not be found so these will be skipped"
);
tasks.withType(getTaskClass("com.carrotsearch.gradle.junit4.RandomizedTestingTask"), task ->
task.setEnabled(false)
);
return;
}
if (project.file(DOCKER_COMPOSE_YML).exists()) {
project.apply(spec -> spec.plugin(BasePlugin.class));
project.apply(spec -> spec.plugin(DockerComposePlugin.class));
ComposeExtension composeExtension = project.getExtensions().getByType(ComposeExtension.class);
composeExtension.setUseComposeFiles(Collections.singletonList(DOCKER_COMPOSE_YML));
composeExtension.setRemoveContainers(true);
composeExtension.setExecutable(
project.file("/usr/local/bin/docker-compose").exists() ?
"/usr/local/bin/docker-compose" : "/usr/bin/docker-compose"
);
project.getTasks().getByName("clean").dependsOn("composeDown");
// convenience boilerplate with build plugin
project.getPluginManager().withPlugin("elasticsearch.build", (appliedPlugin) -> {
// Can't reference tasks that are implemented in Groovy, use reflection instead
disableTaskByType(tasks, getTaskClass("org.elasticsearch.gradle.precommit.LicenseHeadersTask"));
disableTaskByType(tasks, getTaskClass("com.carrotsearch.gradle.junit4.RandomizedTestingTask"));
disableTaskByType(tasks, ThirdPartyAuditTask.class);
disableTaskByType(tasks, JarHellTask.class);
});
} else {
tasks.withType(getTaskClass("com.carrotsearch.gradle.junit4.RandomizedTestingTask"), task ->
extension.fixtures.all(fixtureProject -> {
task.dependsOn(fixtureProject.getTasks().getByName("composeUp"));
task.finalizedBy(fixtureProject.getTasks().getByName("composeDown"));
// Configure ports for the tests as system properties.
// We only know these at execution time so we need to do it in doFirst
task.doFirst(it ->
fixtureProject.getExtensions().getByType(ComposeExtension.class).getServicesInfos()
.forEach((service, infos) ->
infos.getPorts()
.forEach((container, host) -> setSystemProperty(
it,
"test.fixtures." + fixtureProject.getName() + "." + service + "." + container,
host
))
));
}));
}
}
private void setSystemProperty(Task task, String name, Object value) {
try {
Method systemProperty = task.getClass().getMethod("systemProperty", String.class, Object.class);
systemProperty.invoke(task, name, value);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Could not find systemProperty method on RandomizedTestingTask", e);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalArgumentException("Could not call systemProperty method on RandomizedTestingTask", e);
}
}
private void disableTaskByType(TaskContainer tasks, Class<? extends Task> type) {
tasks.withType(type, task -> task.setEnabled(false));
}
@SuppressWarnings("unchecked")
private Class<? extends DefaultTask> getTaskClass(String type) {
Class<?> aClass;
try {
aClass = Class.forName(type);
if (DefaultTask.class.isAssignableFrom(aClass) == false) {
throw new IllegalArgumentException("Not a task type: " + type);
}
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("No such task: " + type);
}
return (Class<? extends DefaultTask>) aClass;
}
}

View File

@ -0,0 +1 @@
implementation-class=org.elasticsearch.gradle.testfixtures.TestFixturesPlugin

View File

@ -1,14 +1,13 @@
Project smbFixtureProject = xpackProject("test:smb-fixture")
evaluationDependsOn(smbFixtureProject.path)
apply plugin: 'elasticsearch.vagrantsupport'
apply plugin: 'elasticsearch.standalone-test'
apply plugin: 'elasticsearch.test.fixtures'
dependencies {
testCompile project(xpackModule('security'))
testCompile project(path: xpackModule('security'), configuration: 'testArtifacts')
}
testFixtures.useFixture ":x-pack:test:smb-fixture"
// add test resources from security, so tests can use example certs
sourceSets.test.resources.srcDirs(project(xpackModule('security')).sourceSets.test.resources.srcDirs)
compileTestJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes,-serial,-try,-unchecked"
@ -32,16 +31,3 @@ test {
// these are just tests, no need to audit
thirdPartyAudit.enabled = false
task smbFixture {
dependsOn "vagrantCheckVersion", "virtualboxCheckVersion", smbFixtureProject.up
}
if (project.rootProject.vagrantSupported) {
if (project.hasProperty('useExternalAD') == false) {
test.dependsOn smbFixture
test.finalizedBy smbFixtureProject.halt
}
} else {
test.enabled = false
}

View File

@ -67,6 +67,7 @@ public class ADLdapUserSearchSessionFactoryTests extends AbstractActiveDirectory
return secureSettings;
}
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35738")
public void testUserSearchWithActiveDirectory() throws Exception {
String groupSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
String userSearchBase = "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";

View File

@ -46,13 +46,14 @@ public abstract class AbstractActiveDirectoryTestCase extends ESTestCase {
// as we cannot control the URL of the referral which may contain a non-resolvable DNS name as
// this name would be served by the samba4 instance
public static final Boolean FOLLOW_REFERRALS = Booleans.parseBoolean(getFromEnv("TESTS_AD_FOLLOW_REFERRALS", "false"));
public static final String AD_LDAP_URL = getFromEnv("TESTS_AD_LDAP_URL", "ldaps://localhost:61636");
public static final String AD_LDAP_GC_URL = getFromEnv("TESTS_AD_LDAP_GC_URL", "ldaps://localhost:63269");
public static final String AD_LDAP_URL = getFromEnv("TESTS_AD_LDAP_URL", "ldaps://localhost:" + getFromProperty("636"));
public static final String AD_LDAP_GC_URL = getFromEnv("TESTS_AD_LDAP_GC_URL", "ldaps://localhost:" + getFromProperty("3269"));
public static final String PASSWORD = getFromEnv("TESTS_AD_USER_PASSWORD", "Passw0rd");
public static final String AD_LDAP_PORT = getFromEnv("TESTS_AD_LDAP_PORT", "61389");
public static final String AD_LDAPS_PORT = getFromEnv("TESTS_AD_LDAPS_PORT", "61636");
public static final String AD_GC_LDAP_PORT = getFromEnv("TESTS_AD_GC_LDAP_PORT", "63268");
public static final String AD_GC_LDAPS_PORT = getFromEnv("TESTS_AD_GC_LDAPS_PORT", "63269");
public static final String AD_LDAP_PORT = getFromEnv("TESTS_AD_LDAP_PORT", getFromProperty("389"));
public static final String AD_LDAPS_PORT = getFromEnv("TESTS_AD_LDAPS_PORT", getFromProperty("636"));
public static final String AD_GC_LDAP_PORT = getFromEnv("TESTS_AD_GC_LDAP_PORT", getFromProperty("3268"));
public static final String AD_GC_LDAPS_PORT = getFromEnv("TESTS_AD_GC_LDAPS_PORT", getFromProperty("3269"));
public static final String AD_DOMAIN = "ad.test.elasticsearch.com";
protected SSLService sslService;
@ -151,4 +152,11 @@ public abstract class AbstractActiveDirectoryTestCase extends ESTestCase {
final String value = System.getenv(envVar);
return value == null ? defaultValue : value;
}
private static String getFromProperty(String port) {
String key = "test.fixtures.smb-fixture.fixture." + port;
final String value = System.getProperty(key);
assertNotNull("Expected the actual value for " + port + " to be in system property " + key, value);
return value;
}
}

View File

@ -34,6 +34,7 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
ldapConnection.getConnectionOptions().setFollowReferrals(AbstractActiveDirectoryTestCase.FOLLOW_REFERRALS);
}
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35738")
@SuppressWarnings("unchecked")
public void testResolveSubTree() throws Exception {
Settings settings = Settings.builder()

View File

@ -60,6 +60,7 @@ public class ActiveDirectoryRunAsIT extends AbstractAdLdapRealmTestCase {
return builder.build();
}
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35738")
public void testRunAs() throws Exception {
String avenger = realmConfig.loginWithCommonName ? "Natasha Romanoff" : "blackwidow";
final AuthenticateRequest request = new AuthenticateRequest(avenger);

View File

@ -7,6 +7,7 @@ package org.elasticsearch.xpack.security.authc.ldap;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import org.apache.lucene.util.LuceneTestCase;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
@ -42,6 +43,7 @@ import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
@LuceneTestCase.AwaitsFix(bugUrl = "ActiveDirectorySessionFactoryTests")
public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryTestCase {
private static final String REALM_NAME = "ad-test";

View File

@ -5,13 +5,17 @@
*/
package org.elasticsearch.xpack.security.authc.ldap;
import org.apache.lucene.util.LuceneTestCase;
import java.io.IOException;
/**
* This tests the group to role mappings from LDAP sources provided by the super class - available from super.realmConfig.
* The super class will provide appropriate group mappings via configGroupMappings()
*/
@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35738")
public class GroupMappingIT extends AbstractAdLdapRealmTestCase {
public void testAuthcAuthz() throws IOException {
String avenger = realmConfig.loginWithCommonName ? "Natasha Romanoff" : "blackwidow";
assertAccessAllowed(avenger, "avengers");

View File

@ -41,6 +41,7 @@ public class MultiGroupMappingIT extends AbstractAdLdapRealmTestCase {
" privileges: [ all ]\n";
}
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35738")
public void testGroupMapping() throws IOException {
String asgardian = "odin";
String securityPhilanthropist = realmConfig.loginWithCommonName ? "Bruce Banner" : "hulk";

View File

@ -62,6 +62,7 @@ public class MultipleAdRealmIT extends AbstractAdLdapRealmTestCase {
* Because one realm is using "common name" (cn) for login, and the other uses the "userid" (sAMAccountName) [see
* {@link #setupSecondaryRealm()}], this is simply a matter of checking that we can authenticate with both identifiers.
*/
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35738")
public void testCanAuthenticateAgainstBothRealms() throws IOException {
assertAccessAllowed("Natasha Romanoff", "avengers");
assertAccessAllowed("blackwidow", "avengers");

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security.authc.ldap;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchScope;
import org.apache.lucene.util.LuceneTestCase;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
@ -22,6 +23,7 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasItems;
@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35738")
public class UserAttributeGroupsResolverTests extends GroupsResolverTestCase {
public static final String BRUCE_BANNER_DN = "cn=Bruce Banner,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";

View File

@ -0,0 +1,12 @@
FROM ubuntu:16.04
RUN apt-get update -qqy && apt-get install -qqy samba ldap-utils
ADD . /fixture
RUN chmod +x /fixture/src/main/resources/provision/installsmb.sh
RUN /fixture/src/main/resources/provision/installsmb.sh
EXPOSE 389
EXPOSE 636
EXPOSE 3268
EXPOSE 3269
CMD service samba-ad-dc restart && sleep infinity

View File

@ -1,20 +0,0 @@
Vagrant.configure("2") do |config|
config.vm.define "test.ad.elastic.local" do |config|
config.vm.box = "elastic/ubuntu-16.04-x86_64"
end
config.vm.hostname = "ad.test.elastic.local"
if Vagrant.has_plugin?("vagrant-cachier")
config.cache.scope = :box
end
config.vm.network "forwarded_port", guest: 389, host: 61389, protocol: "tcp"
config.vm.network "forwarded_port", guest: 636, host: 61636, protocol: "tcp"
config.vm.network "forwarded_port", guest: 3268, host: 63268, protocol: "tcp"
config.vm.network "forwarded_port", guest: 3269, host: 63269, protocol: "tcp"
config.vm.provision "shell", path: "src/main/resources/provision/installsmb.sh"
end

View File

@ -1,42 +1,2 @@
apply plugin: 'elasticsearch.build'
Map<String, String> vagrantEnvVars = [
'VAGRANT_CWD' : "${project.projectDir.absolutePath}",
'VAGRANT_VAGRANTFILE' : 'Vagrantfile',
'VAGRANT_PROJECT_DIR' : "${project.projectDir.absolutePath}"
]
String box = "test.ad.elastic.local"
task update(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) {
command 'box'
subcommand 'update'
boxName box
environmentVars vagrantEnvVars
}
task up(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) {
command 'up'
args '--provision', '--provider', 'virtualbox'
boxName box
environmentVars vagrantEnvVars
dependsOn update
}
task halt(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) {
command 'halt'
boxName box
environmentVars vagrantEnvVars
}
task destroy(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) {
command 'destroy'
args '-f'
boxName box
environmentVars vagrantEnvVars
dependsOn halt
}
thirdPartyAudit.enabled = false
test.enabled = false
jarHell.enabled = false
apply plugin: 'elasticsearch.test.fixtures'

View File

@ -0,0 +1,11 @@
version: '3'
services:
fixture:
build:
context: .
dockerfile: Dockerfile
ports:
- "389"
- "636"
- "3268"
- "3269"

View File

@ -6,29 +6,17 @@
set -ex
MARKER_FILE=/etc/marker
if [ -f $MARKER_FILE ]; then
echo "Already provisioned..."
exit 0;
fi
VDIR=/vagrant
VDIR=/fixture
RESOURCES=$VDIR/src/main/resources
CERTS_DIR=$RESOURCES/certs
SSL_DIR=/var/lib/samba/private/tls
# Update package manager
apt-get update -qqy
# Install krb5 packages
apt-get install -qqy samba ldap-utils
# install ssl certs
mkdir -p $SSL_DIR
cp $CERTS_DIR/*.pem $SSL_DIR
chmod 600 $SSL_DIR/key.pem
mkdir -p /etc/ssl/certs/
cat $SSL_DIR/ca.pem >> /etc/ssl/certs/ca-certificates.crt
mv /etc/samba/smb.conf /etc/samba/smb.conf.orig
@ -92,4 +80,3 @@ EOL
ldapmodify -D Administrator@ad.test.elasticsearch.com -w Passw0rd -H ldaps://127.0.0.1:636 -f /tmp/entrymods -v
touch $MARKER_FILE