|
@@ -0,0 +1,244 @@
|
|
|
|
+/*
|
|
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
+ * or more contributor license agreements. See the NOTICE file
|
|
|
|
+ * distributed with this work for additional information
|
|
|
|
+ * regarding copyright ownership. The ASF 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.apache.ambari.logsearch.steps;
|
|
|
|
+
|
|
|
|
+import com.github.dockerjava.api.DockerClient;
|
|
|
|
+import com.github.dockerjava.api.command.CreateContainerResponse;
|
|
|
|
+import com.github.dockerjava.api.model.Bind;
|
|
|
|
+import com.github.dockerjava.api.model.Container;
|
|
|
|
+import com.github.dockerjava.api.model.ExposedPort;
|
|
|
|
+import com.github.dockerjava.api.model.Ports;
|
|
|
|
+import com.github.dockerjava.api.model.Volume;
|
|
|
|
+import com.github.dockerjava.core.DockerClientBuilder;
|
|
|
|
+import com.github.dockerjava.core.DockerClientConfig;
|
|
|
|
+import com.github.dockerjava.core.command.BuildImageResultCallback;
|
|
|
|
+import com.google.common.base.Preconditions;
|
|
|
|
+import org.apache.ambari.logsearch.domain.StoryDataRegistry;
|
|
|
|
+import org.apache.commons.lang.ArrayUtils;
|
|
|
|
+import org.apache.solr.client.solrj.SolrQuery;
|
|
|
|
+import org.apache.solr.client.solrj.SolrServerException;
|
|
|
|
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
|
|
|
|
+import org.apache.solr.client.solrj.response.QueryResponse;
|
|
|
|
+import org.apache.solr.common.SolrDocumentList;
|
|
|
|
+import org.jbehave.core.annotations.AfterStories;
|
|
|
|
+import org.jbehave.core.annotations.BeforeStories;
|
|
|
|
+import org.jbehave.core.annotations.Given;
|
|
|
|
+import org.jbehave.core.annotations.When;
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
+
|
|
|
|
+import java.io.File;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.net.InetSocketAddress;
|
|
|
|
+import java.net.Socket;
|
|
|
|
+import java.net.URL;
|
|
|
|
+import java.util.List;
|
|
|
|
+
|
|
|
|
+public class LogSearchDockerSteps {
|
|
|
|
+
|
|
|
|
+ private static final Logger LOG = LoggerFactory.getLogger(LogSearchDockerSteps.class);
|
|
|
|
+
|
|
|
|
+ @Given("logsearch docker container")
|
|
|
|
+ public void setupLogSearchContainer() throws Exception {
|
|
|
|
+ boolean logsearchStarted = StoryDataRegistry.INSTANCE.isLogsearchContainerStarted();
|
|
|
|
+ if (!logsearchStarted) {
|
|
|
|
+ DockerClient dockerClient = StoryDataRegistry.INSTANCE.getDockerClient();
|
|
|
|
+ LOG.info("Create new docker container for Log Search ..");
|
|
|
|
+ URL location = LogSearchDockerSteps.class.getProtectionDomain().getCodeSource().getLocation();
|
|
|
|
+ String ambariFolder = new File(location.toURI()).getParentFile().getParentFile().getParentFile().getParent();
|
|
|
|
+ StoryDataRegistry.INSTANCE.setAmbariFolder(ambariFolder);
|
|
|
|
+ String dockerBaseDirectory = ambariFolder + "/ambari-logsearch/docker";
|
|
|
|
+ String dockerFileLocation = dockerBaseDirectory + "/Dockerfile";
|
|
|
|
+
|
|
|
|
+ String imageId = dockerClient.buildImageCmd()
|
|
|
|
+ .withTag("ambari-logsearch:v1.0")
|
|
|
|
+ .withBaseDirectory(new File(dockerBaseDirectory))
|
|
|
|
+ .withDockerfile(new File(dockerFileLocation))
|
|
|
|
+ .exec(new BuildImageResultCallback())
|
|
|
|
+ .awaitImageId();
|
|
|
|
+ LOG.info("Docker image id: {}", imageId);
|
|
|
|
+
|
|
|
|
+ removeLogSearchContainerIfExists();
|
|
|
|
+
|
|
|
|
+ // volume bindings
|
|
|
|
+ Volume testLogsVolume = new Volume("/root/test-logs");
|
|
|
|
+ Volume testConfigVolume = new Volume("/root/test-config");
|
|
|
|
+ Volume ambariVolume = new Volume("/root/ambari");
|
|
|
|
+ Volume logfeederClassesVolume = new Volume("/root/ambari/ambari-logsearch/ambari-logsearch-logfeeder/target/package/classes");
|
|
|
|
+ Volume logsearchClassesVolume = new Volume("/root/ambari/ambari-logsearch/ambari-logsearch-portal/target/package/classes");
|
|
|
|
+ Volume logsearchWebappVolume = new Volume("/root/ambari/ambari-logsearch/ambari-logsearch-portal/target/package/classes/webapps/app");
|
|
|
|
+ Bind testLogsBind = new Bind(ambariFolder +"/ambari-logsearch/docker/test-logs", testLogsVolume);
|
|
|
|
+ Bind testConfigBind = new Bind(ambariFolder +"/ambari-logsearch/docker/test-config", testConfigVolume);
|
|
|
|
+ Bind ambariRootBind = new Bind(ambariFolder, ambariVolume);
|
|
|
|
+ Bind logfeederClassesBind = new Bind(ambariFolder + "/ambari-logsearch/ambari-logsearch-logfeeder/target/classes", logfeederClassesVolume);
|
|
|
|
+ Bind logsearchClassesBind = new Bind(ambariFolder + "/ambari-logsearch/ambari-logsearch-portal/target/classes", logsearchClassesVolume);
|
|
|
|
+ Bind logsearchWebappBind = new Bind(ambariFolder + "/ambari-logsearch/ambari-logsearch-portal/src/main/webapp", logsearchWebappVolume);
|
|
|
|
+
|
|
|
|
+ // port bindings
|
|
|
|
+ Ports ports = new Ports();
|
|
|
|
+ ports.bind(new ExposedPort(5005), new Ports.Binding("0.0.0.0", "5005"));
|
|
|
|
+ ports.bind(new ExposedPort(5006), new Ports.Binding("0.0.0.0", "5006"));
|
|
|
|
+ ports.bind(new ExposedPort(StoryDataRegistry.INSTANCE.getSolrPort()), new Ports.Binding("0.0.0.0", "8886"));
|
|
|
|
+ ports.bind(new ExposedPort(StoryDataRegistry.INSTANCE.getLogsearchPort()), new Ports.Binding("0.0.0.0", "61888"));
|
|
|
|
+ ports.bind(new ExposedPort(StoryDataRegistry.INSTANCE.getZookeeperPort()), new Ports.Binding("0.0.0.0", "9983"));
|
|
|
|
+
|
|
|
|
+ LOG.info("Creating docker cointainer...");
|
|
|
|
+ CreateContainerResponse createResponse = dockerClient.createContainerCmd("ambari-logsearch:v1.0")
|
|
|
|
+ .withHostName("logsearch.apache.org")
|
|
|
|
+ .withName("logsearch")
|
|
|
|
+ .withVolumes(testLogsVolume, testConfigVolume, ambariVolume, logfeederClassesVolume, logsearchClassesVolume, logsearchWebappVolume)
|
|
|
|
+ .withBinds(testLogsBind, testConfigBind, ambariRootBind, logfeederClassesBind, logsearchClassesBind, logsearchWebappBind)
|
|
|
|
+ .withExposedPorts(
|
|
|
|
+ new ExposedPort(StoryDataRegistry.INSTANCE.getLogsearchPort()),
|
|
|
|
+ new ExposedPort(5005),
|
|
|
|
+ new ExposedPort(5006),
|
|
|
|
+ new ExposedPort(StoryDataRegistry.INSTANCE.getSolrPort()),
|
|
|
|
+ new ExposedPort(StoryDataRegistry.INSTANCE.getZookeeperPort()))
|
|
|
|
+ .withPortBindings(ports)
|
|
|
|
+ .exec();
|
|
|
|
+ LOG.info("Created docker container id: {}", createResponse.getId());
|
|
|
|
+
|
|
|
|
+ dockerClient.startContainerCmd(createResponse.getId()).exec();
|
|
|
|
+ StoryDataRegistry.INSTANCE.setLogsearchContainerStarted(true);
|
|
|
|
+ String dockerHostFromUri = StoryDataRegistry.INSTANCE.getDockerClientConfig().getDockerHost().getHost();
|
|
|
|
+ StoryDataRegistry.INSTANCE.setDockerHost(dockerHostFromUri);
|
|
|
|
+ checkHostAndPortReachable(dockerHostFromUri, StoryDataRegistry.INSTANCE.getLogsearchPort(), "LogSearch");
|
|
|
|
+ waitUntilSolrHasAnyData();
|
|
|
|
+
|
|
|
|
+ LOG.info("Waiting for logfeeder to finish the test log parsings... (10 sec)");
|
|
|
|
+ Thread.sleep(10000);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @When("logfeeder started (parse logs & send data to solr)")
|
|
|
|
+ public void logfeederStarted() throws Exception {
|
|
|
|
+ // TODO: run ps aux to check LogFeeder process with docker exec
|
|
|
|
+ /**
|
|
|
|
+ DockerClient dockerClient = StoryDataRegistry.INSTANCE.getDockerClient();
|
|
|
|
+ ExecCreateCmdResponse execResp = dockerClient
|
|
|
|
+ .execCreateCmd(containerId)
|
|
|
|
+ .withAttachStdout(true)
|
|
|
|
+ .withCmd("ps", "aux").exec();
|
|
|
|
+ execResp.getId();
|
|
|
|
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
|
|
+ ExecStartResultCallback res = dockerClient
|
|
|
|
+ .execStartCmd(execResp.getId())
|
|
|
|
+ .withDetach(true)
|
|
|
|
+ .withTty(true)
|
|
|
|
+ .exec(new ExecStartResultCallback(outputStream, outputStream)).awaitCompletion();
|
|
|
|
+ **/
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @BeforeStories
|
|
|
|
+ public void checkDockerApi() {
|
|
|
|
+ LOG.info("Tries to setup docker client configuration");
|
|
|
|
+ final String dockerHost = System.getenv("DOCKER_HOST");
|
|
|
|
+ final String dockerCertPath = System.getenv("DOCKER_CERT_PATH");
|
|
|
|
+ final String dockerApiVersion = System.getenv("DOCKER_API_VERSION") == null ? "1.20" : System.getenv("DOCKER_API_VERSION");
|
|
|
|
+
|
|
|
|
+ Preconditions.checkArgument(dockerHost != null, "Set 'DOCKER_HOST' env variable");
|
|
|
|
+ Preconditions.checkArgument(dockerCertPath != null, "Set 'DOCKER_CERT_PATH' env variable");
|
|
|
|
+ LOG.info("DOCKER_HOST: {}", dockerHost);
|
|
|
|
+ LOG.info("DOCKER_CERT_PATH: {}", dockerCertPath);
|
|
|
|
+ LOG.info("DOCKER_API_VERSION: {}", dockerApiVersion);
|
|
|
|
+ DockerClientConfig dockerClientConfig = DockerClientConfig.createDefaultConfigBuilder()
|
|
|
|
+ .withDockerHost(dockerHost)
|
|
|
|
+ .withDockerCertPath(dockerCertPath)
|
|
|
|
+ .withApiVersion(dockerApiVersion)
|
|
|
|
+ .withDockerTlsVerify(true)
|
|
|
|
+ .build();
|
|
|
|
+ StoryDataRegistry.INSTANCE.setDockerClientConfig(dockerClientConfig);
|
|
|
|
+ DockerClient dockerClient = DockerClientBuilder.getInstance(dockerClientConfig).build();
|
|
|
|
+ StoryDataRegistry.INSTANCE.setDockerClient(dockerClient);
|
|
|
|
+ LOG.info("Docker client setup successfully.");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @AfterStories
|
|
|
|
+ public void removeLogSearchContainer() {
|
|
|
|
+ removeLogSearchContainerIfExists();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void removeLogSearchContainerIfExists() {
|
|
|
|
+ DockerClient dockerClient = StoryDataRegistry.INSTANCE.getDockerClient();
|
|
|
|
+ List<Container> containerList = dockerClient
|
|
|
|
+ .listContainersCmd()
|
|
|
|
+ .withShowAll(true)
|
|
|
|
+ .exec();
|
|
|
|
+
|
|
|
|
+ boolean isLogSearchContainerExists = false;
|
|
|
|
+ String containerId = null;
|
|
|
|
+ for (Container container : containerList) {
|
|
|
|
+ isLogSearchContainerExists = ArrayUtils.contains(container.getNames(), "/logsearch");
|
|
|
|
+ if (isLogSearchContainerExists) {
|
|
|
|
+ containerId = container.getId();
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (isLogSearchContainerExists) {
|
|
|
|
+ LOG.info("Remove logsearch container: {}", containerId);
|
|
|
|
+ dockerClient.removeContainerCmd(containerId).withForce(true).exec();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void waitUntilSolrHasAnyData() throws IOException, SolrServerException, InterruptedException {
|
|
|
|
+ boolean solrHasData = false;
|
|
|
|
+ CloudSolrClient solrClient = new CloudSolrClient(String.format("%s:%d",
|
|
|
|
+ StoryDataRegistry.INSTANCE.getDockerHost(),
|
|
|
|
+ StoryDataRegistry.INSTANCE.getZookeeperPort()));
|
|
|
|
+ StoryDataRegistry.INSTANCE.setCloudSolrClient(solrClient);
|
|
|
|
+ SolrQuery solrQuery = new SolrQuery();
|
|
|
|
+ solrQuery.setQuery("*:*");
|
|
|
|
+
|
|
|
|
+ int maxTries = 60;
|
|
|
|
+ for (int tries = 1; tries < maxTries; tries++) {
|
|
|
|
+ QueryResponse queryResponse = solrClient.query(StoryDataRegistry.INSTANCE.getServiceLogsCollection(), solrQuery);
|
|
|
|
+ SolrDocumentList list = queryResponse.getResults();
|
|
|
|
+ if (list.size() > 0) {
|
|
|
|
+ solrHasData = true;
|
|
|
|
+ break;
|
|
|
|
+ } else {
|
|
|
|
+ Thread.sleep(2000);
|
|
|
|
+ LOG.info("Solr has no data yet, retrying...");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (!solrHasData) {
|
|
|
|
+ throw new IllegalStateException(String.format("Solr has no data after %d tries", maxTries));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private void checkHostAndPortReachable(String host, int port, String serviceName) throws InterruptedException {
|
|
|
|
+ boolean reachable = false;
|
|
|
|
+ int maxTries = 60;
|
|
|
|
+ for (int tries = 1; tries < maxTries; tries++ ) {
|
|
|
|
+ try (Socket socket = new Socket()) {
|
|
|
|
+ socket.connect(new InetSocketAddress(host, port), 1000);
|
|
|
|
+ reachable = true;
|
|
|
|
+ break;
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ Thread.sleep(2000);
|
|
|
|
+ LOG.info("{} is not reachable yet, retrying..", serviceName);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (!reachable) {
|
|
|
|
+ throw new IllegalStateException(String.format("%s is not reachable after %s tries", serviceName, maxTries));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|