Skip to content

Commit

Permalink
Merge pull request #522 from mesos/regression/CustomSettings
Browse files Browse the repository at this point in the history
Fix docker custom settings regression. Add system test.
  • Loading branch information
Phil Winder committed Mar 3, 2016
2 parents a057f98 + 055d2ad commit 5c67058
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class Configuration {
public static final String ELASTICSEARCH_PORTS = "--elasticsearchPorts";
public static final String CONTAINER_PATH_DATA = "/usr/share/elasticsearch/data";
public static final String CONTAINER_PATH_CONF = "/usr/share/elasticsearch/config";
public static final String CONTAINER_PATH_CONF_YML = CONTAINER_PATH_CONF + "/elasticsearch.yml";
public static final String HOST_SANDBOX = "./."; // Due to some protobuf weirdness. Requires './.' Not just '.'
public static final String HOST_PATH_HOME = HOST_SANDBOX + "/es_home";
public static final String HOST_PATH_CONF = HOST_SANDBOX;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,10 @@ private Protos.ContainerInfo getContainer(Configuration configuration, Protos.Ta
throw new IllegalArgumentException("Cannot parse filename from settings location. Please include the /elasticsearch.yml in the settings location.");
}
final String settingsFilename = fileName.toString();
// Mount the custom yml file over the top of the standard elasticsearch.yml file.
builder.addVolumes(Protos.Volume.newBuilder()
.setHostPath("./" + settingsFilename) // Because the file has been uploaded by the uris.
.setContainerPath(Configuration.CONTAINER_PATH_CONF)
.setContainerPath(Configuration.CONTAINER_PATH_CONF_YML)
.setMode(Protos.Volume.Mode.RO)
.build());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void testCreateTaskInfo() {
assertEquals(Configuration.CONTAINER_PATH_DATA, taskInfo.getContainer().getVolumes(0).getContainerPath());
assertEquals(Configuration.DEFAULT_HOST_DATA_DIR + "/" + configuration.getElasticsearchClusterName() + "/" + offer.getSlaveId().getValue(), taskInfo.getContainer().getVolumes(0).getHostPath());
assertEquals(Protos.Volume.Mode.RW, taskInfo.getContainer().getVolumes(0).getMode());
assertEquals(Configuration.CONTAINER_PATH_CONF, taskInfo.getContainer().getVolumes(1).getContainerPath());
assertEquals(Configuration.CONTAINER_PATH_CONF_YML, taskInfo.getContainer().getVolumes(1).getContainerPath());
assertEquals(Configuration.HOST_PATH_CONF, taskInfo.getContainer().getVolumes(1).getHostPath());
assertEquals(Protos.Volume.Mode.RO, taskInfo.getContainer().getVolumes(1).getMode());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package org.apache.mesos.elasticsearch.systemtest;

import com.containersol.minimesos.cluster.MesosCluster;
import com.containersol.minimesos.container.AbstractContainer;
import com.containersol.minimesos.mesos.ClusterArchitecture;
import com.containersol.minimesos.mesos.DockerClientFactory;
import com.containersol.minimesos.mesos.ZooKeeper;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerjava.api.model.AccessMode;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Volume;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import org.apache.log4j.Logger;
import org.apache.mesos.elasticsearch.common.cli.ElasticsearchCLIParameter;
import org.apache.mesos.elasticsearch.common.cli.ZookeeperCLIParameter;
import org.apache.mesos.elasticsearch.systemtest.base.SchedulerTestBase;
import org.apache.mesos.elasticsearch.systemtest.callbacks.ElasticsearchNodesResponse;
import org.apache.mesos.elasticsearch.systemtest.containers.AlpineContainer;
import org.apache.mesos.elasticsearch.systemtest.containers.MesosMasterTagged;
import org.apache.mesos.elasticsearch.systemtest.containers.MesosSlaveTagged;
import org.apache.mesos.elasticsearch.systemtest.util.DockerUtil;
import org.apache.mesos.elasticsearch.systemtest.util.IpTables;
import org.json.JSONObject;
import org.junit.*;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.apache.mesos.elasticsearch.scheduler.Configuration.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
* Test that custom settings work
*/
public class CustomSettingsDockerSystemTest {
public static final String CUSTOM_YML = "elasticsearch.yml";
public static final String CUSTOM_CONFIG_PATH = "/tmp/config/"; // In the container and on the VM/Host
public static final String CUSTOM_CONFIG_FILE = "/tmp/config/" + CUSTOM_YML; // In the container and on the VM/Host
public static final String TEST_PATH_PLUGINS = "/tmp/plugins";
public static final String TEST_AUTO_EXPAND_REPLICAS = "false";
private static final Logger LOGGER = Logger.getLogger(SchedulerTestBase.class);
private static final DockerClient dockerClient = DockerClientFactory.build();
private DockerUtil dockerUtil = new DockerUtil(dockerClient);

// Need full control over the cluster, so need to do all the lifecycle stuff.
private static MesosCluster cluster;
protected static final org.apache.mesos.elasticsearch.systemtest.Configuration TEST_CONFIG = new org.apache.mesos.elasticsearch.systemtest.Configuration();


@Rule
public final TestWatcher watcher = new TestWatcher() {
@Override
protected void failed(Throwable e, Description description) {
cluster.stop();
}
};

@BeforeClass
public static void startCluster() {
final ClusterArchitecture clusterArchitecture = new ClusterArchitecture.Builder()
.withZooKeeper()
.withMaster(MesosMasterTagged::new)
.withSlave(zooKeeper -> new MesosSlaveWithVolume(zooKeeper, TEST_CONFIG.getPortRanges().get(0)))
.withSlave(zooKeeper -> new MesosSlaveWithVolume(zooKeeper, TEST_CONFIG.getPortRanges().get(1)))
.withSlave(zooKeeper -> new MesosSlaveWithVolume(zooKeeper, TEST_CONFIG.getPortRanges().get(2)))
.build();
cluster = new MesosCluster(clusterArchitecture);
cluster.start(TEST_CONFIG.getClusterTimeout());
IpTables.apply(clusterArchitecture.dockerClient, cluster, TEST_CONFIG);
}

@AfterClass
public static void killAllContainers() {
cluster.stop();
new DockerUtil(dockerClient).killAllSchedulers();
new DockerUtil(dockerClient).killAllExecutors();
}


@After
public void after() {
dockerUtil.killAllSchedulers();
dockerUtil.killAllExecutors();
}

@Test
public void shouldHaveCustomSettingsBasedOnPath() throws UnirestException {
final AlpineContainer ymlWrite = new AlpineContainer(dockerClient, CUSTOM_CONFIG_PATH, CUSTOM_CONFIG_PATH,
"sh", "-c", "echo \"index.auto_expand_replicas: " + TEST_AUTO_EXPAND_REPLICAS + "\npath.plugins: " + TEST_PATH_PLUGINS + "\" > " + CUSTOM_CONFIG_FILE);
ymlWrite.start(TEST_CONFIG.getClusterTimeout());
ymlWrite.remove();

LOGGER.info("Starting Elasticsearch scheduler");

AbstractContainer scheduler = new CustomSettingsScheduler(dockerClient, cluster.getZkContainer(), cluster.getClusterId(), CUSTOM_CONFIG_FILE);
cluster.addAndStartContainer(scheduler, TEST_CONFIG.getClusterTimeout());

LOGGER.info("Started Elasticsearch scheduler on " + scheduler.getIpAddress() + ":" + TEST_CONFIG.getSchedulerGuiPort());

ESTasks esTasks = new ESTasks(TEST_CONFIG, scheduler.getIpAddress());
new TasksResponse(esTasks, TEST_CONFIG.getElasticsearchNodesCount());

ElasticsearchNodesResponse nodesResponse = new ElasticsearchNodesResponse(esTasks, TEST_CONFIG.getElasticsearchNodesCount());
assertTrue("Elasticsearch nodes did not discover each other within 5 minutes", nodesResponse.isDiscoverySuccessful());

final JSONObject root = Unirest.get("http://" + esTasks.getEsHttpAddressList().get(0) + "/_nodes").asJson().getBody().getObject();
final JSONObject nodes = root.getJSONObject("nodes");
final String firstNode = nodes.keys().next().toString();

// Test a setting that is not specified by the framework (to test that it is written correctly)
final String pathPlugins = nodes.getJSONObject(firstNode).getJSONObject("settings").getJSONObject("path").getString("plugins");
assertEquals(TEST_PATH_PLUGINS, pathPlugins);

// Test a setting that is specified by the framework (to test it is overwritten correctly)
final String autoExpandReplicas = nodes.getJSONObject(firstNode).getJSONObject("settings").getJSONObject("index").getString("auto_expand_replicas");
assertEquals(TEST_AUTO_EXPAND_REPLICAS, autoExpandReplicas);
}

@Test
public void shouldHaveCustomSettingsBasedOnURL() throws UnirestException {
LOGGER.info("Starting Elasticsearch scheduler");

String url = "https://gist.githubusercontent.com/philwinder/afece65f5560f1f7e1a2/raw/64dfca8cf76253de3185013b92697c7aea72bf5f/elasticsearch.yml";
AbstractContainer scheduler = new CustomSettingsScheduler(dockerClient, cluster.getZkContainer(), cluster.getClusterId(), url);
cluster.addAndStartContainer(scheduler, TEST_CONFIG.getClusterTimeout());

LOGGER.info("Started Elasticsearch scheduler on " + scheduler.getIpAddress() + ":" + TEST_CONFIG.getSchedulerGuiPort());

ESTasks esTasks = new ESTasks(TEST_CONFIG, scheduler.getIpAddress());
new TasksResponse(esTasks, TEST_CONFIG.getElasticsearchNodesCount());

ElasticsearchNodesResponse nodesResponse = new ElasticsearchNodesResponse(esTasks, TEST_CONFIG.getElasticsearchNodesCount());
assertTrue("Elasticsearch nodes did not discover each other within 5 minutes", nodesResponse.isDiscoverySuccessful());

final JSONObject root = Unirest.get("http://" + esTasks.getEsHttpAddressList().get(0) + "/_nodes").asJson().getBody().getObject();
final JSONObject nodes = root.getJSONObject("nodes");
final String firstNode = nodes.keys().next().toString();

// Test a setting that is not specified by the framework (to test that it is written correctly)
final String pathPlugins = nodes.getJSONObject(firstNode).getJSONObject("settings").getJSONObject("path").getString("plugins");
assertEquals(TEST_PATH_PLUGINS, pathPlugins);

// Test a setting that is specified by the framework (to test it is overwritten correctly)
final String autoExpandReplicas = nodes.getJSONObject(firstNode).getJSONObject("settings").getJSONObject("index").getString("auto_expand_replicas");
assertEquals(TEST_AUTO_EXPAND_REPLICAS, autoExpandReplicas);
}

private static class CustomSettingsScheduler extends AbstractContainer {
private final ZooKeeper zooKeeperContainer;
private final String clusterId;
private final String configPath;

protected CustomSettingsScheduler(DockerClient dockerClient, ZooKeeper zooKeeperContainer, String clusterId, String configPath) {
super(dockerClient);
this.zooKeeperContainer = zooKeeperContainer;
this.clusterId = clusterId;
this.configPath = configPath;
}

@Override
public void pullImage() {
dockerClient.pullImageCmd(TEST_CONFIG.getSchedulerImageName());
}

@Override
protected CreateContainerCmd dockerCommand() {
return dockerClient
.createContainerCmd(TEST_CONFIG.getSchedulerImageName())
.withName(TEST_CONFIG.getSchedulerName() + "_" + clusterId + "_" + new SecureRandom().nextInt())
.withBinds(new Bind(CUSTOM_CONFIG_PATH, new Volume(CUSTOM_CONFIG_PATH), AccessMode.ro))
.withEnv("JAVA_OPTS=-Xms128m -Xmx256m")
.withCmd(
ElasticsearchCLIParameter.ELASTICSEARCH_SETTINGS_LOCATION, configPath,
ZookeeperCLIParameter.ZOOKEEPER_MESOS_URL, getZookeeperMesosUrl(),
ELASTICSEARCH_CPU, "0.25",
ELASTICSEARCH_RAM, "256",
ELASTICSEARCH_DISK, "10",
USE_IP_ADDRESS, "true"
);
}

public String getZookeeperMesosUrl() {
return "zk://" + zooKeeperContainer.getIpAddress() + ":2181/mesos";
}

@Override
public String getRole() {
return TEST_CONFIG.getSchedulerName();
}
}

/**
* Adds a volume to the sandbox location
*/
private static class MesosSlaveWithVolume extends MesosSlaveTagged {
public static final String MESOS_SANDBOX = "/tmp/mesos/slaves";

public MesosSlaveWithVolume(ZooKeeper zooKeeperContainer, String resources) {
super(zooKeeperContainer, resources);
}

@Override
protected CreateContainerCmd dockerCommand() {
final CreateContainerCmd command = super.dockerCommand();
List<Bind> binds = new ArrayList<>();
binds.addAll(Arrays.asList(command.getBinds()));
binds.add(new Bind(MESOS_SANDBOX, new Volume(MESOS_SANDBOX), AccessMode.rw));
binds.add(new Bind(CUSTOM_CONFIG_PATH, new Volume(CUSTOM_CONFIG_PATH), AccessMode.ro));
return command.withBinds(binds.toArray(new Bind[binds.size()]));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public static void startCluster() {

final AlpineContainer ymlWrite = new AlpineContainer(dockerClient, CUSTOM_CONFIG_PATH, CUSTOM_CONFIG_PATH,
"sh", "-c", "echo \"index.auto_expand_replicas: " + TEST_AUTO_EXPAND_REPLICAS + "\npath.plugins: " + TEST_PATH_PLUGINS + "\" > " + CUSTOM_CONFIG_FILE);
ymlWrite.start(10);
ymlWrite.start(TEST_CONFIG.getClusterTimeout());
ymlWrite.remove();

ClusterArchitecture.Builder builder = new ClusterArchitecture.Builder()
Expand Down

0 comments on commit 5c67058

Please sign in to comment.