Skip to content

Commit a8c876a

Browse files
committed
implement kwasm lockfile support to avoid unnecessary restarts
1 parent 73cc3d8 commit a8c876a

File tree

4 files changed

+143
-11
lines changed

4 files changed

+143
-11
lines changed

cmd/install.go

+17-5
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,33 @@ var installCmd = &cobra.Command{
3535
slog.Error(err.Error())
3636
return
3737
}
38+
anythingChanged := false
3839
for _, file := range files {
39-
binPath, err := shim.Install(&config, file.Name())
40+
fileName := file.Name()
41+
runtimeName := shim.RuntimeName(fileName)
42+
43+
binPath, changed, err := shim.Install(&config, fileName)
4044
if err != nil {
41-
slog.Error(err.Error())
45+
slog.Error("failed to install shim", "shim", runtimeName, "error", err)
4246
return
4347
}
44-
slog.Info("shim installed", "shim", shim.RuntimeName(file.Name()), "path", binPath)
48+
anythingChanged = anythingChanged || changed
49+
slog.Info("shim installed", "shim", runtimeName, "path", binPath, "new-version", changed)
50+
4551
configPath, err := containerd.WriteConfig(&config, binPath)
4652
if err != nil {
47-
slog.Error(err.Error())
53+
slog.Error("failed to write containerd config", "shim", runtimeName, "path", configPath, "error", err)
4854
return
4955
}
50-
slog.Info("shim configured", "shim", shim.RuntimeName(file.Name()), "path", configPath)
56+
slog.Info("shim configured", "shim", runtimeName, "path", configPath)
57+
}
58+
59+
if !anythingChanged {
60+
slog.Info("nothing changed, nothing more to do")
61+
return
5162
}
5263

64+
slog.Info("restarting containerd")
5365
err = containerd.RestartRuntime()
5466
if err != nil {
5567
slog.Error("failed to restart containerd", "error", err)

pkg/shim/install.go

+26-6
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,52 @@
1717
package shim
1818

1919
import (
20+
"crypto/sha256"
2021
"io"
2122
"os"
2223
"path"
2324

2425
"github.com/kwasm/kwasm-node-installer/pkg/config"
26+
"github.com/kwasm/kwasm-node-installer/pkg/state"
2527
)
2628

27-
func Install(config *config.Config, shimName string) (string, error) {
29+
func Install(config *config.Config, shimName string) (string, bool, error) {
2830
shimPath := config.AssetPath(shimName)
2931
srcFile, err := os.OpenFile(shimPath, os.O_RDONLY, 0000)
3032
if err != nil {
31-
return "", err
33+
return "", false, err
3234
}
3335
dstFilePath := path.Join(config.Kwasm.Path, "bin", shimName)
3436
dstFilePathHost := config.PathWithHost(dstFilePath)
3537

3638
err = os.MkdirAll(path.Dir(dstFilePathHost), 0755)
3739
if err != nil {
38-
return dstFilePath, err
40+
return dstFilePath, false, err
3941
}
4042

4143
dstFile, err := os.OpenFile(dstFilePathHost, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
4244
if err != nil {
43-
return "", err
45+
return "", false, err
4446
}
45-
_, err = io.Copy(dstFile, srcFile)
4647

47-
return dstFilePath, err
48+
st, err := state.Get(config)
49+
if err != nil {
50+
return "", false, err
51+
}
52+
shimSha256 := sha256.New()
53+
54+
_, err = io.Copy(io.MultiWriter(dstFile, shimSha256), srcFile)
55+
runtimeName := RuntimeName(shimName)
56+
changed := st.ShimChanged(runtimeName, shimSha256.Sum(nil), dstFilePath)
57+
if changed {
58+
st.UpdateShim(runtimeName, state.Shim{
59+
Path: dstFilePath,
60+
Sha256: shimSha256.Sum(nil),
61+
})
62+
if err := st.Write(); err != nil {
63+
return "", false, err
64+
}
65+
}
66+
67+
return dstFilePath, changed, err
4868
}

pkg/state/shim.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package state
2+
3+
import (
4+
"encoding/hex"
5+
"encoding/json"
6+
)
7+
8+
type Shim struct {
9+
Sha256 []byte
10+
Path string
11+
}
12+
13+
func (s *Shim) MarshalJSON() ([]byte, error) {
14+
return json.Marshal(&struct {
15+
Sha256 string `json:"sha256"`
16+
Path string `json:"path"`
17+
}{
18+
Sha256: hex.EncodeToString(s.Sha256),
19+
Path: s.Path,
20+
})
21+
}
22+
23+
func (s *Shim) UnmarshalJSON(data []byte) error {
24+
aux := &struct {
25+
Sha256 string `json:"sha256"`
26+
Path string `json:"path"`
27+
}{}
28+
if err := json.Unmarshal(data, &aux); err != nil {
29+
return err
30+
}
31+
s.Path = aux.Path
32+
sha256, err := hex.DecodeString(aux.Sha256)
33+
if err != nil {
34+
return err
35+
}
36+
s.Sha256 = sha256
37+
return nil
38+
}

pkg/state/state.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package state
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"errors"
7+
"log/slog"
8+
"os"
9+
"path"
10+
11+
"github.com/kwasm/kwasm-node-installer/pkg/config"
12+
)
13+
14+
type state struct {
15+
Shims map[string]*Shim `json:"shims"`
16+
config *config.Config
17+
}
18+
19+
func Get(config *config.Config) (*state, error) {
20+
out := state{
21+
Shims: make(map[string]*Shim),
22+
config: config,
23+
}
24+
content, err := os.ReadFile(filePath(config))
25+
if err == nil {
26+
err := json.Unmarshal(content, &out)
27+
return &out, err
28+
}
29+
if !errors.Is(err, os.ErrNotExist) {
30+
return nil, err
31+
}
32+
33+
return &out, nil
34+
}
35+
36+
func (l *state) ShimChanged(shimName string, sha256 []byte, path string) bool {
37+
shim, ok := l.Shims[shimName]
38+
if !ok {
39+
return true
40+
}
41+
42+
return !bytes.Equal(shim.Sha256, sha256) || shim.Path != path
43+
}
44+
45+
func (l *state) UpdateShim(shimName string, shim Shim) {
46+
l.Shims[shimName] = &shim
47+
}
48+
49+
func (l *state) Write() error {
50+
out, err := json.MarshalIndent(l, "", " ")
51+
if err != nil {
52+
return err
53+
}
54+
55+
slog.Info("writing lock file", "content", string(out))
56+
57+
return os.WriteFile(filePath(l.config), out, 0644)
58+
}
59+
60+
func filePath(config *config.Config) string {
61+
return config.PathWithHost(path.Join(config.Kwasm.Path, "kwasm-lock.json"))
62+
}

0 commit comments

Comments
 (0)