diff --git a/docs/userdocs/k8s/use-experiment-template.md b/docs/userdocs/k8s/use-experiment-template.md new file mode 100644 index 0000000000..732b29af06 --- /dev/null +++ b/docs/userdocs/k8s/use-experiment-template.md @@ -0,0 +1,142 @@ + + +# 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 0000000000..f5a327f8fe --- /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 params; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getParams() { + return params; + } + + public void setParams(Map 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 8dc5b5826f..1b0deaacc6 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 List getParameters() { public void setParameters(List parameters) { this.parameters = parameters; } + + public List getExperimentTemplateParamSpec() { + return this.parameters; + } + + public void setExperimentTemplateParamSpec(List 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 feac673710..cd8c828aad 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 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 @@ private ExperimentTemplate createOrUpdateExperimentTemplate(ExperimentTemplateSp } 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 @@ private ExperimentTemplate getExperimentTemplateDetails(String name) throws Subm 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 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 parmMap = new HashMap(); - for (ExperimentTemplateParamSpec parm : tplSpec.getParameters()) { + Map paramMap = new HashMap(); + 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 paramMap) { + Pattern pattern = Pattern.compile("\\{\\{(.+?)\\}\\}"); StringBuffer sb = new StringBuffer(); Matcher matcher = pattern.matcher(spec); @@ -297,33 +349,34 @@ private ExperimentTemplateSpec parameterMapping(String spec) { 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 b57ae1d504..b0cd30e7b5 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 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 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 Response ping() { .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", @@ -100,6 +105,34 @@ public Response createExperiment(ExperimentSpec spec) { } } + /** + * 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(Response.Status.OK) + .success(true).result(experiment).build(); + } catch (SubmarineRuntimeException e) { + return parseExperimentServiceException(e); + } + } + /** * List all experiment for the user * 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 699109cab8..da6640215f 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 1813609338..6945de51f6 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 static void init() { @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 void createAndUpdateExperimentTemplate() { @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 void deleteExperimentTemplate() { @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 @@ private ExperimentTemplate getExperimentTemplateFromResponse(Response response) @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 61448d5254..c7e6d3c8cd 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 0c8cae161e..0000000000 --- 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 87d1c3731f..fd87e64024 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.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 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 static void startUp() throws Exception { @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 void testCreateExperimentTemplate() throws Exception { @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 void testGetExperimentTemplate() throws Exception { 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 getExperimentTemplates = + gson.fromJson(gson.toJson(jsonResponse.getResult()), new TypeToken>() { + }.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 @@ protected void deleteExperimentTemplate() throws IOException { } 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 @@ protected void verifyCreateExperimentTemplateApiResult(ExperimentTemplate tpl) 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()); + 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()); + } }