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/02/15 11:58:37 UTC

[submarine] branch master updated: SUBMARINE-385. [k8s-e2e] Create k8s e2e test module

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 d9e0282  SUBMARINE-385. [k8s-e2e] Create k8s e2e test module
d9e0282 is described below

commit d9e0282e2eaf79f2b0028c325aa81d8763404c37
Author: Xun Liu <li...@apache.org>
AuthorDate: Sat Feb 15 19:29:53 2020 +0800

    SUBMARINE-385. [k8s-e2e] Create k8s e2e test module
    
    ### What is this PR for?
    1. Add new travis job, create k8s by Kind & deploy submairne in k8s
    1. Support developers to use kind to create k8s cluster in local / travis
    2. depoly submarine in kind cluster
    3. By calling the submarine's rest interface, determine whether the submarine is deployed correctly in k8s.  like this : curl `http://127.0.0.1/api/v1/cluster/nodes`
    
    ### What type of PR is it?
    Feature
    
    ### Todos
    * [ ] - Support tensorflow in k8s e2e test
    * [ ] - Support pytorch in k8s e2e test
    
    ### What is the Jira issue?
    * https://issues.apache.org/jira/browse/SUBMARINE-385
    
    ### How should this be tested?
    * https://travis-ci.org/liuxunorg/submarine/builds/650766597
    
    ### Screenshots (if appropriate)
    
    ### Questions:
    * Does the licenses files need update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    Author: Xun Liu <li...@apache.org>
    
    Closes #184 from liuxunorg/SUBMARINE-385 and squashes the following commits:
    
    ac774a0 [Xun Liu] Add version number in dependencyManagement
    59ddd77 [Xun Liu] Remove the version number of the dependent package
    3ff31dc [Xun Liu] SUBMARINE-385. [k8s-e2e] Create k8s e2e test module
---
 .travis.yml                                        |   8 +
 pom.xml                                            |  20 +++
 submarine-cloud/hack/deploy-submarine.sh           |  22 ++-
 submarine-cloud/hack/integration-test.sh           |  47 ++++++
 submarine-cloud/hack/kind-cluster-build.sh         |   4 +
 submarine-dist/src/assembly/distribution.xml       |  13 ++
 submarine-test/e2e/pom.xml                         |  18 ++
 submarine-test/pom.xml                             |  21 ++-
 submarine-test/{e2e => test-k8s}/pom.xml           |  99 +++++------
 .../rest/AbstractSubmarineServerTest.java          | 181 +++++++++++++++++++++
 .../java/org/apache/submarine/rest/ClusterIT.java  |  50 ++++++
 .../test-k8s/src/test/resources/log4j.properties   |  24 +++
 12 files changed, 445 insertions(+), 62 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index d389ec4..6ac14fa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -165,6 +165,14 @@ matrix:
         chrome: stable
       env: PROFILE="-Phadoop-2.9" BUILD_FLAG="clean package -DskipTests" TEST_FLAG="verify -DskipRat -am" TEST_MODULES="-pl submarine-test/e2e" TEST_PROJECTS=""
 
+    - name: Test submarine-test-k8s
+      language: java
+      jdk: "openjdk8"
+      dist: xenial
+      addons:
+        chrome: stable
+      env: PROFILE="-Phadoop-2.9" BUILD_FLAG="clean package -DskipTests" TEST_FLAG="verify -DskipRat -am" TEST_MODULES="-pl submarine-test/test-k8s" TEST_PROJECTS=""
+
     # Template disable by SUBMARINE-381
     #- name: Test submarine interpreter
     #  language: java
