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 2019/12/15 14:04:16 UTC

[submarine] branch master updated: SUBMARINE-316. Support listen k8s event in submarine operator

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 07ef255  SUBMARINE-316. Support listen k8s event in submarine operator
07ef255 is described below

commit 07ef2558752a6cb12eebb28c3fb49a5298b65ec5
Author: Xun Liu <li...@apache.org>
AuthorDate: Sat Dec 14 17:03:59 2019 +0800

    SUBMARINE-316. Support listen k8s event in submarine operator
    
    ### What is this PR for?
    Listen to k8s pod, svc creation, destruction and other events in the submarine operator.
    * [x] - Submarine CRD auto register
    * [x] - Submarine Cluster SVC add/remove event listen
    * [x] - Submarine Cluster POD add/remove event listen
    
    #### How To Test
    ```
    cd submarine-cloud
    make
    ./submarine-operator --kubeconfig=$KUBECONFIG --alsologtostderr
    ```
    open new console
    ```
    kubectl apply test1.yaml
    kubectl apply test2.yaml
    kubectl delete test2.yaml
    kubectl delete test2.yaml
    ```
    
    ### What type of PR is it?
    Feature
    
    ### What is the Jira issue?
    * https://issues.apache.org/jira/browse/SUBMARINE-316
    
    ### How should this be tested?
    * [CI Pass](https://travis-ci.org/liuxunorg/submarine/builds/624659081)
    
    ### 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 #129 from liuxunorg/SUBMARINE-316 and squashes the following commits:
    
    dc3fd11 [Xun Liu] Fixed function & var name
    a95cb8f [Xun Liu] Fixed licenses head.
    073f05f [Xun Liu] SUBMARINE-316. Support k8s event listen in submarine operator
---
 submarine-cloud/Makefile                           |   4 +-
 submarine-cloud/cmd/operator/controller.go         | 253 --------------------
 submarine-cloud/cmd/operator/main.go               | 158 ++-----------
 submarine-cloud/go.mod                             |   6 +-
 submarine-cloud/go.sum                             | 108 +++++++++
 submarine-cloud/hack/update-codegen.sh             |   4 -
 submarine-cloud/manifests/test1.yaml               |   2 +-
 submarine-cloud/manifests/test2.yaml               |   2 +-
 .../pkg/apis/submarine/v1alpha1/default.go         |  66 ++++++
 submarine-cloud/pkg/apis/submarine/v1alpha1/doc.go |   1 -
 .../pkg/apis/submarine/v1alpha1/register.go        |  17 +-
 .../pkg/apis/submarine/v1alpha1/types.go           | 195 +++++++++++++++-
 .../submarine/v1alpha1/zz_generated.deepcopy.go    | 151 ++++++++++--
 submarine-cloud/pkg/client/client.go               | 114 +++++++++
 .../v1alpha1/fake/fake_submarine_client.go         |   4 +-
 .../v1alpha1/fake/fake_submarinecluster.go         | 141 +++++++++++
 .../v1alpha1/fake/fake_submarineserver.go          | 129 -----------
 .../submarine/v1alpha1/generated_expansion.go      |   2 +-
 .../typed/submarine/v1alpha1/submarine_client.go   |   6 +-
 .../typed/submarine/v1alpha1/submarinecluster.go   | 192 +++++++++++++++
 .../typed/submarine/v1alpha1/submarineserver.go    | 175 --------------
 .../client/informers/externalversions/generic.go   |   4 +-
 .../submarine/v1alpha1/interface.go                |  10 +-
 .../{submarineserver.go => submarinecluster.go}    |  38 +--
 .../submarine/v1alpha1/expansion_generated.go      |  12 +-
 .../listers/submarine/v1alpha1/submarinecluster.go |  95 ++++++++
 .../listers/submarine/v1alpha1/submarineserver.go  |  95 --------
 .../{signals/signal_posix.go => config/cluster.go} |  20 +-
 submarine-cloud/pkg/config/submarine.go            | 101 ++++++++
 .../signal_windows.go => controller/config.go}     |  19 +-
 submarine-cloud/pkg/controller/controller.go       | 258 +++++++++++++++++++++
 submarine-cloud/pkg/controller/pod/control.go      |  88 +++++++
 .../pkg/controller/poddisruptionbudgets_control.go |  66 ++++++
 submarine-cloud/pkg/controller/services_control.go |  70 ++++++
 submarine-cloud/pkg/operator/config.go             |  43 ++++
 submarine-cloud/pkg/operator/operator.go           |  95 ++++++++
 submarine-cloud/pkg/{signals => signal}/signal.go  |  34 ++-
 submarine-cloud/pkg/utils/build.go                 |  47 ++++
 submarine-cloud/submarine-operator.md              |   1 +
 39 files changed, 1932 insertions(+), 894 deletions(-)

diff --git a/submarine-cloud/Makefile b/submarine-cloud/Makefile
index c4ea405..830c7b7 100644
--- a/submarine-cloud/Makefile
+++ b/submarine-cloud/Makefile
@@ -31,7 +31,7 @@ LDFLAGS		:= -s -w \
 
 .PHONY: build
 build:
-	$(GOBUILD) -o ./$(BINARY_NAME) -v cmd/operator/main.go cmd/operator/controller.go
+	$(GOBUILD) -o ./$(BINARY_NAME) -v cmd/operator/main.go
 
 test:
 	$(GOTEST) -v ./...
@@ -47,4 +47,4 @@ fmt:
 	@go fmt $(CURDIR)/...
 
 release:
-	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -a -installsuffix cgo -ldflags "$(LDFLAGS)" -o ./$(BINARY_UNIX)-linux-amd64-$(VERSION) -v cmd/operator/main.go cmd/operator/controller.go
+	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -a -installsuffix cgo -ldflags "$(LDFLAGS)" -o ./$(BINARY_UNIX)-linux-amd64-$(VERSION) -v cmd/operator/main.go
diff --git a/submarine-cloud/cmd/operator/controller.go b/submarine-cloud/cmd/operator/controller.go
deleted file mode 100644
index 576b352..0000000
--- a/submarine-cloud/cmd/operator/controller.go
+++ /dev/null
@@ -1,253 +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.
- */
-package main
-
-import (
-	"fmt"
-	"time"
-
-	"github.com/golang/glog"
-	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/util/runtime"
-	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-	"k8s.io/apimachinery/pkg/util/wait"
-	"k8s.io/client-go/kubernetes"
-	"k8s.io/client-go/kubernetes/scheme"
-	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
-	"k8s.io/client-go/tools/cache"
-	"k8s.io/client-go/tools/record"
-	"k8s.io/client-go/util/workqueue"
-
-	stablev1 "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
-	clientset "github.com/apache/submarine/submarine-cloud/pkg/client/clientset/versioned"
-	studentscheme "github.com/apache/submarine/submarine-cloud/pkg/client/clientset/versioned/scheme"
-	informers "github.com/apache/submarine/submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1"
-	listers "github.com/apache/submarine/submarine-cloud/pkg/client/listers/submarine/v1alpha1"
-)
-
-const controllerName = "submarine-controller"
-
-const (
-	SuccessSynced         = "Synced"
-	MessageResourceSynced = "SubmarineServer Object synced successfully"
-	MessageTest           = "Someone changed the email in a distributed system," + controllerName + "automatically completed the evolution to the end state"
-)
-
-// Controller is the controller implementation for Student resources
-type SubmarineController struct {
-	// k8s's clientset
-	kubeclientset kubernetes.Interface
-	// our own API group的clientset
-	submarineServerClientset clientset.Interface
-
-	submarineServerLister listers.SubmarineServerLister
-	submarineServerSynced cache.InformerSynced
-
-	workQueue workqueue.RateLimitingInterface
-
-	syncHandler func(dKey string) error
-
-	recorder record.EventRecorder
-}
-
-// New SubmarineController
-func NewSubmarineController(
-	kubeclientset kubernetes.Interface,
-	studentclientset clientset.Interface,
-	studentInformer informers.SubmarineServerInformer) *SubmarineController {
-
-	utilruntime.Must(studentscheme.AddToScheme(scheme.Scheme))
-	glog.Info("Create event broadcaster")
-	eventBroadcaster := record.NewBroadcaster()
-	eventBroadcaster.StartLogging(glog.Infof)
-	eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")})
-
-	// FIXME Create a source is the controllerAgentName event logger, which is itself part of the audit / log
-	// kubectl describe crd test1
-	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
-	controller := &SubmarineController{
-		kubeclientset:            kubeclientset,
-		submarineServerClientset: studentclientset,
-		submarineServerLister:    studentInformer.Lister(),
-		submarineServerSynced:    studentInformer.Informer().HasSynced,
-		workQueue:                workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Students"),
-		recorder:                 recorder,
-	}
-
-	glog.Info("Listen for SubmarineServer's add / update / delete events")
-
-	studentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
-		AddFunc: controller.addSubmarineServerHandler,
-		UpdateFunc: func(old, new interface{}) {
-			oldSubmarineServer := old.(*stablev1.SubmarineServer)
-			newSubmarineServer := new.(*stablev1.SubmarineServer)
-			// The versions are the same, which means that there is no actual update operation
-			if oldSubmarineServer.ResourceVersion == newSubmarineServer.ResourceVersion {
-				return
-			}
-			controller.addSubmarineServerHandler(new)
-		},
-		DeleteFunc: controller.deleteSubmarineServerHandler,
-	})
-
-	controller.syncHandler = controller.syncSubmarineServer
-
-	return controller
-}
-
-// Start your controller business here
-func (c *SubmarineController) Run(max int, stopCh <-chan struct{}) error {
-	defer runtime.HandleCrash()
-	defer c.workQueue.ShutDown()
-
-	glog.Info("Start the controller event and start a cache data synchronization")
-	if ok := cache.WaitForCacheSync(stopCh, c.submarineServerSynced); !ok {
-		return fmt.Errorf("failed to wait for caches to sync")
-	}
-
-	glog.Infof("Start %d worker", max)
-	for i := 0; i < max; i++ {
-		go wait.Until(c.runWorker, time.Second, stopCh)
-	}
-
-	glog.Info("worker all started")
-
-	<-stopCh
-
-	glog.Info("worker all stop")
-	glog.Infof("%s final.", controllerName)
-
-	return nil
-}
-
-func (c *SubmarineController) runWorker() {
-	for c.processNextWorkItem() {
-	}
-}
-
-// worker Fetching data
-func (c *SubmarineController) processNextWorkItem() bool {
-	// Get a student from the queue
-	obj, shutdown := c.workQueue.Get()
-	if shutdown {
-		return false
-	}
-
-	err := func(obj interface{}) error {
-		defer c.workQueue.Done(obj)
-		var key string
-		var ok bool
-
-		if key, ok = obj.(string); !ok {
-			c.workQueue.Forget(obj)
-			runtime.HandleError(fmt.Errorf("expected string in workQueue but got %#v", obj))
-			return nil
-		}
-		// Handling event in syncHandler
-		if err := c.syncSubmarineServer(key); err != nil {
-			return fmt.Errorf("error syncing '%s': %s", key, err.Error())
-		}
-
-		c.workQueue.Forget(obj)
-		glog.Infof("Successfully synced '%s'", key)
-		return nil
-	}(obj)
-
-	if err != nil {
-		runtime.HandleError(err)
-		return true
-	}
-
-	return true
-}
-
-// Specific process
-func (c *SubmarineController) syncSubmarineServer(key string) error {
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
-	if err != nil {
-		runtime.HandleError(fmt.Errorf("invalid resource key: %s", key))
-		return nil
-	}
-
-	// Fetching objects from the cache
-	submarineServer, err := c.submarineServerLister.SubmarineServers(namespace).Get(name)
-	if err != nil {
-		// If the Student object is deleted, it will come here, so you should add execution here
-		if errors.IsNotFound(err) {
-			glog.Infof("SubmarineServer object is deleted, please perform the actual deletion event here: %s/%s ...", namespace, name)
-
-			return nil
-		}
-
-		runtime.HandleError(fmt.Errorf("failed to list SubmarineServer by: %s/%s", namespace, name))
-
-		return err
-	}
-
-	glog.Infof("Here is the actual state of the SubmarineServer object: %v ...", submarineServer)
-	submarineServerCopy := submarineServer.DeepCopy()
-
-	// FIXME Here is the simulated end-state business
-	if submarineServerCopy.Spec.Name == "gfandada" && submarineServerCopy.Spec.Email != "gfandada@gmail.com" {
-		glog.Infof("===========================================================================================================")
-		submarineServerCopy.Spec.Email = "gfandada@gmail.com"
-		glog.Infof("Expected state%v", submarineServerCopy)
-		glog.Infof("name=%v resourceVersion=%v ns=%v 类型=%v owner=%v uid=%v", submarineServerCopy.Name, submarineServerCopy.ResourceVersion,
-			submarineServerCopy.Namespace, submarineServerCopy.Kind, submarineServerCopy.OwnerReferences, submarineServerCopy.UID)
-
-		// FIXME  crd's curd generally needs to be encapsulated, you can refer to the encapsulation of Deployment
-		result := &stablev1.SubmarineServer{}
-		c.submarineServerClientset.SubmarineV1alpha1().RESTClient().Put().
-			Namespace(submarineServerCopy.Namespace).
-			Resource("submarineservers").Name(submarineServerCopy.Name).Body(submarineServerCopy).Do().Into(result)
-		c.recorder.Event(submarineServer, corev1.EventTypeWarning, SuccessSynced, MessageTest)
-		glog.Infof("===========================================================================================================")
-		return nil
-	}
-	c.recorder.Event(submarineServer, corev1.EventTypeNormal, SuccessSynced, MessageResourceSynced)
-	return nil
-}
-
-// Add SubmarineServer
-func (c *SubmarineController) addSubmarineServerHandler(obj interface{}) {
-	var key string
-	var err error
-
-	// Put objects in cache
-	if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
-		runtime.HandleError(err)
-		return
-	}
-	// Put the key into the queue
-	c.workQueue.AddRateLimited(key)
-}
-
-// Delete SubmarineServer
-func (c *SubmarineController) deleteSubmarineServerHandler(obj interface{}) {
-	var key string
-	var err error
-
-	// Removes the specified object from the cache
-	key, err = cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
-	if err != nil {
-		runtime.HandleError(err)
-		return
-	}
-	// Put the key into the queue
-	c.workQueue.AddRateLimited(key)
-}
diff --git a/submarine-cloud/cmd/operator/main.go b/submarine-cloud/cmd/operator/main.go
index 3c94fc6..aafcc9a 100644
--- a/submarine-cloud/cmd/operator/main.go
+++ b/submarine-cloud/cmd/operator/main.go
@@ -18,154 +18,46 @@
 package main
 
 import (
+	"context"
+	goflag "flag"
 	"fmt"
-	clientset "github.com/apache/submarine/submarine-cloud/pkg/client/clientset/versioned"
-	informers "github.com/apache/submarine/submarine-cloud/pkg/client/informers/externalversions"
-	"github.com/apache/submarine/submarine-cloud/pkg/signals"
+	"github.com/apache/submarine/submarine-cloud/pkg/operator"
+	"github.com/apache/submarine/submarine-cloud/pkg/signal"
+	"github.com/apache/submarine/submarine-cloud/pkg/utils"
 	"github.com/golang/glog"
-	"github.com/mitchellh/go-homedir"
-	"github.com/spf13/cobra"
-	"github.com/spf13/viper"
-	"k8s.io/client-go/kubernetes"
-	"k8s.io/client-go/tools/clientcmd"
+	"github.com/spf13/pflag"
 	"os"
-	"strings"
-	"time"
+	"runtime"
 )
 
