Skip to content

Commit 752fb55

Browse files
ymCopilotadamshiervani
authored
refactor: OTA (#912)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Adam Shiervani <adam.shiervani@gmail.com>
1 parent d49e268 commit 752fb55

Some content is hidden

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

44 files changed

+2659
-723
lines changed

.vscode/settings.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,13 @@
1111
},
1212
"git.ignoreLimitWarning": true,
1313
"cmake.sourceDirectory": "/workspaces/kvm-static-ip/internal/native/cgo",
14-
"cmake.ignoreCMakeListsMissing": true
14+
"cmake.ignoreCMakeListsMissing": true,
15+
"json.schemas": [
16+
{
17+
"fileMatch": [
18+
"/internal/ota/testdata/ota/*.json"
19+
],
20+
"url": "./internal/ota/testdata/ota.schema.json"
21+
}
22+
]
1523
}

config.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"strconv"
8+
"strings"
89
"sync"
910

1011
"github.com/jetkvm/kvm/internal/confparser"
@@ -15,6 +16,10 @@ import (
1516
"github.com/prometheus/client_golang/prometheus/promauto"
1617
)
1718

19+
const (
20+
DefaultAPIURL = "https://api.jetkvm.com"
21+
)
22+
1823
type WakeOnLanDevice struct {
1924
Name string `json:"name"`
2025
MacAddress string `json:"macAddress"`
@@ -80,6 +85,7 @@ func (m *KeyboardMacro) Validate() error {
8085

8186
type Config struct {
8287
CloudURL string `json:"cloud_url"`
88+
UpdateAPIURL string `json:"update_api_url"`
8389
CloudAppURL string `json:"cloud_app_url"`
8490
CloudToken string `json:"cloud_token"`
8591
GoogleIdentity string `json:"google_identity"`
@@ -109,6 +115,15 @@ type Config struct {
109115
VideoQualityFactor float64 `json:"video_quality_factor"`
110116
}
111117

118+
// GetUpdateAPIURL returns the update API URL
119+
func (c *Config) GetUpdateAPIURL() string {
120+
if c.UpdateAPIURL == "" {
121+
return DefaultAPIURL
122+
}
123+
return strings.TrimSuffix(c.UpdateAPIURL, "/") + "/releases"
124+
}
125+
126+
// GetDisplayRotation returns the display rotation
112127
func (c *Config) GetDisplayRotation() uint16 {
113128
rotationInt, err := strconv.ParseUint(c.DisplayRotation, 10, 16)
114129
if err != nil {
@@ -118,6 +133,7 @@ func (c *Config) GetDisplayRotation() uint16 {
118133
return uint16(rotationInt)
119134
}
120135

136+
// SetDisplayRotation sets the display rotation
121137
func (c *Config) SetDisplayRotation(rotation string) error {
122138
_, err := strconv.ParseUint(rotation, 10, 16)
123139
if err != nil {
@@ -156,7 +172,8 @@ var (
156172

157173
func getDefaultConfig() Config {
158174
return Config{
159-
CloudURL: "https://api.jetkvm.com",
175+
CloudURL: DefaultAPIURL,
176+
UpdateAPIURL: DefaultAPIURL,
160177
CloudAppURL: "https://app.jetkvm.com",
161178
AutoUpdateEnabled: true, // Set a default value
162179
ActiveExtension: "",

hw.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"strings"
99
"sync"
1010
"time"
11+
12+
"github.com/jetkvm/kvm/internal/ota"
1113
)
1214

1315
func extractSerialNumber() (string, error) {
@@ -29,22 +31,16 @@ func extractSerialNumber() (string, error) {
2931
return matches[1], nil
3032
}
3133

32-
func readOtpEntropy() ([]byte, error) { //nolint:unused
33-
content, err := os.ReadFile("/sys/bus/nvmem/devices/rockchip-otp0/nvmem")
34-
if err != nil {
35-
return nil, err
36-
}
37-
return content[0x17:0x1C], nil
38-
}
39-
40-
func hwReboot(force bool, postRebootAction *PostRebootAction, delay time.Duration) error {
41-
logger.Info().Msgf("Reboot requested, rebooting in %d seconds...", delay)
34+
func hwReboot(force bool, postRebootAction *ota.PostRebootAction, delay time.Duration) error {
35+
logger.Info().Dur("delayMs", delay).Msg("reboot requested")
4236

4337
writeJSONRPCEvent("willReboot", postRebootAction, currentSession)
4438
time.Sleep(1 * time.Second) // Wait for the JSONRPCEvent to be sent
4539

4640
nativeInstance.SwitchToScreenIfDifferent("rebooting_screen")
47-
time.Sleep(delay - (1 * time.Second)) // wait requested extra settle time
41+
if delay > 1*time.Second {
42+
time.Sleep(delay - 1*time.Second) // wait requested extra settle time
43+
}
4844

4945
args := []string{}
5046
if force {

internal/ota/app.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package ota
2+
3+
import (
4+
"context"
5+
"time"
6+
)
7+
8+
const (
9+
appUpdatePath = "/userdata/jetkvm/jetkvm_app.update"
10+
)
11+
12+
// DO NOT call it directly, it's not thread safe
13+
// Mutex is currently held by the caller, e.g. doUpdate
14+
func (s *State) updateApp(ctx context.Context, appUpdate *componentUpdateStatus) error {
15+
l := s.l.With().Str("path", appUpdatePath).Logger()
16+
17+
if err := s.downloadFile(ctx, appUpdatePath, appUpdate.url, "app"); err != nil {
18+
return s.componentUpdateError("Error downloading app update", err, &l)
19+
}
20+
21+
downloadFinished := time.Now()
22+
appUpdate.downloadFinishedAt = downloadFinished
23+
appUpdate.downloadProgress = 1
24+
s.triggerComponentUpdateState("app", appUpdate)
25+
26+
if err := s.verifyFile(
27+
appUpdatePath,
28+
appUpdate.hash,
29+
&appUpdate.verificationProgress,
30+
); err != nil {
31+
return s.componentUpdateError("Error verifying app update hash", err, &l)
32+
}
33+
verifyFinished := time.Now()
34+
appUpdate.verifiedAt = verifyFinished
35+
appUpdate.verificationProgress = 1
36+
appUpdate.updatedAt = verifyFinished
37+
appUpdate.updateProgress = 1
38+
s.triggerComponentUpdateState("app", appUpdate)
39+
40+
l.Info().Msg("App update downloaded")
41+
42+
s.rebootNeeded = true
43+
44+
return nil
45+
}

internal/ota/errors.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package ota
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
7+
"github.com/rs/zerolog"
8+
)
9+
10+
var (
11+
// ErrVersionNotFound is returned when the specified version is not found
12+
ErrVersionNotFound = errors.New("specified version not found")
13+
)
14+
15+
func (s *State) componentUpdateError(prefix string, err error, l *zerolog.Logger) error {
16+
if l == nil {
17+
l = s.l
18+
}
19+
l.Error().Err(err).Msg(prefix)
20+
s.error = fmt.Sprintf("%s: %v", prefix, err)
21+
s.updating = false
22+
s.triggerStateUpdate()
23+
return err
24+
}

0 commit comments

Comments
 (0)