You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by li...@apache.org on 2020/09/12 07:51:53 UTC
[submarine] branch master updated: SUBMARINE-558. Define Swagger
API for pre-defined template submission
This is an automated email from the ASF dual-hosted git repository.
liuxun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git
The following commit(s) were added to refs/heads/master by this push:
new 57eb259 SUBMARINE-558. Define Swagger API for pre-defined template submission
57eb259 is described below
commit 57eb259f6aebc2e1c918eff7ae13d027f0ef891d
Author: JohnTing <jo...@gmail.com>
AuthorDate: Wed Sep 9 02:45:38 2020 +0800
SUBMARINE-558. Define Swagger API for pre-defined template submission
### What is this PR for?
Make basic rest api for submi template.
Convert submitted template to experiment
post
http://localhost/V1/template/submit
### What type of PR is it?
[Bug Fix | Improvement | Feature | Documentation | Hot Fix | Refactoring]
### Todos
* [x] - API
* [x] - test
### What is the Jira issue?
https://issues.apache.org/jira/projects/SUBMARINE/issues/SUBMARINE-558
### How should this be tested?
```sh
# Register experiment template
curl -X POST -H "Content-Type: application/json" -d '
{
"name": "tf-mnist-test",
"author": "author",
"description": "This is a template to run tf-mnist\n",
"parameters": [
{
"name": "training.learning_rate",
"value": 0.1,
"required": true,
"description": " mnist learning_rate "
},
{
"name": "training.batch_size",
"value": 150,
"required": false,
"description": "This is batch size of training"
}
],
"experimentSpec": {
"meta": {
"cmd": "python /var/tf_mnist/mnist_with_summaries.py --log_dir=/train/log --learning_rate={{training.learning_rate}} --batch_size={{training.batch_size}}",
"name": "tf-mnist-template-test",
"envVars": {
"ENV1": "ENV1"
},
"framework": "TensorFlow",
"namespace": "default"
},
"spec": {
"Ps": {
"replicas": 1,
"resources": "cpu=1,memory=1024M"
},
"Worker": {
"replicas": 1,
"resources": "cpu=1,memory=1024M"
}
},
"environment": {
"image": "gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0"
}
}
}
' http://127.0.0.1:8080/api/v1/template
```
```sh
# Submit experiment template to experiment
curl -X POST -H "Content-Type: application/json" -d '
{
"name": "tf-mnist-test",
"params": {
"training.learning_rate":"0.01",
"training.batch_size":"150"
}
}
' http://127.0.0.1:8080/api/v1/template/submit
```
If the submission is successful, it will return
```json
{
"status": "OK",
"code": 200,
"success": true,
"message": null,
"result": {
"experimentId": "experiment_1597853926000_0035",
"name": "tf-mnist-template-test",
"uid": "0a79d641-871e-4ba6-9e3e-eab1d4690f4e",
"status": "Accepted",
"acceptedTime": "2020-08-22T18:36:32.000+08:00",
"createdTime": null,
"runningTime": null,
"finishedTime": null,
"spec": {
"meta": {
"name": "tf-mnist-template-test",
"namespace": "default",
"framework": "TensorFlow",
"cmd": "python /var/tf_mnist/mnist_with_summaries.py --log_dir=/train/log --learning_rate=0.01 --batch_size=150",
"envVars": {
"ENV1": "ENV1"
}
},
"environment": {
"name": null,
"dockerImage": null,
"kernelSpec": null,
"description": null,
"image": "gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0"
},
"spec": {
"Ps": {
"replicas": 1,
"resources": "cpu=1,memory=1024M",
"name": null,
"image": null,
"cmd": null,
"envVars": null,
"resourceMap": {
"memory": "1024M",
"cpu": "1"
}
},
"Worker": {
"replicas": 1,
"resources": "cpu=1,memory=1024M",
"name": null,
"image": null,
"cmd": null,
"envVars": null,
"resourceMap": {
"memory": "1024M",
"cpu": "1"
}
}
}
}
},
"attributes": {}
}
```
### Screenshots (if appropriate)
![image](https://user-images.githubusercontent.com/19265751/90865269-b64fce00-e3c4-11ea-8e9e-6f3a3a24c892.png)
### Questions:
* Does the licenses files need update? Yes/No
* Is there breaking changes for older versions? Yes/No
* Does this needs documentation? Yes/No
Author: JohnTing <jo...@gmail.com>
Closes #382 from JohnTing/SUBMARINE-558 and squashes the following commits:
1ea2588 [JohnTing] change
e98083b [JohnTing] change
b7b6dd4 [JohnTing] ExperimentAndTemplateMixed api
eca2ec3 [JohnTing] change3
b358d3b [JohnTing] change3
6a4bad2 [JohnTing] change2
7fc82ef [JohnTing] change
27a86f3 [JohnTing] Update submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/ExperimentTemplateManagerRestApiIT.java
1de1d4f [JohnTing] Update submarine-server/server-core/src/main/java/org/apache/submarine/server/experimenttemplate/ExperimentTemplateManager.java
386ff82 [JohnTing] Update submarine-server/server-core/src/main/java/org/apache/submarine/server/experimenttemplate/ExperimentTemplateManager.java
a28d1a5 [JohnTing] Update submarine-server/server-core/src/main/java/org/apache/submarine/server/experimenttemplate/ExperimentTemplateManager.java
e11a097 [JohnTing] add simple doc
64cc6a7 [JohnTing] test1
5060482 [JohnTing] Add testList, testUpdate
4b264cb [JohnTing] usable
---
docs/userdocs/k8s/use-experiment-template.md | 142 +++++++++++++++++++++
.../ExperimentTemplateSubmit.java | 44 +++++++
.../server/api/spec/ExperimentTemplateSpec.java | 8 ++
.../ExperimentTemplateManager.java | 87 ++++++++++---
.../submarine/server/rest/ExperimentRestApi.java | 33 +++++
.../submarine/server/rest/RestConstants.java | 2 +
.../server/rest/ExperimentTemplateRestApiTest.java | 14 +-
.../test_template_1.json | 18 +--
.../experimenttemplate/test_template_2.json | 43 -------
.../rest/ExperimentTemplateManagerRestApiIT.java | 119 ++++++++++++++---
10 files changed, 415 insertions(+), 95 deletions(-)
diff --git a/docs/userdocs/k8s/use-experiment-template.md b/docs/userdocs/k8s/use-experiment-template.md
new file mode 100644
index 0000000..732b29a
--- /dev/null
+++ b/docs/userdocs/k8s/use-experiment-template.md
@@ -0,0 +1,142 @@
+<!--
+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.
+-->
+
+# Use Experiment Template Guide
+
+The {{name}} variable in "experimentSpec" will be replace by the parameters value.
+
+JSON Format example:
+```json
+{
+ "name": "tf-mnist-test",
+ "author": "author",
+ "description": "This is a template to run tf-mnist",
+ "parameters": [
+ {
+ "name": "training.learning_rate",
+ "value": 0.1,
+ "required": true,
+ "description": " mnist learning_rate "
+ },
+ {
+ "name": "training.batch_size",
+ "value": 150,
+ "required": false,
+ "description": "This is batch size of training"
+ }
+ ],
+ "experimentSpec": {
+ "meta": {
+ "cmd": "python /var/tf_mnist/mnist_with_summaries.py --log_dir=/train/log --learning_rate={{training.learning_rate}} --batch_size={{training.batch_size}}",
+ "name": "tf-mnist-template-test",
+ "envVars": {
+ "ENV1": "ENV1"
+ },
+ "framework": "TensorFlow",
+ "namespace": "default"
+ },
+ "spec": {
+ "Ps": {
+ "replicas": 1,
+ "resources": "cpu=1,memory=1024M"
+ },
+ "Worker": {
+ "replicas": 1,
+ "resources": "cpu=1,memory=1024M"
+ }
+ },
+ "environment": {
+ "image": "gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0"
+ }
+ }
+}
+```
+
+### Register experiment template
+```sh
+curl -X POST -H "Content-Type: application/json" -d '
+{
+ "name": "tf-mnist-test",
+ "author": "author",
+ "description": "This is a template to run tf-mnist",
+ "parameters": [
+ {
+ "name": "training.learning_rate",
+ "value": 0.1,
+ "required": true,
+ "description": " mnist learning_rate "
+ },
+ {
+ "name": "training.batch_size",
+ "value": 150,
+ "required": false,
+ "description": "This is batch size of training"
+ }
+ ],
+ "experimentSpec": {
+ "meta": {
+ "cmd": "python /var/tf_mnist/mnist_with_summaries.py --log_dir=/train/log --learning_rate={{training.learning_rate}} --batch_size={{training.batch_size}}",
+ "name": "tf-mnist-template-test",
+ "envVars": {
+ "ENV1": "ENV1"
+ },
+ "framework": "TensorFlow",
+ "namespace": "default"
+ },
+ "spec": {
+ "Ps": {
+ "replicas": 1,
+ "resources": "cpu=1,memory=1024M"
+ },
+ "Worker": {
+ "replicas": 1,
+ "resources": "cpu=1,memory=1024M"
+ }
+ },
+ "environment": {
+ "image": "gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0"
+ }
+ }
+}
+' http://127.0.0.1:8080/api/v1/template
+```
+
+JSON Format example:
+```json
+{
+ "name": "tf-mnist-test",
+ "params": {
+ "training.learning_rate":"0.01",
+ "training.batch_size":"150"
+ }
+}
+```
+
+### Submit experiment template
+```sh
+curl -X POST -H "Content-Type: application/json" -d '
+{
+ "name": "tf-mnist-test",
+ "params": {
+ "training.learning_rate":"0.01",
+ "training.batch_size":"150"
+ }
+}
+' http://127.0.0.1:8080/api/v1/template/submit
+```
diff --git a/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/experimenttemplate/ExperimentTemplateSubmit.java b/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/experimenttemplate/ExperimentTemplateSubmit.java
new file mode 100644
index 0000000..f5a327f
--- /dev/null
+++ b/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/experimenttemplate/ExperimentTemplateSubmit.java
@@ -0,0 +1,44 @@
+/*
+ * 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.submarine.server.api.experimenttemplate;
+
+import java.util.Map;
+
+public class ExperimentTemplateSubmit {
+ // template name
+ String name;
+ Map<String, String> params;
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map<String, String> getParams() {
+ return params;
+ }
+
+ public void setParams(Map<String, String> params) {
+ this.params = params;
+ }
+}
diff --git a/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/spec/ExperimentTemplateSpec.java b/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/spec/ExperimentTemplateSpec.java
index 8dc5b58..1b0deaa 100644
--- a/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/spec/ExperimentTemplateSpec.java
+++ b/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/spec/ExperimentTemplateSpec.java
@@ -67,4 +67,12 @@ public class ExperimentTemplateSpec {
public void setParameters(List<ExperimentTemplateParamSpec> parameters) {
this.parameters = parameters;
}
+
+ public List<ExperimentTemplateParamSpec> getExperimentTemplateParamSpec() {
+ return this.parameters;
+ }
+
+ public void setExperimentTemplateParamSpec(List<ExperimentTemplateParamSpec> parameters) {
+ this.parameters = parameters;
+ }
}
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/experimenttemplate/ExperimentTemplateManager.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/experimenttemplate/ExperimentTemplateManager.java
index feac673..cd8c828 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/experimenttemplate/ExperimentTemplateManager.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/experimenttemplate/ExperimentTemplateManager.java
@@ -34,13 +34,17 @@ import javax.ws.rs.core.Response.Status;
import org.apache.ibatis.session.SqlSession;
import org.apache.submarine.commons.utils.exception.SubmarineRuntimeException;
import org.apache.submarine.server.SubmarineServer;
+import org.apache.submarine.server.api.experiment.Experiment;
import org.apache.submarine.server.api.experimenttemplate.ExperimentTemplate;
import org.apache.submarine.server.api.experimenttemplate.ExperimentTemplateId;
+import org.apache.submarine.server.api.experimenttemplate.ExperimentTemplateSubmit;
import org.apache.submarine.server.api.spec.ExperimentTemplateParamSpec;
import org.apache.submarine.server.api.spec.ExperimentTemplateSpec;
import org.apache.submarine.server.database.utils.MyBatisUtil;
+import org.apache.submarine.server.experiment.ExperimentManager;
import org.apache.submarine.server.experimenttemplate.database.entity.ExperimentTemplateEntity;
import org.apache.submarine.server.experimenttemplate.database.mappers.ExperimentTemplateMapper;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -137,19 +141,27 @@ public class ExperimentTemplateManager {
}
sqlSession.commit();
- ExperimentTemplate experimentTemplate = new ExperimentTemplate();
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ throw new SubmarineRuntimeException(Status.BAD_REQUEST.getStatusCode(),
+ "Unable to insert or update the experimentTemplate spec: " + e.getMessage());
+ }
+
+ ExperimentTemplate experimentTemplate;
+ try {
+ experimentTemplate = new ExperimentTemplate();
experimentTemplate.setExperimentTemplateId(ExperimentTemplateId.fromString(experimentTemplateId));
experimentTemplate.setExperimentTemplateSpec(spec);
- // Update cache
- cachedExperimentTemplates.putIfAbsent(spec.getName(), experimentTemplate);
-
- return experimentTemplate;
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new SubmarineRuntimeException(Status.BAD_REQUEST.getStatusCode(),
- "Unable to process the experimentTemplate spec.");
+ "Unable to parse the experimentTemplate spec: " + e.getMessage());
}
+ // Update cache
+ cachedExperimentTemplates.putIfAbsent(spec.getName(), experimentTemplate);
+
+ return experimentTemplate;
}
private ExperimentTemplateId generateExperimentTemplateId() {
@@ -276,19 +288,59 @@ public class ExperimentTemplateManager {
return tpl;
}
- private ExperimentTemplateSpec parameterMapping(String spec) {
+ /**
+ * Create ExperimentTemplate
+ *
+ * @param SubmittedParam experimentTemplate spec
+ * @return Experiment experiment
+ * @throws SubmarineRuntimeException the service error
+ */
+ public Experiment submitExperimentTemplate(ExperimentTemplateSubmit SubmittedParam)
+ throws SubmarineRuntimeException {
+
+ if (SubmittedParam == null) {
+ throw new SubmarineRuntimeException(Status.BAD_REQUEST.getStatusCode(),
+ "Invalid ExperimentTemplateSubmit spec.");
+ }
+
+ ExperimentTemplate experimentTemplate = getExperimentTemplate(SubmittedParam.getName());
+ Map<String, String> params = SubmittedParam.getParams();
+
+
+ for (ExperimentTemplateParamSpec paramSpec:
+ experimentTemplate.getExperimentTemplateSpec().getExperimentTemplateParamSpec()) {
+
+ String value = params.get(paramSpec.getName());
+ if (value != null) {
+ paramSpec.setValue(value);
+ }
+ }
+ String spec = new Gson().toJson(experimentTemplate.getExperimentTemplateSpec());
+
+ ExperimentTemplateSpec experimentTemplateSpec = parameterMapping(spec, params);
+
+ return ExperimentManager.getInstance().createExperiment(experimentTemplateSpec.getExperimentSpec());
+ }
+
+ private ExperimentTemplateSpec parameterMapping(String spec) {
ExperimentTemplateSpec tplSpec = new Gson().fromJson(spec, ExperimentTemplateSpec.class);
- Map<String, String> parmMap = new HashMap<String, String>();
- for (ExperimentTemplateParamSpec parm : tplSpec.getParameters()) {
+ Map<String, String> paramMap = new HashMap<String, String>();
+ for (ExperimentTemplateParamSpec parm : tplSpec.getExperimentTemplateParamSpec()) {
if (parm.getValue() != null) {
- parmMap.put(parm.getName(), parm.getValue());
+ paramMap.put(parm.getName(), parm.getValue());
} else {
- parmMap.put(parm.getName(), "");
+ paramMap.put(parm.getName(), "");
}
}
+ return parameterMapping(spec, paramMap);
+ }
+
+ // Use params to replace the content of spec
+ private ExperimentTemplateSpec parameterMapping(String spec, Map<String, String> paramMap) {
+
Pattern pattern = Pattern.compile("\\{\\{(.+?)\\}\\}");
StringBuffer sb = new StringBuffer();
Matcher matcher = pattern.matcher(spec);
@@ -297,33 +349,34 @@ public class ExperimentTemplateManager {
while (matcher.find()) {
final String key = matcher.group(1);
- final String replacement = parmMap.get(key);
+ final String replacement = paramMap.get(key);
if (replacement == null) {
unmappedKeys.add(key);
}
else {
matcher.appendReplacement(sb, replacement);
}
- parmMap.remove(key);
+ paramMap.remove(key);
}
matcher.appendTail(sb);
- if (parmMap.size() > 0) {
+ if (paramMap.size() > 0) {
throw new SubmarineRuntimeException(Status.BAD_REQUEST.getStatusCode(),
- "Parameters contains unused key: " + parmMap.keySet());
+ "Parameters contains unused key: " + paramMap.keySet());
}
if (unmappedKeys.size() > 0) {
throw new SubmarineRuntimeException(Status.BAD_REQUEST.getStatusCode(),
"Template contains unmapped key: " + unmappedKeys);
}
+ ExperimentTemplateSpec experimentTemplateSpec;
try {
- tplSpec = new Gson().fromJson(sb.toString(), ExperimentTemplateSpec.class);
+ experimentTemplateSpec = new Gson().fromJson(sb.toString(), ExperimentTemplateSpec.class);
} catch (Exception e) {
throw new SubmarineRuntimeException(Status.BAD_REQUEST.getStatusCode(),
"Template mapping fail: " + e.getMessage() + sb.toString());
}
- return tplSpec;
+ return experimentTemplateSpec;
}
}
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/ExperimentRestApi.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/ExperimentRestApi.java
index b57ae1d..b0cd30e 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/ExperimentRestApi.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/ExperimentRestApi.java
@@ -33,6 +33,7 @@ import javax.ws.rs.core.Response;
import java.util.List;
import com.google.common.annotations.VisibleForTesting;
+
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -41,7 +42,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.apache.submarine.commons.utils.exception.SubmarineRuntimeException;
import org.apache.submarine.server.api.experiment.Experiment;
import org.apache.submarine.server.experiment.ExperimentManager;
+import org.apache.submarine.server.experimenttemplate.ExperimentTemplateManager;
import org.apache.submarine.server.api.experiment.ExperimentLog;
+import org.apache.submarine.server.api.experimenttemplate.ExperimentTemplateSubmit;
import org.apache.submarine.server.api.spec.ExperimentSpec;
import org.apache.submarine.server.response.JsonResponse;
@@ -77,12 +80,14 @@ public class ExperimentRestApi {
.success(true).result("Pong").build();
}
+
/**
* Returns the contents of {@link Experiment} that submitted by user.
*
* @param spec spec
* @return the contents of experiment
*/
+
@POST
@Consumes({RestConstants.MEDIA_TYPE_YAML, MediaType.APPLICATION_JSON})
@Operation(summary = "Create an experiment",
@@ -101,6 +106,34 @@ public class ExperimentRestApi {
}
/**
+ * Returns the contents of {@link Experiment} that submitted by user.
+ *
+ * @param id template id
+ * @param spec
+ * @return the contents of experiment
+ */
+ @POST
+ @Path("/{name}")
+ @Consumes({RestConstants.MEDIA_TYPE_YAML, MediaType.APPLICATION_JSON})
+ @Operation(summary = "use experiment template to create an experiment",
+ tags = {"experiment"},
+ responses = {
+ @ApiResponse(description = "successful operation", content = @Content(
+ schema = @Schema(implementation = JsonResponse.class)))})
+ public Response SubmitExperimentTemplate(@PathParam("name") String name,
+ ExperimentTemplateSubmit spec) {
+ try {
+ spec.setName(name);
+
+ Experiment experiment = ExperimentTemplateManager.getInstance().submitExperimentTemplate(spec);
+ return new JsonResponse.Builder<Experiment>(Response.Status.OK)
+ .success(true).result(experiment).build();
+ } catch (SubmarineRuntimeException e) {
+ return parseExperimentServiceException(e);
+ }
+ }
+
+ /**
* List all experiment for the user
*
* @return experiment list
diff --git a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java
index 699109c..da66402 100644
--- a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java
+++ b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/RestConstants.java
@@ -61,6 +61,8 @@ public class RestConstants {
public static final String EXPERIMENT_TEMPLATE_ID = "id";
+ public static final String EXPERIMENT_TEMPLATE_SUBMIT = "submit";
+
/**
* Notebook
*/
diff --git a/submarine-server/server-core/src/test/java/org/apache/submarine/server/rest/ExperimentTemplateRestApiTest.java b/submarine-server/server-core/src/test/java/org/apache/submarine/server/rest/ExperimentTemplateRestApiTest.java
index 1813609..6945de5 100644
--- a/submarine-server/server-core/src/test/java/org/apache/submarine/server/rest/ExperimentTemplateRestApiTest.java
+++ b/submarine-server/server-core/src/test/java/org/apache/submarine/server/rest/ExperimentTemplateRestApiTest.java
@@ -49,6 +49,8 @@ public class ExperimentTemplateRestApiTest {
private static GsonBuilder gsonBuilder = new GsonBuilder();
private static Gson gson = gsonBuilder.setDateFormat("yyyy-MM-dd HH:mm:ss").create();
+ protected static String TPL_FILE = "experimentTemplate/test_template_1.json";
+
@BeforeClass
public static void init() {
SubmarineConfiguration submarineConf = SubmarineConfiguration.getInstance();
@@ -62,13 +64,13 @@ public class ExperimentTemplateRestApiTest {
@Before
public void createAndUpdateExperimentTemplate() {
- String body = loadContent("experimenttemplate/test_template_1.json");
+ String body = loadContent(TPL_FILE);
experimentTemplateSpec = gson.fromJson(body, ExperimentTemplateSpec.class);
// Create ExperimentTemplate
Response createEnvResponse = experimentTemplateStoreApi.createExperimentTemplate(experimentTemplateSpec);
assertEquals(Response.Status.OK.getStatusCode(), createEnvResponse.getStatus());
-
+
// Update ExperimentTemplate
experimentTemplateSpec.setDescription("newdescription");
Response updateTplResponse = experimentTemplateStoreApi.
@@ -78,8 +80,8 @@ public class ExperimentTemplateRestApiTest {
@After
public void deleteExperimentTemplate() {
-
- String body = loadContent("experimenttemplate/test_template_1.json");
+
+ String body = loadContent(TPL_FILE);
experimentTemplateSpec = gson.fromJson(body, ExperimentTemplateSpec.class);
Response deleteEnvResponse = experimentTemplateStoreApi.
@@ -90,7 +92,7 @@ public class ExperimentTemplateRestApiTest {
@Test
public void getExperimentTemplate() {
- String body = loadContent("experimenttemplate/test_template_1.json");
+ String body = loadContent(TPL_FILE);
experimentTemplateSpec = gson.fromJson(body, ExperimentTemplateSpec.class);
Response getEnvResponse = experimentTemplateStoreApi.
@@ -111,7 +113,7 @@ public class ExperimentTemplateRestApiTest {
@Test
public void listExperimentTemplate() {
- String body = loadContent("experimenttemplate/test_template_1.json");
+ String body = loadContent(TPL_FILE);
experimentTemplateSpec = gson.fromJson(body, ExperimentTemplateSpec.class);
Response getEnvResponse = experimentTemplateStoreApi.listExperimentTemplate("");
diff --git a/submarine-server/server-core/src/test/resources/experimenttemplate/test_template_1.json b/submarine-server/server-core/src/test/resources/experimentTemplate/test_template_1.json
similarity index 60%
rename from submarine-server/server-core/src/test/resources/experimenttemplate/test_template_1.json
rename to submarine-server/server-core/src/test/resources/experimentTemplate/test_template_1.json
index 61448d5..c7e6d3c 100644
--- a/submarine-server/server-core/src/test/resources/experimenttemplate/test_template_1.json
+++ b/submarine-server/server-core/src/test/resources/experimentTemplate/test_template_1.json
@@ -1,12 +1,13 @@
{
- "name": "tf-mnist-test",
- "author": "author",
- "description": "This is a template to run tf-mnist\n",
+ "name": "tf-mnist-test_1",
+ "author": "test_author_1",
+ "description": "This is a test template to run tf-mnist\n",
"parameters": [
{
- "name": "input.train_data",
+ "name": "training.learning_rate",
+ "value": 0.1,
"required": true,
- "description": "train data is expected in SVM format, and can be stored in HDFS/S3 \n"
+ "description": " mnist learning_rate "
},
{
"name": "training.batch_size",
@@ -17,11 +18,10 @@
],
"experimentSpec": {
"meta": {
- "cmd": "python /var/tf_mnist/mnist_with_summaries.py --log_dir=/train/log --learning_rate=0.01 --batch_size={{training.batch_size}}",
- "name": "tf-mnist-json",
+ "cmd": "python /var/tf_mnist/mnist_with_summaries.py --log_dir=/train/log --learning_rate={{training.learning_rate}} --batch_size={{training.batch_size}}",
+ "name": "tf-mnist-template-test1",
"envVars": {
- "input_path": "{{input.train_data}}",
- "ENV2": "ENV2"
+ "ENV1": "ENV1"
},
"framework": "TensorFlow",
"namespace": "default"
diff --git a/submarine-server/server-core/src/test/resources/experimenttemplate/test_template_2.json b/submarine-server/server-core/src/test/resources/experimenttemplate/test_template_2.json
deleted file mode 100644
index 0c8cae1..0000000
--- a/submarine-server/server-core/src/test/resources/experimenttemplate/test_template_2.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "name": "tf-mnist-test2",
- "author": "author",
- "description": "This is a template to run tf-mnist\n",
- "parameters": [
- {
- "name": "input.train_data",
- "required": true,
- "description": "train data is expected in SVM format, and can be stored in HDFS/S3 \n"
- },
- {
- "name": "training.batch_size",
- "value": 150,
- "required": false,
- "description": "This is batch size of training"
- }
- ],
- "experimentSpec": {
- "meta": {
- "cmd": "python /var/tf_mnist/mnist_with_summaries.py --log_dir=/train/log --learning_rate=0.01 --batch_size={{training.batch_size}}",
- "name": "tf-mnist-json",
- "envVars": {
- "input_path": "{{input.train_data}}",
- "ENV2": "ENV2"
- },
- "framework": "TensorFlow",
- "namespace": "default"
- },
- "spec": {
- "Ps": {
- "replicas": 1,
- "resources": "cpu=1,memory=1024M"
- },
- "Worker": {
- "replicas": 1,
- "resources": "cpu=1,memory=1024M"
- }
- },
- "environment": {
- "image": "gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0"
- }
- }
-}
diff --git a/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/ExperimentTemplateManagerRestApiIT.java b/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/ExperimentTemplateManagerRestApiIT.java
index 87d1c37..fd87e64 100644
--- a/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/ExperimentTemplateManagerRestApiIT.java
+++ b/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/ExperimentTemplateManagerRestApiIT.java
@@ -20,6 +20,8 @@
package org.apache.submarine.rest;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
import javax.ws.rs.core.Response;
@@ -27,7 +29,15 @@ import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.submarine.server.AbstractSubmarineServerTest;
+import org.apache.submarine.server.api.experiment.Experiment;
+import org.apache.submarine.server.api.experiment.ExperimentId;
import org.apache.submarine.server.api.experimenttemplate.ExperimentTemplate;
+import org.apache.submarine.server.api.experimenttemplate.ExperimentTemplateSubmit;
+import org.apache.submarine.server.api.spec.ExperimentSpec;
+import org.apache.submarine.server.api.spec.ExperimentTemplateParamSpec;
+import org.apache.submarine.server.api.spec.ExperimentTemplateSpec;
+import org.apache.submarine.server.gson.ExperimentIdDeserializer;
+import org.apache.submarine.server.gson.ExperimentIdSerializer;
import org.apache.submarine.server.response.JsonResponse;
import org.apache.submarine.server.rest.RestConstants;
import org.junit.Assert;
@@ -36,14 +46,22 @@ import org.junit.Test;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
@SuppressWarnings("rawtypes")
public class ExperimentTemplateManagerRestApiIT extends AbstractSubmarineServerTest {
-
+
protected static String TPL_PATH =
"/api/" + RestConstants.V1 + "/" + RestConstants.EXPERIMENT_TEMPLATES;
- protected static String TPL_NAME = "tf-mnist-test2";
-
+ protected static String EXP_PATH =
+ "/api/" + RestConstants.V1 + "/" + RestConstants.EXPERIMENT;
+ protected static String TPL_NAME = "tf-mnist-test_1";
+ protected static String TPL_FILE = "experimentTemplate/test_template_1.json";
+
+ private final Gson gson = new GsonBuilder()
+ .registerTypeAdapter(ExperimentId.class, new ExperimentIdSerializer())
+ .registerTypeAdapter(ExperimentId.class, new ExperimentIdDeserializer())
+ .create();
@BeforeClass
public static void startUp() throws Exception {
@@ -52,7 +70,7 @@ public class ExperimentTemplateManagerRestApiIT extends AbstractSubmarineServerT
@Test
public void testCreateExperimentTemplate() throws Exception {
- String body = loadContent("experimenttemplate/test_template_2.json");
+ String body = loadContent(TPL_FILE);
run(body, "application/json");
deleteExperimentTemplate();
}
@@ -60,10 +78,9 @@ public class ExperimentTemplateManagerRestApiIT extends AbstractSubmarineServerT
@Test
public void testGetExperimentTemplate() throws Exception {
- String body = loadContent("experimenttemplate/test_template_2.json");
+ String body = loadContent(TPL_FILE);
run(body, "application/json");
- Gson gson = new GsonBuilder().create();
GetMethod getMethod = httpGet(TPL_PATH + "/" + TPL_NAME);
Assert.assertEquals(Response.Status.OK.getStatusCode(),
getMethod.getStatusCode());
@@ -79,31 +96,47 @@ public class ExperimentTemplateManagerRestApiIT extends AbstractSubmarineServerT
deleteExperimentTemplate();
}
-
- @Test
- public void testUpdateExperimentTemplate() throws IOException {
-
- }
-
@Test
public void testDeleteExperimentTemplate() throws Exception {
- String body = loadContent("experimenttemplate/test_template_2.json");
+ LOG.info("testDeleteExperimentTemplate");
+
+ String body = loadContent(TPL_FILE);
run(body, "application/json");
deleteExperimentTemplate();
GetMethod getMethod = httpGet(TPL_PATH + "/" + TPL_NAME);
Assert.assertEquals(Response.Status.NOT_FOUND.getStatusCode(),
getMethod.getStatusCode());
-
}
@Test
- public void testListExperimentTemplates() throws IOException {
+ public void testListExperimentTemplates() throws Exception {
+ LOG.info("testListExperimentTemplates");
+
+ String body = loadContent(TPL_FILE);
+ run(body, "application/json");
+
+ GetMethod getMethod = httpGet(TPL_PATH + "/");
+ Assert.assertEquals(Response.Status.OK.getStatusCode(),
+ getMethod.getStatusCode());
+
+ String json = getMethod.getResponseBodyAsString();
+ JsonResponse jsonResponse = gson.fromJson(json, JsonResponse.class);
+ Assert.assertEquals(Response.Status.OK.getStatusCode(),
+ jsonResponse.getCode());
+
+ List<ExperimentTemplate> getExperimentTemplates =
+ gson.fromJson(gson.toJson(jsonResponse.getResult()), new TypeToken<List<ExperimentTemplate>>() {
+ }.getType());
+
+ Assert.assertEquals(TPL_NAME, getExperimentTemplates.get(0).getExperimentTemplateSpec().getName());
+ deleteExperimentTemplate();
}
protected void deleteExperimentTemplate() throws IOException {
- Gson gson = new GsonBuilder().create();
+
+ LOG.info("deleteExperimentTemplate");
DeleteMethod deleteMethod = httpDelete(TPL_PATH + "/" + TPL_NAME);
Assert.assertEquals(Response.Status.OK.getStatusCode(),
deleteMethod.getStatusCode());
@@ -119,13 +152,9 @@ public class ExperimentTemplateManagerRestApiIT extends AbstractSubmarineServerT
}
protected void run(String body, String contentType) throws Exception {
- Gson gson = new GsonBuilder().create();
- // create
LOG.info("Create ExperimentTemplate using ExperimentTemplate REST API");
- LOG.info(body);
PostMethod postMethod = httpPost(TPL_PATH, body, contentType);
-
LOG.info(postMethod.getResponseBodyAsString());
Assert.assertEquals(Response.Status.OK.getStatusCode(),
@@ -146,4 +175,54 @@ public class ExperimentTemplateManagerRestApiIT extends AbstractSubmarineServerT
Assert.assertNotNull(tpl.getExperimentTemplateSpec().getName());
Assert.assertNotNull(tpl.getExperimentTemplateSpec());
}
+
+ @Test
+ public void submitExperimentTemplate() throws Exception {
+
+ String body = loadContent(TPL_FILE);
+ run(body, "application/json");
+
+ ExperimentTemplateSpec tplspec =
+ gson.fromJson(body, ExperimentTemplateSpec.class);
+
+ String url = EXP_PATH + "/" + tplspec.getName();
+ LOG.info("Submit ExperimentTemplate using ExperimentTemplate REST API");
+ LOG.info(body);
+
+ ExperimentTemplateSubmit submit = new ExperimentTemplateSubmit();
+ submit.setParams(new HashMap<String, String>());
+ submit.setName(tplspec.getName());
+ for (ExperimentTemplateParamSpec parmSpec: tplspec.getExperimentTemplateParamSpec()) {
+ submit.getParams().put(parmSpec.getName(), parmSpec.getValue());
+ }
+
+ PostMethod postMethod = httpPost(url, gson.toJson(submit), "application/json");
+ LOG.info(postMethod.getResponseBodyAsString());
+ Assert.assertEquals(Response.Status.OK.getStatusCode(),
+ postMethod.getStatusCode());
+
+ String json = postMethod.getResponseBodyAsString();
+ LOG.info(json);
+ JsonResponse jsonResponse = gson.fromJson(json, JsonResponse.class);
+ Assert.assertEquals(Response.Status.OK.getStatusCode(),
+ jsonResponse.getCode());
+
+ deleteExperimentTemplate();
+ LOG.info(gson.toJson(jsonResponse.getResult()));
+
+ Experiment experiment = gson.fromJson(gson.toJson(jsonResponse.getResult()), Experiment.class);
+
+ DeleteMethod deleteMethod = httpDelete("/api/" + RestConstants.V1 + "/" + RestConstants.EXPERIMENT + "/"
+ + experiment.getExperimentId().toString());
+ Assert.assertEquals(Response.Status.OK.getStatusCode(), deleteMethod.getStatusCode());
+
+ json = deleteMethod.getResponseBodyAsString();
+ jsonResponse = gson.fromJson(json, JsonResponse.class);
+ Assert.assertEquals(Response.Status.OK.getStatusCode(), jsonResponse.getCode());
+
+ ExperimentSpec tplExpSpec = tplspec.getExperimentSpec();
+ ExperimentSpec expSpec = experiment.getSpec();
+
+ Assert.assertEquals(tplExpSpec.getMeta().getName(), expSpec.getMeta().getName());
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org