-// rootCmd represents the base command when called without any subcommands
-var rootCmd = &cobra.Command{
-	Use:   "submarineController",
-	Short: "Apache submarine operator",
-	Long: "Apache submarine operator",
-	// Uncomment the following line if your bare application
-	// has an action associated with it:
-	//	Run: func(cmd *cobra.Command, args []string) { },
-}
-
-var cfgFile string
-
-func init() {
-	cobra.OnInitialize(initConfig)
-
-	// Here you will define your flags and configuration settings.
-	// Cobra supports persistent flags, which, if defined here,
-	// will be global for your application.
-	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.samplecontroller.yaml)")
-
-	// Cobra also supports local flags, which will only run
-	// when this action is called directly.
-	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
-
-	rootCmd.AddCommand(runCmd)
-	// Here you will define your flags and configuration settings.
-
-	// Cobra supports Persistent Flags which will work for this command
-	// and all subcommands, e.g.:
-	// runCmd.PersistentFlags().String("foo", "", "A help for foo")
-
-	// Cobra supports local flags which will only run when this command
-	// is called directly, e.g.:
-	// runCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
-}
-
 func main() {
-	if err := rootCmd.Execute(); err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
-}
-
-// runCmd represents the run command
-var runCmd = &cobra.Command{
-	Use:   "run",
-	Short: "run config=[kubeConfig's path]",
-	Long:  ``,
-	Run: func(cmd *cobra.Command, args []string) {
-		NewApp(args)
-	},
-}
+	utils.BuildInfos()
+	runtime.GOMAXPROCS(runtime.NumCPU())
 
-// initConfig reads in config file and ENV variables if set.
-func initConfig() {
-	if cfgFile != "" {
-		// Use config file from the flag.
-		viper.SetConfigFile(cfgFile)
-	} else {
-		// Find home directory.
-		home, err := homedir.Dir()
-		if err != nil {
-			fmt.Println(err)
-			os.Exit(1)
-		}
+	config := operator.NewSubmarineOperatorConfig()
+	config.AddFlags(pflag.CommandLine)
 
-		// Search config in home directory with name ".samplecontroller" (without extension).
-		viper.AddConfigPath(home)
-		viper.SetConfigName(".submarineController")
-	}
+	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
+	pflag.Parse()
+	goflag.CommandLine.Parse([]string{})
 
-	viper.AutomaticEnv() // read in environment variables that match
+	fmt.Println("config:", config)
 
-	// If a config file is found, read it in.
-	if err := viper.ReadInConfig(); err == nil {
-		fmt.Println("Using config file:", viper.ConfigFileUsed())
-	}
-}
+	op := operator.NewSubmarineOperator(config)
 
-func getKubeCfg(args []string) string {
-	for _, v := range args {
-		strs := strings.Split(v, "=")
-		if strs[0] == "config" {
-			return strs[1]
-		}
+	if err := run(op); err != nil {
+		glog.Errorf("SubmarineOperator returns an error:%v", err)
+		os.Exit(1)
 	}
-	return ""
-}
 
-func getKubeMasterUrl(args []string) string {
-	for _, v := range args {
-		strs := strings.Split(v, "=")
-		if strs[0] == "master" {
-			return strs[1]
-		}
-	}
-	return ""
+	os.Exit(0)
 }
 
-func NewApp(args []string) {
-	defer glog.Flush()
-	// Processing semaphores
-	stopCh := signals.SetupSignalHandler()
+func run(op *operator.SubmarineOperator) error {
+	ctx, cancelFunc := context.WithCancel(context.Background())
+	go signal.HandleSignal(cancelFunc)
 
-	// Processing input parameters
-	cfg, err := clientcmd.BuildConfigFromFlags(getKubeMasterUrl(args), getKubeCfg(args))
-	if err != nil {
-		glog.Fatalf("Error building kubeconfig: %s", err.Error())
-	}
+	op.Run(ctx.Done())
 
-	// Create a standard client
-	kubeClient, err := kubernetes.NewForConfig(cfg)
-	if err != nil {
-		glog.Fatalf("Error building kubernetes clientset: %s", err.Error())
-	}
-
-	// Create a client for student resources
-	studentClient, err := clientset.NewForConfig(cfg)
-	if err != nil {
-		glog.Fatalf("Error building example clientset: %s", err.Error())
-	}
-
-	// Create informer
-	studentInformerFactory := informers.NewSharedInformerFactory(studentClient, time.Second*30)
-
-	controller := NewSubmarineController(kubeClient, studentClient,
-		studentInformerFactory.Submarine().V1alpha1().SubmarineServers())
-
-	// Start informer
-	go studentInformerFactory.Start(stopCh)
-
-	// Start controller
-	if err = controller.Run(10, stopCh); err != nil {
-		glog.Fatalf("Error running controller: %s", err.Error())
-	}
+	return nil
 }
diff --git a/submarine-cloud/go.mod b/submarine-cloud/go.mod
index bf749d7..f85ba40 100644
--- a/submarine-cloud/go.mod
+++ b/submarine-cloud/go.mod
@@ -8,10 +8,12 @@ require (
 	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
 	github.com/mitchellh/go-homedir v1.1.0
 	github.com/spf13/cobra v0.0.5
+	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.5.0
 	k8s.io/api v0.0.0-20191121015604-11707872ac1c
-	k8s.io/apimachinery v0.0.0-20191121015412-41065c7a8c2a
-	k8s.io/client-go v0.0.0-20191121015835-571c0ef67034
+	k8s.io/apiextensions-apiserver v0.0.0-20191204090421-cd61debedab5
+	k8s.io/apimachinery v0.0.0-20191203211716-adc6f4cd9e7d
+	k8s.io/client-go v0.0.0-20191204082520-bc9b51d240b2
 	k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e
 )
 
diff --git a/submarine-cloud/go.sum b/submarine-cloud/go.sum
index 9882011..ce4a608 100644
--- a/submarine-cloud/go.sum
+++ b/submarine-cloud/go.sum
@@ -1,6 +1,7 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
 github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
 github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
@@ -13,62 +14,117 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/apache/submarine v0.0.0-20191203011414-d394781a2c7d h1:jX4hWmypNtuNhMONSBeT+UvxhOSNSr4hq9PgGLwFI20=
+github.com/apache/submarine v0.0.0-20191210121440-855c010715db h1:ZLSnW9MFe1alw+vfwrGx6n326f9Yj/zpwLPTRp5x1CA=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
 github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
+github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
+github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
+github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
+github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
+github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
 github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
+github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
 github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
 github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
+github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
 github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
 github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
 github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
+github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
+github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
+github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
+github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
 github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
+github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
 github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
 github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
+github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
+github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
+github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
 github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
+github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
+github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -97,16 +153,20 @@ github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
 github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -119,6 +179,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
 github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@@ -136,10 +198,15 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
 github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
 github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -153,33 +220,43 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
@@ -187,11 +264,13 @@ github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -207,13 +286,23 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
@@ -221,7 +310,9 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -237,15 +328,18 @@ golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -265,6 +359,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -283,31 +378,43 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 k8s.io/api v0.0.0-20191121015604-11707872ac1c h1:Z87my3sF4WhG0OMxzARkWY/IKBtOr+MhXZAb4ts6qFc=
 k8s.io/api v0.0.0-20191121015604-11707872ac1c/go.mod h1:R/s4gKT0V/cWEnbQa9taNRJNbWUK57/Dx6cPj6MD3A0=
+k8s.io/apiextensions-apiserver v0.0.0-20191204090421-cd61debedab5 h1:g+GvnbGqLU1Jxb/9iFm/BFcmkqG9HdsGh52+wHirpsM=
+k8s.io/apiextensions-apiserver v0.0.0-20191204090421-cd61debedab5/go.mod h1:CPw0IHz1YrWGy0+8mG/76oTHXvChlgCb3EAezKQKB2I=
 k8s.io/apimachinery v0.0.0-20191121015412-41065c7a8c2a h1:9V03T5lHv/iF4fSgvMCd+iB86AgEgmzLpheMqIJy7hs=
 k8s.io/apimachinery v0.0.0-20191121015412-41065c7a8c2a/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
+k8s.io/apiserver v0.0.0-20191204084332-137a9d3b886b/go.mod h1:itgfam5HJbT/4b2BGfpUkkxfheMmDH+Ix+tEAP3uqZk=
 k8s.io/client-go v0.0.0-20191121015835-571c0ef67034 h1:+/ppGIi1rJThJAz/xJSSOuD82gb6E5jRv2305MSznxQ=
 k8s.io/client-go v0.0.0-20191121015835-571c0ef67034/go.mod h1:Adhj+OyDRsEXTnL9BfL7xbLWGWMCqGLWpMqGHkZI4J8=
 k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e h1:HB9Zu5ZUvJfNpLiTPhz+CebVKV8C39qTBMQkAgAZLNw=
 k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
+k8s.io/component-base v0.0.0-20191204083903-0d4d24e738e4/go.mod h1:8VIh1jErItC4bg9hLBkPneyS77Tin8KwSzbYepHJnQI=
+k8s.io/component-base v0.0.0-20191204083906-3ac1376c73aa/go.mod h1:mECWvHCPhJudDVDMtBl+AIf/YnTMp5r1F947OYFUwP0=
 k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM=
 k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
@@ -325,5 +432,6 @@ modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03
 modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
 modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
 sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
+sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
 sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
diff --git a/submarine-cloud/hack/update-codegen.sh b/submarine-cloud/hack/update-codegen.sh
index e6aaf49..e16484f 100755
--- a/submarine-cloud/hack/update-codegen.sh
+++ b/submarine-cloud/hack/update-codegen.sh
@@ -24,10 +24,6 @@ set -o pipefail
 SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
 CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo $GOPATH/src/k8s.io/code-generator/)}
 
-#cp -R "$(dirname "${BASH_SOURCE[0]}")/../github.com/apache/submarine/submarine-cloud/pkg" "$(dirname "${BASH_SOURCE[0]}")/.."
-
-#exit;
-
 # generate the code with:
 # --output-base    because this script should also be able to run inside the vendor dir of
 #                  k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
diff --git a/submarine-cloud/manifests/test1.yaml b/submarine-cloud/manifests/test1.yaml
index f242e06..ab1cbbf 100644
--- a/submarine-cloud/manifests/test1.yaml
+++ b/submarine-cloud/manifests/test1.yaml
@@ -15,7 +15,7 @@
 # limitations under the License.
 #
 apiVersion: submarine.apache.org/v1alpha1
-kind: SubmarineServer
+kind: SubmarineCluster
 metadata:
   name: test1
 spec:
diff --git a/submarine-cloud/manifests/test2.yaml b/submarine-cloud/manifests/test2.yaml
index 2c6fc27..e05a637 100644
--- a/submarine-cloud/manifests/test2.yaml
+++ b/submarine-cloud/manifests/test2.yaml
@@ -15,7 +15,7 @@
 # limitations under the License.
 #
 apiVersion: submarine.apache.org/v1alpha1
-kind: SubmarineServer
+kind: SubmarineCluster
 metadata:
   name: test2
 spec:
