DodoApp: Individually configure dev-vm code-server, ssh, vpn

Refactor openPortMap handling a bit.

Change-Id: I2ea4d4c9b090f2791700149dda6cc8dcd8ab6820
diff --git a/core/installer/dodo_app_test.go b/core/installer/dodo_app_test.go
index 3501620..579d3b2 100644
--- a/core/installer/dodo_app_test.go
+++ b/core/installer/dodo_app_test.go
@@ -42,7 +42,8 @@
 		network: "private"
 		subdomain: "3"
         port: value: 101
-    }]
+    }],
+    dev: enabled: false
 }]
 
 postgresql: [{
@@ -343,25 +344,34 @@
 
 const exposeSVCRemoteCluster = `
 {
-    "cluster": "remote",
-    "service": [{
-		"name": "echo",
-		"type": "golang:1.20.0",
-		"source": {
-			"repository": "ssh://foo.bar"
-		},
-		"ports": [{
-			"name": "echo",
-			"value": 9090
-		}],
-		"expose": [{
-			"port": {
-				"name": "echo"
-			},
-			"network": "Private",
-			"subdomain": "echo"
-		}]
-	}]
+  "cluster": "remote",
+  "service": [
+    {
+      "name": "echo",
+      "type": "golang:1.20.0",
+      "source": {
+        "repository": "ssh://foo.bar"
+      },
+      "ports": [
+        {
+          "name": "echo",
+          "value": 9090
+        }
+      ],
+      "expose": [
+        {
+          "port": {
+            "name": "echo"
+          },
+          "network": "Private",
+          "subdomain": "echo"
+        }
+      ],
+      "dev": {
+        "enabled": false
+      }
+    }
+  ]
 }
 `
 
@@ -409,3 +419,109 @@
 	t.Log(string(r.Raw))
 	t.Log(fmt.Sprintf("%+v", r.Ports))
 }
+
+const sshCodeServer = `
+{
+  "service": [
+    {
+      "type": "nextjs:deno-2.0.0",
+      "name": "app",
+      "source": {
+        "repository": "ssh://d.p.v1.dodo.cloud:62533/myblog",
+        "branch": "master",
+        "rootDir": "/"
+      },
+      "ports": [
+        {
+          "name": "web",
+          "value": 3000,
+          "protocol": "TCP"
+        }
+      ],
+      "env": [
+        {
+          "name": "DODO_POSTGRESQL_DB_CONNECTION_URL"
+        }
+      ],
+      "ingress": [
+        {
+          "network": "Private",
+          "subdomain": "foo",
+          "port": {
+            "name": "web"
+          },
+          "auth": {
+            "enabled": false
+          }
+        },
+        {
+          "network": "Public",
+          "subdomain": "foo",
+          "port": {
+            "name": "web"
+          },
+          "auth": {
+            "enabled": false
+          }
+        }
+      ],
+      "expose": [{
+		"network": "Public",
+		"subdomain": "foo",
+		"port": {
+		  "name": "web"
+		},
+      }],
+      "dev": {
+        "enabled": true,
+        "username": "gio",
+        "codeServer": {
+          "network": "Private",
+          "subdomain": "code"
+        },
+        "ssh": {
+          "network": "Public",
+          "subdomain": "ssh"
+        }
+      }
+    }
+  ],
+  "volume": [],
+  "postgresql": [
+    {
+      "name": "db",
+      "size": "1Gi",
+      "expose": []
+    }
+  ],
+  "mongodb": []
+}
+`
+
+func TestSSHCodeServer(t *testing.T) {
+	app, err := NewDodoApp([]byte(sshCodeServer))
+	if err != nil {
+		for _, e := range errors.Errors(err) {
+			t.Log(e)
+		}
+		t.Fatal(err)
+	}
+	release := Release{
+		Namespace:     "foo",
+		AppInstanceId: "foo-bar",
+		RepoAddr:      "ssh://192.168.100.210:22/config",
+		AppDir:        "/foo/bar",
+	}
+	keyGen := testKeyGen{}
+	r, err := app.Render(release, env, networks, nil, map[string]any{
+		"managerAddr":          "",
+		"appId":                "",
+		"sshPrivateKey":        "",
+		"port_service_app_ssh": 12,
+		"port_service_app_0":   13,
+	}, nil, keyGen)
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(string(r.Raw))
+}