Skip to content

Commit 96fc1a2

Browse files
committed
network enhanecment / refactor (jetkvm#361)
* chore(network): improve connectivity check * refactor(network): rewrite network and timesync component * feat(display): show cloud connection status * chore: change logging verbosity * chore(websecure): update log message * fix(ota): validate root certificate when downloading update * feat(ui): add network settings tab * fix(display): cloud connecting animation * fix: golintci issues * feat: add network settings tab * feat(timesync): query servers in parallel * refactor(network): move to internal/network package * feat(timesync): add metrics * refactor(log): move log to internal/logging package * refactor(mdms): move mdns to internal/mdns package * feat(developer): add pprof endpoint * feat(logging): add a simple logging streaming endpoint * fix(mdns): do not start mdns until network is up * feat(network): allow users to update network settings from ui * fix(network): handle errors when net.IPAddr is nil * fix(mdns): scopedLogger SIGSEGV * fix(dhcp): watch directory instead of file to catch fsnotify.Create event * refactor(nbd): move platform-specific code to different files * refactor(native): move platform-specific code to different files * chore: fix linter issues * chore(dev_deploy): allow to override PION_LOG_TRACE
1 parent d2ed343 commit 96fc1a2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+4928
-815
lines changed

block_device.go

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"os"
88
"time"
99

10-
"github.com/pojntfx/go-nbd/pkg/client"
1110
"github.com/pojntfx/go-nbd/pkg/server"
1211
"github.com/rs/zerolog"
1312
)
@@ -149,30 +148,3 @@ func (d *NBDDevice) runServerConn() {
149148

150149
d.l.Info().Err(err).Msg("nbd server exited")
151150
}
152-
153-
func (d *NBDDevice) runClientConn() {
154-
err := client.Connect(d.clientConn, d.dev, &client.Options{
155-
ExportName: "jetkvm",
156-
BlockSize: uint32(4 * 1024),
157-
})
158-
d.l.Info().Err(err).Msg("nbd client exited")
159-
}
160-
161-
func (d *NBDDevice) Close() {
162-
if d.dev != nil {
163-
err := client.Disconnect(d.dev)
164-
if err != nil {
165-
d.l.Warn().Err(err).Msg("error disconnecting nbd client")
166-
}
167-
_ = d.dev.Close()
168-
}
169-
if d.listener != nil {
170-
_ = d.listener.Close()
171-
}
172-
if d.clientConn != nil {
173-
_ = d.clientConn.Close()
174-
}
175-
if d.serverConn != nil {
176-
_ = d.serverConn.Close()
177-
}
178-
}

block_device_linux.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//go:build linux
2+
3+
package kvm
4+
5+
import (
6+
"github.com/pojntfx/go-nbd/pkg/client"
7+
)
8+
9+
func (d *NBDDevice) runClientConn() {
10+
err := client.Connect(d.clientConn, d.dev, &client.Options{
11+
ExportName: "jetkvm",
12+
BlockSize: uint32(4 * 1024),
13+
})
14+
d.l.Info().Err(err).Msg("nbd client exited")
15+
}
16+
17+
func (d *NBDDevice) Close() {
18+
if d.dev != nil {
19+
err := client.Disconnect(d.dev)
20+
if err != nil {
21+
d.l.Warn().Err(err).Msg("error disconnecting nbd client")
22+
}
23+
_ = d.dev.Close()
24+
}
25+
if d.listener != nil {
26+
_ = d.listener.Close()
27+
}
28+
if d.clientConn != nil {
29+
_ = d.clientConn.Close()
30+
}
31+
if d.serverConn != nil {
32+
_ = d.serverConn.Close()
33+
}
34+
}

block_device_notlinux.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//go:build !linux
2+
3+
package kvm
4+
5+
import (
6+
"os"
7+
)
8+
9+
func (d *NBDDevice) runClientConn() {
10+
d.l.Error().Msg("platform not supported")
11+
os.Exit(1)
12+
}
13+
14+
func (d *NBDDevice) Close() {
15+
d.l.Error().Msg("platform not supported")
16+
os.Exit(1)
17+
}

cloud.go

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,40 @@ var (
139139
)
140140
)
141141

142+
type CloudConnectionState uint8
143+
144+
const (
145+
CloudConnectionStateNotConfigured CloudConnectionState = iota
146+
CloudConnectionStateDisconnected
147+
CloudConnectionStateConnecting
148+
CloudConnectionStateConnected
149+
)
150+
142151
var (
152+
cloudConnectionState CloudConnectionState = CloudConnectionStateNotConfigured
153+
cloudConnectionStateLock = &sync.Mutex{}
154+
143155
cloudDisconnectChan chan error
144156
cloudDisconnectLock = &sync.Mutex{}
145157
)
146158