diff --git a/submarine-cloud/pkg/apis/submarine/v1alpha1/default.go b/submarine-cloud/pkg/apis/submarine/v1alpha1/default.go
new file mode 100644
index 0000000..15ab53f
--- /dev/null
+++ b/submarine-cloud/pkg/apis/submarine/v1alpha1/default.go
@@ -0,0 +1,66 @@
+/*
+ * 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 v1alpha1
+
+import (
+	"github.com/golang/glog"
+	kapiv1 "k8s.io/api/core/v1"
+)
+
+// IsDefaultedSubmarineCluster check if the SubmarineCluster is already defaulted
+func IsDefaultedSubmarineCluster(rc *SubmarineCluster) bool {
+	if rc.Spec.NumberOfMaster == nil {
+		return false
+	}
+	if rc.Spec.ReplicationFactor == nil {
+		return false
+	}
+	return true
+}
+
+// DefaultSubmarineCluster defaults SubmarineCluster
+func DefaultSubmarineCluster(undefaultSubmarineCluster *SubmarineCluster) *SubmarineCluster {
+	glog.Infof("DefaultSubmarineCluster()")
+	rc := undefaultSubmarineCluster.DeepCopy()
+	if rc.Spec.NumberOfMaster == nil {
+		rc.Spec.NumberOfMaster = NewInt32(3)
+	}
+	if rc.Spec.ReplicationFactor == nil {
+		rc.Spec.ReplicationFactor = NewInt32(1)
+	}
+
+	if rc.Spec.PodTemplate == nil {
+		rc.Spec.PodTemplate = &kapiv1.PodTemplateSpec{}
+	}
+
+	rc.Status.Cluster.NumberOfMaster = 0
+	rc.Status.Cluster.MinReplicationFactor = 0
+	rc.Status.Cluster.MaxReplicationFactor = 0
+	rc.Status.Cluster.NbPods = 0
+	rc.Status.Cluster.NbPodsReady = 0
+	rc.Status.Cluster.NbSubmarineRunning = 0
+
+	return rc
+}
+
+// NewInt32 use to instanciate a int32 pointer
+func NewInt32(val int32) *int32 {
+	output := new(int32)
+	*output = val
+
+	return output
+}
diff --git a/submarine-cloud/pkg/apis/submarine/v1alpha1/doc.go b/submarine-cloud/pkg/apis/submarine/v1alpha1/doc.go
index 731dd9a..d848b4c 100644
--- a/submarine-cloud/pkg/apis/submarine/v1alpha1/doc.go
+++ b/submarine-cloud/pkg/apis/submarine/v1alpha1/doc.go
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 // +k8s:deepcopy-gen=package
 
 // +groupName=submarine.apache.org
diff --git a/submarine-cloud/pkg/apis/submarine/v1alpha1/register.go b/submarine-cloud/pkg/apis/submarine/v1alpha1/register.go
index b55355e..cbff1d9 100644
--- a/submarine-cloud/pkg/apis/submarine/v1alpha1/register.go
+++ b/submarine-cloud/pkg/apis/submarine/v1alpha1/register.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 // The purpose of this file is to make the client know the
-// Student type API object through the addKnownTypes method:
+// Submarine type API object through the addKnownTypes method:
 package v1alpha1
 
 import (
@@ -46,11 +46,22 @@ func Kind(kind string) schema.GroupKind {
 func addKnownTypes(scheme *runtime.Scheme) error {
 	scheme.AddKnownTypes(
 		SchemeGroupVersion,
-		&SubmarineServer{},
-		&SubmarineServerList{},
+		&SubmarineCluster{},
+		&SubmarineClusterList{},
 	)
 
 	// register the type in the scheme
 	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
 	return nil
 }
+
+const (
+	// ResourcePlural is the id to indentify pluarals
+	ResourcePlural = "submarineclusters"
+	// ResourceSingular represents the id for identify singular resource
+	ResourceSingular = "submarinecluster"
+	// ResourceKind represent the resource kind
+	ResourceKind = "SubmarineCluster"
+	// ResourceVersion represent the resource version
+	ResourceVersion = "v1alpha1"
+)
diff --git a/submarine-cloud/pkg/apis/submarine/v1alpha1/types.go b/submarine-cloud/pkg/apis/submarine/v1alpha1/types.go
index aa9d3e1..68e32c8 100644
--- a/submarine-cloud/pkg/apis/submarine/v1alpha1/types.go
+++ b/submarine-cloud/pkg/apis/submarine/v1alpha1/types.go
@@ -14,36 +14,209 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package v1alpha1
 
 import (
+	"fmt"
+	kapiv1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 // +genclient
-// +genclient:noStatus
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
-type SubmarineServer struct {
+// SubmarineCluster represents a Submarine Cluster
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type SubmarineCluster struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
-	Spec              SubmarineServerSpec `json:"spec"`
+
+	// Spec represents the desired SubmarineCluster specification
+	Spec SubmarineClusterSpec `json:"spec,omitempty"`
+
+	// Status represents the current SubmarineCluster status
+	Status SubmarineClusterStatus `json:"status,omitempty"`
 }
 
-type SubmarineServerSpec struct {
+// SubmarineClusterList is a list of Submarine resources
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type SubmarineClusterList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata"`
+
+	Items []SubmarineCluster `json:"items"`
+}
+
+// SubmarineClusterSpec contains SubmarineCluster specification
+type SubmarineClusterSpec struct {
+	NumberOfMaster    *int32 `json:"numberOfMaster,omitempty"`
+	ReplicationFactor *int32 `json:"replicationFactor,omitempty"`
+
+	// ServiceName name used to create the Kubernetes Service that reference the Submarine Cluster nodes.
+	// if ServiceName is empty, the SubmarineCluster.Name will be use for creating the service.
+	ServiceName string `json:"serviceName,omitempty"`
+
+	// PodTemplate contains the pod specificaton that should run the Submarine-server process
+	PodTemplate *kapiv1.PodTemplateSpec `json:"podTemplate,omitempty"`
+
+	// Labels for created Submarine-cluster (deployment, rs, pod) (if any)
+	AdditionalLabels map[string]string `json:"AdditionalLabels,omitempty"`
+
 	Name    string `json:"name"`
 	School  string `json:"school"`
 	Email   string `json:"email"`
 	Address string `json:"address"`
 }
 
-// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// SubmarineClusterNode represent a SubmarineCluster Node
+type SubmarineClusterNode struct {
+	ID        string                   `json:"id"`
+	Role      SubmarineClusterNodeRole `json:"role"`
+	IP        string                   `json:"ip"`
+	Port      string                   `json:"port"`
+	Slots     []string                 `json:"slots,omitempty"`
+	MasterRef string                   `json:"masterRef,omitempty"`
+	PodName   string                   `json:"podName"`
+	Pod       *kapiv1.Pod              `json:"-"`
+}
 
-// StudentList is a list of Student resources
-type SubmarineServerList struct {
-	metav1.TypeMeta `json:",inline"`
-	metav1.ListMeta `json:"metadata"`
+func (n SubmarineClusterNode) String() string {
+	if n.Role != SubmarineClusterNodeRoleSlave {
+		return fmt.Sprintf("(Master:%s, Addr:%s:%s, PodName:%s, Slots:%v)", n.ID, n.IP, n.Port, n.PodName, n.Slots)
+	}
+	return fmt.Sprintf("(Slave:%s, Addr:%s:%s, PodName:%s, MasterRef:%s)", n.ID, n.IP, n.Port, n.PodName, n.MasterRef)
+}
+
+// SubmarineClusterConditionType is the type of SubmarineClusterCondition
+type SubmarineClusterConditionType string
+
+const (
+	// SubmarineClusterOK means the SubmarineCluster is in a good shape
+	SubmarineClusterOK SubmarineClusterConditionType = "ClusterOK"
+
+	// SubmarineClusterScaling means the SubmarineCluster is currenlty in a scaling stage
+	SubmarineClusterScaling SubmarineClusterConditionType = "Scaling"
+
+	// SubmarineClusterRebalancing means the SubmarineCluster is currenlty rebalancing slots and keys
+	SubmarineClusterRebalancing SubmarineClusterConditionType = "Rebalancing"
+
+	// SubmarineClusterRollingUpdate means the SubmarineCluster is currenlty performing a rolling update of its nodes
+	SubmarineClusterRollingUpdate SubmarineClusterConditionType = "RollingUpdate"
+)
+
+// SubmarineClusterNodeRole SubmarineCluster Node Role type
+type SubmarineClusterNodeRole string
 
-	Items []SubmarineServer `json:"items"`
+const (
+	// SubmarineClusterNodeRoleMaster SubmarineCluster Master node role
+	SubmarineClusterNodeRoleMaster SubmarineClusterNodeRole = "Master"
+
+	// SubmarineClusterNodeRoleSlave SubmarineCluster Master node role
+	SubmarineClusterNodeRoleSlave SubmarineClusterNodeRole = "Slave"
+
+	// SubmarineClusterNodeRoleNone None node role
+	SubmarineClusterNodeRoleNone SubmarineClusterNodeRole = "None"
+)
+
+// ClusterStatus Submarine Cluster status
+type ClusterStatus string
+
+const (
+	// ClusterStatusOK ClusterStatus OK
+	ClusterStatusOK ClusterStatus = "OK"
+
+	// ClusterStatusError ClusterStatus Error
+	ClusterStatusError ClusterStatus = "Error"
+
+	// ClusterStatusScaling ClusterStatus Scaling
+	ClusterStatusScaling ClusterStatus = "Scaling"
+
+	// ClusterStatusCalculatingRebalancing ClusterStatus Rebalancing
+	ClusterStatusCalculatingRebalancing ClusterStatus = "Calculating Rebalancing"
+
+	// ClusterStatusRebalancing ClusterStatus Rebalancing
+	ClusterStatusRebalancing ClusterStatus = "Rebalancing"
+
+	// ClusterStatusRollingUpdate ClusterStatus RollingUpdate
+	ClusterStatusRollingUpdate ClusterStatus = "RollingUpdate"
+)
+
+// SubmarineClusterStatus contains SubmarineCluster status
+type SubmarineClusterStatus struct {
+	// Conditions represent the latest available observations of an object's current state.
+	Conditions []SubmarineClusterCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
+	// Status of the condition, one of True, False, Unknown.
+	Status kapiv1.ConditionStatus `json:"status"`
+	// StartTime represents time when the workflow was acknowledged by the Workflow controller
+	// It is not guaranteed to be set in happens-before order across separate operations.
+	// It is represented in RFC3339 form and is in UTC.
+	// StartTime doesn't consider startime of `ExternalReference`
+	StartTime *metav1.Time `json:"startTime,omitempty"`
+	// (brief) reason for the condition's last transition.
+	Reason string `json:"reason,omitempty"`
+	// Human readable message indicating details about last transition.
+	Message string `json:"message,omitempty"`
+	// Cluster a view of the current SubmarineCluster
+	Cluster SubmarineClusterClusterStatus
+}
+
+// SubmarineClusterCondition represent the condition of the SubmarineCluster
+type SubmarineClusterCondition struct {
+	// Type of workflow condition
+	Type SubmarineClusterConditionType `json:"type"`
+	// Status of the condition, one of True, False, Unknown.
+	Status kapiv1.ConditionStatus `json:"status"`
+	// Last time the condition was checked.
+	LastProbeTime metav1.Time `json:"lastProbeTime,omitempty"`
+	// Last time the condition transited from one status to another.
+	LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
+	// (brief) reason for the condition's last transition.
+	Reason string `json:"reason,omitempty"`
+	// Human readable message indicating details about last transition.
+	Message string `json:"message,omitempty"`
 }
+
+// SubmarineClusterClusterStatus represent the Submarine Cluster status
+type SubmarineClusterClusterStatus struct {
+	Status               ClusterStatus `json:"status"`
+	NumberOfMaster       int32         `json:"numberOfMaster,omitempty"`
+	MinReplicationFactor int32         `json:"minReplicationFactor,omitempty"`
+	MaxReplicationFactor int32         `json:"maxReplicationFactor,omitempty"`
+
+	NodesPlacement NodesPlacementInfo `json:"nodesPlacementInfo,omitempty"`
+
+	// In theory, we always have NbPods > NbSubmarineRunning > NbPodsReady
+	NbPods             int32 `json:"nbPods,omitempty"`
+	NbPodsReady        int32 `json:"nbPodsReady,omitempty"`
+	NbSubmarineRunning int32 `json:"nbSubmarineNodesRunning,omitempty"`
+
+	Nodes []SubmarineClusterNode `json:"nodes"`
+}
+
+func (s SubmarineClusterClusterStatus) String() string {
+	output := ""
+	output += fmt.Sprintf("status:%s\n", s.Status)
+	output += fmt.Sprintf("NumberOfMaster:%d\n", s.NumberOfMaster)
+	output += fmt.Sprintf("MinReplicationFactor:%d\n", s.MinReplicationFactor)
+	output += fmt.Sprintf("MaxReplicationFactor:%d\n", s.MaxReplicationFactor)
+	output += fmt.Sprintf("NodesPlacement:%s\n\n", s.NodesPlacement)
+	output += fmt.Sprintf("NbPods:%d\n", s.NbPods)
+	output += fmt.Sprintf("NbPodsReady:%d\n", s.NbPodsReady)
+	output += fmt.Sprintf("NbSubmarineRunning:%d\n\n", s.NbSubmarineRunning)
+
+	output += fmt.Sprintf("Nodes (%d): %s\n", len(s.Nodes), s.Nodes)
+
+	return output
+}
+
+// NodesPlacementInfo Submarine Nodes placement mode information
+type NodesPlacementInfo string
+
+const (
+	// NodesPlacementInfoBestEffort the cluster nodes placement is in best effort,
+	// it means you can have 2 masters (or more) on the same VM.
+	NodesPlacementInfoBestEffort NodesPlacementInfo = "BestEffort"
+	// NodesPlacementInfoOptimal the cluster nodes placement is optimal,
+	// it means on master by VM
+	NodesPlacementInfoOptimal NodesPlacementInfo = "Optimal"
+)
diff --git a/submarine-cloud/pkg/apis/submarine/v1alpha1/zz_generated.deepcopy.go b/submarine-cloud/pkg/apis/submarine/v1alpha1/zz_generated.deepcopy.go
index a24e9b5..7a9fde4 100644
--- a/submarine-cloud/pkg/apis/submarine/v1alpha1/zz_generated.deepcopy.go
+++ b/submarine-cloud/pkg/apis/submarine/v1alpha1/zz_generated.deepcopy.go
@@ -22,30 +22,32 @@
 package v1alpha1
 
 import (
+	v1 "k8s.io/api/core/v1"
 	runtime "k8s.io/apimachinery/pkg/runtime"
 )
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *SubmarineServer) DeepCopyInto(out *SubmarineServer) {
+func (in *SubmarineCluster) DeepCopyInto(out *SubmarineCluster) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
-	out.Spec = in.Spec
+	in.Spec.DeepCopyInto(&out.Spec)
+	in.Status.DeepCopyInto(&out.Status)
 	return
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineServer.
-func (in *SubmarineServer) DeepCopy() *SubmarineServer {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineCluster.
+func (in *SubmarineCluster) DeepCopy() *SubmarineCluster {
 	if in == nil {
 		return nil
 	}
-	out := new(SubmarineServer)
+	out := new(SubmarineCluster)
 	in.DeepCopyInto(out)
 	return out
 }
 
 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *SubmarineServer) DeepCopyObject() runtime.Object {
+func (in *SubmarineCluster) DeepCopyObject() runtime.Object {
 	if c := in.DeepCopy(); c != nil {
 		return c
 	}
@@ -53,13 +55,54 @@ func (in *SubmarineServer) DeepCopyObject() runtime.Object {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *SubmarineServerList) DeepCopyInto(out *SubmarineServerList) {
+func (in *SubmarineClusterClusterStatus) DeepCopyInto(out *SubmarineClusterClusterStatus) {
+	*out = *in
+	if in.Nodes != nil {
+		in, out := &in.Nodes, &out.Nodes
+		*out = make([]SubmarineClusterNode, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineClusterClusterStatus.
+func (in *SubmarineClusterClusterStatus) DeepCopy() *SubmarineClusterClusterStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(SubmarineClusterClusterStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *SubmarineClusterCondition) DeepCopyInto(out *SubmarineClusterCondition) {
+	*out = *in
+	in.LastProbeTime.DeepCopyInto(&out.LastProbeTime)
+	in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineClusterCondition.
+func (in *SubmarineClusterCondition) DeepCopy() *SubmarineClusterCondition {
+	if in == nil {
+		return nil
+	}
+	out := new(SubmarineClusterCondition)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *SubmarineClusterList) DeepCopyInto(out *SubmarineClusterList) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta
 	in.ListMeta.DeepCopyInto(&out.ListMeta)
 	if in.Items != nil {
 		in, out := &in.Items, &out.Items
-		*out = make([]SubmarineServer, len(*in))
+		*out = make([]SubmarineCluster, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
@@ -67,18 +110,18 @@ func (in *SubmarineServerList) DeepCopyInto(out *SubmarineServerList) {
 	return
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineServerList.
-func (in *SubmarineServerList) DeepCopy() *SubmarineServerList {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineClusterList.
+func (in *SubmarineClusterList) DeepCopy() *SubmarineClusterList {
 	if in == nil {
 		return nil
 	}
-	out := new(SubmarineServerList)
+	out := new(SubmarineClusterList)
 	in.DeepCopyInto(out)
 	return out
 }
 
 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *SubmarineServerList) DeepCopyObject() runtime.Object {
+func (in *SubmarineClusterList) DeepCopyObject() runtime.Object {
 	if c := in.DeepCopy(); c != nil {
 		return c
 	}
@@ -86,17 +129,93 @@ func (in *SubmarineServerList) DeepCopyObject() runtime.Object {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *SubmarineServerSpec) DeepCopyInto(out *SubmarineServerSpec) {
+func (in *SubmarineClusterNode) DeepCopyInto(out *SubmarineClusterNode) {
+	*out = *in
+	if in.Slots != nil {
+		in, out := &in.Slots, &out.Slots
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.Pod != nil {
+		in, out := &in.Pod, &out.Pod
+		*out = new(v1.Pod)
+		(*in).DeepCopyInto(*out)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineClusterNode.
+func (in *SubmarineClusterNode) DeepCopy() *SubmarineClusterNode {
+	if in == nil {
+		return nil
+	}
+	out := new(SubmarineClusterNode)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *SubmarineClusterSpec) DeepCopyInto(out *SubmarineClusterSpec) {
+	*out = *in
+	if in.NumberOfMaster != nil {
+		in, out := &in.NumberOfMaster, &out.NumberOfMaster
+		*out = new(int32)
+		**out = **in
+	}
+	if in.ReplicationFactor != nil {
+		in, out := &in.ReplicationFactor, &out.ReplicationFactor
+		*out = new(int32)
+		**out = **in
+	}
+	if in.PodTemplate != nil {
+		in, out := &in.PodTemplate, &out.PodTemplate
+		*out = new(v1.PodTemplateSpec)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.AdditionalLabels != nil {
+		in, out := &in.AdditionalLabels, &out.AdditionalLabels
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineClusterSpec.
+func (in *SubmarineClusterSpec) DeepCopy() *SubmarineClusterSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(SubmarineClusterSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *SubmarineClusterStatus) DeepCopyInto(out *SubmarineClusterStatus) {
 	*out = *in
+	if in.Conditions != nil {
+		in, out := &in.Conditions, &out.Conditions
+		*out = make([]SubmarineClusterCondition, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	if in.StartTime != nil {
+		in, out := &in.StartTime, &out.StartTime
+		*out = (*in).DeepCopy()
+	}
+	in.Cluster.DeepCopyInto(&out.Cluster)
 	return
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineServerSpec.
-func (in *SubmarineServerSpec) DeepCopy() *SubmarineServerSpec {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubmarineClusterStatus.
+func (in *SubmarineClusterStatus) DeepCopy() *SubmarineClusterStatus {
 	if in == nil {
 		return nil
 	}
-	out := new(SubmarineServerSpec)
+	out := new(SubmarineClusterStatus)
 	in.DeepCopyInto(out)
 	return out
 }
diff --git a/submarine-cloud/pkg/client/client.go b/submarine-cloud/pkg/client/client.go
new file mode 100644
index 0000000..3e04db2
--- /dev/null
+++ b/submarine-cloud/pkg/client/client.go
@@ -0,0 +1,114 @@
+/*
+ * 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 client
+
+import (
+	"reflect"
+	"time"
+
+	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
+	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	"k8s.io/apimachinery/pkg/util/errors"
+	"k8s.io/apimachinery/pkg/util/wait"
+	"k8s.io/client-go/rest"
+
+	"github.com/golang/glog"
+
+	"github.com/apache/submarine/submarine-cloud/pkg/apis/submarine"
+	v1 "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
+	"github.com/apache/submarine/submarine-cloud/pkg/client/clientset/versioned"
+)
+
+// DefineSubmarineClusterResource defines a SubmarineClusterResource as a k8s CR
+func DefineSubmarineClusterResource(clientset apiextensionsclient.Interface) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
+	glog.Info("DefineSubmarineClusterResource()")
+	submarineClusterResourceName := v1.ResourcePlural + "." + submarine.GroupName
+	crd := &apiextensionsv1beta1.CustomResourceDefinition{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: submarineClusterResourceName,
+		},
+		Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
+			Group:   submarine.GroupName,
+			Version: v1.SchemeGroupVersion.Version,
+			Scope:   apiextensionsv1beta1.NamespaceScoped,
+			Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
+				Plural:     v1.ResourcePlural,
+				Singular:   v1.ResourceSingular,
+				Kind:       reflect.TypeOf(v1.SubmarineCluster{}).Name(),
+				ShortNames: []string{"submarine"},
+			},
+		},
+	}
+	_, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
+	if err != nil {
+		return nil, err
+	}
+
+	// wait for CRD being established
+	err = wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) {
+		crd, err = clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(submarineClusterResourceName, metav1.GetOptions{})
+		if err != nil {
+			return false, err
+		}
+		for _, cond := range crd.Status.Conditions {
+			switch cond.Type {
+			case apiextensionsv1beta1.Established:
+				if cond.Status == apiextensionsv1beta1.ConditionTrue {
+					return true, err
+				}
+			case apiextensionsv1beta1.NamesAccepted:
+				if cond.Status == apiextensionsv1beta1.ConditionFalse {
+					glog.Errorf("Name conflict: %v\n", cond.Reason)
+				}
+			}
+		}
+		return false, err
+	})
+	if err != nil {
+		deleteErr := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(submarineClusterResourceName, nil)
+		if deleteErr != nil {
+			return nil, errors.NewAggregate([]error{err, deleteErr})
+		}
+		return nil, err
+	}
+
+	return crd, nil
+}
+
+// NewClient builds and initializes a Client and a Scheme for SubmarineCluster CR
+func NewClient(cfg *rest.Config) (versioned.Interface, error) {
+	glog.Info("NewClient()")
+	scheme := runtime.NewScheme()
+	if err := v1.AddToScheme(scheme); err != nil {
+		return nil, err
+	}
+
+	config := *cfg
+	config.GroupVersion = &v1.SchemeGroupVersion
+	config.APIPath = "/apis"
+	config.ContentType = runtime.ContentTypeJSON
+	config.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: serializer.NewCodecFactory(scheme)}
+
+	cs, err := versioned.NewForConfig(&config)
+	if err != nil {
+		return nil, err
+	}
+	return cs, nil
+}
diff --git a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarine_client.go b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarine_client.go
index 90abcd4..883949b 100644
--- a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarine_client.go
+++ b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarine_client.go
@@ -29,8 +29,8 @@ type FakeSubmarineV1alpha1 struct {
 	*testing.Fake
 }
 
-func (c *FakeSubmarineV1alpha1) SubmarineServers(namespace string) v1alpha1.SubmarineServerInterface {
-	return &FakeSubmarineServers{c, namespace}
+func (c *FakeSubmarineV1alpha1) SubmarineClusters(namespace string) v1alpha1.SubmarineClusterInterface {
+	return &FakeSubmarineClusters{c, namespace}
 }
 
 // RESTClient returns a RESTClient that is used to communicate
diff --git a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarinecluster.go b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarinecluster.go
new file mode 100644
index 0000000..b45545c
--- /dev/null
+++ b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarinecluster.go
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	v1alpha1 "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	labels "k8s.io/apimachinery/pkg/labels"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	testing "k8s.io/client-go/testing"
+)
+
+// FakeSubmarineClusters implements SubmarineClusterInterface
+type FakeSubmarineClusters struct {
+	Fake *FakeSubmarineV1alpha1
+	ns   string
+}
+
+var submarineclustersResource = schema.GroupVersionResource{Group: "submarine.apache.org", Version: "v1alpha1", Resource: "submarineclusters"}
+
+var submarineclustersKind = schema.GroupVersionKind{Group: "submarine.apache.org", Version: "v1alpha1", Kind: "SubmarineCluster"}
+
+// Get takes name of the submarineCluster, and returns the corresponding submarineCluster object, and an error if there is any.
+func (c *FakeSubmarineClusters) Get(name string, options v1.GetOptions) (result *v1alpha1.SubmarineCluster, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(submarineclustersResource, c.ns, name), &v1alpha1.SubmarineCluster{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.SubmarineCluster), err
+}
+
+// List takes label and field selectors, and returns the list of SubmarineClusters that match those selectors.
+func (c *FakeSubmarineClusters) List(opts v1.ListOptions) (result *v1alpha1.SubmarineClusterList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(submarineclustersResource, submarineclustersKind, c.ns, opts), &v1alpha1.SubmarineClusterList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v1alpha1.SubmarineClusterList{ListMeta: obj.(*v1alpha1.SubmarineClusterList).ListMeta}
+	for _, item := range obj.(*v1alpha1.SubmarineClusterList).Items {
+		if label.Matches(labels.Set(item.Labels)) {
+			list.Items = append(list.Items, item)
+		}
+	}
+	return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested submarineClusters.
+func (c *FakeSubmarineClusters) Watch(opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(submarineclustersResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a submarineCluster and creates it.  Returns the server's representation of the submarineCluster, and an error, if there is any.
+func (c *FakeSubmarineClusters) Create(submarineCluster *v1alpha1.SubmarineCluster) (result *v1alpha1.SubmarineCluster, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(submarineclustersResource, c.ns, submarineCluster), &v1alpha1.SubmarineCluster{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.SubmarineCluster), err
+}
+
+// Update takes the representation of a submarineCluster and updates it. Returns the server's representation of the submarineCluster, and an error, if there is any.
+func (c *FakeSubmarineClusters) Update(submarineCluster *v1alpha1.SubmarineCluster) (result *v1alpha1.SubmarineCluster, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(submarineclustersResource, c.ns, submarineCluster), &v1alpha1.SubmarineCluster{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.SubmarineCluster), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeSubmarineClusters) UpdateStatus(submarineCluster *v1alpha1.SubmarineCluster) (*v1alpha1.SubmarineCluster, error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateSubresourceAction(submarineclustersResource, "status", c.ns, submarineCluster), &v1alpha1.SubmarineCluster{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.SubmarineCluster), err
+}
+
+// Delete takes name of the submarineCluster and deletes it. Returns an error if one occurs.
+func (c *FakeSubmarineClusters) Delete(name string, options *v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteAction(submarineclustersResource, c.ns, name), &v1alpha1.SubmarineCluster{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeSubmarineClusters) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(submarineclustersResource, c.ns, listOptions)
+
+	_, err := c.Fake.Invokes(action, &v1alpha1.SubmarineClusterList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched submarineCluster.
+func (c *FakeSubmarineClusters) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.SubmarineCluster, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(submarineclustersResource, c.ns, name, pt, data, subresources...), &v1alpha1.SubmarineCluster{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.SubmarineCluster), err
+}
diff --git a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarineserver.go b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarineserver.go
deleted file mode 100644
index f6aa963..0000000
--- a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/fake/fake_submarineserver.go
+++ /dev/null
@@ -1,129 +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.
- */
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package fake
-
-import (
-	v1alpha1 "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	labels "k8s.io/apimachinery/pkg/labels"
-	schema "k8s.io/apimachinery/pkg/runtime/schema"
-	types "k8s.io/apimachinery/pkg/types"
-	watch "k8s.io/apimachinery/pkg/watch"
-	testing "k8s.io/client-go/testing"
-)
-
-// FakeSubmarineServers implements SubmarineServerInterface
-type FakeSubmarineServers struct {
-	Fake *FakeSubmarineV1alpha1
-	ns   string
-}
-
-var submarineserversResource = schema.GroupVersionResource{Group: "submarine.apache.org", Version: "v1alpha1", Resource: "submarineservers"}
-
-var submarineserversKind = schema.GroupVersionKind{Group: "submarine.apache.org", Version: "v1alpha1", Kind: "SubmarineServer"}
-
-// Get takes name of the submarineServer, and returns the corresponding submarineServer object, and an error if there is any.
-func (c *FakeSubmarineServers) Get(name string, options v1.GetOptions) (result *v1alpha1.SubmarineServer, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewGetAction(submarineserversResource, c.ns, name), &v1alpha1.SubmarineServer{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha1.SubmarineServer), err
-}
-
-// List takes label and field selectors, and returns the list of SubmarineServers that match those selectors.
-func (c *FakeSubmarineServers) List(opts v1.ListOptions) (result *v1alpha1.SubmarineServerList, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewListAction(submarineserversResource, submarineserversKind, c.ns, opts), &v1alpha1.SubmarineServerList{})
-
-	if obj == nil {
-		return nil, err
-	}
-
-	label, _, _ := testing.ExtractFromListOptions(opts)
-	if label == nil {
-		label = labels.Everything()
-	}
-	list := &v1alpha1.SubmarineServerList{ListMeta: obj.(*v1alpha1.SubmarineServerList).ListMeta}
-	for _, item := range obj.(*v1alpha1.SubmarineServerList).Items {
-		if label.Matches(labels.Set(item.Labels)) {
-			list.Items = append(list.Items, item)
-		}
-	}
-	return list, err
-}
-
-// Watch returns a watch.Interface that watches the requested submarineServers.
-func (c *FakeSubmarineServers) Watch(opts v1.ListOptions) (watch.Interface, error) {
-	return c.Fake.
-		InvokesWatch(testing.NewWatchAction(submarineserversResource, c.ns, opts))
-
-}
-
-// Create takes the representation of a submarineServer and creates it.  Returns the server's representation of the submarineServer, and an error, if there is any.
-func (c *FakeSubmarineServers) Create(submarineServer *v1alpha1.SubmarineServer) (result *v1alpha1.SubmarineServer, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewCreateAction(submarineserversResource, c.ns, submarineServer), &v1alpha1.SubmarineServer{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha1.SubmarineServer), err
-}
-
-// Update takes the representation of a submarineServer and updates it. Returns the server's representation of the submarineServer, and an error, if there is any.
-func (c *FakeSubmarineServers) Update(submarineServer *v1alpha1.SubmarineServer) (result *v1alpha1.SubmarineServer, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewUpdateAction(submarineserversResource, c.ns, submarineServer), &v1alpha1.SubmarineServer{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha1.SubmarineServer), err
-}
-
-// Delete takes name of the submarineServer and deletes it. Returns an error if one occurs.
-func (c *FakeSubmarineServers) Delete(name string, options *v1.DeleteOptions) error {
-	_, err := c.Fake.
-		Invokes(testing.NewDeleteAction(submarineserversResource, c.ns, name), &v1alpha1.SubmarineServer{})
-
-	return err
-}
-
-// DeleteCollection deletes a collection of objects.
-func (c *FakeSubmarineServers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
-	action := testing.NewDeleteCollectionAction(submarineserversResource, c.ns, listOptions)
-
-	_, err := c.Fake.Invokes(action, &v1alpha1.SubmarineServerList{})
-	return err
-}
-
-// Patch applies the patch and returns the patched submarineServer.
-func (c *FakeSubmarineServers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.SubmarineServer, err error) {
-	obj, err := c.Fake.
-		Invokes(testing.NewPatchSubresourceAction(submarineserversResource, c.ns, name, pt, data, subresources...), &v1alpha1.SubmarineServer{})
-
-	if obj == nil {
-		return nil, err
-	}
-	return obj.(*v1alpha1.SubmarineServer), err
-}
diff --git a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/generated_expansion.go b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/generated_expansion.go
index 0ede7ea..67abfe0 100644
--- a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/generated_expansion.go
+++ b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/generated_expansion.go
@@ -19,4 +19,4 @@
 
 package v1alpha1
 
