Improve scenario builder and test cases (#216)

diff --git a/dubbo-samples-configcenter/dubbo-samples-configcenter-apollo/case-configuration.yml b/dubbo-samples-configcenter/dubbo-samples-configcenter-apollo/case-configuration.yml
index d715f8e..c5ceaa2 100644
--- a/dubbo-samples-configcenter/dubbo-samples-configcenter-apollo/case-configuration.yml
+++ b/dubbo-samples-configcenter/dubbo-samples-configcenter-apollo/case-configuration.yml
@@ -14,45 +14,36 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from: app-builtin-zookeeper.yml
 timeout: 120
 
-props:
-  project_name: dubbo-samples-configcenter-apollo
-  main_class: org.apache.dubbo.samples.configcenter.ApolloProvider
-  zookeeper_port: 2181
-  dubbo_port: 20880
-  apollo_address: apollo-quick-start
-  apollo_port: 8080
-
 services:
-  ${project_name}:
+  dubbo-samples-configcenter-apollo:
     type: app
     basedir: .
-    mainClass: ${main_class}
+    mainClass: org.apache.dubbo.samples.configcenter.ApolloProvider
     systemProps:
-      - apollo.address=${apollo_address}
-      - apollo.port=${apollo_port}
+      - apollo.address=apollo-quick-start
+      - apollo.port=8080
     waitPortsBeforeRun:
-      - ${apollo_address}:${apollo_port}
+      - apollo-quick-start:8080
     checkTimeout: 100
 
-  ${project_name}-test:
+  dubbo-samples-configcenter-apollo-test:
     type: test
     basedir: .
     tests:
       - "**/*IT.class"
     systemProps:
-      - zookeeper.address=${project_name}
-      - zookeeper.port=${zookeeper_port}
-      - dubbo.address=${project_name}
-      - dubbo.port=${dubbo_port}
+      - zookeeper.address=dubbo-samples-configcenter-apollo
+      - zookeeper.port=2181
+      - dubbo.address=dubbo-samples-configcenter-apollo
+      - dubbo.port=20880
     waitPortsBeforeRun:
-      - ${project_name}:${zookeeper_port}
-      - ${project_name}:${dubbo_port}
+      - dubbo-samples-configcenter-apollo:2181
+      - dubbo-samples-configcenter-apollo:20880
     checkTimeout: 100
     depends_on:
-      - ${project_name}
+      - dubbo-samples-configcenter-apollo
   
   apollo-quick-start:
     image: nobodyiam/apollo-quick-start
@@ -72,7 +63,7 @@
 #    ports:
 #      - "13306:3306"
     volumes:
-      - ./classes/docker/sql:/docker-entrypoint-initdb.d
+      - ${_basedir}/src/main/resources/docker/sql:/docker-entrypoint-initdb.d
 #      - ./sql_data:/var/lib/mysql
 #    volumes_from:
 #      - apollo-dbdata
diff --git a/dubbo-samples-rest/pom.xml b/dubbo-samples-rest/pom.xml
index d80004d..85a7429 100644
--- a/dubbo-samples-rest/pom.xml
+++ b/dubbo-samples-rest/pom.xml
@@ -184,6 +184,13 @@
         <dependency>
             <groupId>io.swagger</groupId>
             <artifactId>swagger-jaxrs</artifactId>
+            <exclusions>
+                <!-- fix javax.ws.rs conflicts, exclude jsr311-api, use jboss-jaxrs-api_2.0 -->
+                <exclusion>
+                    <artifactId>jsr311-api</artifactId>
+                    <groupId>javax.ws.rs</groupId>
+                </exclusion>
+            </exclusions>
         </dependency>
 
         <dependency>
diff --git a/dubbo-samples-transaction/case-configuration.yml b/dubbo-samples-transaction/case-configuration.yml
new file mode 100644
index 0000000..dc18ef8
--- /dev/null
+++ b/dubbo-samples-transaction/case-configuration.yml
@@ -0,0 +1,104 @@
+# 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.
+
+# global system props for all service
+systemProps:
+  - mysql.address=seata-mysql
+  - mysql.port=3306
+
+services:
+  zookeeper:
+    image: zookeeper:latest
+    expose:
+      - 2181
+
+  seata-mysql:
+    image: mysql:5.7
+    environment:
+      - MYSQL_DATABASE=seata-demo
+      - MYSQL_ROOT_PASSWORD=helloworld
+    expose:
+      - 3306
+    volumes:
+      - ${_basedir}/src/main/resources/docker/mysql/sql:/docker-entrypoint-initdb.d
+
+  seata-server:
+    #build: ${_basedir}/src/main/resources/docker/seata
+    #removeOnExit: true
+    image: seataio/seata-server:latest
+    volumes:
+      - ${_basedir}/src/main/resources/docker/seata/conf:/root/seata-config
+    expose:
+      - 8091
+    depends_on:
+      - seata-mysql
+
+  storage-service:
+    type: app
+    basedir: .
+    mainClass: org.apache.dubbo.samples.starter.DubboStorageServiceStarter
+    systemProps:
+      - zookeeper.address=zookeeper
+      - zookeeper.port=2181
+    waitPortsBeforeRun:
+      - zookeeper:2181
+    expose:
+      - 20882
+    depends_on:
+      - seata-mysql
+
+  account-service:
+    type: app
+    basedir: .
+    mainClass: org.apache.dubbo.samples.starter.DubboAccountServiceStarter
+    systemProps:
+      - zookeeper.address=zookeeper
+      - zookeeper.port=2181
+    waitPortsBeforeRun:
+      - zookeeper:2181
+    expose:
+      - 20881
+    depends_on:
+      - seata-mysql
+
+  order-service:
+    type: app
+    basedir: .
+    mainClass: org.apache.dubbo.samples.starter.DubboOrderServiceStarter
+    systemProps:
+      - zookeeper.address=zookeeper
+      - zookeeper.port=2181
+    waitPortsBeforeRun:
+      - zookeeper:2181
+    expose:
+      - 20883
+    depends_on:
+      - seata-mysql
+
+  transaction-test:
+    type: test
+    basedir: .
+    tests:
+      - "**/*IT.class"
+    systemProps:
+      - zookeeper.address=zookeeper
+      - zookeeper.port=2181
+    waitPortsBeforeRun:
+      - zookeeper:2181
+      - storage-service:20882
+      - account-service:20881
+      - order-service:20883
+
diff --git a/dubbo-samples-transaction/src/main/resources/jdbc.properties b/dubbo-samples-transaction/src/main/resources/jdbc.properties
index 3b13f61..03d2dda 100644
--- a/dubbo-samples-transaction/src/main/resources/jdbc.properties
+++ b/dubbo-samples-transaction/src/main/resources/jdbc.properties
@@ -13,17 +13,17 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 #
-jdbc.account.url=jdbc:mysql://localhost:3306/seata-demo
+jdbc.account.url=jdbc:mysql://${mysql.address:127.0.0.1}:3306/seata-demo
 jdbc.account.username=root
 jdbc.account.password=helloworld
 jdbc.account.driver=com.mysql.jdbc.Driver
 # storage db config
-jdbc.storage.url=jdbc:mysql://localhost:3306/seata-demo
+jdbc.storage.url=jdbc:mysql://${mysql.address:127.0.0.1}:3306/seata-demo
 jdbc.storage.username=root
 jdbc.storage.password=helloworld
 jdbc.storage.driver=com.mysql.jdbc.Driver
 # order db config
-jdbc.order.url=jdbc:mysql://localhost:3306/seata-demo
+jdbc.order.url=jdbc:mysql://${mysql.address:127.0.0.1}:3306/seata-demo
 jdbc.order.username=root
 jdbc.order.password=helloworld
 jdbc.order.driver=com.mysql.jdbc.Driver
diff --git a/dubbo-samples-transaction/src/main/resources/spring/dubbo-account-service.xml b/dubbo-samples-transaction/src/main/resources/spring/dubbo-account-service.xml
index 12de164..96bfa98 100644
--- a/dubbo-samples-transaction/src/main/resources/spring/dubbo-account-service.xml
+++ b/dubbo-samples-transaction/src/main/resources/spring/dubbo-account-service.xml
@@ -56,7 +56,7 @@
     </bean>
 
     <dubbo:application name="dubbo-demo-account-service"/>
-    <dubbo:registry address="zookeeper://localhost:2181" />
+    <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181" />
     <dubbo:protocol name="dubbo" port="20881"/>
     <dubbo:service interface="org.apache.dubbo.samples.service.AccountService" ref="service" timeout="10000"/>
 
diff --git a/dubbo-samples-transaction/src/main/resources/spring/dubbo-order-service.xml b/dubbo-samples-transaction/src/main/resources/spring/dubbo-order-service.xml
index 15eb3c4..100e018 100644
--- a/dubbo-samples-transaction/src/main/resources/spring/dubbo-order-service.xml
+++ b/dubbo-samples-transaction/src/main/resources/spring/dubbo-order-service.xml
@@ -56,7 +56,7 @@
     </bean>
 
     <dubbo:application name="dubbo-demo-order-service"/>
-    <dubbo:registry address="zookeeper://localhost:2181" />
+    <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181" />
     <dubbo:protocol name="dubbo" port="20883"/>
     <dubbo:service interface="org.apache.dubbo.samples.service.OrderService" ref="service" timeout="10000"/>
 
diff --git a/dubbo-samples-transaction/src/main/resources/spring/dubbo-storage-service.xml b/dubbo-samples-transaction/src/main/resources/spring/dubbo-storage-service.xml
index e450e4c..56c739c 100644
--- a/dubbo-samples-transaction/src/main/resources/spring/dubbo-storage-service.xml
+++ b/dubbo-samples-transaction/src/main/resources/spring/dubbo-storage-service.xml
@@ -56,7 +56,7 @@
     </bean>
 
     <dubbo:application name="dubbo-demo-storage-service"/>
-    <dubbo:registry address="zookeeper://localhost:2181"/>
+    <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
     <dubbo:protocol name="dubbo" port="20882"/>
     <dubbo:service interface="org.apache.dubbo.samples.service.StorageService" ref="service" timeout="10000"/>
 
diff --git a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/AbstractRunningGenerator.java b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/AbstractRunningGenerator.java
index 4ed7101..453dedc 100644
--- a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/AbstractRunningGenerator.java
+++ b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/AbstractRunningGenerator.java
@@ -51,7 +51,6 @@
         generateAdditionFiles(configuration);
 
         final Map<String, Object> root = configuration.toMap();
-        root.put("running_script", runningScript(configuration));
 
         String scriptPath = configuration.outputDir() + File.separator + "scenario.sh";
         try (FileWriter writer = new FileWriter(new File(scriptPath))) {
@@ -64,5 +63,4 @@
 
     public abstract void generateAdditionFiles(IConfiguration configuration) throws GenerateFailedException;
 
-    public abstract String runningScript(IConfiguration configuration);
 }
diff --git a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/ConfigurationImpl.java b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/ConfigurationImpl.java
index 34533a9..d444e3c 100644
--- a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/ConfigurationImpl.java
+++ b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/ConfigurationImpl.java
@@ -47,6 +47,8 @@
     public static final String SAMPLE_TEST_IMAGE = "dubbo/sample-test";
     public static final String DUBBO_APP_DIR = "/usr/local/dubbo/app";
     public static final String DUBBO_LOG_DIR = "/usr/local/dubbo/logs";
+
+    //ENV
     public static final String ENV_SERVICE_NAME = "SERVICE_NAME";
     public static final String ENV_SERVICE_TYPE = "SERVICE_TYPE";
     public static final String ENV_APP_MAIN_CLASS = "APP_MAIN_CLASS";
@@ -59,6 +61,11 @@
     public static final String ENV_DEBUG_OPTS = "DEBUG_OPTS";
     public static final String ENV_SCENARIO_HOME = "SCENARIO_HOME";
 
+    //PROPS
+    public static final String PROP_BASEDIR = "_basedir";
+    public static final String PROP_SCENARIO_HOME = "_scenario_home";
+    public static final String PROP_SCENARIO_NAME = "_scenario_name";
+
     private static final Logger logger = LoggerFactory.getLogger(ConfigurationImpl.class);
     private final CaseConfiguration configuration;
     private String scenarioHome;
@@ -134,6 +141,10 @@
         String configYaml = readFully(configureFile);
         CaseConfiguration tmpConfiguration = parseConfiguration(configYaml);
         Map<String, String> props = tmpConfiguration.getProps();
+        if (props == null) {
+            props = new HashMap<>();
+        }
+        configProps(props);
 
         // process 'from', load parent config
         CaseConfiguration parentConfiguration = null;
@@ -142,9 +153,11 @@
             CaseConfiguration tmpParentConfiguration = parseConfiguration(parentConfigYaml);
 
             //merge props, overwrite parent props
-            Map<String, String> newProps = new HashMap<>(tmpParentConfiguration.getProps());
-            newProps.putAll(props);
-            props = newProps;
+            if (tmpParentConfiguration.getProps() != null) {
+                Map<String, String> newProps = new HashMap<>(tmpParentConfiguration.getProps());
+                newProps.putAll(props);
+                props = newProps;
+            }
 
             // replace variables '${...}'
             String newParentConfigYaml = replaceHolders(parentConfigYaml, props);
@@ -174,6 +187,12 @@
         return caseConfiguration;
     }
 
