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/app_test.go b/core/installer/app_test.go
index 18cf85c..4f3f7dd 100644
--- a/core/installer/app_test.go
+++ b/core/installer/app_test.go
@@ -82,7 +82,7 @@
 				"groups":  "a,b",
 			},
 		}
-		rendered, err := a.Render(release, env, networks, values, nil, nil)
+		rendered, err := a.Render(release, env, networks, nil, values, nil, nil)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -112,7 +112,7 @@
 				"enabled": false,
 			},
 		}
-		rendered, err := a.Render(release, env, networks, values, nil, nil)
+		rendered, err := a.Render(release, env, networks, nil, values, nil, nil)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -138,7 +138,7 @@
 		"network":    "Public",
 		"authGroups": "foo,bar",
 	}
-	rendered, err := a.Render(release, env, networks, values, nil, nil)
+	rendered, err := a.Render(release, env, networks, nil, values, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -168,7 +168,7 @@
 		},
 		"sshPort": 22,
 	}
-	rendered, err := a.Render(release, env, networks, values, nil, nil)
+	rendered, err := a.Render(release, env, networks, nil, values, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -193,7 +193,7 @@
 		"subdomain": "jenkins",
 		"network":   "Private",
 	}
-	rendered, err := a.Render(release, env, networks, values, nil, nil)
+	rendered, err := a.Render(release, env, networks, nil, values, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -252,7 +252,7 @@
 		},
 		"sshPrivateKey": "private",
 	}
-	rendered, err := a.Render(release, env, networks, values, nil, nil)
+	rendered, err := a.Render(release, env, networks, nil, values, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -285,7 +285,7 @@
 			"groups":  "a,b",
 		},
 	}
-	rendered, err := app.Render(release, env, networks, values, nil, nil)
+	rendered, err := app.Render(release, env, networks, nil, values, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -388,7 +388,7 @@
 		AppDir:        "/foo/bar",
 	}
 	keyGen := testKeyGen{}
-	r, err := app.Render(release, env, networks, map[string]any{
+	r, err := app.Render(release, env, networks, nil, map[string]any{
 		"repoAddr":       "",
 		"repoPublicAddr": "",
 		"managerAddr":    "",
@@ -425,7 +425,7 @@
 		AppDir:        "/foo/bar",
 	}
 	keyGen := testKeyGen{}
-	r, err := app.Render(release, env, networks, map[string]any{
+	r, err := app.Render(release, env, networks, nil, map[string]any{
 		"repoAddr":       "",
 		"repoPublicAddr": "",
 		"managerAddr":    "",
@@ -463,7 +463,7 @@
 		"gitRepoPublicKey": "",
 		"username":         "",
 	}
-	rendered, err := a.Render(release, env, networks, values, nil, nil)
+	rendered, err := a.Render(release, env, networks, nil, values, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -487,3 +487,37 @@
 	}
 	t.Log(app.Schema())
 }
+
+func TestAppVirtualMachine(t *testing.T) {
+	contents, err := valuesTmpls.ReadFile("values-tmpl/virtual-machine.cue")
+	if err != nil {
+		t.Fatal(err)
+	}
+	app, err := NewCueEnvApp(CueAppData{
+		"base.cue":   []byte(cueBaseConfig),
+		"app.cue":    []byte(contents),
+		"global.cue": []byte(cueEnvAppGlobal),
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	release := Release{
+		Namespace: "foo",
+	}
+	values := map[string]any{
+		"name":     "foo",
+		"username": "bar",
+		"cpuCores": 1,
+		"memory":   "1Gi",
+	}
+	rendered, err := app.Render(release, env, networks, nil, values, nil, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	for _, r := range rendered.Resources {
+		t.Log(string(r))
+	}
+	for _, r := range rendered.Data {
+		t.Log(string(r))
+	}
+}