Skip to content

Commit ae34f45

Browse files
committed
codex v1
1 parent 19840f6 commit ae34f45

12 files changed

Lines changed: 201 additions & 136 deletions

File tree

cmd/create.go

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ package cmd
33
import (
44
"fmt"
55

6-
units "github.com/docker/go-units"
76
"github.com/projecteru2/core/log"
87
"github.com/spf13/cobra"
9-
10-
"github.com/projecteru2/cocoon/types"
118
)
129

1310
var createCmd = func() *cobra.Command {
@@ -32,41 +29,17 @@ func runCreate(cmd *cobra.Command, args []string) error {
3229
}
3330
image := args[0]
3431

35-
vmName, _ := cmd.Flags().GetString("name")
36-
cpu, _ := cmd.Flags().GetInt("cpu")
37-
memStr, _ := cmd.Flags().GetString("memory")
38-
storStr, _ := cmd.Flags().GetString("storage")
39-
40-
if vmName == "" {
41-
vmName = fmt.Sprintf("cocoon-%s", image)
42-
}
43-
44-
memBytes, err := units.RAMInBytes(memStr)
45-
if err != nil {
46-
return fmt.Errorf("invalid --memory %q: %w", memStr, err)
47-
}
48-
storBytes, err := units.RAMInBytes(storStr)
32+
vmCfg, err := vmConfigFromFlags(cmd, image)
4933
if err != nil {
50-
return fmt.Errorf("invalid --storage %q: %w", storStr, err)
51-
}
52-
53-
vmCfg := &types.VMConfig{
54-
Name: vmName,
55-
CPU: cpu,
56-
Memory: memBytes,
57-
Storage: storBytes,
58-
Image: image,
34+
return err
5935
}
6036

6137
storageConfigs, bootCfg, err := resolveImage(ctx, backends, vmCfg)
6238
if err != nil {
6339
return err
6440
}
6541

66-
// If cloudimg, set firmware path from global config.
67-
if bootCfg.KernelPath == "" && bootCfg.FirmwarePath == "" {
68-
bootCfg.FirmwarePath = conf.FirmwarePath()
69-
}
42+
ensureFirmwarePath(bootCfg)
7043

7144
info, err := hyper.Create(ctx, vmCfg, storageConfigs, bootCfg)
7245
if err != nil {

cmd/helper.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77

88
units "github.com/docker/go-units"
9+
"github.com/spf13/cobra"
910

1011
"github.com/projecteru2/cocoon/hypervisor"
1112
"github.com/projecteru2/cocoon/hypervisor/cloudhypervisor"
@@ -78,6 +79,41 @@ func resolveImage(ctx context.Context, backends []images.Images, vmCfg *types.VM
7879
return storageConfigs, bootCfg, nil
7980
}
8081

82+
// vmConfigFromFlags builds VMConfig for create/run commands.
83+
func vmConfigFromFlags(cmd *cobra.Command, image string) (*types.VMConfig, error) {
84+
vmName, _ := cmd.Flags().GetString("name")
85+
cpu, _ := cmd.Flags().GetInt("cpu")
86+
memStr, _ := cmd.Flags().GetString("memory")
87+
storStr, _ := cmd.Flags().GetString("storage")
88+
89+
if vmName == "" {
90+
vmName = fmt.Sprintf("cocoon-%s", image)
91+
}
92+
93+
memBytes, err := units.RAMInBytes(memStr)
94+
if err != nil {
95+
return nil, fmt.Errorf("invalid --memory %q: %w", memStr, err)
96+
}
97+
storBytes, err := units.RAMInBytes(storStr)
98+
if err != nil {
99+
return nil, fmt.Errorf("invalid --storage %q: %w", storStr, err)
100+
}
101+
102+
return &types.VMConfig{
103+
Name: vmName,
104+
CPU: cpu,
105+
Memory: memBytes,
106+
Storage: storBytes,
107+
Image: image,
108+
}, nil
109+
}
110+
111+
func ensureFirmwarePath(bootCfg *types.BootConfig) {
112+
if bootCfg != nil && bootCfg.KernelPath == "" && bootCfg.FirmwarePath == "" {
113+
bootCfg.FirmwarePath = conf.FirmwarePath()
114+
}
115+
}
116+
81117
func formatSize(bytes int64) string {
82118
return units.HumanSize(float64(bytes))
83119
}

cmd/run.go

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ package cmd
33
import (
44
"fmt"
55

6-
units "github.com/docker/go-units"
76
"github.com/projecteru2/core/log"
87
"github.com/spf13/cobra"
9-
10-
"github.com/projecteru2/cocoon/types"
118
)
129

1310
var runCmd = func() *cobra.Command {
@@ -33,40 +30,17 @@ func runRun(cmd *cobra.Command, args []string) error {
3330
}
3431
image := args[0]
3532

36-
vmName, _ := cmd.Flags().GetString("name")
37-
cpu, _ := cmd.Flags().GetInt("cpu")
38-
memStr, _ := cmd.Flags().GetString("memory")
39-
storStr, _ := cmd.Flags().GetString("storage")
40-
41-
if vmName == "" {
42-
vmName = fmt.Sprintf("cocoon-%s", image)
43-
}
44-
45-
memBytes, err := units.RAMInBytes(memStr)
46-
if err != nil {
47-
return fmt.Errorf("invalid --memory %q: %w", memStr, err)
48-
}
49-
storBytes, err := units.RAMInBytes(storStr)
33+
vmCfg, err := vmConfigFromFlags(cmd, image)
5034
if err != nil {
51-
return fmt.Errorf("invalid --storage %q: %w", storStr, err)
52-
}
53-
54-
vmCfg := &types.VMConfig{
55-
Name: vmName,
56-
CPU: cpu,
57-
Memory: memBytes,
58-
Storage: storBytes,
59-
Image: image,
35+
return err
6036
}
6137

6238
storageConfigs, bootCfg, err := resolveImage(ctx, backends, vmCfg)
6339
if err != nil {
6440
return err
6541
}
6642

67-
if bootCfg.KernelPath == "" && bootCfg.FirmwarePath == "" {
68-
bootCfg.FirmwarePath = conf.FirmwarePath()
69-
}
43+
ensureFirmwarePath(bootCfg)
7044

7145
info, err := hyper.Create(ctx, vmCfg, storageConfigs, bootCfg)
7246
if err != nil {

hypervisor/cloudhypervisor/cloudhypervisor.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ func (ch *CloudHypervisor) Delete(ctx context.Context, refs []string, force bool
128128
}); err != nil {
129129
return err
130130
}
131-
_ = ch.removeVMDirs(ctx, id)
131+
if err := ch.removeVMDirs(ctx, id); err != nil {
132+
return fmt.Errorf("cleanup VM dirs: %w", err)
133+
}
132134
return nil
133135
})
134136
}