+    private void configProps(Map<String, String> props) {
+        props.put(PROP_BASEDIR, configBasedir);
+        props.put(PROP_SCENARIO_HOME, scenarioHome);
+        props.put(PROP_SCENARIO_NAME, scenarioName);
+    }
+
     private List<String> mergeSystemProps(List<String> parentSystemProps, List<String> childSystemProps) {
         List<String> newSystemProps = new ArrayList<>(parentSystemProps != null ? parentSystemProps : Collections.emptyList());
         if (childSystemProps != null) {
@@ -556,6 +575,7 @@
             String imageName = dependency.getImage();
             service.setName(name);
             service.setImageName(imageName);
+            service.setBuild(dependency.getBuild());
             service.setHostname(dependency.getHostname());
             service.setExpose(dependency.getExpose());
             service.setPorts(dependency.getPorts());
diff --git a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/DockerComposeRunningGenerator.java b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/DockerComposeRunningGenerator.java
index b7e17a5..fabdc8f 100644
--- a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/DockerComposeRunningGenerator.java
+++ b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/DockerComposeRunningGenerator.java
@@ -58,18 +58,4 @@
             LOGGER.error("", e);
         }
     }
-
-    @Override
-    public String runningScript(IConfiguration configuration) {
-        final Map<String, Object> root = configuration.toMap();
-
-        StringWriter out = new StringWriter();
-
-        try {
-            cfg.getTemplate("compose-start-script.template").process(root, out);
-        } catch (Exception e) {
-            LOGGER.error("Failed to generate running script.", e);
-        }
-        return out.toString();
-    }
 }