159+
func setCloudConnectionState(state CloudConnectionState) {
160+
cloudConnectionStateLock.Lock()
161+
defer cloudConnectionStateLock.Unlock()
162+
163+
if cloudConnectionState == CloudConnectionStateDisconnected &&
164+
(config.CloudToken == "" || config.CloudURL == "") {
165+
state = CloudConnectionStateNotConfigured
166+
}
167+
168+
previousState := cloudConnectionState
169+
cloudConnectionState = state
170+
171+
go waitCtrlAndRequestDisplayUpdate(
172+
previousState != state,
173+
)
174+
}
175+
147176
func wsResetMetrics(established bool, sourceType string, source string) {
148177
metricConnectionLastPingTimestamp.WithLabelValues(sourceType, source).Set(-1)
149178
metricConnectionLastPingDuration.WithLabelValues(sourceType, source).Set(-1)
@@ -285,6 +314,8 @@ func runWebsocketClient() error {
285314
wsURL.Scheme = "wss"
286315
}
287316

317+
setCloudConnectionState(CloudConnectionStateConnecting)
318+
288319
header := http.Header{}
289320
header.Set("X-Device-ID", GetDeviceID())
290321
header.Set("X-App-Version", builtAppVersion)
@@ -302,20 +333,26 @@ func runWebsocketClient() error {
302333
c, resp, err := websocket.Dial(dialCtx, wsURL.String(), &websocket.DialOptions{
303334
HTTPHeader: header,
304335
OnPingReceived: func(ctx context.Context, payload []byte) bool {
305-
scopedLogger.Info().Bytes("payload", payload).Int("length", len(payload)).Msg("ping frame received")
336+
scopedLogger.Debug().Bytes("payload", payload).Int("length", len(payload)).Msg("ping frame received")
306337

307338
metricConnectionTotalPingReceivedCount.WithLabelValues("cloud", wsURL.Host).Inc()
308339
metricConnectionLastPingReceivedTimestamp.WithLabelValues("cloud", wsURL.Host).SetToCurrentTime()
309340

341+
setCloudConnectionState(CloudConnectionStateConnected)
342+
310343
return true
311344
},
312345
})
313346

314-
// get the request id from the response header
315-
connectionId := resp.Header.Get("X-Request-ID")
316-
if connectionId == "" {
317-
connectionId = resp.Header.Get("Cf-Ray")
347+
var connectionId string
348+
if resp != nil {
349+
// get the request id from the response header
350+
connectionId = resp.Header.Get("X-Request-ID")
351+
if connectionId == "" {
352+
connectionId = resp.Header.Get("Cf-Ray")
353+
}
318354
}
355+
319356
if connectionId == "" {
320357
connectionId = uuid.New().String()
321358
scopedLogger.Warn().
@@ -332,6 +369,8 @@ func runWebsocketClient() error {
332369
if err != nil {
333370
if errors.Is(err, context.Canceled) {
334371
cloudLogger.Info().Msg("websocket connection canceled")
372+
setCloudConnectionState(CloudConnectionStateDisconnected)
373+
335374
return nil
336375
}
337376
return err
@@ -450,14 +489,14 @@ func RunWebsocketClient() {
450489
}
451490

452491
// If the network is not up, well, we can't connect to the cloud.
453-
if !networkState.Up {
454-
cloudLogger.Warn().Msg("waiting for network to be up, will retry in 3 seconds")
492+
if !networkState.IsOnline() {
493+
cloudLogger.Warn().Msg("waiting for network to be online, will retry in 3 seconds")
455494
time.Sleep(3 * time.Second)
456495
continue
457496
}
458497

459498
// If the system time is not synchronized, the API request will fail anyway because the TLS handshake will fail.
460-
if isTimeSyncNeeded() && !timeSyncSuccess {
499+
if isTimeSyncNeeded() && !timeSync.IsSyncSuccess() {
461500
cloudLogger.Warn().Msg("system time is not synced, will retry in 3 seconds")
462501
time.Sleep(3 * time.Second)
463502
continue
@@ -520,6 +559,8 @@ func rpcDeregisterDevice() error {
520559
cloudLogger.Info().Msg("device deregistered, disconnecting from cloud")
521560
disconnectCloud(fmt.Errorf("device deregistered"))
522561

562+
setCloudConnectionState(CloudConnectionStateNotConfigured)
563+
523564
return nil
524565
}
525566

config.go

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"os"
77
"sync"
88

9+
"github.com/jetkvm/kvm/internal/logging"
10+
"github.com/jetkvm/kvm/internal/network"
911
"github.com/jetkvm/kvm/internal/usbgadget"
1012
)
1113

@@ -73,27 +75,28 @@ func (m *KeyboardMacro) Validate() error {
7375
}
7476

7577
type Config struct {
76-
CloudURL string `json:"cloud_url"`
77-
CloudAppURL string `json:"cloud_app_url"`
78-
CloudToken string `json:"cloud_token"`
79-
GoogleIdentity string `json:"google_identity"`
80-
JigglerEnabled bool `json:"jiggler_enabled"`
81-
AutoUpdateEnabled bool `json:"auto_update_enabled"`
82-
IncludePreRelease bool `json:"include_pre_release"`
83-
HashedPassword string `json:"hashed_password"`
84-
LocalAuthToken string `json:"local_auth_token"`
85-
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
86-
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
87-
KeyboardMacros []KeyboardMacro `json:"keyboard_macros"`
88-
EdidString string `json:"hdmi_edid_string"`
89-
ActiveExtension string `json:"active_extension"`
90-
DisplayMaxBrightness int `json:"display_max_brightness"`
91-
DisplayDimAfterSec int `json:"display_dim_after_sec"`
92-
DisplayOffAfterSec int `json:"display_off_after_sec"`
93-
TLSMode string `json:"tls_mode"` // options: "self-signed", "user-defined", ""
94-
UsbConfig *usbgadget.Config `json:"usb_config"`
95-
UsbDevices *usbgadget.Devices `json:"usb_devices"`
96-
DefaultLogLevel string `json:"default_log_level"`
78+
CloudURL string `json:"cloud_url"`
79+
CloudAppURL string `json:"cloud_app_url"`
80+
CloudToken string `json:"cloud_token"`
81+
GoogleIdentity string `json:"google_identity"`
82+
JigglerEnabled bool `json:"jiggler_enabled"`
83+
AutoUpdateEnabled bool `json:"auto_update_enabled"`
84+
IncludePreRelease bool `json:"include_pre_release"`
85+
HashedPassword string `json:"hashed_password"`
86+
LocalAuthToken string `json:"local_auth_token"`
87+
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
88+
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
89+
KeyboardMacros []KeyboardMacro `json:"keyboard_macros"`
90+
EdidString string `json:"hdmi_edid_string"`
91+
ActiveExtension string `json:"active_extension"`
92+
DisplayMaxBrightness int `json:"display_max_brightness"`
93+
DisplayDimAfterSec int `json:"display_dim_after_sec"`
94+
DisplayOffAfterSec int `json:"display_off_after_sec"`
95+
TLSMode string `json:"tls_mode"` // options: "self-signed", "user-defined", ""
96+
UsbConfig *usbgadget.Config `json:"usb_config"`
97+
UsbDevices *usbgadget.Devices `json:"usb_devices"`
98+
NetworkConfig *network.NetworkConfig `json:"network_config"`
99+
DefaultLogLevel string `json:"default_log_level"`
97100
}
98101

99102
const configPath = "/userdata/kvm_config.json"
@@ -121,6 +124,7 @@ var defaultConfig = &Config{
121124
Keyboard: true,
122125
MassStorage: true,
123126
},
127+
NetworkConfig: &network.NetworkConfig{},
124128
DefaultLogLevel: "INFO",
125129
}
126130

@@ -134,7 +138,7 @@ func LoadConfig() {
134138
defer configLock.Unlock()
135139

136140
if config != nil {
137-
logger.Info().Msg("config already loaded, skipping")
141+
logger.Debug().Msg("config already loaded, skipping")
138142
return
139143
}
140144

@@ -164,9 +168,15 @@ func LoadConfig() {
164168
loadedConfig.UsbDevices = defaultConfig.UsbDevices
165169
}
166170

171+
if loadedConfig.NetworkConfig == nil {
172+
loadedConfig.NetworkConfig = defaultConfig.NetworkConfig
173+
}
174+
167175
config = &loadedConfig
168176

169-
rootLogger.UpdateLogLevel()
177+
logging.GetRootLogger().UpdateLogLevel(config.DefaultLogLevel)
178+
179+
logger.Info().Str("path", configPath).Msg("config loaded")
170180
}
171181

172182
func SaveConfig() error {

dev_deploy.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ show_help() {
2424
REMOTE_USER="root"
2525
REMOTE_PATH="/userdata/jetkvm/bin"
2626
SKIP_UI_BUILD=false
27+
LOG_TRACE_SCOPES="${LOG_TRACE_SCOPES:-jetkvm,cloud,websocket,native,jsonrpc}"
2728

2829
# Parse command line arguments
2930
while [[ $# -gt 0 ]]; do
@@ -91,7 +92,7 @@ cd "${REMOTE_PATH}"
9192
chmod +x jetkvm_app_debug
9293
9394
# Run the application in the background
94-
PION_LOG_TRACE=jetkvm,cloud,websocket ./jetkvm_app_debug
95+
PION_LOG_TRACE=${LOG_TRACE_SCOPES} ./jetkvm_app_debug
9596
EOF
9697

9798
echo "Deployment complete."

0 commit comments

Comments
 (0)