Installer: Move port forwarding config into the apps

Change-Id: I6d70c8ce72d21ebe74d126a680fa66ba6c3f2857
diff --git a/core/installer/app.go b/core/installer/app.go
index 1bf632c..612f2e5 100644
--- a/core/installer/app.go
+++ b/core/installer/app.go
@@ -95,13 +95,15 @@
 }
 
 type PortForward struct {
-	Allocator     string `json:"allocator"`
-	ReserveAddr   string `json:"reservator"`
-	RemoveAddr    string `json:"deallocator"`
-	Protocol      string `json:"protocol"`
-	SourcePort    int    `json:"sourcePort"`
-	TargetService string `json:"targetService"`
-	TargetPort    int    `json:"targetPort"`
+	Allocator   string `json:"allocator"`
+	ReserveAddr string `json:"reservator"`
+	RemoveAddr  string `json:"deallocator"`
+	Protocol    string `json:"protocol"`
+	Port        int    `json:"port"`
+	Service     struct {
+		Name string `json:"name"`
+		Port int    `json:"port"`
+	} `json:"service"`
 }
 
 type AppType int
@@ -363,7 +365,7 @@
 	if err := res.LookupPath(cue.ParsePath("namespaces")).Decode(&ret.Namespaces); err != nil {
 		return rendered{}, err
 	}
-	if err := res.LookupPath(cue.ParsePath("portForward")).Decode(&ret.Ports); err != nil {
+	if err := res.LookupPath(cue.ParsePath("output.openPort")).Decode(&ret.Ports); err != nil {
 		return rendered{}, err
 	}
 	{
diff --git a/core/installer/app_configs/app_base.cue b/core/installer/app_configs/app_base.cue
index 65b0813..4f59a73 100644
--- a/core/installer/app_configs/app_base.cue
+++ b/core/installer/app_configs/app_base.cue
@@ -108,18 +108,20 @@
 	imageRegistry: string | *"docker.io"
 }
 
+// TODO(gio): Support inter-cluster porf forwarding
 #PortForward: {
-	allocator: string
-	reservator: string
-	deallocator: string
+	network: #Network
+	port: int
+	service: close({
+		name: string
+		port: int & > 0
+	})
 	protocol: "TCP" | "UDP" | *"TCP"
-	sourcePort: int
-	serviceName: string
-	targetService: "\(release.namespace)/\(serviceName)"
-	targetPort: int
-}
 
-portForward: [...#PortForward] | *[]
+	allocator: network.allocatePortAddr
+	reservator: network.reservePortAddr
+	deallocator: network.deallocatePortAddr
+}
 
 #Cluster: {
 	name: string
