URL Encode project name Project name need to be URL encoded to allow Gerrit REST API to find the resource. Furthermore Docker compose doesn't like container names with '/', hence we replace it with '-' and add a timestamp to make them unique. Bug: Issue 9067 Change-Id: I4fe990ff20946a43e08a2f1505676d36b1514ffd
diff --git a/src/main/resources/static/js/analyticswizard.js b/src/main/resources/static/js/analyticswizard.js index 0b0cc79..579285e 100644 --- a/src/main/resources/static/js/analyticswizard.js +++ b/src/main/resources/static/js/analyticswizard.js
@@ -24,7 +24,7 @@ function submitDetailsForm() { - var projectName = $("#input-project-name").val(); + var projectName = encodeURIComponent($("#input-project-name").val()); $.ajax({ type : "PUT", url : `/a/projects/${projectName}/analytics-wizard~stack`, @@ -54,7 +54,7 @@ }; function dashboardService(command) { - var projectName = $("#input-project-name").val(); + var projectName = encodeURIComponent($("#input-project-name").val()); $.ajax({ type : "POST", url : `/a/projects/${projectName}/analytics-wizard~server`,
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 1c07435..f288fab 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
@@ -13,8 +13,7 @@ // limitations under the License. package com.googlesource.gerrit.plugins.analytics.wizard -import java.io.PrintWriter -import java.nio.charset.{Charset, StandardCharsets} +import java.nio.charset.StandardCharsets import java.nio.file.{Files, Path} trait ConfigWriter { @@ -30,7 +29,13 @@ case class AnalyticDashboardSetup(name: String, dockerComposeYamlPath: Path)( implicit val writer: ConfigWriter) { - private val dockerComposeTemplate = { (name: String) => + // Docker doesn't like container names with '/', hence the replace with '-' + // Furthermore timestamp has been added to avoid conflicts among container names, i.e.: + // A project named 'foo/bar' would be encoded as 'foo-bar' and thus its container + // would be potentially in conflict with another 'foo-bar' project's one + private val sanitisedName = + s"${name.replace("/", "-")}-${System.currentTimeMillis}" + private val dockerComposeTemplate = { s""" |version: '3' |services: @@ -45,7 +50,7 @@ | | kibana: | image: gerritforge/analytics-kibana:latest - | container_name: "kibana-for-${name}-project" + | container_name: "kibana-for-${sanitisedName}-project" | networks: | - ek | depends_on: @@ -55,7 +60,7 @@ | | elasticsearch: | image: gerritforge/analytics-elasticsearch:latest - | container_name: "es-for-${name}-project" + | container_name: "es-for-${sanitisedName}-project" | networks: | - ek | environment: @@ -69,7 +74,7 @@ } def createDashboardSetupFile(): Unit = { - writer.write(dockerComposeYamlPath, dockerComposeTemplate(name)) + writer.write(dockerComposeYamlPath, dockerComposeTemplate) } }
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 f63447c..6763e3a 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,10 +13,12 @@ // limitations under the License. package com.googlesource.gerrit.plugins.analytics.wizard +import java.net.URLEncoder import java.nio.charset.StandardCharsets.UTF_8 import java.nio.file.Path import com.google.common.io.ByteStreams +import com.google.gerrit.extensions.annotations.PluginData import com.google.gerrit.extensions.restapi.{ Response, RestApiException, @@ -25,12 +27,10 @@ } import com.google.gerrit.server.project.ProjectResource import com.google.inject.Inject -import AnalyticDashboardSetup.writer -import com.google.gerrit.extensions.annotations.PluginData +import com.googlesource.gerrit.plugins.analytics.wizard.AnalyticDashboardSetup.writer -import scala.io.Source - -class GetAnalyticsStack @Inject()(@PluginData val dataPath: Path) extends RestReadView[ProjectResource] { +class GetAnalyticsStack @Inject()(@PluginData val dataPath: Path) + extends RestReadView[ProjectResource] { override def apply( resource: ProjectResource): Response[AnalyticDashboardSetup] = { @@ -50,8 +50,15 @@ input: Input): Response[String] = { val projectName = resource.getControl.getProject.getName - AnalyticDashboardSetup(projectName, dataPath.resolve(s"docker-compose.${projectName}.yaml")).createDashboardSetupFile() - Response.created(s"Dashboard configuration created for $projectName!") + val encodedName = AnalyticsWizardActions + .encodedName(projectName) + + AnalyticDashboardSetup( + projectName, + dataPath.resolve(s"docker-compose.${encodedName}.yaml")) + .createDashboardSetupFile() + Response.created(s"Dashboard configuration created for $encodedName!") + } } @@ -62,16 +69,20 @@ input: DockerComposeCommand): Response[String] = { val projectName = resource.getControl.getProject.getName + val encodedName = AnalyticsWizardActions + .encodedName(projectName) + val pb = new ProcessBuilder( "docker-compose", "-f", - s"${dataPath.toFile.getAbsolutePath}/docker-compose.${projectName}.yaml", + s"${dataPath.toFile.getAbsolutePath}/docker-compose.${encodedName}.yaml", input.action.toLowerCase) pb.redirectErrorStream(true) val ps: Process = pb.start ps.getOutputStream.close - val output = new String(ByteStreams.toByteArray(ps.getInputStream), UTF_8) + val output = + new String(ByteStreams.toByteArray(ps.getInputStream), UTF_8) ps.waitFor ps.exitValue match { @@ -83,3 +94,14 @@ } } + +object AnalyticsWizardActions { + // URLEncoder could potentially throw UnsupportedEncodingException, + // but UTF-8 will *always* be resolved, otherwise, Gerrit wouldn't work at all + def encodedName(name: String) = + try { + URLEncoder.encode(name, "UTF-8") + } catch { + case e: Throwable => throw new RuntimeException(e) + } +}