Skip to content

Commit 01bc7e7

Browse files
authored
refactor: dedup helpers across cmd/hypervisor/snapshot (#59)
- hypervisor.ResolveAndLoad combines ResolveRef+LoadRecord under one DB lock; collapses 3 call sites (CH console, FC console, CH extend's runningVMClientWithRecord) - cmdcore.CloseOnCancel wraps context.AfterFunc(stream.Close); 4 call sites (cmd/snapshot/handler.go × 2, cmd/vm/run.go × 2) shrink to one defer line each - snapshot/localfile.insertRecord folds the 18-line name-collision + Snapshots[id]+Names[name] block shared by Create (Pending) and Import - cmd/vm/status_test.go: equalStrings → slices.Equal (Go stdlib)
1 parent 3fe0fd0 commit 01bc7e7

10 files changed

Lines changed: 59 additions & 88 deletions

File tree

cmd/core/helpers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77
"errors"
88
"fmt"
9+
"io"
910
"os"
1011
"strings"
1112
"text/tabwriter"
@@ -448,6 +449,13 @@ func IsURL(ref string) bool {
448449
return strings.HasPrefix(ref, "http://") || strings.HasPrefix(ref, "https://")
449450
}
450451

452+
// CloseOnCancel closes c when ctx is canceled; callers `defer CloseOnCancel(ctx, c)()` to stop the watcher on return.
453+
func CloseOnCancel(ctx context.Context, c io.Closer) func() bool {
454+
return context.AfterFunc(ctx, func() {
455+
c.Close() //nolint:errcheck,gosec
456+
})
457+
}
458+
451459
// resolveOwner returns the unique backend where found==true; notFound on zero, ambiguous wrapped on multi-match (lists matched types).
452460
func resolveOwner[T interface{ Type() string }](backends []T, ref string, found func(T) (bool, error), notFound, ambiguous error) (T, error) {
453461
var matches []T

cmd/snapshot/handler.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package snapshot
22

33
import (
44
"cmp"
5-
"context"
65
"errors"
76
"fmt"
87
"io"
@@ -58,11 +57,7 @@ func (h Handler) Save(cmd *cobra.Command, args []string) error {
5857
}
5958
defer stream.Close() //nolint:errcheck
6059

61-
// Close stream on ctx cancel so Ctrl+C doesn't hang on the pipe.
62-
stop := context.AfterFunc(ctx, func() {
63-
stream.Close() //nolint:errcheck,gosec
64-
})
65-
defer stop()
60+
defer cmdcore.CloseOnCancel(ctx, stream)()
6661

6762
cfg.Name = name
6863
cfg.Description = description
@@ -199,11 +194,7 @@ func (h Handler) Export(cmd *cobra.Command, args []string) (err error) {
199194
return fmt.Errorf("export: %w", err)
200195
}
201196
defer stream.Close() //nolint:errcheck
202-
203-
stop := context.AfterFunc(ctx, func() {
204-
stream.Close() //nolint:errcheck,gosec
205-
})
206-
defer stop()
197+
defer cmdcore.CloseOnCancel(ctx, stream)()
207198

208199
if output == "-" {
209200
if _, err = io.Copy(os.Stdout, stream); err != nil {

cmd/vm/run.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,7 @@ func (h Handler) Clone(cmd *cobra.Command, args []string) error {
112112
return fmt.Errorf("open snapshot %s: %w", snapRef, err)
113113
}
114114
defer stream.Close() //nolint:errcheck
115-
116-
stop := context.AfterFunc(ctx, func() {
117-
stream.Close() //nolint:errcheck,gosec
118-
})
119-
defer stop()
115+
defer cmdcore.CloseOnCancel(ctx, stream)()
120116

121117
vmCfg, vmID, netProvider, netSetup, err := h.prepareClone(ctx, cmd, conf, cfg)
122118
if err != nil {
@@ -191,11 +187,7 @@ func (h Handler) Restore(cmd *cobra.Command, args []string) error {
191187
return fmt.Errorf("open snapshot: %w", err)
192188
}
193189
defer stream.Close() //nolint:errcheck
194-
195-
stop := context.AfterFunc(ctx, func() {
196-
stream.Close() //nolint:errcheck,gosec
197-
})
198-
defer stop()
190+
defer cmdcore.CloseOnCancel(ctx, stream)()
199191

200192
logger.Infof(ctx, "restoring VM %s from snapshot %s ...", vmRef, snapRef)
201193

cmd/vm/status_test.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package vm
33
import (
44
"io"
55
"os"
6+
"slices"
67
"strings"
78
"testing"
89
"time"
@@ -176,21 +177,9 @@ func TestApplyFilters(t *testing.T) {
176177
for _, vm := range got {
177178
gotIDs = append(gotIDs, vm.ID)
178179
}
179-
if !equalStrings(gotIDs, tt.wantIDs) {
180+
if !slices.Equal(gotIDs, tt.wantIDs) {
180181
t.Errorf("applyFilters(%v) = %v, want %v", tt.filters, gotIDs, tt.wantIDs)
181182
}
182183
})
183184
}
184185
}
185-
186-
func equalStrings(a, b []string) bool {
187-
if len(a) != len(b) {
188-
return false
189-
}
190-
for i := range a {
191-
if a[i] != b[i] {
192-
return false
193-
}
194-
}
195-
return true
196-
}

hypervisor/cloudhypervisor/console.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,7 @@ import (
1515
// Console returns a bidirectional stream to the VM console: console.sock (UEFI) or the CH-allocated PTY (OCI).
1616
// Caller closes the returned ReadWriteCloser.
1717
func (ch *CloudHypervisor) Console(ctx context.Context, ref string) (io.ReadWriteCloser, error) {
18-
id, err := ch.ResolveRef(ctx, ref)
19-
if err != nil {
20-
return nil, err
21-
}
22-
23-
rec, err := ch.LoadRecord(ctx, id)
18+
id, rec, err := ch.ResolveAndLoad(ctx, ref)
2419
if err != nil {
2520
return nil, err
2621
}

hypervisor/cloudhypervisor/extend.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,7 @@ func (ch *CloudHypervisor) runningVMClient(ctx context.Context, vmRef string) (*
202202
}
203203

204204
func (ch *CloudHypervisor) runningVMClientWithRecord(ctx context.Context, vmRef string) (*http.Client, string, hypervisor.VMRecord, error) {
205-
vmID, err := ch.ResolveRef(ctx, vmRef)
206-
if err != nil {
207-
return nil, "", hypervisor.VMRecord{}, err
208-
}
209-
rec, err := ch.LoadRecord(ctx, vmID)
205+
vmID, rec, err := ch.ResolveAndLoad(ctx, vmRef)
210206
if err != nil {
211207
return nil, "", hypervisor.VMRecord{}, err
212208
}

hypervisor/firecracker/console.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@ import (
1010
)
1111

1212
func (fc *Firecracker) Console(ctx context.Context, ref string) (io.ReadWriteCloser, error) {
13-
id, err := fc.ResolveRef(ctx, ref)
14-
if err != nil {
15-
return nil, err
16-
}
17-
18-
rec, err := fc.LoadRecord(ctx, id)
13+
id, rec, err := fc.ResolveAndLoad(ctx, ref)
1914
if err != nil {
2015
return nil, err
2116
}

hypervisor/inspect.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,23 @@ func (b *Backend) LoadRecord(ctx context.Context, id string) (VMRecord, error) {
7373
})
7474
}
7575

76+
// ResolveAndLoad combines ResolveRef + LoadRecord under a single DB lock.
77+
func (b *Backend) ResolveAndLoad(ctx context.Context, ref string) (string, VMRecord, error) {
78+
var (
79+
id string
80+
rec VMRecord
81+
)
82+
return id, rec, b.DB.With(ctx, func(idx *VMIndex) error {
83+
var err error
84+
id, err = idx.Resolve(ref)
85+
if err != nil {
86+
return err
87+
}
88+
rec, err = utils.LookupCopy(idx.VMs, id)
89+
return err
90+
})
91+
}
92+
7693
func vsockBound(path string) bool {
7794
_, err := os.Stat(path)
7895
return err == nil

snapshot/localfile/import.go

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,11 @@ func (lf *LocalFile) Import(ctx context.Context, r io.Reader, name, description
5959
return "", fmt.Errorf("compute data dir size: %w", sizeErr)
6060
}
6161
now := time.Now()
62-
if err = lf.store.Update(ctx, func(idx *snapshot.SnapshotIndex) error {
63-
if cfg.Name != "" {
64-
if existingID, ok := idx.Names[cfg.Name]; ok {
65-
return fmt.Errorf("snapshot name %q already in use by %s", cfg.Name, existingID)
66-
}
67-
}
68-
idx.Snapshots[id] = &snapshot.SnapshotRecord{
69-
Snapshot: types.Snapshot{
70-
SnapshotConfig: cfg,
71-
CreatedAt: now,
72-
},
73-
DataDir: dataDir,
74-
SizeBytes: size,
75-
LastAccessedAt: now,
76-
}
77-
if cfg.Name != "" {
78-
idx.Names[cfg.Name] = id
79-
}
80-
return nil
62+
if err = lf.insertRecord(ctx, id, cfg.Name, &snapshot.SnapshotRecord{
63+
Snapshot: types.Snapshot{SnapshotConfig: cfg, CreatedAt: now},
64+
DataDir: dataDir,
65+
SizeBytes: size,
66+
LastAccessedAt: now,
8167
}); err != nil {
8268
return "", err
8369
}

snapshot/localfile/localfile.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -90,24 +90,10 @@ func (lf *LocalFile) Create(ctx context.Context, cfg *types.SnapshotConfig, stre
9090
dataDir := lf.conf.SnapshotDataDir(id)
9191
now := time.Now()
9292

93-
if err = lf.store.Update(ctx, func(idx *snapshot.SnapshotIndex) error {
94-
if cfg.Name != "" {
95-
if existingID, ok := idx.Names[cfg.Name]; ok {
96-
return fmt.Errorf("snapshot name %q already in use by %s", cfg.Name, existingID)
97-
}
98-
}
99-
idx.Snapshots[id] = &snapshot.SnapshotRecord{
100-
Snapshot: types.Snapshot{
101-
SnapshotConfig: *cfg,
102-
CreatedAt: now,
103-
},
104-
Pending: true,
105-
DataDir: dataDir,
106-
}
107-
if cfg.Name != "" {
108-
idx.Names[cfg.Name] = id
109-
}
110-
return nil
93+
if err = lf.insertRecord(ctx, id, cfg.Name, &snapshot.SnapshotRecord{
94+
Snapshot: types.Snapshot{SnapshotConfig: *cfg, CreatedAt: now},
95+
Pending: true,
96+
DataDir: dataDir,
11197
}); err != nil {
11298
return "", err
11399
}
@@ -235,6 +221,22 @@ func (lf *LocalFile) deleteOne(ctx context.Context, id string) error {
235221
return nil
236222
}
237223

224+
// insertRecord adds rec under id with name-collision check; both Create (Pending) and Import (finalized) go through here.
225+
func (lf *LocalFile) insertRecord(ctx context.Context, id, name string, rec *snapshot.SnapshotRecord) error {
226+
return lf.store.Update(ctx, func(idx *snapshot.SnapshotIndex) error {
227+
if name != "" {
228+
if existingID, ok := idx.Names[name]; ok {
229+
return fmt.Errorf("snapshot name %q already in use by %s", name, existingID)
230+
}
231+
}
232+
idx.Snapshots[id] = rec
233+
if name != "" {
234+
idx.Names[name] = id
235+
}
236+
return nil
237+
})
238+
}
239+
238240
// rollbackCreate removes a placeholder snapshot record from the DB.
239241
func (lf *LocalFile) rollbackCreate(ctx context.Context, id, name string) {
240242
if err := lf.store.Update(ctx, func(idx *snapshot.SnapshotIndex) error {

0 commit comments

Comments
 (0)