DodoApp: Output access points
Change-Id: I078adfd43bd254e260bf63113a2fb3ab059c7706
diff --git a/core/installer/app.go b/core/installer/app.go
index 0b59ca1..a971653 100644
--- a/core/installer/app.go
+++ b/core/installer/app.go
@@ -31,6 +31,109 @@
//go:embed app_configs/app_global_infra.cue
var cueInfraAppGlobal []byte
+type Access struct {
+ Type string `json:"type"`
+ Name string `json:"name"`
+ HTTPS *AccessHTTPS
+ SSH *AccessSSH
+ TCP *AccessTCP
+ UDP *AccessUDP
+ PostgreSQL *AccessPostgreSQL
+ MongoDB *AccessMongoDB
+}
+
+func (a Access) MarshalJSON() ([]byte, error) {
+ var buf bytes.Buffer
+ switch a.Type {
+ case "https":
+ if err := json.NewEncoder(&buf).Encode(struct {
+ AccessHTTPS
+ Type string `json:"type"`
+ Name string `json:"name"`
+ }{*a.HTTPS, a.Type, a.Name}); err != nil {
+ return nil, err
+ }
+ case "ssh":
+ if err := json.NewEncoder(&buf).Encode(struct {
+ AccessSSH
+ Type string `json:"type"`
+ Name string `json:"name"`
+ }{*a.SSH, a.Type, a.Name}); err != nil {
+ return nil, err
+ }
+ case "tcp":
+ if err := json.NewEncoder(&buf).Encode(struct {
+ AccessTCP
+ Type string `json:"type"`
+ Name string `json:"name"`
+ }{*a.TCP, a.Type, a.Name}); err != nil {
+ return nil, err
+ }
+ case "udp":
+ if err := json.NewEncoder(&buf).Encode(struct {
+ AccessUDP
+ Type string `json:"type"`
+ Name string `json:"name"`
+ }{*a.UDP, a.Type, a.Name}); err != nil {
+ return nil, err
+ }
+ case "postgresql":
+ if err := json.NewEncoder(&buf).Encode(struct {
+ AccessPostgreSQL
+ Type string `json:"type"`
+ Name string `json:"name"`
+ }{*a.PostgreSQL, a.Type, a.Name}); err != nil {
+ return nil, err
+ }
+ case "mongodb":
+ if err := json.NewEncoder(&buf).Encode(struct {
+ AccessMongoDB
+ Type string `json:"type"`
+ Name string `json:"name"`
+ }{*a.MongoDB, a.Type, a.Name}); err != nil {
+ return nil, err
+ }
+ default:
+ panic("MUST NOT REACH!")
+ }
+ return buf.Bytes(), nil
+}
+
+type AccessHTTPS struct {
+ Address string `json:"address"`
+}
+
+type AccessSSH struct {
+ Host string `json:"host"`
+ Port int `json:"port"`
+}
+
+type AccessTCP struct {
+ Host string `json:"host"`
+ Port int `json:"port"`
+}
+
+type AccessUDP struct {
+ Host string `json:"host"`
+ Port int `json:"port"`
+}
+
+type AccessPostgreSQL struct {
+ Host string `json:"host"`
+ Port int `json:"port"`
+ Database string `json:"database"`
+ Username string `json:"username"`
+ Password string `json:"password"`
+}
+
+type AccessMongoDB struct {
+ Host string `json:"host"`
+ Port int `json:"port"`
+ Database string `json:"database"`
+ Username string `json:"username"`
+ Password string `json:"password"`
+}
+
type rendered struct {
Name string
Readme string
@@ -45,6 +148,7 @@
URL string
Help []HelpDocument
Icon string
+ Access []Access
Raw []byte
}
@@ -449,6 +553,122 @@
return rendered{}, err
}
ret.Icon = icon
+ access, err := extractAccess(res.LookupPath(cue.ParsePath("outs")))
+ if err != nil {
+ return rendered{}, err
+ }
+ ret.Access = access
+ return ret, nil
+}
+
+func extractAccessInternal(v cue.Value) ([]Access, error) {
+ ret := []Access{}
+ a := v.LookupPath(cue.ParsePath("access"))
+ if err := a.Err(); err != nil {
+ return nil, err
+ }
+ i, err := a.List()
+ if err != nil {
+ return nil, err
+ }
+ for i.Next() {
+ n := i.Value().LookupPath(cue.ParsePath("name"))
+ if err := n.Err(); err != nil {
+ return nil, err
+ }
+ nn, err := n.String()
+ if err != nil {
+ return nil, err
+ }
+ t := i.Value().LookupPath(cue.ParsePath("type"))
+ if err := t.Err(); err != nil {
+ return nil, err
+ }
+ d, err := t.String()
+ if err != nil {
+ return nil, err
+ }
+ switch d {
+ case "https":
+ {
+ var q AccessHTTPS
+ if err := i.Value().Decode(&q); err != nil {
+ return nil, err
+ }
+ ret = append(ret, Access{Type: "https", Name: nn, HTTPS: &q})
+ }
+ case "ssh":
+ {
+ var q AccessSSH
+ if err := i.Value().Decode(&q); err != nil {
+ return nil, err
+ }
+ ret = append(ret, Access{Type: "ssh", Name: nn, SSH: &q})
+ }
+ case "tcp":
+ {
+ var q AccessTCP
+ if err := i.Value().Decode(&q); err != nil {
+ return nil, err
+ }
+ ret = append(ret, Access{Type: "tcp", Name: nn, TCP: &q})
+ }
+ case "udp":
+ {
+ var q AccessUDP
+ if err := i.Value().Decode(&q); err != nil {
+ return nil, err
+ }
+ ret = append(ret, Access{Type: "udp", Name: nn, UDP: &q})
+ }
+ case "postgresql":
+ {
+ var q AccessPostgreSQL
+ if err := i.Value().Decode(&q); err != nil {
+ return nil, err
+ }
+ ret = append(ret, Access{Type: "postgresql", Name: nn, PostgreSQL: &q})
+ }
+ case "mongodb":
+ {
+ var q AccessMongoDB
+ if err := i.Value().Decode(&q); err != nil {
+ return nil, err
+ }
+ ret = append(ret, Access{Type: "mongodb", Name: nn, MongoDB: &q})
+ }
+ }
+ }
+ for _, sub := range []string{"ingress", "postgresql", "mongodb", "services", "vm"} {
+ subout := v.LookupPath(cue.ParsePath(sub))
+ if subout.Err() != nil {
+ continue
+ }
+ if a, err := extractAccess(subout); err != nil {
+ return nil, err
+ } else {
+ ret = append(ret, a...)
+ }
+ }
+ return ret, nil
+}
+
+func extractAccess(v cue.Value) ([]Access, error) {
+ if err := v.Err(); err != nil {
+ return nil, err
+ }
+ i, err := v.Fields()
+ if err != nil {
+ return nil, err
+ }
+ ret := []Access{}
+ for i.Next() {
+ if a, err := extractAccessInternal(i.Value()); err != nil {
+ return nil, fmt.Errorf(errors.Details(err, nil))
+ } else {
+ ret = append(ret, a...)
+ }
+ }
return ret, nil
}
diff --git a/core/installer/app_configs/app_base.cue b/core/installer/app_configs/app_base.cue
index d4ee363..c189f54 100644
--- a/core/installer/app_configs/app_base.cue
+++ b/core/installer/app_configs/app_base.cue
@@ -337,6 +337,12 @@
if ssh.enabled {
if ssh.expose != null {
+ access: [{
+ name: _name
+ type: "ssh"
+ host: ssh.expose.network.domain
+ port: input[ssh.expose.input_port_name]
+ }]
openPort: [{
name: "\(_name)-ssh"
network: ssh.expose.network
@@ -515,6 +521,15 @@
port: {name: string} | {value: #PortValue}
}
+#MongoDBAccess: #Access & {
+ type: "mongodb"
+ host: string
+ port: number
+ database: string
+ username: string
+ password: string
+}
+
#MongoDBInput: {
cluster?: #Cluster
name: string
@@ -539,6 +554,19 @@
_volumeClaimName: "\(name)-mongodb"
_initdbScripts: initdbScripts
+ access: [...#MongoDBAccess]
+ access: [
+ for i in openPort {
+ name: _name
+ type: "mongodb"
+ host: i.network.domain
+ port: i.port
+ database: "mongo"
+ username: "mongo"
+ password: "mongo"
+ },
+ ]
+
openPort: [for i, e in expose {
name: "port_mongodb_\(_name)_\(i)"
network: networks[strings.ToLower(e.network)]
@@ -687,6 +715,21 @@
...
}
+#Access: {
+ name: string
+ type: string
+ ...
+}
+
+#PostgreSQLAccess: #Access & {
+ type: "postgresql"
+ host: string
+ port: number
+ database: string
+ username: string
+ password: string
+}
+
#PostgreSQL: #WithOut & #PostgreSQLInput & {
cluster?: #Cluster
name: string
@@ -700,6 +743,19 @@
_volumeClaimName: "\(name)-postgresql"
_name: name
+ access: [...#PostgreSQLAccess]
+ access: [
+ for i in openPort {
+ name: _name
+ type: "postgresql"
+ host: i.network.domain
+ port: i.port
+ database: "postgres"
+ username: "postgres"
+ password: "postgres"
+ },
+ ]
+
openPort: [for i, e in expose {
name: "port_postgresql_\(_name)_\(i)"
network: networks[strings.ToLower(e.network)]
@@ -920,6 +976,24 @@
url: string | *""
+#SSHAccess: #Access & {
+ type: "ssh"
+ host: string
+ port: number
+}
+
+#TCPAccess: #Access & {
+ type: "tcp"
+ host: string
+ port: number
+}
+
+#UDPAccess: #Access & {
+ type: "udp"
+ host: string
+ port: number
+}
+
#WithOut: {
cluster?: #Cluster
if input.cluster != _|_ {
@@ -932,6 +1006,7 @@
images: {...}
charts: {...}
helm: {...}
+ access: [...#Access] | *[]
openPort: [...#PortForward] | *[]
openPortMap: {
"_self": list.FlattenN([for i, e in openPort {
diff --git a/core/installer/app_configs/app_global_env.cue b/core/installer/app_configs/app_global_env.cue
index e9e5332..25211a8 100644
--- a/core/installer/app_configs/app_global_env.cue
+++ b/core/installer/app_configs/app_global_env.cue
@@ -31,6 +31,11 @@
networks: #Networks
+#IngressAccess: #Access & {
+ type: "https"
+ address: string
+}
+
#Ingress: #WithOut & {
name: string
auth: #Auth
@@ -47,6 +52,18 @@
_cluster: cluster
g?: #Global
+ _name: name
+
+ access: [...#IngressAccess]
+
+ if _cluster == _|_ {
+ access: [{
+ name: _name
+ type: "https"
+ address: "https://\(subdomain).\(network.domain)"
+ }]
+ }
+
_domain: "\(subdomain).\(network.domain)"
_appRoot: appRoot
_authProxyName: "\(name)-auth-proxy"
diff --git a/core/installer/app_configs/dodo_app.cue b/core/installer/app_configs/dodo_app.cue
index 810bcf5..50641e2 100644
--- a/core/installer/app_configs/dodo_app.cue
+++ b/core/installer/app_configs/dodo_app.cue
@@ -627,6 +627,7 @@
}
// TODO(gio): Support remote clusters.
+// TODO(gio): pass all svc ports and not only ssh and code-server
#ServiceDevEnabled: #WithOut & {
cluster?: #Cluster
_cc: #Cluster | null | *null
@@ -736,6 +737,20 @@
...
}
+#Service: {
+ svc: name: string
+ access: [...(#TCPAccess | #UDPAccess)]
+ access: [
+ for i in openPort {
+ name: svc.name
+ type: strings.ToLower(i.protocol)
+ host: i.network.domain
+ port: i.port
+ },
+ ]
+ openPort: [...#PortForward]
+}
+
#WithOut: {
cluster?: #Cluster
_cc: #Cluster | null | *null
diff --git a/core/installer/app_manager.go b/core/installer/app_manager.go
index d0faed4..c98be4b 100644
--- a/core/installer/app_manager.go
+++ b/core/installer/app_manager.go
@@ -222,6 +222,7 @@
}
func openPorts(ports []PortForward, reservations map[string]reservePortResp, allocators map[string]string, ns string) error {
+ fmt.Printf("-- %+v\n-- %+v\n--%+v\n", ports, reservations, allocators)
for _, p := range ports {
var target string
if p.Cluster == "" {
@@ -333,6 +334,7 @@
type ReleaseResources struct {
Release Release
Helm []Resource
+ Access []Access
RenderedRaw []byte
}
@@ -575,6 +577,7 @@
return ReleaseResources{
Release: rendered.Config.Release,
RenderedRaw: rendered.Raw,
+ Access: rendered.Access,
Helm: extractHelm(rendered.Resources),
}, nil
}
@@ -662,6 +665,7 @@
portFields = append(portFields, np)
}
}
+ fmt.Printf("%+v %+v %+v\n", oldPorts, newPorts, portFields)
fakeReservations := map[string]reservePortResp{}
for i, f := range portFields {
fakeReservations[f] = reservePortResp{Port: i}
@@ -784,6 +788,7 @@
return ReleaseResources{
Release: rendered.Config.Release,
RenderedRaw: rendered.Raw,
+ Access: rendered.Access,
Helm: extractHelm(rendered.Resources),
}, nil
}
diff --git a/core/installer/dodo_app_test.go b/core/installer/dodo_app_test.go
index cc92d6b..b2cca5d 100644
--- a/core/installer/dodo_app_test.go
+++ b/core/installer/dodo_app_test.go
@@ -562,6 +562,15 @@
}
}
],
+ "expose": [
+ {
+ "network": "Private",
+ "subdomain": "blog",
+ "port": {
+ "name": "web"
+ }
+ }
+ ],
"preBuildCommands": [],
"dev": {
"enabled": true,
@@ -582,7 +591,12 @@
{
"name": "db",
"size": "1Gi",
- "expose": []
+ "expose": [
+ {
+ "network": "Private",
+ "subdomain": "pg"
+ }
+ ]
}
],
"mongodb": []
@@ -608,11 +622,16 @@
"managerAddr": "",
"appId": "",
"sshPrivateKey": "",
- "port_service_qwe_ssh": 12,
- "port_service_qwe_0": 13,
+ "port_service_qwe_ssh": 1,
+ "port_service_qwe_0": 2,
+ "port_postgresql_db_0": 3,
}, nil, keyGen)
if err != nil {
t.Fatal(err)
}
- t.Log(string(r.Raw))
+ access, err := json.Marshal(r.Access)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log(string(access))
}
diff --git a/core/installer/server/appmanager/server.go b/core/installer/server/appmanager/server.go
index e0aeab2..ffa710c 100644
--- a/core/installer/server/appmanager/server.go
+++ b/core/installer/server/appmanager/server.go
@@ -170,8 +170,9 @@
}
type dodoAppInstallResp struct {
- Id string `json:"id"`
- DeployKey string `json:"deployKey"`
+ Id string `json:"id"`
+ DeployKey string `json:"deployKey"`
+ Access []installer.Access `json:"access"`
}
type dodoAppRendered struct {
@@ -224,6 +225,7 @@
if err := json.NewEncoder(w).Encode(dodoAppInstallResp{
Id: instanceId,
DeployKey: cfg.Input.Key.Public,
+ Access: rr.Access,
}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
@@ -285,6 +287,17 @@
s.tasks[instanceId] = &taskForward{t, fmt.Sprintf("/instance/%s", instanceId), 0}
t.OnDone(s.cleanTask(instanceId, 0))
go t.Start()
+ var rend dodoAppRendered
+ if err := json.NewDecoder(bytes.NewReader(rr.RenderedRaw)).Decode(&rend); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+ if err := json.NewEncoder(w).Encode(dodoAppInstallResp{
+ Id: instanceId,
+ DeployKey: rend.Input.Key.Public,
+ Access: rr.Access,
+ }); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
}
func (s *Server) handleNetworks(w http.ResponseWriter, r *http.Request) {