ClusterManager: Implements support of remote clusters.

After this change users will be able to:
* Create cluster and add/remove servers to it
* Install apps on remote cluster
* Move already installed apps between clusters
* Apps running on server being removed will auto-migrate
  to another server from that same cluster

This is achieved by:
* Installing and running minimal version of dodo on remote cluster
* Ingress-nginx is installed automatically on new clusters
* Next to nginx we run VPN client in the same pod, so that
  default cluster can establish secure communication with it
* Multiple reverse proxies are configured to get to the
  remote cluster service from ingress installed on default cluster.

Next steps:
* Support remote clusters in dodo apps (prototype ready)
* Clean up old cluster when moving app to the new one. Currently
  old cluster keeps running app pods even though no ingress can
  reach it anymore.

Change-Id: Iffc908c93416d4126a8e1c2832eae7b659cb8044
diff --git a/core/installer/kube/client.go b/core/installer/kube/client.go
new file mode 100644
index 0000000..fc19eab
--- /dev/null
+++ b/core/installer/kube/client.go
@@ -0,0 +1,51 @@
+package kube
+
+import (
+	"fmt"
+
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/clientcmd"
+	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+	clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest"
+)
+
+type KubeConfigOpts struct {
+	KubeConfig     string
+	KubeConfigPath string
+}
+
+func NewKubeClient(opts KubeConfigOpts) (*kubernetes.Clientset, error) {
+	if opts.KubeConfig != "" && opts.KubeConfigPath != "" {
+		return nil, fmt.Errorf("both path and config can not be defined")
+	}
+	if opts.KubeConfig != "" {
+		var cfg clientcmdapi.Config
+		decoded, _, err := clientcmdlatest.Codec.Decode([]byte(opts.KubeConfig), &schema.GroupVersionKind{Version: clientcmdlatest.Version, Kind: "Config"}, &cfg)
+		if err != nil {
+			return nil, err
+		}
+		getter := func() (*clientcmdapi.Config, error) {
+			return decoded.(*clientcmdapi.Config), nil
+		}
+		config, err := clientcmd.BuildConfigFromKubeconfigGetter("", getter)
+		if err != nil {
+			return nil, err
+		}
+		return kubernetes.NewForConfig(config)
+	}
+	if opts.KubeConfigPath == "" {
+		config, err := rest.InClusterConfig()
+		if err != nil {
+			return nil, err
+		}
+		return kubernetes.NewForConfig(config)
+	} else {
+		config, err := clientcmd.BuildConfigFromFlags("", opts.KubeConfigPath)
+		if err != nil {
+			return nil, err
+		}
+		return kubernetes.NewForConfig(config)
+	}
+}