-type SubmarineServerExpansion interface{}
+type SubmarineClusterExpansion interface{}
diff --git a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarine_client.go b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarine_client.go
index 286fea9..f45bc70 100644
--- a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarine_client.go
+++ b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarine_client.go
@@ -27,7 +27,7 @@ import (
 
 type SubmarineV1alpha1Interface interface {
 	RESTClient() rest.Interface
-	SubmarineServersGetter
+	SubmarineClustersGetter
 }
 
 // SubmarineV1alpha1Client is used to interact with features provided by the submarine.apache.org group.
@@ -35,8 +35,8 @@ type SubmarineV1alpha1Client struct {
 	restClient rest.Interface
 }
 
-func (c *SubmarineV1alpha1Client) SubmarineServers(namespace string) SubmarineServerInterface {
-	return newSubmarineServers(c, namespace)
+func (c *SubmarineV1alpha1Client) SubmarineClusters(namespace string) SubmarineClusterInterface {
+	return newSubmarineClusters(c, namespace)
 }
 
 // NewForConfig creates a new SubmarineV1alpha1Client for the given config.
diff --git a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarinecluster.go b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarinecluster.go
new file mode 100644
index 0000000..d72fa81
--- /dev/null
+++ b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarinecluster.go
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	"time"
+
+	v1alpha1 "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
+	scheme "github.com/apache/submarine/submarine-cloud/pkg/client/clientset/versioned/scheme"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	rest "k8s.io/client-go/rest"
+)
+
+// SubmarineClustersGetter has a method to return a SubmarineClusterInterface.
+// A group's client should implement this interface.
+type SubmarineClustersGetter interface {
+	SubmarineClusters(namespace string) SubmarineClusterInterface
+}
+
+// SubmarineClusterInterface has methods to work with SubmarineCluster resources.
+type SubmarineClusterInterface interface {
+	Create(*v1alpha1.SubmarineCluster) (*v1alpha1.SubmarineCluster, error)
+	Update(*v1alpha1.SubmarineCluster) (*v1alpha1.SubmarineCluster, error)
+	UpdateStatus(*v1alpha1.SubmarineCluster) (*v1alpha1.SubmarineCluster, error)
+	Delete(name string, options *v1.DeleteOptions) error
+	DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
+	Get(name string, options v1.GetOptions) (*v1alpha1.SubmarineCluster, error)
+	List(opts v1.ListOptions) (*v1alpha1.SubmarineClusterList, error)
+	Watch(opts v1.ListOptions) (watch.Interface, error)
+	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.SubmarineCluster, err error)
+	SubmarineClusterExpansion
+}
+
+// submarineClusters implements SubmarineClusterInterface
+type submarineClusters struct {
+	client rest.Interface
+	ns     string
+}
+
+// newSubmarineClusters returns a SubmarineClusters
+func newSubmarineClusters(c *SubmarineV1alpha1Client, namespace string) *submarineClusters {
+	return &submarineClusters{
+		client: c.RESTClient(),
+		ns:     namespace,
+	}
+}
+
+// Get takes name of the submarineCluster, and returns the corresponding submarineCluster object, and an error if there is any.
+func (c *submarineClusters) Get(name string, options v1.GetOptions) (result *v1alpha1.SubmarineCluster, err error) {
+	result = &v1alpha1.SubmarineCluster{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("submarineclusters").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do().
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of SubmarineClusters that match those selectors.
+func (c *submarineClusters) List(opts v1.ListOptions) (result *v1alpha1.SubmarineClusterList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v1alpha1.SubmarineClusterList{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("submarineclusters").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do().
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested submarineClusters.
+func (c *submarineClusters) Watch(opts v1.ListOptions) (watch.Interface, error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	opts.Watch = true
+	return c.client.Get().
+		Namespace(c.ns).
+		Resource("submarineclusters").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch()
+}
+
+// Create takes the representation of a submarineCluster and creates it.  Returns the server's representation of the submarineCluster, and an error, if there is any.
+func (c *submarineClusters) Create(submarineCluster *v1alpha1.SubmarineCluster) (result *v1alpha1.SubmarineCluster, err error) {
+	result = &v1alpha1.SubmarineCluster{}
+	err = c.client.Post().
+		Namespace(c.ns).
+		Resource("submarineclusters").
+		Body(submarineCluster).
+		Do().
+		Into(result)
+	return
+}
+
+// Update takes the representation of a submarineCluster and updates it. Returns the server's representation of the submarineCluster, and an error, if there is any.
+func (c *submarineClusters) Update(submarineCluster *v1alpha1.SubmarineCluster) (result *v1alpha1.SubmarineCluster, err error) {
+	result = &v1alpha1.SubmarineCluster{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("submarineclusters").
+		Name(submarineCluster.Name).
+		Body(submarineCluster).
+		Do().
+		Into(result)
+	return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+
+func (c *submarineClusters) UpdateStatus(submarineCluster *v1alpha1.SubmarineCluster) (result *v1alpha1.SubmarineCluster, err error) {
+	result = &v1alpha1.SubmarineCluster{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("submarineclusters").
+		Name(submarineCluster.Name).
+		SubResource("status").
+		Body(submarineCluster).
+		Do().
+		Into(result)
+	return
+}
+
+// Delete takes name of the submarineCluster and deletes it. Returns an error if one occurs.
+func (c *submarineClusters) Delete(name string, options *v1.DeleteOptions) error {
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("submarineclusters").
+		Name(name).
+		Body(options).
+		Do().
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *submarineClusters) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
+	var timeout time.Duration
+	if listOptions.TimeoutSeconds != nil {
+		timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
+	}
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("submarineclusters").
+		VersionedParams(&listOptions, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(options).
+		Do().
+		Error()
+}
+
+// Patch applies the patch and returns the patched submarineCluster.
+func (c *submarineClusters) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.SubmarineCluster, err error) {
+	result = &v1alpha1.SubmarineCluster{}
+	err = c.client.Patch(pt).
+		Namespace(c.ns).
+		Resource("submarineclusters").
+		SubResource(subresources...).
+		Name(name).
+		Body(data).
+		Do().
+		Into(result)
+	return
+}
diff --git a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarineserver.go b/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarineserver.go
deleted file mode 100644
index 1d89515..0000000
--- a/submarine-cloud/pkg/client/clientset/versioned/typed/submarine/v1alpha1/submarineserver.go
+++ /dev/null
@@ -1,175 +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.
- */
-
-// Code generated by client-gen. DO NOT EDIT.
-
-package v1alpha1
-
-import (
-	"time"
-
-	v1alpha1 "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
-	scheme "github.com/apache/submarine/submarine-cloud/pkg/client/clientset/versioned/scheme"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	types "k8s.io/apimachinery/pkg/types"
-	watch "k8s.io/apimachinery/pkg/watch"
-	rest "k8s.io/client-go/rest"
-)
-
-// SubmarineServersGetter has a method to return a SubmarineServerInterface.
-// A group's client should implement this interface.
-type SubmarineServersGetter interface {
-	SubmarineServers(namespace string) SubmarineServerInterface
-}
-
-// SubmarineServerInterface has methods to work with SubmarineServer resources.
-type SubmarineServerInterface interface {
-	Create(*v1alpha1.SubmarineServer) (*v1alpha1.SubmarineServer, error)
-	Update(*v1alpha1.SubmarineServer) (*v1alpha1.SubmarineServer, error)
-	Delete(name string, options *v1.DeleteOptions) error
-	DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
-	Get(name string, options v1.GetOptions) (*v1alpha1.SubmarineServer, error)
-	List(opts v1.ListOptions) (*v1alpha1.SubmarineServerList, error)
-	Watch(opts v1.ListOptions) (watch.Interface, error)
-	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.SubmarineServer, err error)
-	SubmarineServerExpansion
-}
-
-// submarineServers implements SubmarineServerInterface
-type submarineServers struct {
-	client rest.Interface
-	ns     string
-}
-
-// newSubmarineServers returns a SubmarineServers
-func newSubmarineServers(c *SubmarineV1alpha1Client, namespace string) *submarineServers {
-	return &submarineServers{
-		client: c.RESTClient(),
-		ns:     namespace,
-	}
-}
-
-// Get takes name of the submarineServer, and returns the corresponding submarineServer object, and an error if there is any.
-func (c *submarineServers) Get(name string, options v1.GetOptions) (result *v1alpha1.SubmarineServer, err error) {
-	result = &v1alpha1.SubmarineServer{}
-	err = c.client.Get().
-		Namespace(c.ns).
-		Resource("submarineservers").
-		Name(name).
-		VersionedParams(&options, scheme.ParameterCodec).
-		Do().
-		Into(result)
-	return
-}
-
-// List takes label and field selectors, and returns the list of SubmarineServers that match those selectors.
-func (c *submarineServers) List(opts v1.ListOptions) (result *v1alpha1.SubmarineServerList, err error) {
-	var timeout time.Duration
-	if opts.TimeoutSeconds != nil {
-		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
-	}
-	result = &v1alpha1.SubmarineServerList{}
-	err = c.client.Get().
-		Namespace(c.ns).
-		Resource("submarineservers").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Timeout(timeout).
-		Do().
-		Into(result)
-	return
-}
-
-// Watch returns a watch.Interface that watches the requested submarineServers.
-func (c *submarineServers) Watch(opts v1.ListOptions) (watch.Interface, error) {
-	var timeout time.Duration
-	if opts.TimeoutSeconds != nil {
-		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
-	}
-	opts.Watch = true
-	return c.client.Get().
-		Namespace(c.ns).
-		Resource("submarineservers").
-		VersionedParams(&opts, scheme.ParameterCodec).
-		Timeout(timeout).
-		Watch()
-}
-
-// Create takes the representation of a submarineServer and creates it.  Returns the server's representation of the submarineServer, and an error, if there is any.
-func (c *submarineServers) Create(submarineServer *v1alpha1.SubmarineServer) (result *v1alpha1.SubmarineServer, err error) {
-	result = &v1alpha1.SubmarineServer{}
-	err = c.client.Post().
-		Namespace(c.ns).
-		Resource("submarineservers").
-		Body(submarineServer).
-		Do().
-		Into(result)
-	return
-}
-
-// Update takes the representation of a submarineServer and updates it. Returns the server's representation of the submarineServer, and an error, if there is any.
-func (c *submarineServers) Update(submarineServer *v1alpha1.SubmarineServer) (result *v1alpha1.SubmarineServer, err error) {
-	result = &v1alpha1.SubmarineServer{}
-	err = c.client.Put().
-		Namespace(c.ns).
-		Resource("submarineservers").
-		Name(submarineServer.Name).
-		Body(submarineServer).
-		Do().
-		Into(result)
-	return
-}
-
-// Delete takes name of the submarineServer and deletes it. Returns an error if one occurs.
-func (c *submarineServers) Delete(name string, options *v1.DeleteOptions) error {
-	return c.client.Delete().
-		Namespace(c.ns).
-		Resource("submarineservers").
-		Name(name).
-		Body(options).
-		Do().
-		Error()
-}
-
-// DeleteCollection deletes a collection of objects.
-func (c *submarineServers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
-	var timeout time.Duration
-	if listOptions.TimeoutSeconds != nil {
-		timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
-	}
-	return c.client.Delete().
-		Namespace(c.ns).
-		Resource("submarineservers").
-		VersionedParams(&listOptions, scheme.ParameterCodec).
-		Timeout(timeout).
-		Body(options).
-		Do().
-		Error()
-}
-
-// Patch applies the patch and returns the patched submarineServer.
-func (c *submarineServers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.SubmarineServer, err error) {
-	result = &v1alpha1.SubmarineServer{}
-	err = c.client.Patch(pt).
-		Namespace(c.ns).
-		Resource("submarineservers").
-		SubResource(subresources...).
-		Name(name).
-		Body(data).
-		Do().
-		Into(result)
-	return
-}
diff --git a/submarine-cloud/pkg/client/informers/externalversions/generic.go b/submarine-cloud/pkg/client/informers/externalversions/generic.go
index 58c49d3..bbf7ac7 100644
--- a/submarine-cloud/pkg/client/informers/externalversions/generic.go
+++ b/submarine-cloud/pkg/client/informers/externalversions/generic.go
@@ -54,8 +54,8 @@ func (f *genericInformer) Lister() cache.GenericLister {
 func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
 	switch resource {
 	// Group=submarine.apache.org, Version=v1alpha1
-	case v1alpha1.SchemeGroupVersion.WithResource("submarineservers"):
-		return &genericInformer{resource: resource.GroupResource(), informer: f.Submarine().V1alpha1().SubmarineServers().Informer()}, nil
+	case v1alpha1.SchemeGroupVersion.WithResource("submarineclusters"):
+		return &genericInformer{resource: resource.GroupResource(), informer: f.Submarine().V1alpha1().SubmarineClusters().Informer()}, nil
 
 	}
 
diff --git a/submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/interface.go b/submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/interface.go
index 04288b2..59af52f 100644
--- a/submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/interface.go
+++ b/submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/interface.go
@@ -25,8 +25,8 @@ import (
 
 // Interface provides access to all the informers in this group version.
 type Interface interface {
-	// SubmarineServers returns a SubmarineServerInformer.
-	SubmarineServers() SubmarineServerInformer
+	// SubmarineClusters returns a SubmarineClusterInformer.
+	SubmarineClusters() SubmarineClusterInformer
 }
 
 type version struct {
@@ -40,7 +40,7 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
 	return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
 }
 
-// SubmarineServers returns a SubmarineServerInformer.
-func (v *version) SubmarineServers() SubmarineServerInformer {
-	return &submarineServerInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+// SubmarineClusters returns a SubmarineClusterInformer.
+func (v *version) SubmarineClusters() SubmarineClusterInformer {
+	return &submarineClusterInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
 }
diff --git a/submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/submarineserver.go b/submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/submarinecluster.go
similarity index 58%
rename from submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/submarineserver.go
rename to submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/submarinecluster.go
index b9f663b..35c510e 100644
--- a/submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/submarineserver.go
+++ b/submarine-cloud/pkg/client/informers/externalversions/submarine/v1alpha1/submarinecluster.go
@@ -32,59 +32,59 @@ import (
 	cache "k8s.io/client-go/tools/cache"
 )
 
-// SubmarineServerInformer provides access to a shared informer and lister for
-// SubmarineServers.
-type SubmarineServerInformer interface {
+// SubmarineClusterInformer provides access to a shared informer and lister for
+// SubmarineClusters.
+type SubmarineClusterInformer interface {
 	Informer() cache.SharedIndexInformer
-	Lister() v1alpha1.SubmarineServerLister
+	Lister() v1alpha1.SubmarineClusterLister
 }
 
-type submarineServerInformer struct {
+type submarineClusterInformer struct {
 	factory          internalinterfaces.SharedInformerFactory
 	tweakListOptions internalinterfaces.TweakListOptionsFunc
 	namespace        string
 }
 
-// NewSubmarineServerInformer constructs a new informer for SubmarineServer type.
+// NewSubmarineClusterInformer constructs a new informer for SubmarineCluster type.
 // Always prefer using an informer factory to get a shared informer instead of getting an independent
 // one. This reduces memory footprint and number of connections to the server.
-func NewSubmarineServerInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
-	return NewFilteredSubmarineServerInformer(client, namespace, resyncPeriod, indexers, nil)
+func NewSubmarineClusterInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+	return NewFilteredSubmarineClusterInformer(client, namespace, resyncPeriod, indexers, nil)
 }
 
-// NewFilteredSubmarineServerInformer constructs a new informer for SubmarineServer type.
+// NewFilteredSubmarineClusterInformer constructs a new informer for SubmarineCluster type.
 // Always prefer using an informer factory to get a shared informer instead of getting an independent
 // one. This reduces memory footprint and number of connections to the server.
-func NewFilteredSubmarineServerInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+func NewFilteredSubmarineClusterInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
 	return cache.NewSharedIndexInformer(
 		&cache.ListWatch{
 			ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
 				if tweakListOptions != nil {
 					tweakListOptions(&options)
 				}
-				return client.SubmarineV1alpha1().SubmarineServers(namespace).List(options)
+				return client.SubmarineV1alpha1().SubmarineClusters(namespace).List(options)
 			},
 			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
 				if tweakListOptions != nil {
 					tweakListOptions(&options)
 				}
-				return client.SubmarineV1alpha1().SubmarineServers(namespace).Watch(options)
+				return client.SubmarineV1alpha1().SubmarineClusters(namespace).Watch(options)
 			},
 		},
-		&submarinev1alpha1.SubmarineServer{},
+		&submarinev1alpha1.SubmarineCluster{},
 		resyncPeriod,
 		indexers,
 	)
 }
 
-func (f *submarineServerInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
-	return NewFilteredSubmarineServerInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+func (f *submarineClusterInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+	return NewFilteredSubmarineClusterInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
 }
 
-func (f *submarineServerInformer) Informer() cache.SharedIndexInformer {
-	return f.factory.InformerFor(&submarinev1alpha1.SubmarineServer{}, f.defaultInformer)
+func (f *submarineClusterInformer) Informer() cache.SharedIndexInformer {
+	return f.factory.InformerFor(&submarinev1alpha1.SubmarineCluster{}, f.defaultInformer)
 }
 
-func (f *submarineServerInformer) Lister() v1alpha1.SubmarineServerLister {
-	return v1alpha1.NewSubmarineServerLister(f.Informer().GetIndexer())
+func (f *submarineClusterInformer) Lister() v1alpha1.SubmarineClusterLister {
+	return v1alpha1.NewSubmarineClusterLister(f.Informer().GetIndexer())
 }
diff --git a/submarine-cloud/pkg/client/listers/submarine/v1alpha1/expansion_generated.go b/submarine-cloud/pkg/client/listers/submarine/v1alpha1/expansion_generated.go
index 8cd2035..1265463 100644
--- a/submarine-cloud/pkg/client/listers/submarine/v1alpha1/expansion_generated.go
+++ b/submarine-cloud/pkg/client/listers/submarine/v1alpha1/expansion_generated.go
@@ -19,10 +19,10 @@
 
 package v1alpha1
 
-// SubmarineServerListerExpansion allows custom methods to be added to
-// SubmarineServerLister.
-type SubmarineServerListerExpansion interface{}
+// SubmarineClusterListerExpansion allows custom methods to be added to
+// SubmarineClusterLister.
+type SubmarineClusterListerExpansion interface{}
 
-// SubmarineServerNamespaceListerExpansion allows custom methods to be added to
-// SubmarineServerNamespaceLister.
-type SubmarineServerNamespaceListerExpansion interface{}
+// SubmarineClusterNamespaceListerExpansion allows custom methods to be added to
+// SubmarineClusterNamespaceLister.
+type SubmarineClusterNamespaceListerExpansion interface{}
diff --git a/submarine-cloud/pkg/client/listers/submarine/v1alpha1/submarinecluster.go b/submarine-cloud/pkg/client/listers/submarine/v1alpha1/submarinecluster.go
new file mode 100644
index 0000000..f2acaa3
--- /dev/null
+++ b/submarine-cloud/pkg/client/listers/submarine/v1alpha1/submarinecluster.go
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+	v1alpha1 "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/client-go/tools/cache"
+)
+
+// SubmarineClusterLister helps list SubmarineClusters.
+type SubmarineClusterLister interface {
+	// List lists all SubmarineClusters in the indexer.
+	List(selector labels.Selector) (ret []*v1alpha1.SubmarineCluster, err error)
+	// SubmarineClusters returns an object that can list and get SubmarineClusters.
+	SubmarineClusters(namespace string) SubmarineClusterNamespaceLister
+	SubmarineClusterListerExpansion
+}
+
+// submarineClusterLister implements the SubmarineClusterLister interface.
+type submarineClusterLister struct {
+	indexer cache.Indexer
+}
+
+// NewSubmarineClusterLister returns a new SubmarineClusterLister.
+func NewSubmarineClusterLister(indexer cache.Indexer) SubmarineClusterLister {
+	return &submarineClusterLister{indexer: indexer}
+}
+
+// List lists all SubmarineClusters in the indexer.
+func (s *submarineClusterLister) List(selector labels.Selector) (ret []*v1alpha1.SubmarineCluster, err error) {
+	err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+		ret = append(ret, m.(*v1alpha1.SubmarineCluster))
+	})
+	return ret, err
+}
+
+// SubmarineClusters returns an object that can list and get SubmarineClusters.
+func (s *submarineClusterLister) SubmarineClusters(namespace string) SubmarineClusterNamespaceLister {
+	return submarineClusterNamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// SubmarineClusterNamespaceLister helps list and get SubmarineClusters.
+type SubmarineClusterNamespaceLister interface {
+	// List lists all SubmarineClusters in the indexer for a given namespace.
+	List(selector labels.Selector) (ret []*v1alpha1.SubmarineCluster, err error)
+	// Get retrieves the SubmarineCluster from the indexer for a given namespace and name.
+	Get(name string) (*v1alpha1.SubmarineCluster, error)
+	SubmarineClusterNamespaceListerExpansion
+}
+
+// submarineClusterNamespaceLister implements the SubmarineClusterNamespaceLister
+// interface.
+type submarineClusterNamespaceLister struct {
+	indexer   cache.Indexer
+	namespace string
+}
+
+// List lists all SubmarineClusters in the indexer for a given namespace.
+func (s submarineClusterNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.SubmarineCluster, err error) {
+	err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+		ret = append(ret, m.(*v1alpha1.SubmarineCluster))
+	})
+	return ret, err
+}
+
+// Get retrieves the SubmarineCluster from the indexer for a given namespace and name.
+func (s submarineClusterNamespaceLister) Get(name string) (*v1alpha1.SubmarineCluster, error) {
+	obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+	if err != nil {
+		return nil, err
+	}
+	if !exists {
+		return nil, errors.NewNotFound(v1alpha1.Resource("submarinecluster"), name)
+	}
+	return obj.(*v1alpha1.SubmarineCluster), nil
+}
diff --git a/submarine-cloud/pkg/client/listers/submarine/v1alpha1/submarineserver.go b/submarine-cloud/pkg/client/listers/submarine/v1alpha1/submarineserver.go
deleted file mode 100644
index 82a7c29..0000000
--- a/submarine-cloud/pkg/client/listers/submarine/v1alpha1/submarineserver.go
+++ /dev/null
@@ -1,95 +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.
- */
-
-// Code generated by lister-gen. DO NOT EDIT.
-
-package v1alpha1
-
-import (
-	v1alpha1 "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
-	"k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/labels"
-	"k8s.io/client-go/tools/cache"
-)
-
-// SubmarineServerLister helps list SubmarineServers.
-type SubmarineServerLister interface {
-	// List lists all SubmarineServers in the indexer.
-	List(selector labels.Selector) (ret []*v1alpha1.SubmarineServer, err error)
-	// SubmarineServers returns an object that can list and get SubmarineServers.
-	SubmarineServers(namespace string) SubmarineServerNamespaceLister
-	SubmarineServerListerExpansion
-}
-
-// submarineServerLister implements the SubmarineServerLister interface.
-type submarineServerLister struct {
-	indexer cache.Indexer
-}
-
-// NewSubmarineServerLister returns a new SubmarineServerLister.
-func NewSubmarineServerLister(indexer cache.Indexer) SubmarineServerLister {
-	return &submarineServerLister{indexer: indexer}
-}
-
-// List lists all SubmarineServers in the indexer.
-func (s *submarineServerLister) List(selector labels.Selector) (ret []*v1alpha1.SubmarineServer, err error) {
-	err = cache.ListAll(s.indexer, selector, func(m interface{}) {
-		ret = append(ret, m.(*v1alpha1.SubmarineServer))
-	})
-	return ret, err
-}
-
-// SubmarineServers returns an object that can list and get SubmarineServers.
-func (s *submarineServerLister) SubmarineServers(namespace string) SubmarineServerNamespaceLister {
-	return submarineServerNamespaceLister{indexer: s.indexer, namespace: namespace}
-}
-
-// SubmarineServerNamespaceLister helps list and get SubmarineServers.
-type SubmarineServerNamespaceLister interface {
-	// List lists all SubmarineServers in the indexer for a given namespace.
-	List(selector labels.Selector) (ret []*v1alpha1.SubmarineServer, err error)
-	// Get retrieves the SubmarineServer from the indexer for a given namespace and name.
-	Get(name string) (*v1alpha1.SubmarineServer, error)
-	SubmarineServerNamespaceListerExpansion
-}
-
-// submarineServerNamespaceLister implements the SubmarineServerNamespaceLister
-// interface.
-type submarineServerNamespaceLister struct {
-	indexer   cache.Indexer
-	namespace string
-}
-
-// List lists all SubmarineServers in the indexer for a given namespace.
-func (s submarineServerNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.SubmarineServer, err error) {
-	err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
-		ret = append(ret, m.(*v1alpha1.SubmarineServer))
-	})
-	return ret, err
-}
-
-// Get retrieves the SubmarineServer from the indexer for a given namespace and name.
-func (s submarineServerNamespaceLister) Get(name string) (*v1alpha1.SubmarineServer, error) {
-	obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
-	if err != nil {
-		return nil, err
-	}
-	if !exists {
-		return nil, errors.NewNotFound(v1alpha1.Resource("submarineserver"), name)
-	}
-	return obj.(*v1alpha1.SubmarineServer), nil
-}
diff --git a/submarine-cloud/pkg/signals/signal_posix.go b/submarine-cloud/pkg/config/cluster.go
similarity index 63%
rename from submarine-cloud/pkg/signals/signal_posix.go
rename to submarine-cloud/pkg/config/cluster.go
index 78d541f..2f5b6dd 100644
--- a/submarine-cloud/pkg/signals/signal_posix.go
+++ b/submarine-cloud/pkg/config/cluster.go
@@ -15,13 +15,19 @@
  * limitations under the License.
  */
 
-// +build !windows
+package config
 
-package signals
+import "github.com/spf13/pflag"
 
-import (
-	"os"
-	"syscall"
-)
+// Cluster used to store all Submarine Cluster configuration information
+type Cluster struct {
+	Namespace   string
+	NodeService string
+}
 
-var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
+// AddFlags use to add the Submarine-Cluster Config flags to the command line
+func (c *Cluster) AddFlags(fs *pflag.FlagSet) {
+	fs.StringVar(&c.Namespace, "ns", "", "Submarine-node k8s namespace")
+	fs.StringVar(&c.NodeService, "rs", "", "Submarine-node k8s service name")
+
+}
diff --git a/submarine-cloud/pkg/config/submarine.go b/submarine-cloud/pkg/config/submarine.go
new file mode 100644
index 0000000..c6acd83
--- /dev/null
+++ b/submarine-cloud/pkg/config/submarine.go
@@ -0,0 +1,101 @@
+/*
+ * 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 config
+
+import (
+	"fmt"
+	"path"
+
+	"github.com/spf13/pflag"
+)
+
+const (
+	// DefaultSubmarineTimeout default Submarine timeout (ms)
+	DefaultSubmarineTimeout = 2000
+	//DefaultClusterNodeTimeout default cluster node timeout (ms)
+	//The maximum amount of time a Submarine Cluster node can be unavailable, without it being considered as failing
+	DefaultClusterNodeTimeout = 2000
+	// SubmarineRenameCommandsDefaultPath default path to volume storing rename commands
+	SubmarineRenameCommandsDefaultPath = "/etc/secret-volume"
+	// SubmarineRenameCommandsDefaultFile default file name containing rename commands
+	SubmarineRenameCommandsDefaultFile = ""
+	// SubmarineConfigFileDefault default config file path
+	SubmarineConfigFileDefault = "/submarine-conf/submarine.conf"
+	// SubmarineServerBinDefault default binary name
+	SubmarineServerBinDefault = "submarine-server"
+	// SubmarineServerPortDefault default Submarine port
+	SubmarineServerPortDefault = "6379"
+	// SubmarineMaxMemoryDefault default Submarine max memory
+	SubmarineMaxMemoryDefault = 0
+	// SubmarineMaxMemoryPolicyDefault default Submarine max memory evition policy
+	SubmarineMaxMemoryPolicyDefault = "noeviction"
+)
+
+// Submarine used to store all Submarine configuration information
+type Submarine struct {
+	DialTimeout        int
+	ClusterNodeTimeout int
+	ConfigFileName     string
+	renameCommandsPath string
+	renameCommandsFile string
+	HTTPServerAddr     string
+	ServerBin          string
+	ServerPort         string
+	ServerIP           string
+	MaxMemory          uint32
+	MaxMemoryPolicy    string
+	ConfigFiles        []string
+}
+
+// AddFlags use to add the Submarine Config flags to the command line
+func (r *Submarine) AddFlags(fs *pflag.FlagSet) {
+	fs.IntVar(&r.DialTimeout, "rdt", DefaultSubmarineTimeout, "Submarine dial timeout (ms)")
+	fs.IntVar(&r.ClusterNodeTimeout, "cluster-node-timeout", DefaultClusterNodeTimeout, "Submarine node timeout (ms)")
+	fs.StringVar(&r.ConfigFileName, "c", SubmarineConfigFileDefault, "Submarine config file path")
+	fs.StringVar(&r.renameCommandsPath, "rename-command-path", SubmarineRenameCommandsDefaultPath, "Path to the folder where rename-commands option for Submarine are available")
+	fs.StringVar(&r.renameCommandsFile, "rename-command-file", SubmarineRenameCommandsDefaultFile, "Name of the file where rename-commands option for Submarine are available, disabled if empty")
+	fs.Uint32Var(&r.MaxMemory, "max-memory", SubmarineMaxMemoryDefault, "Submarine max memory")
+	fs.StringVar(&r.MaxMemoryPolicy, "max-memory-policy", SubmarineMaxMemoryPolicyDefault, "Submarine max memory evition policy")
+	fs.StringVar(&r.ServerBin, "bin", SubmarineServerBinDefault, "Submarine server binary file name")
+	fs.StringVar(&r.ServerPort, "port", SubmarineServerPortDefault, "Submarine server listen port")
+	fs.StringVar(&r.ServerIP, "ip", "", "Submarine server listen ip")
+	fs.StringArrayVar(&r.ConfigFiles, "config-file", []string{}, "Location of Submarine configuration file that will be include in the ")
+
+}
+
+// GetRenameCommandsFile return the path to the rename command file, or empty string if not define
+func (r *Submarine) GetRenameCommandsFile() string {
+	if r.renameCommandsFile == "" {
+		return ""
+	}
+	return path.Join(r.renameCommandsPath, r.renameCommandsFile)
+}
+
+// String stringer interface
+func (r Submarine) String() string {
+	var output string
+	output += fmt.Sprintln("[ Submarine Configuration ]")
+	output += fmt.Sprintln("- DialTimeout:", r.DialTimeout)
+	output += fmt.Sprintln("- ClusterNodeTimeout:", r.ClusterNodeTimeout)
+	output += fmt.Sprintln("- Rename commands:", r.GetRenameCommandsFile())
+	output += fmt.Sprintln("- max-memory:", r.MaxMemory)
+	output += fmt.Sprintln("- max-memory-policy:", r.MaxMemoryPolicy)
+	output += fmt.Sprintln("- server-bin:", r.ServerBin)
+	output += fmt.Sprintln("- server-port:", r.ServerPort)
+	return output
+}
diff --git a/submarine-cloud/pkg/signals/signal_windows.go b/submarine-cloud/pkg/controller/config.go
similarity index 67%
rename from submarine-cloud/pkg/signals/signal_windows.go
rename to submarine-cloud/pkg/controller/config.go
index 08d95fc..786865c 100644
--- a/submarine-cloud/pkg/signals/signal_windows.go
+++ b/submarine-cloud/pkg/controller/config.go
@@ -14,11 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package controller
 
-package signals
+import "github.com/apache/submarine/submarine-cloud/pkg/config"
 
-import (
-	"os"
-)
+// Config contains the Controller settings
+type Config struct {
+	NbWorker  int
+	submarine config.Submarine
+}
 
-var shutdownSignals = []os.Signal{os.Interrupt}
+// NewConfig builds and returns new Config instance
+func NewConfig(nbWorker int, submarine config.Submarine) *Config {
+	return &Config{
+		NbWorker:  nbWorker,
+		submarine: submarine,
+	}
+}
diff --git a/submarine-cloud/pkg/controller/controller.go b/submarine-cloud/pkg/controller/controller.go
new file mode 100644
index 0000000..7cdb0da
--- /dev/null
+++ b/submarine-cloud/pkg/controller/controller.go
@@ -0,0 +1,258 @@
+/*
+ * 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 controller
+
+import (
+	"fmt"
+	"github.com/apache/submarine/submarine-cloud/pkg/controller/pod"
+	"github.com/golang/glog"
+	apiv1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	"k8s.io/apimachinery/pkg/util/wait"
+	kubeinformers "k8s.io/client-go/informers"
+	"k8s.io/client-go/kubernetes/scheme"
+	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
+	corev1listers "k8s.io/client-go/listers/core/v1"
+	policyv1listers "k8s.io/client-go/listers/policy/v1beta1"
+	"k8s.io/client-go/tools/cache"
+	"k8s.io/client-go/tools/record"
+	"k8s.io/client-go/util/workqueue"
+	"time"
+
+	rapi "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
+	sClient "github.com/apache/submarine/submarine-cloud/pkg/client/clientset/versioned"
+	sInformers "github.com/apache/submarine/submarine-cloud/pkg/client/informers/externalversions"
+	sListers "github.com/apache/submarine/submarine-cloud/pkg/client/listers/submarine/v1alpha1"
+	clientset "k8s.io/client-go/kubernetes"
+)
+
+// Controller contains all controller fields
+type Controller struct {
+	kubeClient      clientset.Interface
+	submarineClient sClient.Interface
+
+	submarineClusterLister sListers.SubmarineClusterLister
+	submarineClusterSynced cache.InformerSynced
+
+	podLister corev1listers.PodLister
+	PodSynced cache.InformerSynced
+
+	serviceLister corev1listers.ServiceLister
+	ServiceSynced cache.InformerSynced
+
+	podDisruptionBudgetLister  policyv1listers.PodDisruptionBudgetLister
+	PodDiscruptionBudgetSynced cache.InformerSynced
+
+	podControl                 pod.SubmarineClusterControlInteface
+	serviceControl             ServicesControlInterface
+	podDisruptionBudgetControl PodDisruptionBudgetsControlInterface
+
+	updateHandler func(cluster *rapi.SubmarineCluster) (*rapi.SubmarineCluster, error) // callback to update SubmarineCluster. Added as member for testing
+
+	queue    workqueue.RateLimitingInterface // SubmarineClusters to be synced
+	recorder record.EventRecorder
+
+	config *Config
+}
+
+// NewController builds and return new controller instance
+func NewController(cfg *Config, kubeClient clientset.Interface, submarineClient sClient.Interface, kubeInformer kubeinformers.SharedInformerFactory, rInformer sInformers.SharedInformerFactory) *Controller {
+	glog.Info("NewController()")
+	eventBroadcaster := record.NewBroadcaster()
+	eventBroadcaster.StartLogging(glog.Infof)
+	eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeClient.CoreV1().Events("")})
+
+	serviceInformer := kubeInformer.Core().V1().Services()
+	podInformer := kubeInformer.Core().V1().Pods()
+	submarineInformer := rInformer.Submarine().V1alpha1().SubmarineClusters()
+	podDisruptionBudgetInformer := kubeInformer.Policy().V1beta1().PodDisruptionBudgets()
+
+	ctrl := &Controller{
+		kubeClient:                 kubeClient,
+		submarineClient:            submarineClient,
+		submarineClusterLister:     submarineInformer.Lister(),
+		submarineClusterSynced:     submarineInformer.Informer().HasSynced,
+		podLister:                  podInformer.Lister(),
+		PodSynced:                  podInformer.Informer().HasSynced,
+		serviceLister:              serviceInformer.Lister(),
+		ServiceSynced:              serviceInformer.Informer().HasSynced,
+		podDisruptionBudgetLister:  podDisruptionBudgetInformer.Lister(),
+		PodDiscruptionBudgetSynced: podDisruptionBudgetInformer.Informer().HasSynced,
+
+		queue:    workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "submarinecluster"),
+		recorder: eventBroadcaster.NewRecorder(scheme.Scheme, apiv1.EventSource{Component: "submarinecluster-controller"}),
+
+		config: cfg,
+	}
+
+	submarineInformer.Informer().AddEventHandler(
+		cache.ResourceEventHandlerFuncs{
+			AddFunc:    ctrl.onAddSubmarineCluster,
+			UpdateFunc: ctrl.onUpdateSubmarineCluster,
+			DeleteFunc: ctrl.onDeleteSubmarineCluster,
+		},
+	)
+
+	podInformer.Informer().AddEventHandler(
+		cache.ResourceEventHandlerFuncs{
+			AddFunc:    ctrl.onAddPod,
+			UpdateFunc: ctrl.onUpdatePod,
+			DeleteFunc: ctrl.onDeletePod,
+		},
+	)
+
+	ctrl.updateHandler = ctrl.updateSubmarineCluster
+	ctrl.podControl = pod.NewSubmarineClusterControl(ctrl.podLister, ctrl.kubeClient, ctrl.recorder)
+	ctrl.serviceControl = NewServicesControl(ctrl.kubeClient, ctrl.recorder)
+	ctrl.podDisruptionBudgetControl = NewPodDisruptionBudgetsControl(ctrl.kubeClient, ctrl.recorder)
+
+	return ctrl
+}
+
+// Run executes the Controller
+func (c *Controller) Run(stop <-chan struct{}) error {
+	glog.Infof("Starting SubmarineCluster controller")
+
+	if !cache.WaitForCacheSync(stop, c.PodSynced, c.submarineClusterSynced, c.ServiceSynced) {
+		return fmt.Errorf("Timed out waiting for caches to sync")
+	}
+
+	for i := 0; i < c.config.NbWorker; i++ {
+		go wait.Until(c.runWorker, time.Second, stop)
+	}
+
+	<-stop
+	return nil
+}
+
+func (c *Controller) runWorker() {
+	for c.processNextItem() {
+	}
+}
+
+func (c *Controller) processNextItem() bool {
+	key, quit := c.queue.Get()
+	if quit {
+		return false
+	}
+	defer c.queue.Done(key)
+	needRequeue, err := c.sync(key.(string))
+	if err == nil {
+		c.queue.Forget(key)
+	} else {
+		utilruntime.HandleError(fmt.Errorf("Error syncing submarinecluster: %v", err))
+		c.queue.AddRateLimited(key)
+		return true
+	}
+
+	if needRequeue {
+		glog.V(4).Info("processNextItem: Requeue key:", key)
+		c.queue.AddRateLimited(key)
+	}
+
+	return true
+}
+
+func (c *Controller) sync(key string) (bool, error) {
+	glog.V(2).Infof("sync() key:%s", key)
+	startTime := metav1.Now()
+	defer func() {
+		glog.V(2).Infof("Finished syncing SubmarineCluster %q (%v", key, time.Since(startTime.Time))
+	}()
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		return false, err
+	}
+	glog.V(6).Infof("Syncing %s/%s", namespace, name)
+	sharedSubmarineCluster, err := c.submarineClusterLister.SubmarineClusters(namespace).Get(name)
+	if err != nil {
+		glog.Errorf("unable to get SubmarineCluster %s/%s: %v. Maybe deleted", namespace, name, err)
+		return false, nil
+	}
+
+	if !rapi.IsDefaultedSubmarineCluster(sharedSubmarineCluster) {
+		defaultedSubmarineCluster := rapi.DefaultSubmarineCluster(sharedSubmarineCluster)
+		if _, err = c.updateHandler(defaultedSubmarineCluster); err != nil {
+			glog.Errorf("SubmarineCluster %s/%s updated error:, err", namespace, name)
+			return false, fmt.Errorf("unable to default SubmarineCluster %s/%s: %v", namespace, name, err)
+		}
+		glog.V(6).Infof("SubmarineCluster-Operator.sync Defaulted %s/%s", namespace, name)
+		return false, nil
+	}
+
+	// TODO add validation
+
+	// TODO: add test the case of graceful deletion
+	if sharedSubmarineCluster.DeletionTimestamp != nil {
+		return false, nil
+	}
+
+	submarinecluster := sharedSubmarineCluster.DeepCopy()
+
+	// Init status.StartTime
+	if submarinecluster.Status.StartTime == nil {
+		submarinecluster.Status.StartTime = &startTime
+		if _, err := c.updateHandler(submarinecluster); err != nil {
+			glog.Errorf("SubmarineCluster %s/%s: unable init startTime: %v", namespace, name, err)
+			return false, nil
+		}
+		glog.V(4).Infof("SubmarineCluster %s/%s: startTime updated", namespace, name)
+		return false, nil
+	}
+	return c.syncCluster(submarinecluster)
+}
+
+func (c *Controller) syncCluster(submarineCluster *rapi.SubmarineCluster) (forceRequeue bool, err error) {
+	glog.Infof("syncCluster()")
+	return false, nil
+}
+
+func (c *Controller) onAddSubmarineCluster(obj interface{}) {
+	glog.Infof("onAddSubmarineCluster(%v)", obj)
+}
+
+func (c *Controller) onDeleteSubmarineCluster(obj interface{}) {
+	glog.Infof("onDeleteSubmarineCluster(%v)", obj)
+}
+
+func (c *Controller) onUpdateSubmarineCluster(oldObj, newObj interface{}) {
+	glog.Infof("onUpdateSubmarineCluster(%v, %v)", oldObj, newObj)
+}
+
+func (c *Controller) onAddPod(obj interface{}) {
+	glog.Infof("onAddPod(%v)", obj)
+}
+
+func (c *Controller) onUpdatePod(oldObj, newObj interface{}) {
+	glog.Infof("onUpdatePod()")
+}
+
+func (c *Controller) onDeletePod(obj interface{}) {
+	glog.Infof("onDeletePod()")
+}
+
+func (c *Controller) updateSubmarineCluster(submarineCluster *rapi.SubmarineCluster) (*rapi.SubmarineCluster, error) {
+	rc, err := c.submarineClient.SubmarineV1alpha1().SubmarineClusters(submarineCluster.Namespace).Update(submarineCluster)
+	if err != nil {
+		glog.Errorf("updateSubmarineCluster cluster: [%v] error: %v", *submarineCluster, err)
+		return rc, err
+	}
+
+	glog.V(6).Infof("SubmarineCluster %s/%s updated", submarineCluster.Namespace, submarineCluster.Name)
+	return rc, nil
+}
diff --git a/submarine-cloud/pkg/controller/pod/control.go b/submarine-cloud/pkg/controller/pod/control.go
new file mode 100644
index 0000000..a3ab1b7
--- /dev/null
+++ b/submarine-cloud/pkg/controller/pod/control.go
@@ -0,0 +1,88 @@
+/*
+ * 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 pod
+
+import (
+	rapi "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
+	"github.com/golang/glog"
+	kapiv1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	clientset "k8s.io/client-go/kubernetes"
+	corev1listers "k8s.io/client-go/listers/core/v1"
+	"k8s.io/client-go/tools/record"
+)
+
+// SubmarineClusterControlInteface interface for the SubmarineClusterPodControl
+type SubmarineClusterControlInteface interface {
+	// GetSubmarineClusterPods return list of Pod attached to a SubmarineCluster
+	GetSubmarineClusterPods(submarineCluster *rapi.SubmarineCluster) ([]*kapiv1.Pod, error)
+	// CreatePod used to create a Pod from the SubmarineCluster pod template
+	CreatePod(submarineCluster *rapi.SubmarineCluster) (*kapiv1.Pod, error)
+	// DeletePod used to delete a pod from its name
+	DeletePod(submarineCluster *rapi.SubmarineCluster, podName string) error
+	// DeletePodNow used to delete now (force) a pod from its name
+	DeletePodNow(submarineCluster *rapi.SubmarineCluster, podName string) error
+}
+
+// SubmarineClusterControl contains requieres accessor to managing the SubmarineCluster pods
+type SubmarineClusterControl struct {
+	PodLister  corev1listers.PodLister
+	KubeClient clientset.Interface
+	Recorder   record.EventRecorder
+}
+
+// NewSubmarineClusterControl builds and returns new NewSubmarineClusterControl instance
+func NewSubmarineClusterControl(lister corev1listers.PodLister, client clientset.Interface, rec record.EventRecorder) *SubmarineClusterControl {
+	glog.Infof("NewSubmarineClusterControl()")
+	ctrl := &SubmarineClusterControl{
+		PodLister:  lister,
+		KubeClient: client,
+		Recorder:   rec,
+	}
+	return ctrl
+}
+
+// GetSubmarineClusterPods return list of Pod attached to a SubmarineCluster
+func (p *SubmarineClusterControl) GetSubmarineClusterPods(submarineCluster *rapi.SubmarineCluster) ([]*kapiv1.Pod, error) {
+	glog.Infof("GetSubmarineClusterPods()")
+	return nil, nil
+}
+
+// CreatePod used to create a Pod from the SubmarineCluster pod template
+func (p *SubmarineClusterControl) CreatePod(submarineCluster *rapi.SubmarineCluster) (*kapiv1.Pod, error) {
+	glog.Infof("CreatePod()")
+	return nil, nil
+}
+
+// DeletePod used to delete a pod from its name
+func (p *SubmarineClusterControl) DeletePod(submarineCluster *rapi.SubmarineCluster, podName string) error {
+	glog.V(6).Infof("DeletePod: %s/%s", submarineCluster.Namespace, podName)
+	return p.deletePodGracefullperiode(submarineCluster, podName, nil)
+}
+
+// DeletePodNow used to delete now (force) a pod from its name
+func (p *SubmarineClusterControl) DeletePodNow(submarineCluster *rapi.SubmarineCluster, podName string) error {
+	glog.V(6).Infof("DeletePod: %s/%s", submarineCluster.Namespace, podName)
+	now := int64(0)
+	return p.deletePodGracefullperiode(submarineCluster, podName, &now)
+}
+
+// DeletePodNow used to delete now (force) a pod from its name
+func (p *SubmarineClusterControl) deletePodGracefullperiode(submarineCluster *rapi.SubmarineCluster, podName string, period *int64) error {
+	glog.Infof("deletePodGracefullperiode()")
+	return p.KubeClient.CoreV1().Pods(submarineCluster.Namespace).Delete(podName, &metav1.DeleteOptions{GracePeriodSeconds: period})
+}
diff --git a/submarine-cloud/pkg/controller/poddisruptionbudgets_control.go b/submarine-cloud/pkg/controller/poddisruptionbudgets_control.go
new file mode 100644
index 0000000..3b283ae
--- /dev/null
+++ b/submarine-cloud/pkg/controller/poddisruptionbudgets_control.go
@@ -0,0 +1,66 @@
+/*
+ * 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 controller
+
+import (
+	rapi "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
+	policyv1 "k8s.io/api/policy/v1beta1"
+	clientset "k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/tools/record"
+)
+
+// PodDisruptionBudgetsControlInterface inferface for the PodDisruptionBudgetsControl
+type PodDisruptionBudgetsControlInterface interface {
+	// CreateSubmarineClusterPodDisruptionBudget used to create the Kubernetes PodDisruptionBudget needed to access the Submarine Cluster
+	CreateSubmarineClusterPodDisruptionBudget(submarineCluster *rapi.SubmarineCluster) (*policyv1.PodDisruptionBudget, error)
+	// DeleteSubmarineClusterPodDisruptionBudget used to delete the Kubernetes PodDisruptionBudget linked to the Submarine Cluster
+	DeleteSubmarineClusterPodDisruptionBudget(submarineCluster *rapi.SubmarineCluster) error
+	// GetSubmarineClusterPodDisruptionBudget used to retrieve the Kubernetes PodDisruptionBudget associated to the SubmarineCluster
+	GetSubmarineClusterPodDisruptionBudget(submarineCluster *rapi.SubmarineCluster) (*policyv1.PodDisruptionBudget, error)
+}
+
+// PodDisruptionBudgetsControl contains all information for managing Kube PodDisruptionBudgets
+type PodDisruptionBudgetsControl struct {
+	KubeClient clientset.Interface
+	Recorder   record.EventRecorder
+}
+
+// NewPodDisruptionBudgetsControl builds and returns new PodDisruptionBudgetsControl instance
+func NewPodDisruptionBudgetsControl(client clientset.Interface, rec record.EventRecorder) *PodDisruptionBudgetsControl {
+	ctrl := &PodDisruptionBudgetsControl{
+		KubeClient: client,
+		Recorder:   rec,
+	}
+
+	return ctrl
+}
+
+// GetSubmarineClusterPodDisruptionBudget used to retrieve the Kubernetes PodDisruptionBudget associated to the SubmarineCluster
+func (s *PodDisruptionBudgetsControl) GetSubmarineClusterPodDisruptionBudget(submarineCluster *rapi.SubmarineCluster) (*policyv1.PodDisruptionBudget, error) {
+	return nil, nil
+}
+
+// DeleteSubmarineClusterPodDisruptionBudget used to delete the Kubernetes PodDisruptionBudget linked to the Submarine Cluster
+func (s *PodDisruptionBudgetsControl) DeleteSubmarineClusterPodDisruptionBudget(submarineCluster *rapi.SubmarineCluster) error {
+	return nil
+}
+
+// CreateSubmarineClusterPodDisruptionBudget used to create the Kubernetes PodDisruptionBudget needed to access the Submarine Cluster
+func (s *PodDisruptionBudgetsControl) CreateSubmarineClusterPodDisruptionBudget(submarineCluster *rapi.SubmarineCluster) (*policyv1.PodDisruptionBudget, error) {
+
+	return nil, nil
+}
diff --git a/submarine-cloud/pkg/controller/services_control.go b/submarine-cloud/pkg/controller/services_control.go
new file mode 100644
index 0000000..7137995
--- /dev/null
+++ b/submarine-cloud/pkg/controller/services_control.go
@@ -0,0 +1,70 @@
+/*
+ * 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 controller
+
+import (
+	rapi "github.com/apache/submarine/submarine-cloud/pkg/apis/submarine/v1alpha1"
+	"github.com/golang/glog"
+	kapiv1 "k8s.io/api/core/v1"
+	clientset "k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/tools/record"
+)
+
+// ServicesControlInterface inferface for the ServicesControl
+type ServicesControlInterface interface {
+	// CreateSubmarineClusterService used to create the Kubernetes Service needed to access the Submarine Cluster
+	CreateSubmarineClusterService(submarineCluster *rapi.SubmarineCluster) (*kapiv1.Service, error)
+	// DeleteSubmarineClusterService used to delete the Kubernetes Service linked to the Submarine Cluster
+	DeleteSubmarineClusterService(submarineCluster *rapi.SubmarineCluster) error
+	// GetSubmarineClusterService used to retrieve the Kubernetes Service associated to the SubmarineCluster
+	GetSubmarineClusterService(submarineCluster *rapi.SubmarineCluster) (*kapiv1.Service, error)
+}
+
+// ServicesControl contains all information for managing Kube Services
+type ServicesControl struct {
+	KubeClient clientset.Interface
+	Recorder   record.EventRecorder
+}
+
+// NewServicesControl builds and returns new ServicesControl instance
+func NewServicesControl(client clientset.Interface, rec record.EventRecorder) *ServicesControl {
+	glog.Infof("NewServicesControl()")
+	ctrl := &ServicesControl{
+		KubeClient: client,
+		Recorder:   rec,
+	}
+
+	return ctrl
+}
+
+// GetSubmarineClusterService used to retrieve the Kubernetes Service associated to the SubmarineCluster
+func (s *ServicesControl) GetSubmarineClusterService(submarineCluster *rapi.SubmarineCluster) (*kapiv1.Service, error) {
+	glog.Infof("GetSubmarineClusterService()")
+	return nil, nil
+}
+
+// CreateSubmarineClusterService used to create the Kubernetes Service needed to access the Submarine Cluster
+func (s *ServicesControl) CreateSubmarineClusterService(submarineCluster *rapi.SubmarineCluster) (*kapiv1.Service, error) {
+	glog.Infof("CreateSubmarineClusterService()")
+	return nil, nil
+}
+
+// DeleteSubmarineClusterService used to delete the Kubernetes Service linked to the Submarine Cluster
+func (s *ServicesControl) DeleteSubmarineClusterService(submarineCluster *rapi.SubmarineCluster) error {
+	glog.Infof("DeleteSubmarineClusterService()")
+	return nil
+}
diff --git a/submarine-cloud/pkg/operator/config.go b/submarine-cloud/pkg/operator/config.go
new file mode 100644
index 0000000..eaa01ce
--- /dev/null
+++ b/submarine-cloud/pkg/operator/config.go
@@ -0,0 +1,43 @@
+/*
+ * 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 operator
+
+import (
+	"github.com/apache/submarine/submarine-cloud/pkg/config"
+	"github.com/spf13/pflag"
+)
+
+// Config contains configuration for submarine-operator
+type Config struct {
+	KubeConfigFile string
+	Master         string
+	ListenAddr     string
+	Submarine      config.Submarine
+}
+
+// NewSubmarineOperatorConfig builds and returns a submarine-operator Config
+func NewSubmarineOperatorConfig() *Config {
+	return &Config{}
+}
+
+// AddFlags add cobra flags to populate Config
+func (c *Config) AddFlags(fs *pflag.FlagSet) {
+	fs.StringVar(&c.KubeConfigFile, "kubeconfig", c.KubeConfigFile, "Location of kubecfg file for access to kubernetes master service")
+	fs.StringVar(&c.Master, "master", c.Master, "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
+	fs.StringVar(&c.ListenAddr, "addr", "0.0.0.0:8086", "listen address of the http server which serves kubernetes probes and prometheus endpoints")
+	c.Submarine.AddFlags(fs)
+}
diff --git a/submarine-cloud/pkg/operator/operator.go b/submarine-cloud/pkg/operator/operator.go
new file mode 100644
index 0000000..dff37cc
--- /dev/null
+++ b/submarine-cloud/pkg/operator/operator.go
@@ -0,0 +1,95 @@
+/*
+ * 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 operator
+
+import (
+	"github.com/apache/submarine/submarine-cloud/pkg/client"
+	"github.com/apache/submarine/submarine-cloud/pkg/controller"
+	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/clientcmd"
+	"time"
+
+	submarineInformers "github.com/apache/submarine/submarine-cloud/pkg/client/informers/externalversions"
+	kubeinformers "k8s.io/client-go/informers"
+	clientset "k8s.io/client-go/kubernetes"
+
+	"github.com/golang/glog"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+)
+
+// Controller is the controller implementation for Student resources
+type SubmarineOperator struct {
+	kubeInformerFactory      kubeinformers.SharedInformerFactory
+	submarineInformerFactory submarineInformers.SharedInformerFactory
+	controller               *controller.Controller
+}
+
+func NewSubmarineOperator(cfg *Config) *SubmarineOperator {
+	kubeConfig, err := initKubeConfig(cfg)
+	if err != nil {
+		glog.Fatalf("Unable to init submarinecluster controller: %v", err)
+	}
+
+	extClient, err := apiextensionsclient.NewForConfig(kubeConfig)
+	if err != nil {
+		glog.Fatalf("Unable to init submarinClientset from kubeconfig:%v", err)
+	}
+	_, err = client.DefineSubmarineClusterResource(extClient)
+	if err != nil && !apierrors.IsAlreadyExists(err) {
+		glog.Fatalf("Unable to define SubmarineCluster resource:%v", err)
+	}
+
+	kubeClient, err := clientset.NewForConfig(kubeConfig)
+	if err != nil {
+		glog.Fatalf("Unable to initialize kubeClient:%v", err)
+	}
+
+	submarineClient, err := client.NewClient(kubeConfig)
+	if err != nil {
+		glog.Fatalf("Unable to init submarine.submarinClientset from kubeconfig:%v", err)
+	}
+
+	kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30)
+	submarineInformerFactory := submarineInformers.NewSharedInformerFactory(submarineClient, time.Second*30)
+	op := &SubmarineOperator{
+		kubeInformerFactory:      kubeInformerFactory,
+		submarineInformerFactory: submarineInformerFactory,
+		controller:               controller.NewController(controller.NewConfig(1, cfg.Submarine), kubeClient, submarineClient, kubeInformerFactory, submarineInformerFactory),
+	}
+
+	return op
+}
+
+func initKubeConfig(c *Config) (*rest.Config, error) {
+	if len(c.KubeConfigFile) > 0 {
+		return clientcmd.BuildConfigFromFlags(c.Master, c.KubeConfigFile) // out of cluster config
+	}
+	return rest.InClusterConfig()
+}
+
+// Run executes the Submarine Operator
+func (op *SubmarineOperator) Run(stop <-chan struct{}) error {
+	var err error
+	if op.controller != nil {
+		op.kubeInformerFactory.Start(stop)
+		op.submarineInformerFactory.Start(stop)
+		err = op.controller.Run(stop)
+	}
+
+	return err
+}
diff --git a/submarine-cloud/pkg/signals/signal.go b/submarine-cloud/pkg/signal/signal.go
similarity index 66%
rename from submarine-cloud/pkg/signals/signal.go
rename to submarine-cloud/pkg/signal/signal.go
index 7e1a6b4..c494127 100644
--- a/submarine-cloud/pkg/signals/signal.go
+++ b/submarine-cloud/pkg/signal/signal.go
@@ -14,28 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package signals
+package signal
 
 import (
+	"context"
 	"os"
 	"os/signal"
-)
-
-var onlyOneSignalHandler = make(chan struct{})
+	"syscall"
 
-func SetupSignalHandler() (stopCh <-chan struct{}) {
-	close(onlyOneSignalHandler) // panics when called twice
-
-	stop := make(chan struct{})
-	c := make(chan os.Signal, 2)
-	signal.Notify(c, shutdownSignals...)
-	go func() {
-		<-c
-		close(stop)
-		<-c
-		os.Exit(1) // second signal. Exit directly.
-	}()
+	"github.com/golang/glog"
+)
 
-	return stop
+// HandleSignal used to listen several os signal and then execute the cancel function
+func HandleSignal(cancelFunc context.CancelFunc) {
+	sigc := make(chan os.Signal, 1)
+	signal.Notify(sigc,
+		syscall.SIGHUP,
+		syscall.SIGINT,
+		syscall.SIGTERM,
+		syscall.SIGQUIT)
+	sig := <-sigc
+	glog.Infof("Signal received: %s, stop the process", sig.String())
+	cancelFunc()
 }
diff --git a/submarine-cloud/pkg/utils/build.go b/submarine-cloud/pkg/utils/build.go
new file mode 100644
index 0000000..ebcb876
--- /dev/null
+++ b/submarine-cloud/pkg/utils/build.go
@@ -0,0 +1,47 @@
+/*
+ * 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 utils
+
+import (
+	"fmt"
+	"time"
+)
+
+// BUILDTIME should be populated by at build time: -ldflags "-w -X github.com/apache/submarine/submarine-cloud/pkg/utils.BUILDTIME=${DATE}
+// with for example DATE=$(shell date +%Y-%m-%d/%H:%M:%S )   (pay attention not to use space!)
+var BUILDTIME string
+
+// TAG should be populated by at build time: -ldflags "-w -X github.com/apache/submarine/submarine-cloud/pkg/utils.TAG=${TAG}
+// with for example TAG=$(shell git tag|tail -1)
+var TAG string
+
+// COMMIT should be populated by at build time: -ldflags "-w -X github.com/apache/submarine/submarine-cloud/pkg/utils.COMMIT=${COMMIT}
+// with for example COMMIT=$(shell git rev-parse HEAD)
+var COMMIT string
+
+// VERSION should be populated by at build time: -ldflags "-w -X github.com/apache/submarine/submarine-cloud/pkg/utils.VERSION=${VERSION}
+// with for example VERSION=$(shell git rev-parse --abbrev-ref HEAD)
+var VERSION string
+
+// BuildInfos returns builds information
+func BuildInfos() {
+	fmt.Println("Program started at: " + time.Now().String())
+	fmt.Println("BUILDTIME=" + BUILDTIME)
+	fmt.Println("TAG=" + TAG)
+	fmt.Println("COMMIT=" + COMMIT)
+	fmt.Println("VERSION=" + VERSION)
+}
diff --git a/submarine-cloud/submarine-operator.md b/submarine-cloud/submarine-operator.md
index 022f6b6..a785222 100644
--- a/submarine-cloud/submarine-operator.md
+++ b/submarine-cloud/submarine-operator.md
@@ -45,6 +45,7 @@ Mainly prepare the declaration and registration interface of the resource object
 ```
 go get -u k8s.io/apimachinery/pkg/apis/meta/v1
 go get -u k8s.io/code-generator/...
+go get -u k8s.io/apiextensions-apiserver/...
 ```
 
 ## 3. Automatically generate Client, Informer, WorkQueue related code


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