diff --git a/pom.xml b/pom.xml
index 56754c5..3885fd4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -154,6 +154,11 @@
         <artifactId>log4j</artifactId>
         <version>${log4j.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.slf4j</groupId>
+        <artifactId>slf4j-log4j12</artifactId>
+        <version>${slf4j.version}</version>
+      </dependency>
 
       <!--  Unit Tests  -->
       <dependency>
@@ -166,6 +171,11 @@
         <artifactId>mockito-core</artifactId>
         <version>${mockito.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.testng</groupId>
+        <artifactId>testng</artifactId>
+        <version>${testng.version}</version>
+      </dependency>
 
       <!--  Submarine on Kubernetes  -->
       <dependency>
@@ -271,6 +281,16 @@
         <version>${zookeeper.version}</version>
       </dependency>
       <dependency>
+        <groupId>commons-httpclient</groupId>
+        <artifactId>commons-httpclient</artifactId>
+        <version>${commons-httpclient.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>javax.ws.rs</groupId>
+        <artifactId>javax.ws.rs-api</artifactId>
+        <version>${javax.ws.rsapi.version}</version>
+      </dependency>
+      <dependency>
         <groupId>org.codehaus.jackson</groupId>
         <artifactId>jackson-xc</artifactId>
         <version>${codehaus-jackson.version}</version>
diff --git a/submarine-cloud/hack/deploy-submarine.sh b/submarine-cloud/hack/deploy-submarine.sh
index 410e8a2..23571a6 100755
--- a/submarine-cloud/hack/deploy-submarine.sh
+++ b/submarine-cloud/hack/deploy-submarine.sh
@@ -42,11 +42,16 @@ function install_submarine() {
     cp ${SUBMARINE_HOME}/conf/log4j.properties.template ${ROOT}/hack/conf/log4j.properties
   fi
 
-  echo ""
-  echo -e "Have you configured the \033[31m${ROOT}/hack/conf/submarine-site.xml\033[0m file?"
-  echo -e "Have you configured the \033[31m${ROOT}/hack/conf/log4j.properties\033[0m file?"
-  echo -n "Do you want to deploy submarine in k8s cluster now? [y/n]"
-  read myselect
+  if [[ "$TEST" == "" ]]; then
+    echo ""
+    echo -e "Have you configured the \033[31m${ROOT}/hack/conf/submarine-site.xml\033[0m file?"
+    echo -e "Have you configured the \033[31m${ROOT}/hack/conf/log4j.properties\033[0m file?"
+    echo -n "Do you want to deploy submarine in k8s cluster now? [y/n]"
+    read myselect
+  else
+    myselect="y"
+  fi
+
   if [[ "$myselect" == "y" || "$myselect" == "Y" ]]; then
     if $KUBECTL_BIN get configmap --namespace default | grep submarine-config >/dev/null ; then
       $KUBECTL_BIN delete configmap --namespace default submarine-config
@@ -97,6 +102,7 @@ This script use kind to create Submarine cluster, about kind please refer: https
 Options:
        -d,--database           ip/service of submarine database, default value: submarine-database
        -u,--uninstall          uninstall submarine cluster
+       -t,--test               auto install
        -h,--help               prints the usage message
 Usage:
     install: $0 --database database_ip
@@ -115,6 +121,10 @@ case $key in
     shift
     shift
     ;;
+    -t|--test)
+    TEST="TRUE"
+    shift
+    ;;
     -u|--uninstall)
     UNINSTALL="TRUE"
     shift
@@ -130,6 +140,8 @@ done
 DATABASE_IP=${DATABASE_IP:-submarine-database}
 echo "Submarine database ip: ${DATABASE_IP}"
 
+export KUBECONFIG=~/.kube/kind-config-${clusterName:-kind}
+
 if [[ "$UNINSTALL" == "TRUE" ]]; then
   uninstall_submarine
 else
diff --git a/submarine-cloud/hack/integration-test.sh b/submarine-cloud/hack/integration-test.sh
new file mode 100755
index 0000000..714e199
--- /dev/null
+++ b/submarine-cloud/hack/integration-test.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# 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.
+#
+set -e
+
+ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd)
+cd $ROOT
+SUBMARINE_HOME=${ROOT}/..
+
+function start() {
+  $ROOT/hack/kind-cluster-build.sh
+  $ROOT/hack/deploy-submarine.sh --test
+
+  for((i=1;i<=100;i++)); do
+    if curl http://127.0.0.1/api/v1/cluster/address | grep \"status\":\"OK\" ; then
+      echo "Cluster start success!"
+      exit;
+    fi
+    sleep 3
+  done
+
+  echo "Cluster start failure!"
+}
+
+function stop() {
+  $ROOT/hack/kind delete cluster
+}
+
+if [[ "$1" == "stop" ]]; then
+  stop
+else
+  start
+fi
diff --git a/submarine-cloud/hack/kind-cluster-build.sh b/submarine-cloud/hack/kind-cluster-build.sh
index 36242d7..b88ff7b 100755
--- a/submarine-cloud/hack/kind-cluster-build.sh
+++ b/submarine-cloud/hack/kind-cluster-build.sh
@@ -144,8 +144,10 @@ EOF
 done
 
 echo "start to create k8s cluster"
+test -d "~/.kube" || mkdir -p "~/.kube"
 export KUBECONFIG=~/.kube/kind-config-${clusterName}
 $KIND_BIN create cluster --config ${configFile} --image kindest/node:${k8sVersion} --name=${clusterName}
+$KIND_BIN export kubeconfig --kubeconfig ${KUBECONFIG}
 
 echo "deploy docker registry in kind"
 registryNode=${clusterName}-control-plane
@@ -241,6 +243,8 @@ $KUBECTL_BIN apply -f $ROOT/hack/ingress/mandatory.yaml
 $KUBECTL_BIN apply -f $ROOT/hack/ingress/service-nodeport.yaml
 $KUBECTL_BIN patch deployments -n ingress-nginx nginx-ingress-controller -p '{"spec":{"template":{"spec":{"containers":[{"name":"nginx-ingress-controller","ports":[{"containerPort":80,"hostPort":80},{"containerPort":443,"hostPort":443}]}],"nodeSelector":{"ingress-ready":"true"},"tolerations":[{"key":"node-role.kubernetes.io/master","operator":"Equal","effect":"NoSchedule"}]}}}}'
 
+$KUBECTL_BIN get pod -A
+
 echo "############# success create cluster:[${clusterName}] #############"
 
 echo "To start using your cluster, run:"
diff --git a/submarine-dist/src/assembly/distribution.xml b/submarine-dist/src/assembly/distribution.xml
index ed93d05..049146e 100644
--- a/submarine-dist/src/assembly/distribution.xml
+++ b/submarine-dist/src/assembly/distribution.xml
@@ -74,6 +74,19 @@
       </includes>
     </fileSet>
     <fileSet>
+      <directory>../submarine-cloud/manifests</directory>
+      <outputDirectory>/submarine-cloud/manifests</outputDirectory>
+    </fileSet>
+    <fileSet>
+      <directory>../submarine-cloud/hack</directory>
+      <outputDirectory>/submarine-cloud/hack</outputDirectory>
+      <excludes>
+        <exclude>*.go.txt</exclude>
+        <exclude>update-codegen.sh</exclude>
+        <exclude>verify-codegen.sh</exclude>
+      </excludes>
+    </fileSet>
+    <fileSet>
       <directory>../submarine-commons/commons-utils/target</directory>
       <outputDirectory>/lib</outputDirectory>
       <includes>
diff --git a/submarine-test/e2e/pom.xml b/submarine-test/e2e/pom.xml
index 109f9be..bb37a99 100644
--- a/submarine-test/e2e/pom.xml
+++ b/submarine-test/e2e/pom.xml
@@ -1,4 +1,22 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
diff --git a/submarine-test/pom.xml b/submarine-test/pom.xml
index e220ebd..3cc7e42 100644
--- a/submarine-test/pom.xml
+++ b/submarine-test/pom.xml
@@ -1,4 +1,22 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -16,6 +34,7 @@
 
   <modules>
     <module>e2e</module>
+    <module>test-k8s</module>
   </modules>
 
-</project>
\ No newline at end of file
+</project>
diff --git a/submarine-test/e2e/pom.xml b/submarine-test/test-k8s/pom.xml
similarity index 67%
copy from submarine-test/e2e/pom.xml
copy to submarine-test/test-k8s/pom.xml
index 109f9be..33da4c5 100644
--- a/submarine-test/e2e/pom.xml
+++ b/submarine-test/test-k8s/pom.xml
@@ -1,4 +1,22 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -10,10 +28,9 @@
     <version>0.4.0-SNAPSHOT</version>
   </parent>
 
-  <artifactId>submarine-e2e</artifactId>
+  <artifactId>submarine-test-k8s</artifactId>
   <version>0.4.0-SNAPSHOT</version>
-  <name>Submarine: E2E Test</name>
-
+  <name>Submarine: Kubernetes Test</name>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -26,43 +43,13 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.seleniumhq.selenium</groupId>
-      <artifactId>selenium-java</artifactId>
-      <version>${selenium.version}</version>
-      <exclusions>
-        <exclusion>
-          <groupId>commons-codec</groupId>
-          <artifactId>commons-codec</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.commons</groupId>
-      <artifactId>commons-lang3</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-codec</groupId>
-      <artifactId>commons-codec</artifactId>
-      <version>${commons-codec.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>commons-io</groupId>
-      <artifactId>commons-io</artifactId>
-    </dependency>
-    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
-      <version>${slf4j.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.rauschig</groupId>
-      <artifactId>jarchivelib</artifactId>
-      <version>${jarchivelib.version}</version>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.testng</groupId>
       <artifactId>testng</artifactId>
-      <version>${testng.version}</version>
       <scope>test</scope>
       <exclusions>
         <exclusion>
@@ -71,9 +58,23 @@
         </exclusion>
       </exclusions>
     </dependency>
+    <dependency>
+      <groupId>commons-httpclient</groupId>
+      <artifactId>commons-httpclient</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.ws.rs</groupId>
+      <artifactId>javax.ws.rs-api</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
-
   <build>
     <plugins>
       <plugin>
@@ -110,14 +111,12 @@
         <artifactId>maven-antrun-plugin</artifactId>
         <executions>
           <execution>
-            <id>start-submarine-server</id>
+            <id>start-kind</id>
             <phase>pre-integration-test</phase>
             <configuration>
               <target unless="skipTests">
-                <exec executable="./submarine-daemon.sh" dir="${submarine.daemon.package.base}"
-                      spawn="false">
+                <exec executable="./integration-test.sh" dir="${submarine.cloud.path}" spawn="false">
                   <arg value="start"/>
-                  <arg value="getMysqlJar" />
                 </exec>
               </target>
             </configuration>
@@ -126,12 +125,11 @@
             </goals>
           </execution>
           <execution>
-            <id>stop-submarine-server</id>
+            <id>stop-kind</id>
             <phase>post-integration-test</phase>
             <configuration>
               <target unless="skipTests">
-                <exec executable="./submarine-daemon.sh" dir="${submarine.daemon.package.base}"
-                      spawn="false">
+                <exec executable="./integration-test.sh" dir="${submarine.cloud.path}" spawn="false">
                   <arg value="stop"/>
                 </exec>
               </target>
@@ -147,25 +145,14 @@
 
   <profiles>
     <profile>
-      <id>using-source-tree</id>
-      <activation>
-        <activeByDefault>false</activeByDefault>
-      </activation>
-      <properties>
-        <submarine.daemon.package.base>
-          ../bin
-        </submarine.daemon.package.base>
-      </properties>
-    </profile>
-    <profile>
       <id>using-packaged-distr</id>
       <activation>
         <activeByDefault>true</activeByDefault>
       </activation>
       <properties>
-        <submarine.daemon.package.base>
-          ../../submarine-dist/target/submarine-dist-${project.version}-hadoop-2.9/submarine-dist-${project.version}-hadoop-2.9/bin
-        </submarine.daemon.package.base>
+        <submarine.cloud.path>
+          ../../submarine-dist/target/submarine-dist-${project.version}-hadoop-2.9/submarine-dist-${project.version}-hadoop-2.9/submarine-cloud/hack
+        </submarine.cloud.path>
       </properties>
     </profile>
   </profiles>
diff --git a/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/AbstractSubmarineServerTest.java b/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/AbstractSubmarineServerTest.java
new file mode 100644
index 0000000..b4aa70d
--- /dev/null
+++ b/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/AbstractSubmarineServerTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.rest;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
+import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+public abstract class AbstractSubmarineServerTest {
+  protected static final Logger LOG = LoggerFactory.getLogger(AbstractSubmarineServerTest.class);
+
+  static final String URL = getUrlToTest();
+
+  public static String getUrlToTest() {
+    // The submarine server deployed in k8s uses the default port of 8080 in submarine-site.xml.
+    // However, port 80 is exposed through k8s Ingress.
+    String url = "http://localhost:80";
+    if (System.getProperty("url") != null) {
+      url = System.getProperty("url");
+    }
+    return url;
+  }
+
+  protected static PostMethod httpPost(String path, String body) throws IOException {
+    return httpPost(path, body, StringUtils.EMPTY, StringUtils.EMPTY);
+  }
+
+  protected static PostMethod httpPost(String path, String request, String user, String pwd)
+      throws IOException {
+    LOG.info("Connecting to {}", URL + path);
+
+    HttpClient httpClient = new HttpClient();
+    PostMethod postMethod = new PostMethod(URL + path);
+    postMethod.setRequestBody(request);
+    postMethod.setRequestHeader("Content-type", MediaType.APPLICATION_JSON);
+    postMethod.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
+
+    if (userAndPasswordAreNotBlank(user, pwd)) {
+      postMethod.setRequestHeader("Cookie", "JSESSIONID=" + getCookie(user, pwd));
+    }
+
+    httpClient.executeMethod(postMethod);
+
+    LOG.info("{} - {}", postMethod.getStatusCode(), postMethod.getStatusText());
+
+    return postMethod;
+  }
+
+  protected static PutMethod httpPut(String path, String body) throws IOException {
+    return httpPut(path, body, StringUtils.EMPTY, StringUtils.EMPTY);
+  }
+
+  protected static PutMethod httpPut(String path, String body, String user, String pwd) throws IOException {
+    LOG.info("Connecting to {}", URL + path);
+    HttpClient httpClient = new HttpClient();
+    PutMethod putMethod = new PutMethod(URL + path);
+    putMethod.addRequestHeader("Origin", URL);
+    putMethod.setRequestHeader("Content-type", "application/yaml");
+    RequestEntity entity = new ByteArrayRequestEntity(body.getBytes("UTF-8"));
+    putMethod.setRequestEntity(entity);
+    if (userAndPasswordAreNotBlank(user, pwd)) {
+      putMethod.setRequestHeader("Cookie", "JSESSIONID=" + getCookie(user, pwd));
+    }
+    httpClient.executeMethod(putMethod);
+    LOG.info("{} - {}", putMethod.getStatusCode(), putMethod.getStatusText());
+    return putMethod;
+  }
+
+  protected static DeleteMethod httpDelete(String path) throws IOException {
+    return httpDelete(path, StringUtils.EMPTY, StringUtils.EMPTY);
+  }
+
+  protected static DeleteMethod httpDelete(String path, String user, String pwd) throws IOException {
+    LOG.info("Connecting to {}", URL + path);
+    HttpClient httpClient = new HttpClient();
+    DeleteMethod deleteMethod = new DeleteMethod(URL + path);
+    deleteMethod.addRequestHeader("Origin", URL);
+    if (userAndPasswordAreNotBlank(user, pwd)) {
+      deleteMethod.setRequestHeader("Cookie", "JSESSIONID=" + getCookie(user, pwd));
+    }
+    httpClient.executeMethod(deleteMethod);
+    LOG.info("{} - {}", deleteMethod.getStatusCode(), deleteMethod.getStatusText());
+    return deleteMethod;
+  }
+
+  protected static GetMethod httpGet(String path) throws IOException {
+    return httpGet(path, "", "");
+  }
+
+  protected static GetMethod httpGet(String path, String user, String pwd) throws IOException {
+    return httpGet(path, user, pwd, "");
+  }
+
+  protected static GetMethod httpGet(String path, String user, String pwd, String cookies)
+      throws IOException {
+    LOG.info("Connecting to {}", URL + path);
+    HttpClient httpClient = new HttpClient();
+    GetMethod getMethod = new GetMethod(URL + path);
+    getMethod.addRequestHeader("Origin", URL);
+    httpClient.executeMethod(getMethod);
+    LOG.info("{} - {}", getMethod.getStatusCode(), getMethod.getStatusText());
+    return getMethod;
+  }
+
+  protected static boolean checkIfServerIsRunning() {
+    GetMethod request = null;
+    boolean isRunning = false;
+    try {
+      request = httpGet("/");
+      isRunning = request.getStatusCode() == 200;
+    } catch (IOException e) {
+      LOG.warn("AbstractTestRestApi.checkIfServerIsRunning() fails .. " +
+          "Submarine server is not running");
+      isRunning = false;
+    } finally {
+      if (request != null) {
+        request.releaseConnection();
+      }
+    }
+    return isRunning;
+  }
+
+  private static String getCookie(String user, String password) throws IOException {
+    HttpClient httpClient = new HttpClient();
+    PostMethod postMethod = new PostMethod(URL + "/login");
+    postMethod.addRequestHeader("Origin", URL);
+    postMethod.setParameter("password", password);
+    postMethod.setParameter("userName", user);
+    httpClient.executeMethod(postMethod);
+    LOG.info("{} - {}", postMethod.getStatusCode(), postMethod.getStatusText());
+    Pattern pattern = Pattern.compile("JSESSIONID=([a-zA-Z0-9-]*)");
+    Header[] setCookieHeaders = postMethod.getResponseHeaders("Set-Cookie");
+    String jsessionId = null;
+    for (Header setCookie : setCookieHeaders) {
+      java.util.regex.Matcher matcher = pattern.matcher(setCookie.toString());
+      if (matcher.find()) {
+        jsessionId = matcher.group(1);
+      }
+    }
+
+    if (jsessionId != null) {
+      return jsessionId;
+    } else {
+      return StringUtils.EMPTY;
+    }
+  }
+
+  protected static boolean userAndPasswordAreNotBlank(String user, String pwd) {
+    if (StringUtils.isBlank(user) && StringUtils.isBlank(pwd)) {
+      return false;
+    }
+    return true;
+  }
+}
diff --git a/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/ClusterIT.java b/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/ClusterIT.java
new file mode 100644
index 0000000..b3798d4
--- /dev/null
+++ b/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/ClusterIT.java
@@ -0,0 +1,50 @@
+/*
+ * 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.rest;
+
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ClusterIT extends AbstractSubmarineServerTest {
+  public final static Logger LOG = LoggerFactory.getLogger(ClusterIT.class);
+
+  @BeforeClass
+  public static void startUp(){
+    Assert.assertTrue(checkIfServerIsRunning());
+  }
+
+  @Test
+  public void getClusterAddress() throws Exception {
+    GetMethod get = httpGet("/api/v1/cluster/address");
+    Assert.assertEquals(200, get.getStatusCode());
+    String body = get.getResponseBodyAsString();
+    LOG.info("body = {}", body);
+  }
+
+  @Test
+  public void getClusterNodes() throws Exception {
+    GetMethod get = httpGet("/api/v1/cluster/nodes");
+    Assert.assertEquals(200, get.getStatusCode());
+    String body = get.getResponseBodyAsString();
+    LOG.info("body = {}", body);
+  }
+}
diff --git a/submarine-test/test-k8s/src/test/resources/log4j.properties b/submarine-test/test-k8s/src/test/resources/log4j.properties
new file mode 100644
index 0000000..7ad6f5f
--- /dev/null
+++ b/submarine-test/test-k8s/src/test/resources/log4j.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t]: %c{2} (%F:%M(%L)) - %m%n


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org