diff --git a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/vo/DockerService.java b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/vo/DockerService.java
index 124976d..0af47b7 100644
--- a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/vo/DockerService.java
+++ b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/vo/DockerService.java
@@ -22,6 +22,8 @@
 public class DockerService {
     private String name;
     private String imageName;
+    // docker-compose build dir
+    private String build;
     private String hostname;
     private boolean removeOnExit;
     private List<String> links;
@@ -49,6 +51,14 @@
         this.imageName = imageName;
     }
 
+    public String getBuild() {
+        return build;
+    }
+
+    public void setBuild(String build) {
+        this.build = build;
+    }
+
     public String getHostname() {
         return hostname;
     }
@@ -134,6 +144,7 @@
         return "DockerService{" +
                 "name='" + name + '\'' +
                 ", imageName='" + imageName + '\'' +
+                ", build='" + build + '\'' +
                 ", hostname='" + hostname + '\'' +
                 ", removeOnExit=" + removeOnExit +
                 ", links=" + links +
diff --git a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/vo/ServiceComponent.java b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/vo/ServiceComponent.java
index 8490618..e4b2c42 100644
--- a/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/vo/ServiceComponent.java
+++ b/test/dubbo-scenario-builder/src/main/java/org/apache/dubbo/scenario/builder/vo/ServiceComponent.java
@@ -22,6 +22,8 @@
 
 public class ServiceComponent {
     private String image;
+    // docker-compose build dir
+    private String build;
     private String hostname;
     private String version;
     private boolean removeOnExit = false;
@@ -231,10 +233,19 @@
         this.ports = ports;
     }
 
