Skip to content

Commit 6b8fc16

Browse files
authored
feat: embed novnc in runtime
1 parent f3e8985 commit 6b8fc16

10 files changed

Lines changed: 133 additions & 83 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/tmp/
22
/disks/
3+
/socks/

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ COPY --link --from=binary /devmachines-runtime /devmachines-runtime
3030

3131
COPY ./static/ /static/
3232

33+
COPY --link --from=devmachines/novnc /vnc_lite.html /static/vnc/index.html
34+
3335
CMD ["/devmachines-runtime"]

compose.yaml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,5 @@ services:
2626
ports:
2727
- "2222:2222"
2828
- "8080:8080"
29+
- "8081:8081"
2930
stop_grace_period: 60s
30-
31-
novnc:
32-
image: devmachines/novnc
33-
ports:
34-
- "6080:6080"
35-
command: novnc_server --vnc runtime:5977

go.mod

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@ require (
1414

1515
require (
1616
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
17-
github.com/digitalocean/go-libvirt v0.0.0-20220804181439-8648fbde413e // indirect
18-
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
17+
github.com/digitalocean/go-libvirt v0.0.0-20250512231903-57024326652b // indirect
18+
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
1919
github.com/go-playground/locales v0.14.1 // indirect
2020
github.com/go-playground/universal-translator v0.18.1 // indirect
2121
github.com/leodido/go-urn v1.4.0 // indirect
2222
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
23-
github.com/stretchr/testify v1.9.0 // indirect
24-
golang.org/x/crypto v0.33.0 // indirect
25-
golang.org/x/net v0.34.0 // indirect
26-
golang.org/x/sys v0.30.0 // indirect
27-
golang.org/x/text v0.22.0 // indirect
23+
golang.org/x/crypto v0.38.0 // indirect
24+
golang.org/x/net v0.40.0 // indirect
25+
golang.org/x/sys v0.33.0 // indirect
26+
golang.org/x/text v0.25.0 // indirect
2827
)

go.sum

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs=
22
github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y=
3-
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
43
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
54
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6-
github.com/digitalocean/go-libvirt v0.0.0-20220804181439-8648fbde413e h1:SCnqm8SjSa0QqRxXbo5YY//S+OryeJioe17nK+iDZpg=
7-
github.com/digitalocean/go-libvirt v0.0.0-20220804181439-8648fbde413e/go.mod h1:o129ljs6alsIQTc8d6eweihqpmmrbxZ2g1jhgjhPykI=
5+
github.com/digitalocean/go-libvirt v0.0.0-20250512231903-57024326652b h1:o/RoLbHmKtibc3lMpuPcYGUjnboEORpLFnqtC89tfqY=
6+
github.com/digitalocean/go-libvirt v0.0.0-20250512231903-57024326652b/go.mod h1:B2R8mtJc0BNx0NvvfOajL5no+MaFDumyD5sHsxll62g=
87
github.com/digitalocean/go-qemu v0.0.0-20250212194115-ee9b0668d242 h1:rh6rt8pF5U4iyQ86h6lRDenJoX4ht2wFnZXB9ogIrIM=
98
github.com/digitalocean/go-qemu v0.0.0-20250212194115-ee9b0668d242/go.mod h1:LGHUtlhsY4vRGM6AHejEQKVI5e3eHbSylMHwTSpQtVw=
109
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
1110
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
1211
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
1312
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
14-
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
15-
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
13+
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
14+
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
1615
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
1716
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
1817
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -25,46 +24,21 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
2524
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
2625
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
2726
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
28-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2927
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
3028
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
31-
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
32-
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
33-
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
34-
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
35-
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
36-
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
37-
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
38-
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
39-
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
40-
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
41-
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
42-
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
43-
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
44-
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
45-
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
46-
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
47-
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
48-
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
49-
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
50-
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
51-
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
52-
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
53-
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
54-
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
55-
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
56-
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
57-
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
58-
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
59-
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
60-
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
61-
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
62-
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
63-
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
64-
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
65-
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
29+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
30+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
31+
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
32+
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
33+
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
34+
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
35+
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
36+
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
37+
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
38+
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
39+
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
40+
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
6641
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
6742
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
68-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
6943
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
7044
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

novnc/Dockerfile

Lines changed: 0 additions & 5 deletions
This file was deleted.

serial/server.go

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,49 +9,47 @@ import (
99
"github.com/gorilla/websocket"
1010
)
1111

12-
var (
13-
upgrader = websocket.Upgrader{}
14-
)
12+
var upgrader = websocket.Upgrader{}
1513

1614
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
17-
conn, err := upgrader.Upgrade(w, r, nil)
15+
wsConn, err := upgrader.Upgrade(w, r, nil)
1816
if err != nil {
1917
log.Println("WebSocket upgrade error:", err)
2018
return
2119
}
22-
defer conn.Close()
20+
defer wsConn.Close()
2321

24-
usock, err := net.Dial("unix", unixSocketPath)
22+
unixConn, err := net.Dial("unix", unixSocketPath)
2523
if err != nil {
2624
log.Println("Unix socket error:", err)
27-
conn.WriteMessage(websocket.TextMessage, []byte("Failed to connect to Unix socket"))
25+
wsConn.WriteMessage(websocket.TextMessage, []byte("Failed to connect to Unix socket"))
2826
return
2927
}
30-
defer usock.Close()
28+
defer unixConn.Close()
3129

3230
go func() {
3331
buf := make([]byte, 1024)
3432
for {
35-
n, err := usock.Read(buf)
33+
n, err := unixConn.Read(buf)
3634
if err != nil {
3735
if err != io.EOF {
3836
log.Println("Read error:", err)
3937
}
40-
conn.WriteMessage(websocket.TextMessage, []byte("Socket closed"))
41-
conn.Close()
38+
wsConn.WriteMessage(websocket.TextMessage, []byte("Socket closed"))
39+
wsConn.Close()
4240
return
4341
}
44-
conn.WriteMessage(websocket.TextMessage, buf[:n])
42+
wsConn.WriteMessage(websocket.BinaryMessage, buf[:n])
4543
}
4644
}()
4745

4846
for {
49-
_, msg, err := conn.ReadMessage()
47+
_, msg, err := wsConn.ReadMessage()
5048
if err != nil {
5149
log.Println("WebSocket read error:", err)
5250
break
5351
}
54-
_, err = usock.Write(msg)
52+
_, err = unixConn.Write(msg)
5553
if err != nil {
5654
log.Println("Write to Unix socket error:", err)
5755
break
@@ -60,8 +58,9 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
6058
}
6159

6260
func startServer() {
63-
http.HandleFunc("/ws", handleWebSocket)
64-
http.Handle("/", http.FileServer(http.Dir("./static")))
61+
mux := http.NewServeMux()
62+
mux.HandleFunc("/ws", handleWebSocket)
63+
mux.Handle("/", http.FileServer(http.Dir("./static/serial")))
6564

66-
go http.ListenAndServe(":8080", nil)
65+
go http.ListenAndServe(":8080", mux)
6766
}

static/serial/index.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,15 @@
3030

3131
const socket = new WebSocket('ws://' + location.host + '/ws');
3232

33+
socket.binaryType = "arraybuffer";
34+
3335
socket.onmessage = (event) => {
34-
term.write(event.data);
36+
if (event.data instanceof ArrayBuffer) {
37+
const uint8Array = new Uint8Array(event.data);
38+
term.write(uint8Array);
39+
} else {
40+
term.write(event.data);
41+
}
3542
};
3643

3744
socket.onopen = () => {

vnc/server.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package vnc
2+
3+
import (
4+
"io"
5+
"log"
6+
"net"
7+
"net/http"
8+
9+
"github.com/gorilla/websocket"
10+
)
11+
12+
var upgrader = websocket.Upgrader{}
13+
14+
func proxyHandler(w http.ResponseWriter, r *http.Request) {
15+
wsConn, err := upgrader.Upgrade(w, r, nil)
16+
if err != nil {
17+
log.Println("WebSocket upgrade error:", err)
18+
return
19+
}
20+
defer wsConn.Close()
21+
22+
unixConn, err := net.Dial("unix", unixSocketPath)
23+
if err != nil {
24+
log.Println("Unix socket connection error:", err)
25+
return
26+
}
27+
defer unixConn.Close()
28+
29+
go func() {
30+
buf := make([]byte, 1024)
31+
for {
32+
n, err := unixConn.Read(buf)
33+
if err != nil {
34+
if err != io.EOF {
35+
log.Println("Unix socket read error:", err)
36+
}
37+
return
38+
}
39+
err = wsConn.WriteMessage(websocket.BinaryMessage, buf[:n])
40+
if err != nil {
41+
log.Println("WebSocket write error:", err)
42+
return
43+
}
44+
}
45+
}()
46+
47+
for {
48+
_, msg, err := wsConn.ReadMessage()
49+
if err != nil {
50+
log.Println("WebSocket read error:", err)
51+
unixConn.Close()
52+
return
53+
}
54+
_, err = unixConn.Write(msg)
55+
if err != nil {
56+
log.Println("Unix socket write error:", err)
57+
return
58+
}
59+
}
60+
}
61+
62+
func startServer() {
63+
mux := http.NewServeMux()
64+
mux.HandleFunc("/websockify", proxyHandler)
65+
mux.Handle("/", http.FileServer(http.Dir("./static/vnc")))
66+
67+
go http.ListenAndServe(":8081", mux)
68+
}

vnc/vnc.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
package vnc
22

3+
import "fmt"
4+
5+
const unixSocketPath = "/socks/vnc.sock"
6+
37
func Setup(vnc string) []string {
4-
if len(vnc) == 0 {
5-
return nil
6-
}
8+
startServer()
9+
10+
vncOption := fmt.Sprintf("unix:%s", unixSocketPath)
711

8-
return []string{
12+
args := []string{
913
"-vga", "std",
10-
"-vnc", vnc,
14+
"-vnc", vncOption,
1115
}
16+
17+
if len(vnc) == 0 {
18+
args = append(args, "-vnc", vnc)
19+
}
20+
21+
return args
1222
}

0 commit comments

Comments
 (0)