hypervisor/cloudhypervisor/gc.go

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cloudhypervisor
33
import (
44
"context"
55
"errors"
6+
"time"
67

78
"github.com/projecteru2/cocoon/gc"
89
"github.com/projecteru2/cocoon/hypervisor"
@@ -13,10 +14,15 @@ import (
1314
// compile-time interface check.
1415
var _ hypervisor.Hypervisor = (*CloudHypervisor)(nil)
1516

17+
// creatingStateGCGrace is the minimum age for a "creating" record to be
18+
// considered stale by GC. This avoids racing with legitimate long-running
19+
// Create operations.
20+
const creatingStateGCGrace = 24 * time.Hour
21+
1622
type chSnapshot struct {
1723
blobIDs map[string]struct{} // union of all VMs' ImageBlobIDs
1824
vmIDs map[string]struct{} // all VM IDs in the DB
19-
staleCreate []string // IDs in "creating" state (crash remnants)
25+
staleCreate []string // IDs in stale "creating" state (crash remnants)
2026
}
2127

2228
func (s chSnapshot) UsedBlobIDs() map[string]struct{} { return s.blobIDs }
@@ -28,6 +34,7 @@ func (ch *CloudHypervisor) GCModule() gc.Module[chSnapshot] {
2834
Locker: ch.locker,
2935
ReadDB: func(_ context.Context) (chSnapshot, error) {
3036
var snap chSnapshot
37+
cutoff := time.Now().Add(-creatingStateGCGrace)
3138
if err := ch.store.Read(func(idx *hypervisor.VMIndex) error {
3239
snap.blobIDs = make(map[string]struct{})
3340
snap.vmIDs = make(map[string]struct{})
@@ -39,7 +46,7 @@ func (ch *CloudHypervisor) GCModule() gc.Module[chSnapshot] {
3946
for hex := range rec.ImageBlobIDs {
4047
snap.blobIDs[hex] = struct{}{}
4148
}
42-
if rec.State == types.VMStateCreating {
49+
if rec.State == types.VMStateCreating && rec.UpdatedAt.Before(cutoff) {
4350
snap.staleCreate = append(snap.staleCreate, id)
4451
}
4552
}
@@ -50,10 +57,19 @@ func (ch *CloudHypervisor) GCModule() gc.Module[chSnapshot] {
5057
return snap, nil
5158
},
5259
Resolve: func(snap chSnapshot, _ map[string]any) []string {
53-
// Orphan directories not in the DB.
54-
orphans := utils.FilterUnreferenced(utils.ScanSubdirs(ch.conf.CHRunDir()), snap.vmIDs)
55-
// Stale "creating" records from interrupted Create calls.
56-
return append(orphans, snap.staleCreate...)
60+
runOrphans := utils.FilterUnreferenced(utils.ScanSubdirs(ch.conf.CHRunDir()), snap.vmIDs)
61+
logOrphans := utils.FilterUnreferenced(utils.ScanSubdirs(ch.conf.CHLogDir()), snap.vmIDs)
62+
candidates := append(append(runOrphans, logOrphans...), snap.staleCreate...)
63+
seen := make(map[string]struct{}, len(candidates))
64+
var result []string
65+
for _, id := range candidates {
66+
if _, ok := seen[id]; ok {
67+
continue
68+
}
69+
seen[id] = struct{}{}
70+
result = append(result, id)
71+
}
72+
return result
5773
},
5874
Collect: func(ctx context.Context, ids []string) error {
5975
var errs []error
@@ -63,23 +79,37 @@ func (ch *CloudHypervisor) GCModule() gc.Module[chSnapshot] {
6379
errs = append(errs, err)
6480
}
6581
}
66-
// Clean up stale "creating" DB records.
67-
if err := ch.cleanStalePlaceholders(ctx); err != nil {
82+
// Clean up stale "creating" DB records from this GC snapshot.
83+
if err := ch.cleanStalePlaceholders(ctx, ids); err != nil {
6884
errs = append(errs, err)
6985
}
7086
return errors.Join(errs...)
7187
},
7288
}
7389
}
7490

75-
// cleanStalePlaceholders removes DB records stuck in "creating" state.
76-
func (ch *CloudHypervisor) cleanStalePlaceholders(_ context.Context) error {
91+
// cleanStalePlaceholders removes selected DB records stuck in stale "creating"
92+
// state. IDs not found (or no longer stale) are skipped.
93+
func (ch *CloudHypervisor) cleanStalePlaceholders(_ context.Context, ids []string) error {
94+
if len(ids) == 0 {
95+
return nil
96+
}
97+
targets := make(map[string]struct{}, len(ids))
98+
for _, id := range ids {
99+
targets[id] = struct{}{}
100+
}
101+
cutoff := time.Now().Add(-creatingStateGCGrace)
77102
return ch.store.Write(func(idx *hypervisor.VMIndex) error {
78-
for id, rec := range idx.VMs {
79-
if rec != nil && rec.State == types.VMStateCreating {
80-
delete(idx.Names, rec.Config.Name)
81-
delete(idx.VMs, id)
103+
for id := range targets {
104+
rec := idx.VMs[id]
105+
if rec == nil {
106+
continue
107+
}
108+
if rec.State != types.VMStateCreating || rec.UpdatedAt.After(cutoff) {
109+
continue
82110
}
111+
delete(idx.Names, rec.Config.Name)
112+
delete(idx.VMs, id)
83113
}
84114
return nil
85115
})

hypervisor/cloudhypervisor/start.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ func (ch *CloudHypervisor) startOne(ctx context.Context, id string) error {
3939
pid, _ := utils.ReadPIDFile(ch.conf.CHVMPIDFile(id))
4040
if ch.verifyVMProcess(pid, id) {
4141
if rec.State != types.VMStateRunning {
42-
_ = ch.updateState(ctx, id, types.VMStateRunning)
42+
if stateErr := ch.updateState(ctx, id, types.VMStateRunning); stateErr != nil {
43+
return fmt.Errorf("reconcile running state: %w", stateErr)
44+
}
4345
}
4446
return nil
4547
}

images/cloudimg/cloudimg.go

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,35 +57,29 @@ func (c *CloudImg) Pull(ctx context.Context, url string, tracker progress.Tracke
5757
}
5858

5959
// Inspect returns the record for a single image. Returns (nil, nil) if not found.
60-
func (c *CloudImg) Inspect(ctx context.Context, id string) (result *types.Image, err error) {
61-
err = c.store.With(ctx, func(idx *imageIndex) error {
62-
refs := idx.LookupRefs(id)
63-
if len(refs) == 0 {
64-
return nil
65-
}
66-
result = images.EntryToImage(idx.Images[refs[0]], typ, c.imageSizer)
67-
return nil
68-
})
69-
return
60+
func (c *CloudImg) Inspect(ctx context.Context, id string) (*types.Image, error) {
61+
return images.InspectEntry(ctx, c.store, id, typ,
62+
func(idx *imageIndex, q string) []string { return idx.LookupRefs(q) },
63+
func(idx *imageIndex) map[string]*imageEntry { return idx.Images },
64+
c.imageSizer,
65+
)
7066
}
7167

7268
// List returns all locally stored cloud images.
73-
func (c *CloudImg) List(ctx context.Context) (result []*types.Image, err error) {
74-
err = c.store.With(ctx, func(idx *imageIndex) error {
75-
result = images.ListImages(idx.Images, typ, c.imageSizer)
76-
return nil
77-
})
78-
return
69+
func (c *CloudImg) List(ctx context.Context) ([]*types.Image, error) {
70+
return images.ListEntries(ctx, c.store, typ,
71+
func(idx *imageIndex) map[string]*imageEntry { return idx.Images },
72+
c.imageSizer,
73+
)
7974
}
8075

8176
// Delete removes images from the index.
8277
// Returns the list of actually deleted refs.
8378
func (c *CloudImg) Delete(ctx context.Context, ids []string) ([]string, error) {
84-
var deleted []string
85-
return deleted, c.store.Update(ctx, func(idx *imageIndex) error {
86-
deleted = images.DeleteByID(ctx, "cloudimg.Delete", idx.Images, idx.LookupRefs, ids)
87-
return nil
88-
})
79+
return images.DeleteEntries(ctx, c.store, "cloudimg.Delete", ids,
80+
func(idx *imageIndex) map[string]*imageEntry { return idx.Images },
81+
func(idx *imageIndex, q string) []string { return idx.LookupRefs(q) },
82+
)
8983
}
9084

9185
// Config generates StorageConfig and BootConfig entries for the given VMs.

images/cloudimg/gc.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,7 @@ func (c *CloudImg) GCModule() gc.Module[cloudimgSnapshot] {
3434
},
3535
Resolve: func(snap cloudimgSnapshot, others map[string]any) []string {
3636
used := gc.CollectUsedBlobIDs(others)
37-
38-
// Merge index refs + VM-pinned blobs into one protection set.
39-
allRefs := make(map[string]struct{}, len(snap.refs)+len(used))
40-
for k := range snap.refs {
41-
allRefs[k] = struct{}{}
42-
}
43-
for k := range used {
44-
allRefs[k] = struct{}{}
45-
}
46-
37+
allRefs := utils.MergeSets(snap.refs, used)
4738
return utils.FilterUnreferenced(snap.blobs, allRefs)
4839
},
4940
Collect: func(ctx context.Context, ids []string) error {

images/oci/gc.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,7 @@ func (o *OCI) GCModule() gc.Module[ociSnapshot] {
3636
},
3737
Resolve: func(snap ociSnapshot, others map[string]any) []string {
3838
used := gc.CollectUsedBlobIDs(others)
39-
40-
// Merge index refs + VM-pinned blobs into one protection set.
41-
allRefs := make(map[string]struct{}, len(snap.refs)+len(used))
42-
for k := range snap.refs {
43-
allRefs[k] = struct{}{}
44-
}
45-
for k := range used {
46-
allRefs[k] = struct{}{}
47-
}
39+
allRefs := utils.MergeSets(snap.refs, used)
4840

4941
candidates := append(
5042
utils.FilterUnreferenced(snap.blobs, allRefs),

0 commit comments

Comments
 (0)