@@ -600,6 +602,7 @@
 }
 
 output: {
+	openPort: list.Concat([for out in outs { out.openPort }])
 	for _, out in outs {
 		images: out.images
 		charts: out.charts
@@ -658,6 +661,7 @@
 	charts: {...}
 	helm: {...}
 	clusterProxy: {...}
+	openPort: [...#PortForward] | *[]
 	images: {
 		for k, v in images {
 			"\(k)": #Image & v
diff --git a/core/installer/app_manager.go b/core/installer/app_manager.go
index fbee783..c5b849f 100644
--- a/core/installer/app_manager.go
+++ b/core/installer/app_manager.go
@@ -203,25 +203,25 @@
 	return ret, nil
 }
 
-func openPorts(ports []PortForward, reservations map[string]reservePortResp, allocators map[string]string) error {
+func openPorts(ports []PortForward, reservations map[string]reservePortResp, allocators map[string]string, ns string) error {
 	for _, p := range ports {
 		var buf bytes.Buffer
 		req := allocatePortReq{
 			Protocol:      p.Protocol,
-			SourcePort:    p.SourcePort,
-			TargetService: p.TargetService,
-			TargetPort:    p.TargetPort,
+			SourcePort:    p.Port,
+			TargetService: fmt.Sprintf("%s/%s", ns, p.Service.Name),
+			TargetPort:    p.Service.Port,
 		}
 		allocator := ""
 		for n, r := range reservations {
-			if p.SourcePort == r.Port {
+			if p.Port == r.Port {
 				allocator = allocators[n]
 				req.Secret = r.Secret
 				break
 			}
 		}
 		if allocator == "" {
-			return fmt.Errorf("Could not find allocator for: %d", p.SourcePort)
+			return fmt.Errorf("Could not find allocator for: %d", p.Port)
 		}
 		if err := json.NewEncoder(&buf).Encode(req); err != nil {
 			return err
@@ -233,21 +233,21 @@
 		if resp.StatusCode != http.StatusOK {
 			var r bytes.Buffer
 			io.Copy(&r, resp.Body)
-			return fmt.Errorf("Could not allocate port %d, status code %d, message: %s", p.SourcePort, resp.StatusCode, r.String())
+			return fmt.Errorf("Could not allocate port %d, status code %d, message: %s", p.Port, resp.StatusCode, r.String())
 		}
 	}
 	return nil
 }
 
-func closePorts(ports []PortForward) error {
+func closePorts(ports []PortForward, ns string) error {
 	var retErr error
 	for _, p := range ports {
 		var buf bytes.Buffer
 		req := removePortReq{
 			Protocol:      p.Protocol,
-			SourcePort:    p.SourcePort,
-			TargetService: p.TargetService,
-			TargetPort:    p.TargetPort,
+			SourcePort:    p.Port,
+			TargetService: fmt.Sprintf("%s/%s", ns, p.Service.Name),
+			TargetPort:    p.Service.Port,
 		}
 		if err := json.NewEncoder(&buf).Encode(req); err != nil {
 			retErr = err
@@ -259,7 +259,7 @@
 			continue
 		}
 		if resp.StatusCode != http.StatusOK {
-			retErr = fmt.Errorf("Could not deallocate port %d, status code: %d", p.SourcePort, resp.StatusCode)
+			retErr = fmt.Errorf("Could not deallocate port %d, status code: %d", p.Port, resp.StatusCode)
 			continue
 		}
 	}
@@ -478,8 +478,8 @@
 	reservators := map[string]string{}
 	allocators := map[string]string{}
 	for _, pf := range rendered.Ports {
-		reservators[portFields[pf.SourcePort]] = pf.ReserveAddr
-		allocators[portFields[pf.SourcePort]] = pf.Allocator
+		reservators[portFields[pf.Port]] = pf.ReserveAddr
+		allocators[portFields[pf.Port]] = pf.Allocator
 	}
 	portReservations, err := reservePorts(reservators)
 	if err != nil {
@@ -526,7 +526,7 @@
 		return ReleaseResources{}, err
 	}
 	// TODO(gio): add ingress-nginx to release resources
-	if err := openPorts(rendered.Ports, portReservations, allocators); err != nil {
+	if err := openPorts(rendered.Ports, portReservations, allocators, release.Namespace); err != nil {
 		return ReleaseResources{}, err
 	}
 	for _, p := range rendered.ClusterProxies {
@@ -719,7 +719,7 @@
 	}); err != nil {
 		return err
 	}
-	if err := closePorts(cfg.PortForward); err != nil {
+	if err := closePorts(cfg.Output.PortForward, cfg.Release.Namespace); err != nil {
 		return err
 	}
 	for _, cp := range cfg.Out.ClusterProxy {
@@ -1091,9 +1091,14 @@
 }
 
 type renderedInstance struct {
+	Release     Release                                 `json:"release"`
 	LocalCharts map[string]helmv2.HelmChartTemplateSpec `json:"localCharts"`
-	PortForward []PortForward                           `json:"portForward"`
 	Out         outRendered                             `json:"out"`
+	Output      outputRendered                          `json:"output"`
+}
+
+type outputRendered struct {
+	PortForward []PortForward `json:"openPort"`
 }
 
 type outRendered struct {
diff --git a/core/installer/values-tmpl/dodo-app.cue b/core/installer/values-tmpl/dodo-app.cue
index fe7e504..349f0d6 100644
--- a/core/installer/values-tmpl/dodo-app.cue
+++ b/core/installer/values-tmpl/dodo-app.cue
@@ -52,15 +52,6 @@
 _domain: "\(input.subdomain).\(input.network.domain)"
 url: "https://\(_domain)"
 
-portForward: [#PortForward & {
-	allocator: input.network.allocatePortAddr
-	reservator: input.network.reservePortAddr
-	deallocator: input.network.deallocatePortAddr
-	sourcePort: input.sshPort
-	serviceName: "soft-serve"
-	targetPort: 22
-}]
-
 out: {
 	images: {
 		softserve: {
@@ -121,6 +112,15 @@
 		}
 	}
 
+	openPort: [{
+		network: input.network
+		port: input.sshPort
+		service: {
+			name: "soft-serve"
+			port: 22
+		}
+	}]
+
 	helm: {
 		softserve: {
 			chart: charts.softserve
diff --git a/core/installer/values-tmpl/gerrit.cue b/core/installer/values-tmpl/gerrit.cue
index 6d232f7..2c57ed5 100644
--- a/core/installer/values-tmpl/gerrit.cue
+++ b/core/installer/values-tmpl/gerrit.cue
@@ -45,6 +45,15 @@
 		}
 	}
 
+	openPort: [{
+		network: input.network
+		port: input.sshPort
+		service: {
+			name: "gerrit-gerrit-service"
+			port: _sshPort
+		}
+	}]
+
 	// TODO(gio): configure busybox
 	images: {
 		gerrit: {
@@ -315,14 +324,5 @@
 _httpPort: 80
 _sshPort: 22
 
-portForward: [#PortForward & {
-	allocator: input.network.allocatePortAddr
-	reservator: input.network.reservePortAddr
-	deallocator: input.network.deallocatePortAddr
-	sourcePort: input.sshPort
-	serviceName: "gerrit-gerrit-service"
-	targetPort: _sshPort
-}]
-
 _oauth2ClientCredentials: "gerrit-oauth2-credentials"
 _gerritConfigMapName: "gerrit-config"
diff --git a/core/installer/values-tmpl/soft-serve.cue b/core/installer/values-tmpl/soft-serve.cue
index a6f85c0..dae0515 100644
--- a/core/installer/values-tmpl/soft-serve.cue
+++ b/core/installer/values-tmpl/soft-serve.cue
@@ -36,15 +36,6 @@
   </g>
 </svg>"""
 
-portForward: [#PortForward & {
-	allocator: input.network.allocatePortAddr
-	reservator: input.network.reservePortAddr
-	deallocator: input.network.deallocatePortAddr
-	sourcePort: input.sshPort
-	serviceName: "soft-serve"
-	targetPort: 22
-}]
-
 
 out: {
 	images: {
@@ -77,6 +68,15 @@
 		}
 	}
 
+	openPort: [{
+		network: input.network
+		port: input.sshPort
+		service: {
+			name: "soft-serve"
+			port: 22
+		}
+	}]
+
 	volumes: data: size: "1Gi"
 
 	helm: {