DodoApp: Individually configure dev-vm code-server, ssh, vpn
Refactor openPortMap handling a bit.
Change-Id: I2ea4d4c9b090f2791700149dda6cc8dcd8ab6820
diff --git a/Jenkinsfile b/Jenkinsfile
index 9b5aae8..ff51d94 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -36,6 +36,7 @@
dir('core/installer') {
sh 'go mod tidy'
sh 'go build cmd/*.go'
+ sh 'go run cuelang.org/go/cmd/cue fmt app_configs/*.cue --check'
sh 'go test ./...'
}
dir('core/auth/memberships') {
diff --git a/core/installer/app.go b/core/installer/app.go
index f8e6d6f..f830455 100644
--- a/core/installer/app.go
+++ b/core/installer/app.go
@@ -13,6 +13,7 @@
"cuelang.org/go/cue"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/cuecontext"
+ "cuelang.org/go/cue/errors"
"cuelang.org/go/cue/load"
cueyaml "cuelang.org/go/encoding/yaml"
helmv2 "github.com/fluxcd/helm-controller/api/v2"
@@ -342,7 +343,7 @@
d := ctx.CompileBytes(buf.Bytes())
res := a.cfg.Unify(d).Eval()
if err := res.Err(); err != nil {
- return rendered{}, err
+ return rendered{}, fmt.Errorf(errors.Details(err, nil))
}
if err := res.Validate(); err != nil {
return rendered{}, err
diff --git a/core/installer/app_configs/app_base.cue b/core/installer/app_configs/app_base.cue
index d61bfb3..d43cf0f 100644
--- a/core/installer/app_configs/app_base.cue
+++ b/core/installer/app_configs/app_base.cue
@@ -17,7 +17,7 @@
release,
global,
localCharts,
- ])
+])
// TODO(gio): revisit this logic
_uuidSorted: strings.Join(list.Sort([for x in strings.Runes(_uuidData) {strconv.QuoteRune(x)}], list.Ascending), "")
uuid: base64.Encode(null, sha256.Sum256(_uuidSorted))
@@ -253,15 +253,45 @@
#VPN: #VPNEnabled | #VPNDisabled
+#CodeServerDisabled: {
+ enabled: false
+}
+
+#CodeServerEnabled: {
+ enabled: true
+ expose: {
+ network: #Network
+ subdomain: string
+ } | null | *null
+}
+
+#CodeServer: #CodeServerEnabled | #CodeServerDisabled
+
+#SSHServerDisabled: {
+ enabled: false
+}
+
+#SSHServerEnabled: {
+ enabled: true
+ expose: {
+ network: #Network
+ subdomain: string
+ input_port_name: string
+ } | null | *null
+}
+
+#SSHServer: #SSHServerEnabled | #SSHServerDisabled
+
#VirtualMachine: #WithOut & {
cluster?: #Cluster
name: string
username: string
domain: string
vpn: #VPN | *{enabled: false}
- codeServerEnabled: bool | *false
- cpuCores: int
- memory: string
+ ssh: #SSHServer | *{enabled: false}
+ codeServer: #CodeServer | *{enabled: false}
+ cpuCores: int
+ memory: string
sshKnownHosts: [...string] | *[]
sshAuthorizedKeys: [...string] | *[]
cloudInit: #CloudInit
@@ -277,7 +307,7 @@
_configFiles: configFiles
_codeServerCmd: [...[...string]] | *[]
- if codeServerEnabled {
+ if codeServer.enabled {
_codeServerCmd: [
["sh", "-c", "curl -fsSL https://code-server.dev/install.sh | HOME=/home/\(username) sh"],
["sh", "-c", "systemctl enable --now code-server@\(username)"],
@@ -287,6 +317,35 @@
["sh", "-c", "sed -i -e 's/auth: password/auth: none/g' /home/\(username)/.config/code-server/config.yaml"],
["sh", "-c", "systemctl restart --now code-server@\(username)"],
]
+ if codeServer.expose != null {
+ ingress: {
+ "\(name)-code": {
+ label: "VS Code"
+ home: "/?folder=/home/\(username)/code"
+ network: codeServer.expose.network
+ subdomain: codeServer.expose.subdomain
+ auth: enabled: false
+ service: {
+ name: _name
+ port: _codeServerPort
+ }
+ }
+ }
+ }
+ }
+
+ if ssh.enabled {
+ if ssh.expose != null {
+ openPort: [{
+ name: "\(_name)-ssh"
+ network: ssh.expose.network
+ port: input[ssh.expose.input_port_name]
+ service: {
+ name: _name
+ port: _sshPort
+ }
+ }]
+ }
}
_vpnCmd: [...[...string]] | *[]
@@ -334,12 +393,13 @@
source: "https://cloud.debian.org/images/cloud/bookworm-backports/latest/debian-12-backports-generic-amd64.qcow2"
size: "64Gi"
}
- ports: [{
- name: "ssh"
- value: 22
- protocol: "TCP"
- },
- if codeServerEnabled {
+ ports: [
+ if ssh.enabled {
+ name: _sshPortName
+ value: _sshPort
+ protocol: "TCP"
+ },
+ if codeServer.enabled {
name: _codeServerPortName
value: _codeServerPort
protocol: "TCP"
@@ -561,7 +621,9 @@
}
openPortMap: {
for k, v in mongodb {
- "mongodb-\(k)": v.openPortMap
+ for x, y in v.openPortMap {
+ "mongodb-\(k)-\(x)": y
+ }
}
...
}
@@ -783,7 +845,7 @@
}
}
openPort: [...#PortForward] | *[]
- openPort: list.FlattenN([for i in _op {i}], -1)
+ openPort: list.FlattenN([for i in _op {i}], 1)
for _, out in outs {
images: out.images
charts: out.charts
@@ -1020,7 +1082,9 @@
}
openPortMap: {
for k, v in postgresql {
- "postgresql-\(k)": v.openPortMap
+ for x, y in v.openPortMap {
+ "postgresql-\(k)-\(x)": y
+ }
}
...
}
@@ -1066,6 +1130,14 @@
}
}
}
+ openPortMap: {
+ for k, v in vm {
+ for x, y in v.openPortMap {
+ "vm-\(k)-\(x)": y
+ }
+ }
+ ...
+ }
images: {
for k, v in vm {
for x, y in v.images {
@@ -1096,6 +1168,8 @@
_codeServerPortName: "code-server"
_codeServerPort: 9090
+_sshPortName: "ssh"
+_sshPort: 22
resources: {...}
diff --git a/core/installer/app_configs/dodo_app.cue b/core/installer/app_configs/dodo_app.cue
index 08049c4..a02ac1e 100644
--- a/core/installer/app_configs/dodo_app.cue
+++ b/core/installer/app_configs/dodo_app.cue
@@ -68,12 +68,13 @@
if svc.dev.enabled {
username: string | *svc.dev.username
vpnAuthKey: string @role(VPNAuthKey) @usernameField(username)
+ if svc.dev.ssh != null {
+ "port_service_\(svc.name)_ssh": int @role(port)
+ }
}
}
}
-_devVM: {}
-
volume: [...#VolumeInput] | *[]
postgresql: [...#PostgreSQLInput] | *[]
mongodb: [...#MongoDBInput] | *[]
@@ -116,8 +117,11 @@
}
#DevEnabled: {
- enabled: true
- username: string
+ enabled: true
+ username: string
+ vpn: bool | *false
+ codeServer: #Domain | null | *null
+ ssh: #Domain | null | *null
}
#Dev: #DevEnabled | #DevDisabled
@@ -141,8 +145,8 @@
rootDir: string
runConfiguration: [...#Command]
volume: [...string] | *[]
- dev: #Dev | *{enabled: false}
- vm: #VMCustomization
+ dev: #Dev
+ vm: #VMCustomization
// TODO(gio): check for duplicate values
apiPort: #PortValue | *3000
ports: [...#Port]
@@ -653,18 +657,6 @@
}
}
}
- // TODO(gio): code should work even without svc ingress
- "\(svc.name)-\(i)-code": {
- label: "VS Code"
- home: "/?folder=/home/\(svc.dev.username)/code"
- network: networks[strings.ToLower(ingress.network)]
- subdomain: "code-\(ingress.subdomain)"
- auth: enabled: false
- service: {
- name: svc.name
- port: _codeServerPort
- }
- }
}}
}
}
@@ -672,15 +664,36 @@
"\(svc.name)": {
username: svc.dev.username
domain: global.domain
- vpn: {
- enabled: true
- loginServer: "https://headscale.\(global.domain)"
- authKey: input.vpnAuthKey
+ if svc.dev.vpn {
+ // TODO(gio): make VPN network configurable
+ vpn: {
+ enabled: true
+ loginServer: "https://headscale.\(global.domain)"
+ authKey: input.vpnAuthKey
+ }
}
- codeServerEnabled: true
- cpuCores: 2
- memory: "3Gi"
- ports: svc.ports
+ if svc.dev.codeServer != null {
+ codeServer: {
+ enabled: true
+ expose: {
+ network: networks[strings.ToLower(svc.dev.codeServer.network)]
+ subdomain: svc.dev.codeServer.subdomain
+ }
+ }
+ }
+ if svc.dev.ssh != null {
+ ssh: {
+ enabled: true
+ expose: {
+ network: networks[strings.ToLower(svc.dev.ssh.network)]
+ subdomain: svc.dev.ssh.subdomain
+ input_port_name: "port_service_\(svc.name)_ssh"
+ }
+ }
+ }
+ cpuCores: 2
+ memory: "3Gi"
+ ports: svc.ports
configFiles: {
"env.sh": _envProfile
}
@@ -723,7 +736,9 @@
}
openPortMap: {
for k, v in services {
- "service-\(k)": v.openPortMap
+ for x, y in v.openPortMap {
+ "service-\(k)-\(x)": y
+ }
}
...
}
@@ -775,7 +790,7 @@
for v in _service {
"\(v.name)": #Service & {
name: v.name
- svc: v
+ svc: v
}
}
}
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))
+}
diff --git a/core/installer/values-tmpl/virtual-machine.cue b/core/installer/values-tmpl/virtual-machine.cue
index 63cacf0..7416e28 100644
--- a/core/installer/values-tmpl/virtual-machine.cue
+++ b/core/installer/values-tmpl/virtual-machine.cue
@@ -35,7 +35,9 @@
}
}
if input.codeServerEnabled != _|_ {
- codeServerEnabled: input.codeServerEnabled
+ codeServer: {
+ enabled: input.codeServerEnabled
+ }
}
}
}