+    public String getBuild() {
+        return build;
+    }
+
+    public void setBuild(String build) {
+        this.build = build;
+    }
+
     @Override
     public String toString() {
         return "ServiceComponent{" +
                 "image='" + image + '\'' +
+                ", build='" + build + '\'' +
                 ", hostname='" + hostname + '\'' +
                 ", version='" + version + '\'' +
                 ", removeOnExit=" + removeOnExit +
diff --git a/test/dubbo-scenario-builder/src/main/resources/compose-start-script.template b/test/dubbo-scenario-builder/src/main/resources/compose-start-script.template
deleted file mode 100644
index 6c438b5..0000000
--- a/test/dubbo-scenario-builder/src/main/resources/compose-start-script.template
+++ /dev/null
@@ -1,18 +0,0 @@
-<#--
-  ~ 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.
--->
-
-
diff --git a/test/dubbo-scenario-builder/src/main/resources/docker-compose.template b/test/dubbo-scenario-builder/src/main/resources/docker-compose.template
index 1211966..bdba61f 100644
--- a/test/dubbo-scenario-builder/src/main/resources/docker-compose.template
+++ b/test/dubbo-scenario-builder/src/main/resources/docker-compose.template
@@ -23,8 +23,13 @@
 services:
 <#list services as service>
     ${service.name}:
+        <#if service.imageName?has_content>
         image: ${service.imageName}
+        </#if>
         hostname: ${service.hostname}
+        <#if service.build?has_content>
+        build: ${service.build}
+        </#if>
         <#if service.volumes??>
         volumes:
         <#list service.volumes as volume>
diff --git a/test/dubbo-scenario-builder/src/main/resources/scenario.sh b/test/dubbo-scenario-builder/src/main/resources/scenario.sh
index 0376a69..757e89d 100644
--- a/test/dubbo-scenario-builder/src/main/resources/scenario.sh
+++ b/test/dubbo-scenario-builder/src/main/resources/scenario.sh
@@ -107,7 +107,7 @@
 
 # complete pull fail interactive by <<< "NN"
 echo "[$scenario_name] Starting containers .." | tee -a $scenario_log
-docker-compose -p ${project_name} -f ${compose_file} up -d --no-build 2>&1 <<< "NN" | tee -a $scenario_log > /dev/null
+docker-compose -p ${project_name} -f ${compose_file} up -d 2>&1 <<< "NNN" | tee -a $scenario_log > /dev/null
 
 sleep 2
 redirect_container_logs
diff --git a/test/kill-tests.sh b/test/kill-tests.sh
index 3736033..8dbd904 100755
--- a/test/kill-tests.sh
+++ b/test/kill-tests.sh
@@ -11,6 +11,9 @@
 echo "Killing docker logs procs .."
 ps -ef | grep "docker logs" | grep -v grep  | awk '{ print $2 }' | xargs -I {} kill {}
 
+echo "Killing docker-compose .."
+ps -ef | grep "docker-compose" | grep "dubbo-samples" | grep -v grep  | awk '{ print $2 }' | xargs -I {} kill {}
+
 echo "Killing dubbo containers .."
 docker ps -a | grep dubbo | awk '{ print $1}' | xargs -I {} docker kill {}
 
diff --git a/test/quick-start_cn.md b/test/quick-start_cn.md
index f5e82a4..00165db 100644
--- a/test/quick-start_cn.md
+++ b/test/quick-start_cn.md
@@ -101,7 +101,7 @@
 
 `from: app-builtin-zookeeper.yml` : 指定使用`app-builtin-zookeeper.yml`模板,解析时自动将模板的内容合并到当前配置文件。
 
-`props` : 配置文件内部的变量,自动替换模板中的变量占位符。
+`props` : 配置文件内部的变量,自动替换模板中的变量占位符。如要使用全局变量请参考"全局变量"小节。
 
 `project_name` : 工程名称变量,同时也是dubbo provider 服务名。
 
@@ -241,6 +241,18 @@
       - dubbo-samples-chain-middle:20881
 ```
 
+#### 全局变量
+
+`case-configuration.yml`支持的全局变量如下,可以直接使用:
+
+| 名称 | 描述 | 例子 |
+| ---- | --- | --- |
+| `_basedir` | `case-configuration.yml` 配置文件所在的目录 | 用于指定容器挂载的配置文件路径,如挂载mysql的初始化sql脚本目录:${_basedir}/src/main/resources/docker/sql:/docker-entrypoint-initdb.d |
+| `_scenario_home`| scenario home,默认位于${_basedir}/target | |
+| `_scenario_name`| scenario name,默认为${_basedir}的目录名(basename) | |
+
+**注意**:volumes挂载的相对目录是scenario home(即${_basedir}/target),不方便访问工程源码目录。
+如果要挂载工程源码的目录,推荐使用${_basedir}/src/main/resources/xxx的形式,提高可读性.
 
 #### 转换测试案例