/*
* Copyright 2016 The Simple File Server Authors
*
* Licensed 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.sfs.integration.java;
import com.google.common.net.HostAndPort;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.RunTestOnContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.elasticsearch.client.Client;
import org.elasticsearch.node.Node;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.sfs.Server;
import org.sfs.SfsServer;
import org.sfs.TestSubscriber;
import org.sfs.VertxContext;
import org.sfs.integration.java.func.WaitForCluster;
import org.sfs.rx.ObservableFuture;
import org.sfs.rx.RxHelper;
import org.sfs.rx.ToVoid;
import rx.Observable;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkState;
import static io.vertx.core.logging.LoggerFactory.getLogger;
import static java.lang.String.format;
import static java.lang.String.valueOf;
import static java.lang.System.currentTimeMillis;
import static java.lang.Thread.currentThread;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.createTempDirectory;
import static org.elasticsearch.common.settings.Settings.Builder;
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
import static org.elasticsearch.node.NodeBuilder.nodeBuilder;
import static org.sfs.rx.Defer.aVoid;
import static rx.Observable.from;
import static rx.Observable.just;
@RunWith(VertxUnitRunner.class)
public class BaseTestVerticle {
private static final Logger LOGGER = getLogger(BaseTestVerticle.class);
protected Path rootTmpDir;
private Path esTempDir;
private String esClusterName;
private String esNodeName;
private String esHost;
protected Client esClient;
protected Path tmpDir;
private Node esNode;
protected Vertx vertx;
protected HttpClient httpClient;
protected HttpClient httpsClient;
protected VertxContext<Server> vertxContext;
@Rule
public RunTestOnContext rule = new RunTestOnContext();
@Before
public void before(TestContext context) {
vertx = rule.vertx();
Async async = context.async();
aVoid()
.flatMap(aVoid -> {
String clusteruuid = UUID.randomUUID().toString();
try {
rootTmpDir = createTempDirectory("");
esTempDir = createTempDirectory(rootTmpDir, format("test-cluster-%s", clusteruuid));
tmpDir = createTempDirectory(rootTmpDir, valueOf(currentTimeMillis()));
} catch (IOException e) {
throw new RuntimeException(e);
}
int esPort = findFreePort(9300, 9400);
esHost = "127.0.0.1:" + esPort;
esClusterName = format("test-cluster-%s", clusteruuid);
esNodeName = format("test-server-node-%s", clusteruuid);
Builder settings = settingsBuilder();
settings.put("script.groovy.sandbox.enabled", false);
settings.put("cluster.name", esClusterName);
settings.put("node.name", esNodeName);
settings.put("http.enabled", false);
settings.put("discovery.zen.ping.multicast.enabled", false);
settings.put("discovery.zen.ping.unicast.hosts", esHost);
settings.put("transport.tcp.port", esPort);
settings.put("network.host", "127.0.0.1");
settings.put("node.data", true);
settings.put("node.master", true);
settings.put("path.home", esTempDir);
esNode = nodeBuilder()
.settings(settings)
.node();
esClient = esNode.client();
JsonObject verticleConfig;
Buffer buffer = vertx.fileSystem().readFileBlocking(currentThread().getContextClassLoader().getResource("intgtestconfig.json").getFile());
verticleConfig = new JsonObject(buffer.toString(UTF_8));
verticleConfig.put("fs.home", tmpDir.toString());
if (!verticleConfig.containsKey("elasticsearch.cluster.name")) {
verticleConfig.put("elasticsearch.cluster.name", esClusterName);
}
if (!verticleConfig.containsKey("elasticsearch.node.name")) {
verticleConfig.put("elasticsearch.node.name", esNodeName);
}
if (!verticleConfig.containsKey("elasticsearch.discovery.zen.ping.unicast.hosts")) {
verticleConfig.put("elasticsearch.discovery.zen.ping.unicast.hosts", new JsonArray().add(esHost));
}
if (!verticleConfig.containsKey("http.listen.addresses")) {
int freePort = findFreePort(6677, 7777);
verticleConfig.put("http.listen.addresses", new JsonArray().add(HostAndPort.fromParts("127.0.0.1", freePort).toString()));
}
HostAndPort hostAndPort = HostAndPort.fromString(verticleConfig.getJsonArray("http.listen.addresses").getString(0));
HttpClientOptions httpClientOptions = new HttpClientOptions();
httpClientOptions.setDefaultPort(hostAndPort.getPort())
.setDefaultHost(hostAndPort.getHostText())
.setMaxPoolSize(25)
.setConnectTimeout(1000)
.setKeepAlive(false)
.setLogActivity(true);
HttpClientOptions httpsClientOptions = new HttpClientOptions();
httpsClientOptions.setDefaultPort(hostAndPort.getPort())
.setDefaultHost(hostAndPort.getHostText())
.setMaxPoolSize(25)
.setConnectTimeout(1000)
.setKeepAlive(false)
.setLogActivity(true)
.setSsl(true);
httpClient = vertx.createHttpClient(httpClientOptions);
httpsClient = vertx.createHttpClient(httpsClientOptions);
SfsServer sfsServer = new SfsServer();
ObservableFuture<String> handler = RxHelper.observableFuture();
vertx.deployVerticle(
sfsServer,
new DeploymentOptions()
.setConfig(verticleConfig),
handler.toHandler());
return handler
.map(new ToVoid<>())
.doOnNext(aVoid1 -> {
vertxContext = sfsServer.vertxContext();
checkState(vertxContext != null, "VertxContext was null on Verticle %s", sfsServer);
})
.onErrorResumeNext(throwable -> {
throwable.printStackTrace();
return cleanup().flatMap(aVoid1 -> Observable.<Void>error(throwable));
});
}).subscribe(new TestSubscriber(context, async));
}
protected VertxContext<Server> vertxContext() {
return vertxContext;
}
protected Observable<Void> cleanup() {
Set<String> deploymentIds = vertx != null ? vertx.deploymentIDs() : Collections.emptySet();
return from(deploymentIds)
.flatMap(deploymentId -> {
ObservableFuture<Void> handler = RxHelper.observableFuture();
vertx.undeploy(deploymentId, handler.toHandler());
return handler;
})
.count()
.map(new ToVoid<>())
.onErrorResumeNext(throwable -> {
throwable.printStackTrace();
return just(null);
})
.doOnNext(aVoid -> {
if (esNode != null) {
esNode.close();
}
if (rootTmpDir != null && vertx != null) {
vertx.fileSystem().deleteRecursiveBlocking(rootTmpDir.toString(), true);
}
})
.doOnNext(aVoid -> {
rootTmpDir = null;
esTempDir = null;
esClusterName = null;
esNodeName = null;
esHost = null;
esClient = null;
tmpDir = null;
esNode = null;
vertx = null;
httpClient = null;
vertxContext = null;
});
}
@After
public void after(TestContext context) {
Async async = context.async();
cleanup()
.subscribe(new TestSubscriber(context, async));
}
private static int findFreePort(int first, int last) {
while (first <= last) {
try (ServerSocket serverSocket = new ServerSocket(first)) {
return first;
} catch (IOException e) {
first++;
// do nothing since we're search for an open port
}
}
throw new RuntimeException("Unable to find free port in range [" + first + " - " + last + "]");
}
}