Start and stop analytics dashboard

Start/stop dashboard by running services in the docker compose

Change-Id: I6a708345915d6e0959966e5122520d6590572d86
diff --git a/src/main/resources/static/analytics-dashboard.html b/src/main/resources/static/analytics-dashboard.html
index 33524b1..dd4b718 100644
--- a/src/main/resources/static/analytics-dashboard.html
+++ b/src/main/resources/static/analytics-dashboard.html
@@ -32,6 +32,22 @@
     <div id="config-error-alert" class="alert-danger alert-trim" role="alert" style="display: none;">
         Something went wrong while creating Dashboard configuration.
     </div>
+    <br><br>
+    <input type="button" class="btn btn-primary" value="Start dashboard" onClick="dashboardService('up')"/>
+    <input type="button" class="btn btn-primary" value="Stop dashboard" onClick="dashboardService('down')"/>
+    <br><br>
+    <div id="up-ok-alert" class="alert alert-success alert-trim" role="alert" style="display: none;">
+        Dashboard started successfully.
+    </div>
+    <div id="down-ok-alert" class="alert alert-success alert-trim" role="alert" style="display: none;">
+        Dashboard stopped successfully.
+    </div>
+    <div id="up-error-alert" class="alert-danger alert-trim" role="alert" style="display: none;">
+        Error starting the dashboard.
+    </div>
+    <div id="down-error-alert" class="alert-danger alert-trim" role="alert" style="display: none;">
+        Error stopping the dashboard.
+    </div>
 </div>
 
 <!--div.container end -->
diff --git a/src/main/resources/static/js/analyticswizard.js b/src/main/resources/static/js/analyticswizard.js
index d8b2caf..0b0cc79 100644
--- a/src/main/resources/static/js/analyticswizard.js
+++ b/src/main/resources/static/js/analyticswizard.js
@@ -1,3 +1,17 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// 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.
+
 var handle201 = function(data, textStatus, jqXHR) {
     $("#config-error-alert").hide();
     $("#config-created-alert").show();
@@ -31,6 +45,36 @@
     });
 }
 
+var handle201Status = function(command) {
+  $("#up-error-alert").hide();
+  $("#down-error-alert").hide();
+  $("#up-ok-alert").hide();
+  $("#down-ok-alert").hide();
+  $("#" + command + "-ok-alert").show();
+};
+
+function dashboardService(command) {
+    var projectName = $("#input-project-name").val();
+    $.ajax({
+      type : "POST",
+      url : `/a/projects/${projectName}/analytics-wizard~server`,
+      dataType: 'application/json',
+      // Initially project-dashboard is a 1 to 1 relationship
+      data: "{'action': '" + command + "'}",
+      contentType:"application/json; charset=utf-8",
+      // Need to catch the status code since Gerrit doesn't return
+      // a well formed JSON, hence Ajax treats it as an error
+      statusCode: {
+        201: handle201Status(command)
+      },
+      error: function(jqXHR, textStatus, errorThrown) {
+        if(jqXHR.status != 201) {
+          handleError()
+        }
+      }
+    });
+}
+
 function showConfigDetails() {
     var projectName = $("#input-project-name").val();
     $.ajax({
diff --git a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetup.scala b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetup.scala
index 2b0209d..aec2268 100644
--- a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetup.scala
+++ b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetup.scala
@@ -34,21 +34,37 @@
     s"""
        |version: '3'
        |services:
+       |
+       |  dashboard-importer:
+       |    image: gerritforge/analytics-dashboard-importer:latest
+       |    networks:
+       |      - ek
+       |    links:
+       |      - elasticsearch
+       |      - kibana
+       |
        |  kibana:
        |    image: gerritforge/analytics-kibana:latest
        |    container_name: "kibana-for-${name}-project"
-       |    environment:
-       |      SERVER_BASEPATH: "/kibana"
+       |    networks:
+       |      - ek
        |    depends_on:
        |      - elasticsearch
+       |    ports:
+       |      - "5601:5601"
+       |
        |  elasticsearch:
        |    image: gerritforge/analytics-elasticsearch:latest
        |    container_name: "es-for-${name}-project"
+       |    networks:
+       |      - ek
        |    environment:
        |      - ES_JAVA_OPTS=-Xmx4g -Xms4g
        |      - http.host=0.0.0.0
-       |    volumes:
-       |      - es-indexes:/usr/share/elasticsearch/data
+       |
+       |networks:
+       |  ek:
+       |    driver: bridge
      """.stripMargin
   }
 
diff --git a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticsWizardActions.scala b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticsWizardActions.scala
index f0f6632..44a5512 100644
--- a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticsWizardActions.scala
+++ b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticsWizardActions.scala
@@ -13,18 +13,21 @@
 // limitations under the License.
 package com.googlesource.gerrit.plugins.analytics.wizard
 
+import java.nio.charset.StandardCharsets.UTF_8
+
+import com.google.common.io.ByteStreams
 import com.google.gerrit.extensions.restapi.{
   Response,
+  RestApiException,
   RestModifyView,
   RestReadView
 }
 import com.google.gerrit.server.project.ProjectResource
 import com.google.inject.Inject
+import AnalyticDashboardSetup.writer
 
 import scala.io.Source
 
-import AnalyticDashboardSetup.writer
-
 class GetAnalyticsStack @Inject()() extends RestReadView[ProjectResource] {
   override def apply(
       resource: ProjectResource): Response[AnalyticDashboardSetup] = {
@@ -53,3 +56,32 @@
     Response.created(s"Dashboard configuration created for $projectName!")
   }
 }
+
+class DockerComposeCommand(var action: String)
+class PostAnalyticsStack @Inject()()
+    extends RestModifyView[ProjectResource, DockerComposeCommand] {
+  override def apply(resource: ProjectResource,
+                     input: DockerComposeCommand): Response[String] = {
+
+    val projectName = resource.getControl.getProject.getName
+    val pb = new ProcessBuilder(
+      "docker-compose",
+      "-f",
+      s"/tmp/docker-compose.${projectName}.yaml",
+      input.action.toLowerCase) // XXX validate command!
+    pb.redirectErrorStream(true)
+
+    val ps: Process = pb.start
+    ps.getOutputStream.close
+    val output = new String(ByteStreams.toByteArray(ps.getInputStream), UTF_8)
+    ps.waitFor
+
+    ps.exitValue match {
+      case 0 => Response.created(output)
+      case _ =>
+        throw new RestApiException(
+          s"Failed with exit code: ${ps.exitValue} - $output")
+    }
+
+  }
+}
diff --git a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/Module.scala b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/Module.scala
index 9c77724..c9e1021 100644
--- a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/Module.scala
+++ b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/Module.scala
@@ -13,9 +13,7 @@
 // limitations under the License.
 package com.googlesource.gerrit.plugins.analytics.wizard
 
-import com.google.gerrit.extensions.registration.DynamicSet
 import com.google.gerrit.extensions.restapi.RestApiModule
-import com.google.gerrit.httpd.AllRequestFilter
 import com.google.gerrit.server.project.ProjectResource.PROJECT_KIND
 import com.google.inject.AbstractModule
 
@@ -27,6 +25,8 @@
         get(PROJECT_KIND, "stack").to(classOf[GetAnalyticsStack])
 
         put(PROJECT_KIND, "stack").to(classOf[PutAnalyticsStack])
+
+        post(PROJECT_KIND, "server").to(classOf[PostAnalyticsStack])
       }
     })
   }