diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index dac95429..89c606c4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -27,7 +27,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v9.3.0 with: - version: v2.13.2 + version: v2.14.0 - name: Check formatting (gofumpt + goimports) run: golangci-lint fmt --diff diff --git a/Makefile b/Makefile index 4b0609e7..8b4a4bd8 100644 --- a/Makefile +++ b/Makefile @@ -21,15 +21,15 @@ $(LOCALBIN): mkdir -p $(LOCALBIN) ## Tool versions -GOLANGCILINT_VERSION ?= v2.13.2 +GOLANGCILINT_VERSION ?= v2.14.0 GOLANGCILINT_ROOT := $(LOCALBIN)/golangci-lint-$(GOLANGCILINT_VERSION) GOLANGCILINT := $(GOLANGCILINT_ROOT)/golangci-lint -GOFUMPT_VERSION ?= v0.11.0 +GOFUMPT_VERSION ?= v0.12.0 GOFUMPT_ROOT := $(LOCALBIN)/gofumpt-$(GOFUMPT_VERSION) GOFMT := $(GOFUMPT_ROOT)/gofumpt -GOIMPORTS_VERSION ?= v0.49.0 +GOIMPORTS_VERSION ?= v0.51.0 GOIMPORTS_ROOT := $(LOCALBIN)/goimports-$(GOIMPORTS_VERSION) GOIMPORTS := $(GOIMPORTS_ROOT)/goimports diff --git a/README.md b/README.md index 5e25b336..5e758b98 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Lightweight MicroVM engine with dual hypervisor backends: [Cloud Hypervisor](htt - **Docker-like CLI** — `create`, `run`, `start`, `stop`, `list`, `inspect`, `console`, `exec`, `rm`, `clone`, `status` - **Snapshot & clone** — capture a running VM (memory, disks, config) and clone it into new VMs with fresh network identity; clones restore memory copy-on-write by default (page cache shared across sibling clones) and hold shared source leases so parallel clone bursts survive concurrent source removal; restore and atomic hibernate in place; portable export/import between hosts - **CNI networking** — multi-queue virtio-net TAPs wired via TC redirect (no bridge in the data path), multi-NIC, bridge mode, NIC hot-resize -- **Data disks & runtime attach** — extra virtio-blk disks at create/clone time; hot-plug vhost-user-fs shares and VFIO PCI devices (CH); external raw disks and NIC resize on CH or Firecracker `--pci` VMs +- **Data disks & runtime attach** — extra virtio-blk disks at create/clone time; hot-plug vhost-user-fs shares and VFIO PCI devices (CH); external raw disks and NIC resize on CH or Firecracker `--pci` VMs; a private copy of a snapshot's COW disk on CH - **Windows guests** — UEFI + Hyper-V enlightenments via the cocoonstack CH/firmware forks - **Firecracker backend** — `--fc` for ~125ms boots and <5 MiB per-VM overhead (OCI images only) - **Daemon-optional architecture** — one hypervisor process per VM, every command standalone; `cocoon daemon` optionally adopts running VMs to converge crashes as they happen; modular lock-safe GC with snapshot LRU eviction @@ -19,12 +19,12 @@ Lightweight MicroVM engine with dual hypervisor backends: [Cloud Hypervisor](htt ## Quick Start ```bash -# Install the binary (Linux amd64; arm64: cocoon_0.7.1_Linux_arm64.tar.gz) -curl -fsSL -o cocoon.tar.gz https://github.com/cocoonstack/cocoon/releases/download/v0.7.1/cocoon_0.7.1_Linux_x86_64.tar.gz +# Install the binary (Linux amd64; arm64: cocoon_0.7.2_Linux_arm64.tar.gz) +curl -fsSL -o cocoon.tar.gz https://github.com/cocoonstack/cocoon/releases/download/v0.7.2/cocoon_0.7.2_Linux_x86_64.tar.gz tar -xzf cocoon.tar.gz && install -m 0755 cocoon /usr/local/bin/ # One-time environment setup (installs CH, firmware, CNI plugins) -curl -fsSL -o cocoon-check https://raw.githubusercontent.com/cocoonstack/cocoon/refs/tags/v0.7.1/doctor/check.sh +curl -fsSL -o cocoon-check https://raw.githubusercontent.com/cocoonstack/cocoon/refs/tags/v0.7.2/doctor/check.sh install -m 0755 cocoon-check /usr/local/bin/ && sudo cocoon-check --upgrade # Pull an image and run a VM diff --git a/cmd/core/vmconfig_test.go b/cmd/core/vmconfig_test.go index 0abb7f3b..3e6731a1 100644 --- a/cmd/core/vmconfig_test.go +++ b/cmd/core/vmconfig_test.go @@ -115,7 +115,6 @@ func TestCloneVMConfigKnobFlagsOverrideSnapshot(t *testing.T) { t.Run(tt.name, func(t *testing.T) { cmd := &cobra.Command{} cmd.Flags().String("name", "c", "") - cmd.Flags().Int("nics", 0, "") cmd.Flags().Int("queue-size", 0, "") cmd.Flags().Int("disk-queue-size", 0, "") cmd.Flags().Int("cpu-weight", 0, "") diff --git a/cmd/others/handler_test.go b/cmd/others/handler_test.go index 0f29507b..57a7d57e 100644 --- a/cmd/others/handler_test.go +++ b/cmd/others/handler_test.go @@ -8,7 +8,7 @@ import ( ) func TestParseSnapshotPolicy_Defaults(t *testing.T) { - cmd := buildGCCmd() + cmd := gcCommand() if err := cmd.ParseFlags(nil); err != nil { t.Fatal(err) } @@ -33,7 +33,7 @@ func TestParseSnapshotPolicy_NegativeRejected(t *testing.T) { } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - cmd := buildGCCmd() + cmd := gcCommand() if err := cmd.ParseFlags(tt.args); err != nil { t.Fatal(err) } @@ -57,7 +57,7 @@ func TestParseSnapshotPolicy_SubFlagRequiresSnapshot(t *testing.T) { } for _, args := range cases { t.Run(args[0], func(t *testing.T) { - cmd := buildGCCmd() + cmd := gcCommand() if err := cmd.ParseFlags(args); err != nil { t.Fatal(err) } @@ -70,7 +70,7 @@ func TestParseSnapshotPolicy_SubFlagRequiresSnapshot(t *testing.T) { } func TestParseSnapshotPolicy_HappyPath(t *testing.T) { - cmd := buildGCCmd() + cmd := gcCommand() if err := cmd.ParseFlags([]string{ "--snapshot", "--snapshot-keep=10", @@ -89,12 +89,6 @@ func TestParseSnapshotPolicy_HappyPath(t *testing.T) { } } -func buildGCCmd() *cobra.Command { - cmd := &cobra.Command{Use: "gc"} - cmd.Flags().Bool("snapshot", false, "") - cmd.Flags().Int("snapshot-keep", 0, "") - cmd.Flags().Duration("snapshot-age", 0, "") - cmd.Flags().String("snapshot-size", "", "") - cmd.Flags().Bool("snapshot-dry-run", false, "") - return cmd +func gcCommand() *cobra.Command { + return Commands(Handler{})[0] } diff --git a/cmd/storebench/main.go b/cmd/storebench/main.go index 6162a913..dba76a9e 100644 --- a/cmd/storebench/main.go +++ b/cmd/storebench/main.go @@ -9,6 +9,7 @@ import ( "os/exec" "path/filepath" "runtime/pprof" + "slices" "strconv" "time" @@ -182,10 +183,9 @@ func runMicro(ctx context.Context, engine, op string, n, ops int, dir string) er for i := range n { seed = append(seed, fmt.Sprintf("S%023d", i)) } - for base := 0; base < n; base += 1000 { - hi := min(base+1000, n) + for chunk := range slices.Chunk(seed, 1000) { if err := store.Update(ctx, meta.Scope{Write: ns}, meta.CommitDurable, func(w meta.Writer) error { - for _, id := range seed[base:hi] { + for _, id := range chunk { if err := w.PutRaw(ctx, ns, hypervisor.TableRecords, id, benchPayload, false); err != nil { return err } diff --git a/daemon/reconcile_test.go b/daemon/reconcile_test.go index e8c736d4..a52c0f53 100644 --- a/daemon/reconcile_test.go +++ b/daemon/reconcile_test.go @@ -409,8 +409,7 @@ func newFake() *fakeSupervisor { func newTestDaemon(t *testing.T, f *fakeSupervisor) *Daemon { t.Helper() - d := New(Config{RootDir: t.TempDir()}, nil, []Supervisor{f}) - return d + return New(Config{RootDir: t.TempDir()}, nil, []Supervisor{f}) } func runningRec(id string, gen uint64) *hypervisor.VMRecord { diff --git a/docs/cli.md b/docs/cli.md index 9a0f6911..a09a048b 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -36,8 +36,8 @@ cocoon │ │ ├── attach [flags] VM Attach a VFIO PCI device (CH only) │ │ └── detach [flags] VM Detach a VFIO PCI device by --id │ ├── disk -│ │ ├── attach [flags] VM Hot-attach an existing raw disk file (CH, or FC with --pci) -│ │ └── detach [flags] VM Detach a hot-attached disk by --name (keeps the file) +│ │ ├── attach [flags] VM Hot-attach a raw disk file (CH, or FC with --pci) or a snapshot's COW disk copy (CH) +│ │ └── detach [flags] VM Detach a hot-attached disk by --name (keeps a --path file, removes a snapshot copy) │ ├── net [flags] VM Resize NIC count on a running VM (CH, or FC with --pci) │ └── debug [flags] IMAGE Generate hypervisor launch command (dry run) ├── snapshot @@ -294,8 +294,9 @@ Applies to `cocoon vm debug`: `vm debug` prints the VMM launch command only. It does not prepare a netns or a TAP, so it emits no `--net` and no `ip=`, and `--nics`, `--queue-size`, -`--network` and `--bridge` are ignored — setting any of them warns on stderr, -like `--data-disk`. `--max-cpu`, `--balloon`, `--cow` and `--ch` shape the Cloud +`--network`, `--bridge` and the cgroup CPU knobs (`--cpu-weight`, +`--cpu-quota-us`, `--cpu-period-us`, `--cpu-burst-us`, `--cpuset-cpus`) are +ignored — setting any of them warns on stderr, like `--data-disk`. `--max-cpu`, `--balloon`, `--cow` and `--ch` shape the Cloud Hypervisor command only; `vm debug --fc` prints the Firecracker REST sequence and ignores all four. Queue placement happens at launch against the store, so the printed command carries no `queue_affinity` whatever `--cpuset-cpus` says. diff --git a/docs/devices.md b/docs/devices.md index b56fd76d..2b627f11 100644 --- a/docs/devices.md +++ b/docs/devices.md @@ -1,6 +1,6 @@ # Runtime Device Attach -Hot-plug vhost-user-fs shares, VFIO PCI devices, and external raw disks on a running VM (Cloud Hypervisor; raw disks also on Firecracker VMs created with `--pci`). +Hot-plug vhost-user-fs shares, VFIO PCI devices, external raw disks, and copies of a snapshot's COW disk on a running VM (Cloud Hypervisor; raw disks also on Firecracker VMs created with `--pci`). ## Overview @@ -8,7 +8,7 @@ Cocoon can hot-plug three classes of external resources onto a running VM: - **Vhost-user-fs** — a file share served by an external `virtiofsd` (or compatible backend) over a Unix socket. Attach surfaces a virtio-fs device in the guest, accessible via `mount -t virtiofs /mnt/...`. - **VFIO PCI passthrough** — a host PCI device bound to `vfio-pci` (GPU, NIC, NVMe). Attach hands the device to the guest with IOMMU isolation. -- **Data disks** — an existing raw disk file, surfaced as `/dev/disk/by-id/virtio-`. Cocoon never creates or deletes the backing file: it can outlive any VM and be re-attached elsewhere (a persistent volume). +- **Data disks** — an existing raw disk file, surfaced as `/dev/disk/by-id/virtio-`. Cocoon never creates or deletes the backing file: it can outlive any VM and be re-attached elsewhere (a persistent volume). On Cloud Hypervisor the disk can instead be a private read-write copy of a snapshot's COW disk (`--from-snapshot` / `--from-dir`), which cocoon creates in the VM's run dir and removes on detach, stop, or `vm rm`. All three attaches are **runtime-only**: the device lives only for the current VM process and is gone after stop/restart — re-attach after the next start. cocoon refuses `snapshot save` and `vm hibernate` while any of the three is attached — umount an external disk in-guest, detach, capture, then re-attach after the wake/restore; on both backends the refusal arrives before the guest is paused. Cloud Hypervisor does not enforce this itself: it captures a snapshot that names the vhost-user socket, and restoring that snapshot either fails once the backend is gone or hangs against a fresh one. External volumes are host state cocoon does not own: it never creates, copies, or deletes the backing file, and snapshot, restore, and clone carry no trace of them. All mutating verbs on one VM (attach/detach, net resize, snapshot, hibernate, restore, stop) serialize on a per-VM lock, so concurrent invocations cannot interleave with a capture window. While the VM runs, `vm inspect` reports the live attach set under `.attached_devices` (`fs`, `devices`, `disks`). diff --git a/docs/index.md b/docs/index.md index 9283a934..b6456ec7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -54,7 +54,7 @@ cocoon CLI ──► images: OCI (EROFS layers, direct boot) | cloudimg (qcow2, - **TC redirect I/O path** — veth ↔ TAP wired via ingress qdisc + mirred redirect (no bridge in the data path) - **DNS configuration** — custom DNS servers injected into VMs via kernel cmdline (OCI) or cloud-init network-config (cloudimg) - **Cloud-init metadata** — automatic NoCloud cidata FAT12 disk for cloudimg VMs (hostname, configurable user/password via `--user`/`--password`, multi-NIC Netplan v2 network-config); cidata is automatically skipped on subsequent boots -- **User data disks** — `--data-disk` attaches additional virtio-blk disks per VM, with optional ext4 mkfs at create time, cloud-init `mounts:` auto-mount on cloudimg+CH (via `/dev/disk/by-id/virtio-`), per-disk DirectIO override, and 1:1 inheritance through snapshot/clone/restore; `vm clone --data-disk` adds fresh disks to a clone and `vm disk attach/detach` hot-plugs existing raw files on a running VM (CH, or Firecracker with `--pci`) +- **User data disks** — `--data-disk` attaches additional virtio-blk disks per VM, with optional ext4 mkfs at create time, cloud-init `mounts:` auto-mount on cloudimg+CH (via `/dev/disk/by-id/virtio-`), per-disk DirectIO override, and 1:1 inheritance through snapshot/clone/restore; `vm clone --data-disk` adds fresh disks to a clone and `vm disk attach/detach` hot-plugs existing raw files on a running VM (CH, or Firecracker with `--pci`) or a private copy of a snapshot's COW disk (CH) - **Copy-on-write clone restore** — Cloud Hypervisor clones of plain private-anon snapshots default to `mmap` memory restore: no eager copy, page cache shared across sibling clones; hugepages/shared snapshots fall back to eager copy with a warning - **Hugepages** — opt-in via `vm create --hugepages` (Cloud Hypervisor only); backs VM memory with hugetlbfs at the cost of the mmap restore fast path for that VM's snapshots (never supported on Firecracker, whose snapshots cannot restore from hugetlbfs) - **Mergeable memory** — opt-in `--mergeable` (Cloud Hypervisor only) madvises guest memory `MADV_MERGEABLE` so host KSM can dedup identical pages across VMs; excludes `--hugepages`/`--shared-memory` diff --git a/docs/install.md b/docs/install.md index 4932b05e..5baa77da 100644 --- a/docs/install.md +++ b/docs/install.md @@ -24,10 +24,10 @@ Download pre-built binaries from [GitHub Releases](https://github.com/cocoonstac ```bash # Linux amd64 -curl -fsSL -o cocoon.tar.gz https://github.com/cocoonstack/cocoon/releases/download/v0.7.1/cocoon_0.7.1_Linux_x86_64.tar.gz +curl -fsSL -o cocoon.tar.gz https://github.com/cocoonstack/cocoon/releases/download/v0.7.2/cocoon_0.7.2_Linux_x86_64.tar.gz # Linux arm64 -curl -fsSL -o cocoon.tar.gz https://github.com/cocoonstack/cocoon/releases/download/v0.7.1/cocoon_0.7.1_Linux_arm64.tar.gz +curl -fsSL -o cocoon.tar.gz https://github.com/cocoonstack/cocoon/releases/download/v0.7.2/cocoon_0.7.2_Linux_arm64.tar.gz tar -xzf cocoon.tar.gz install -m 0755 cocoon /usr/local/bin/ @@ -54,7 +54,7 @@ Cocoon ships a diagnostic script that checks your environment and can auto-insta ```bash # Get script -curl -fsSL -o cocoon-check https://raw.githubusercontent.com/cocoonstack/cocoon/refs/tags/v0.7.1/doctor/check.sh +curl -fsSL -o cocoon-check https://raw.githubusercontent.com/cocoonstack/cocoon/refs/tags/v0.7.2/doctor/check.sh install -m 0755 cocoon-check /usr/local/bin/ # Check only — reports PASS/FAIL for each requirement diff --git a/docs/known-issues.md b/docs/known-issues.md index 1e2e789a..cdcfd836 100644 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -246,9 +246,9 @@ On CNI plugins with strict per-veth MAC enforcement (Cilium eBPF, Calico eBPF), `cocoon vm fs attach` only works on CH VMs that were created with `--shared-memory`. CH's `memory shared=on` is fixed at VM creation: backend processes (e.g. virtiofsd) need to mmap guest memory via the negotiated memfd, and the memory model cannot be flipped on a running VM. If `--shared-memory` was omitted at create time, the only path is to recreate the VM. Cocoon's preflight reads `vm.info` and surfaces a clear error rather than letting CH return a vague rejection. -## Snapshotting a VM with attached vhost-user-fs / VFIO is refused +## Snapshotting a VM with attached vhost-user-fs / VFIO / hot-attached disks is refused -Cloud Hypervisor captures a snapshot of a VM that holds a vhost-user-fs share or a VFIO PCI passthrough device, but the result cannot be restored: it names a backend socket that is gone (restore fails after 60s) or hangs against a fresh one. Cocoon refuses the capture itself — the pre-flight reads the live `vm.info` before pausing the guest and fails with `hot-attached vhost-user-fs "": detach before snapshot or hibernate` (or `hot-attached device "": ...`) before anything is captured or recorded. `cocoon vm fs detach` / `cocoon vm device detach` first to clear runtime devices, then snapshot. +Cloud Hypervisor captures a snapshot of a VM that holds a vhost-user-fs share or a VFIO PCI passthrough device, but the result cannot be restored: it names a backend socket that is gone (restore fails after 60s) or hangs against a fresh one. Cocoon refuses the capture itself — the pre-flight reads the live `vm.info` before pausing the guest and fails with `hot-attached vhost-user-fs "": detach before snapshot or hibernate` (or `hot-attached device "": ...`, `hot-attached disk "": ...`) before anything is captured or recorded. `cocoon vm fs detach` / `cocoon vm device detach` / `cocoon vm disk detach` first to clear runtime devices, then snapshot. ## Snapshot lease files are never reclaimed (fixed in v0.6.8) @@ -262,7 +262,7 @@ On v0.6.7 and older every `snapshot save` leaves that zero-byte file behind and ## Runtime attached devices do not survive VM stop / clone / restore -Attaches via `cocoon vm fs attach` and `cocoon vm device attach` are runtime-only — they live in the CH process state and are never written into the VM record, sidecar, or snapshot. After `vm stop`, `vm clone`, or `vm restore`, the user must re-run the attach commands. `vm inspect` reflects the live CH `vm.info` for running VMs and omits `attached_devices` for stopped VMs. This is by design: cocoon does not own the backend (virtiofsd / vfio-pci binding) and cannot guarantee the resource still exists across host events. +Attaches via `cocoon vm fs attach`, `cocoon vm device attach` and `cocoon vm disk attach` are runtime-only — they live in the VMM process state and are never written into the VM record, sidecar, or snapshot. After `vm stop`, `vm clone`, or `vm restore`, the user must re-run the attach commands. `vm inspect` reflects the live VMM state for running VMs and omits `attached_devices` for stopped VMs. This is by design: cocoon does not own the backend (virtiofsd, the vfio-pci binding, an external disk file) and cannot guarantee the resource still exists across host events. ## virtiofsd is a single-shot daemon diff --git a/docs/snapshots.md b/docs/snapshots.md index 80b8e150..4bdc159c 100644 --- a/docs/snapshots.md +++ b/docs/snapshots.md @@ -112,7 +112,7 @@ cocoon vm clone imported-snap cocoon snapshot export my-snap -o - | ssh host2 cocoon snapshot import --name my-snap ``` -The archive contains the snapshot config, VM config, COW disk, memory ranges, and device state — sparse files carry cocoon's own pax records so their holes survive the round-trip — everything needed to reconstruct the snapshot on a different machine. The envelope also lists the files the archive carries, and `snapshot import` refuses an archive that ends before every listed file has landed — a plain tar cut at an entry boundary otherwise reads as complete. `snapshot export --to-dir` writes the same files as a directory that `vm clone --from-dir` and `vm restore --from-dir` consume without a tar round-trip. Do not repack an export with a third-party tar: dropping cocoon's sparse records can rebuild a short, shifted disk. `--from-dir` and `snapshot import` reject a `cow.raw` whose size differs from the envelope's recorded storage size. +The archive contains the snapshot config, VM config, COW disk, memory ranges, and device state — sparse files carry cocoon's own pax records so their holes survive the round-trip — everything needed to reconstruct the snapshot on a different machine. The envelope also lists the files the archive carries, and `snapshot import` refuses an archive that ends before every listed file has landed — a plain tar cut at an entry boundary otherwise reads as complete. `snapshot export --to-dir` writes the same files as a directory that `vm clone --from-dir`, `vm restore --from-dir` and `vm disk attach --from-dir` (a copy of the COW disk on a running VM of the same image; see [Devices](devices.md)) consume without a tar round-trip. Do not repack an export with a third-party tar: dropping cocoon's sparse records can rebuild a short, shifted disk. `--from-dir` and `snapshot import` reject a `cow.raw` whose size differs from the envelope's recorded storage size. #### Cross-Node Clone diff --git a/go.mod b/go.mod index 325482dc..6313e451 100644 --- a/go.mod +++ b/go.mod @@ -12,61 +12,59 @@ require ( github.com/gofrs/flock v0.13.1 github.com/google/go-containerregistry v0.22.1 github.com/google/uuid v1.6.0 - github.com/klauspost/compress v1.20.0 + github.com/klauspost/compress v1.20.3 github.com/moby/term v0.5.2 github.com/opencontainers/go-digest v1.0.0 - github.com/projecteru2/core v0.1.5 + github.com/projecteru2/core v0.1.7 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 github.com/vishvananda/netlink v1.3.1 github.com/vishvananda/netns v0.0.5 - golang.org/x/sync v0.23.0 - golang.org/x/sys v0.48.0 - modernc.org/sqlite v1.59.0 + golang.org/x/sync v0.24.0 + golang.org/x/sys v0.49.0 + modernc.org/sqlite v1.60.1 ) require ( - github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/Azure/go-ansiterm v0.0.0-20261013165250-51075c6ea6bc // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/errors v1.14.0 // indirect github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect github.com/cockroachdb/redact v1.1.8 // indirect - github.com/docker/cli v29.8.0+incompatible // indirect - github.com/docker/docker-credential-helpers v0.9.8 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect - github.com/getsentry/sentry-go v0.49.0 // indirect + github.com/docker/cli v29.9.1+incompatible // indirect + github.com/docker/docker-credential-helpers v0.9.10 // indirect + github.com/dustin/go-humanize v1.1.0 // indirect + github.com/getsentry/sentry-go v0.50.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/mattn/go-colorable v0.1.15 // indirect + github.com/mattn/go-colorable v0.1.16 // indirect github.com/mattn/go-isatty v0.0.24 // indirect - github.com/ncruces/go-strftime v1.0.0 // indirect + github.com/ncruces/go-strftime v1.1.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rogpeppe/go-internal v1.16.0 // indirect github.com/rs/zerolog v1.35.1 // indirect - github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/sirupsen/logrus v1.10.1 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect + github.com/sirupsen/logrus v1.10.2 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect - go.opentelemetry.io/otel v1.45.0 // indirect - go.opentelemetry.io/otel/trace v1.45.0 // indirect + go.opentelemetry.io/otel v1.47.0 // indirect + go.opentelemetry.io/otel/trace v1.47.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/mod v0.40.0 // indirect - golang.org/x/text v0.41.0 // indirect + golang.org/x/text v0.43.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 // indirect - google.golang.org/grpc v1.83.2 // indirect + google.golang.org/grpc v1.84.0 // indirect google.golang.org/protobuf v1.36.12 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gotest.tools/v3 v3.5.2 // indirect - modernc.org/libc v1.75.7 // indirect + modernc.org/libc v1.79.0 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.12.1 // indirect ) diff --git a/go.sum b/go.sum index 41e85f94..ce4e163e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20261013165250-51075c6ea6bc h1:58s5YzGPNAP7+e1uemaL0ax/NPSeJOHdRpRIowd+JDk= +github.com/Azure/go-ansiterm v0.0.0-20261013165250-51075c6ea6bc/go.mod h1:DMNF6Kxv+EcuUvVuNkvC3f+BjEEmc/Fn9aU+qiD8zpU= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -20,20 +20,20 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/docker/cli v29.8.0+incompatible h1:ih0c2jq/nN7QfES8zIfwSzIhRScjs4ehER+kZ60aeSk= -github.com/docker/cli v29.8.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker-credential-helpers v0.9.8 h1:bIREROb7So6PRlq6KTtdS9MPEjC29OQRkFNlvK2OX8Q= -github.com/docker/docker-credential-helpers v0.9.8/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/cli v29.9.1+incompatible h1:E4YGy7TMkPk7mFsXTVUyjHxVeJdjTDfIS0X4Q0+ioQ0= +github.com/docker/cli v29.9.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker-credential-helpers v0.9.10 h1:LCWwv7UmS7fQVmdKDWeMuureOeY5kkbC5kZfkzxkS6k= +github.com/docker/docker-credential-helpers v0.9.10/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dustin/go-humanize v1.1.0 h1:dbKTrvD0klcbBV/h4AWJdMuZogJACoMlvWIWZ5b2xWg= +github.com/dustin/go-humanize v1.1.0/go.mod h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= -github.com/getsentry/sentry-go v0.49.0 h1:Ehejknu1l023Ub7QoRBVLAI7g3Jnhqku4oWx4B4Sh5s= -github.com/getsentry/sentry-go v0.49.0/go.mod h1:nuMJAoCfe1u0Bts2ocyNI+TW8HT84vRMqwA5Qq/SKUI= +github.com/getsentry/sentry-go v0.50.0 h1:gkO59rn/i0RCAXUUTflC5QVbTQGPXbaFabs2olVzc9g= +github.com/getsentry/sentry-go v0.50.0/go.mod h1:+xsDuN+SoRN/xfs1hdq78wnrE3omIZf+FCuh9FOW1C8= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= @@ -62,20 +62,20 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA= -github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= +github.com/klauspost/compress v1.20.3 h1:zWNoFqq/lSuYQRim3dUXlXeA0IqwuZGicdG5ZTfY5j4= +github.com/klauspost/compress v1.20.3/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= -github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-colorable v0.1.16 h1:+2YjeAe7xP1OQbZW1jERPWWCDLlNqFNCvjiqGiAt84A= +github.com/mattn/go-colorable v0.1.16/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= -github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= -github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/ncruces/go-strftime v1.1.0 h1:vtPmPWMkb539RGV9WpWT9EwJGvrOCKvMNI4obWL47Hk= +github.com/ncruces/go-strftime v1.1.0/go.mod h1:yVaq7iwCN09vHRrKBrrflXT0M9tTMG2KmZ9Fj7xeG/g= github.com/onsi/ginkgo/v2 v2.25.1 h1:Fwp6crTREKM+oA6Cz4MsO8RhKQzs2/gOIVOUscMAfZY= github.com/onsi/ginkgo/v2 v2.25.1/go.mod h1:ppTWQ1dh9KM/F1XgpeRqelR+zHVwV81DGRSDnFxK7Sk= github.com/onsi/gomega v1.38.1 h1:FaLA8GlcpXDwsb7m0h2A9ew2aTk3vnZMlzFgg5tz/pk= @@ -91,8 +91,8 @@ github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTw github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/projecteru2/core v0.1.5 h1:cS3xfqa6I1bMXtGdbu2A2UzhjiHaP2mogjQWFTkSEF0= -github.com/projecteru2/core v0.1.5/go.mod h1:MDMJmmlJ3ZVvFQl5BnRcKV1/AVCHyiWoI4BI+sCnuYM= +github.com/projecteru2/core v0.1.7 h1:f2EdndjWFBYX6pqLZ68VcFYMkXZu6RivotaKUGCNwDQ= +github.com/projecteru2/core v0.1.7/go.mod h1:iVfdX4r9/qD52il7DbV18BFEp8fgoUMBp/60WMqFxPc= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -101,12 +101,10 @@ github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52Z github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI= github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= -github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= -github.com/sirupsen/logrus v1.10.1 h1:xi4336Zh11WpU14fXR6I67V3yaTPQYwRx2WEtHbRg4Q= -github.com/sirupsen/logrus v1.10.1/go.mod h1:vsQHnG7xzNsxk3NrwboUiWPnIC3dmbjcGPykD7+tiHk= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= +github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= +github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= @@ -132,18 +130,18 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= -go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= -go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= -go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= -go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= -go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= +go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= +go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= +go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= +go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= -go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= @@ -152,8 +150,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs= -golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE= +golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg= +golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -163,34 +161,33 @@ golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= -golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= +golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA= +golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= -golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= +golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= -golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= +golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= -golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= +golang.org/x/tools v0.52.0 h1:ryxg4U63oaUYk7uOLTF37n43f4qgtGvqV4Ioit1BaEs= +golang.org/x/tools v0.52.0/go.mod h1:ItcP5UNaVGG+Yc4eIYmaFLNJ4soEopkx5Y2d4lcEurs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 h1:KmqdJU4vrNcxy/6qdg3JduZtalEXrJLspVltnR1cE+8= google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= -google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= -google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= +google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= +google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -198,20 +195,20 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8= -modernc.org/cc/v4 v4.29.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= -modernc.org/ccgo/v4 v4.35.0 h1:F+TUsmw09QxLzmi3aeYYGxjAXarmZaKgj3mKQHNaA8w= -modernc.org/ccgo/v4 v4.35.0/go.mod h1:qrVGs9S3Sr2Ztcg9ve+kTAYMp5a3YvWjo+SoN06kJ5I= +modernc.org/cc/v4 v4.30.3 h1:aDq8gIX1X7+VFgO/JYYji6pLWL2H4tv3g8WatT3KCys= +modernc.org/cc/v4 v4.30.3/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= +modernc.org/ccgo/v4 v4.37.0 h1:BAuJCUtIHIxFIYaFhqXIw5YaoMBlNCUlFovejHfMip4= +modernc.org/ccgo/v4 v4.37.0/go.mod h1:/8627VhwWtAbqUXmPORxFQU5SgLrAx2jl9seLw9GBhY= modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc= -modernc.org/gc/v3 v3.1.5/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/gc/v3 v3.2.0 h1:/Y3LGDMQaoYf55PebP1qsZ5FDGgAZyG1RYKEHeQzh1o= +modernc.org/gc/v3 v3.2.0/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= -modernc.org/libc v1.75.7 h1:o3DTP9/0p9pKmY2WCKQaySW6wIiZhNM7wc2lUoyhfew= -modernc.org/libc v1.75.7/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ= +modernc.org/libc v1.79.0 h1:9Dvvv++bAMSkSuBzuOFAatbTV5OIoq6Y6FohmD3e7q8= +modernc.org/libc v1.79.0/go.mod h1:uT3kR61NsbeKJSLqq83GJwSnZMVLlSjyAz4DfTaBy1M= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g= @@ -220,8 +217,8 @@ modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg= modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.59.0 h1:X1es1GpqBlS/5T+vbM4HLUdaa8OtQx468DF2vrx+38A= -modernc.org/sqlite v1.59.0/go.mod h1:+paeT2A3iPRHkQDwG7oA6Tk0zQd5woMEI8q7orfry8k= +modernc.org/sqlite v1.60.1 h1:/blz53O951KWFOso4QQvEs/Fq6cDBKLtMVrYNSeJVKw= +modernc.org/sqlite v1.60.1/go.mod h1:1dIoEagfDE72QytD5scH1lxARtaUgKgHC/NuApA27r0= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/hypervisor/cloudhypervisor/netresize_test.go b/hypervisor/cloudhypervisor/netresize_test.go index 30036d48..3bd90847 100644 --- a/hypervisor/cloudhypervisor/netresize_test.go +++ b/hypervisor/cloudhypervisor/netresize_test.go @@ -5,11 +5,8 @@ import ( "encoding/json/jsontext" "encoding/json/v2" "errors" - "net" "net/http" - "net/http/httptest" "slices" - "strings" "sync" "testing" "time" @@ -67,7 +64,7 @@ func TestConvergeOrphanedPause(t *testing.T) { defer mu.Unlock() _ = json.MarshalWrite(w, chVMInfoResponse{State: state}) }) - hc := newStubHTTPClient(t, mux) + hc := &http.Client{Transport: handlerTransport{mux}} info, err := getVMInfo(t.Context(), hc) if err != nil { @@ -92,18 +89,6 @@ func TestConvergeOrphanedPause(t *testing.T) { } } -func newStubHTTPClient(t *testing.T, mux *http.ServeMux) *http.Client { - t.Helper() - srv := httptest.NewServer(mux) - t.Cleanup(srv.Close) - addr := strings.TrimPrefix(srv.URL, "http://") - return &http.Client{Transport: &http.Transport{ - DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { - return net.Dial("tcp", addr) - }, - }} -} - func newCHStubClient(t *testing.T, nets []chNet, stickyIDs ...string) (*http.Client, func() []string) { t.Helper() var mu sync.Mutex @@ -139,7 +124,7 @@ func newCHStubClient(t *testing.T, nets []chNet, stickyIDs ...string) (*http.Cli } _ = json.MarshalWrite(w, chVMInfoResponse{Config: chVMInfoConfig{Nets: live}, DeviceTree: tree}) }) - hc := newStubHTTPClient(t, mux) + hc := &http.Client{Transport: handlerTransport{mux}} return hc, func() []string { mu.Lock() defer mu.Unlock() diff --git a/hypervisor/cloudhypervisor/snapshot_test.go b/hypervisor/cloudhypervisor/snapshot_test.go index 57fb5afc..55312aa2 100644 --- a/hypervisor/cloudhypervisor/snapshot_test.go +++ b/hypervisor/cloudhypervisor/snapshot_test.go @@ -41,7 +41,7 @@ func TestSnapshotPauseRefusesHotAttachedBeforePausing(t *testing.T) { mu.Unlock() w.WriteHeader(http.StatusNoContent) }) - hc := newStubHTTPClient(t, mux) + hc := &http.Client{Transport: handlerTransport{mux}} err := (&CloudHypervisor{}).snapshotSpec(t.Context()).Pause(&hypervisor.VMRecord{}, hc) diff --git a/hypervisor/firecracker/api.go b/hypervisor/firecracker/api.go index d5aadc0b..1525a2b1 100644 --- a/hypervisor/firecracker/api.go +++ b/hypervisor/firecracker/api.go @@ -92,7 +92,7 @@ type fcNetworkOverride struct { HostDevName string `json:"host_dev_name"` } -// pointer+omitempty keeps the field off the wire for FC < v1.16. +// a nil override keeps the field off the wire for FC < v1.16. type fcVsockOverride struct { UDSPath string `json:"uds_path"` } diff --git a/hypervisor/firecracker/extend.go b/hypervisor/firecracker/extend.go index f03078ea..e686f7c0 100644 --- a/hypervisor/firecracker/extend.go +++ b/hypervisor/firecracker/extend.go @@ -100,19 +100,19 @@ func (fc *Firecracker) DiskAttach(ctx context.Context, vmRef string, spec disk.S } func (fc *Firecracker) DiskDetach(ctx context.Context, vmRef, name string) error { - if name == "" { - return fmt.Errorf("name is required") + if err := disk.ValidateName(name); err != nil { + return err } - hc, _, _, unlock, err := fc.lockedDeviceOp(ctx, vmRef, disk.ErrUnsupportedBackend) + id, err := hotDiskID(name) if err != nil { return err } - defer unlock() - cfg, err := getVMConfig(ctx, hc) + hc, _, _, unlock, err := fc.lockedDeviceOp(ctx, vmRef, disk.ErrUnsupportedBackend) if err != nil { return err } - id, err := hotDiskID(name) + defer unlock() + cfg, err := getVMConfig(ctx, hc) if err != nil { return err } diff --git a/hypervisor/firecracker/extend_test.go b/hypervisor/firecracker/extend_test.go index a71b33ce..93c8f6f2 100644 --- a/hypervisor/firecracker/extend_test.go +++ b/hypervisor/firecracker/extend_test.go @@ -101,6 +101,14 @@ func TestHotDiskID(t *testing.T) { } } +func TestDiskDetachRejectsAnInvalidNameBeforeLocking(t *testing.T) { + for _, name := range []string{"", "Bad/Name", "my-disk"} { + if err := (&Firecracker{}).DiskDetach(t.Context(), "vm1", name); err == nil { + t.Errorf("DiskDetach(%q) = nil, want a name rejection", name) + } + } +} + func TestRefuseHotAttachedDisks(t *testing.T) { srv := newFCStub(t, fcVMConfig{Drives: []fcDrive{{DriveID: "drive_0"}, {DriveID: hotDiskIDPrefix + "scratch", PathOnHost: "/vols/s.raw"}}}) err := refuseHotAttachedDisks(t.Context(), srv.client) diff --git a/hypervisor/snapshot_test.go b/hypervisor/snapshot_test.go index c74b34fb..97003a86 100644 --- a/hypervisor/snapshot_test.go +++ b/hypervisor/snapshot_test.go @@ -134,14 +134,8 @@ func TestReverseLayers_Order(t *testing.T) { {Role: types.StorageRoleLayer, Serial: "L2"}, } got := ReverseLayers(configs, func(_ int, sc *types.StorageConfig) string { return sc.Serial }) - want := []string{"L2", "L1", "L0"} - if len(got) != len(want) { - t.Fatalf("got %d, want %d", len(got), len(want)) - } - for i := range want { - if got[i] != want[i] { - t.Errorf("got[%d] = %q, want %q", i, got[i], want[i]) - } + if want := []string{"L2", "L1", "L0"}; !slices.Equal(got, want) { + t.Errorf("got %v, want %v", got, want) } } @@ -152,11 +146,8 @@ func TestReverseLayers_IndexCallback(t *testing.T) { {Role: types.StorageRoleLayer}, } got := ReverseLayers(configs, func(idx int, _ *types.StorageConfig) int { return idx }) - want := []int{2, 1, 0} - for i := range want { - if got[i] != want[i] { - t.Errorf("got[%d] = %d, want %d", i, got[i], want[i]) - } + if want := []int{2, 1, 0}; !slices.Equal(got, want) { + t.Errorf("got %v, want %v", got, want) } } diff --git a/hypervisor/stop.go b/hypervisor/stop.go index 0171e042..2bafcf46 100644 --- a/hypervisor/stop.go +++ b/hypervisor/stop.go @@ -141,7 +141,7 @@ func (b *Backend) deleteOneLocked(ctx context.Context, id string, force bool, st } for _, pid := range procScan.Find(sockPath) { // procScan predates the stop: only a still-live match is a real orphan worth killing. - if !utils.IsProcessAlive(pid) { + if !utils.VerifyProcessCmdline(pid, b.Conf.BinaryName(), sockPath) { continue } if termErr := utils.TerminateProcess(ctx, pid, b.Conf.BinaryName(), sockPath, b.Conf.TerminateGracePeriod()); termErr != nil { diff --git a/hypervisor/stop_linux_test.go b/hypervisor/stop_linux_test.go new file mode 100644 index 00000000..e758ef51 --- /dev/null +++ b/hypervisor/stop_linux_test.go @@ -0,0 +1,66 @@ +package hypervisor + +import ( + "errors" + "os" + "os/exec" + "path/filepath" + "slices" + "strings" + "testing" + "time" + + "github.com/projecteru2/core/log" + coretypes "github.com/projecteru2/core/types" + + "github.com/cocoonstack/cocoon/utils" +) + +func TestDeleteSkipsAScannedVMMThatExitedBeforeTheKill(t *testing.T) { + ctx := t.Context() + logPath := filepath.Join(t.TempDir(), "delete.log") + if err := log.SetupLog(ctx, &coretypes.ServerLogConfig{Level: "info", Filename: logPath}, ""); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = log.SetupLog(ctx, &coretypes.ServerLogConfig{Level: "info"}, "") }) + + b, _ := newMeteringTestBackend(t) + const id = "vm-zombie-scan" + dir := seedStoppedVMWithDirs(t, b, id) + sockPath := SocketPath(dir) + + child := exec.Command("/bin/sh", "-c", "sleep 60; :", "sh", sockPath) + child.Args[0] = b.Conf.BinaryName() + if err := child.Start(); err != nil { + t.Fatalf("start fake VMM child: %v", err) + } + t.Cleanup(func() { _ = child.Wait() }) + scan, err := utils.ScanProcsByBinary(b.Conf.BinaryName()) + if err != nil { + t.Fatalf("scan: %v", err) + } + if !slices.Contains(scan.Find(sockPath), child.Process.Pid) { + t.Fatalf("scan misses the live fake VMM child %d", child.Process.Pid) + } + _ = child.Process.Kill() + if err := utils.WaitFor(ctx, 2*time.Second, 10*time.Millisecond, func() (bool, error) { + return !utils.VerifyProcessCmdline(child.Process.Pid, b.Conf.BinaryName(), sockPath), nil + }); err != nil { + t.Fatalf("killed child kept its cmdline: %v", err) + } + if !utils.IsProcessAlive(child.Process.Pid) { + t.Fatal("precondition: the unreaped child must linger as a zombie") + } + + rec := recordOf(t, b, id) + if err := b.deleteOneLocked(ctx, id, false, nil, rec, scan); err != nil { + t.Fatalf("deleteOneLocked: %v", err) + } + out, err := os.ReadFile(logPath) + if err != nil && !errors.Is(err, os.ErrNotExist) { + t.Fatalf("read log: %v", err) + } + if strings.Contains(string(out), "killed orphan VMM") { + t.Errorf("delete reported killing an already-exited VMM:\n%s", out) + } +} diff --git a/images/oci/boot.go b/images/oci/boot.go index 62deb377..f99af829 100644 --- a/images/oci/boot.go +++ b/images/oci/boot.go @@ -3,6 +3,7 @@ package oci import ( "archive/tar" "bufio" + "bytes" "cmp" "compress/gzip" "context" @@ -17,6 +18,8 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/projecteru2/core/log" + + "github.com/cocoonstack/cocoon/utils" ) // maxKernelBytes bounds arm64 gzip decompression against a bomb; real kernels are under 100 MiB. @@ -108,7 +111,7 @@ func scanBootFiles(ctx context.Context, r io.Reader, workDir, namePrefix string) return "", "", fmt.Errorf("read tar entry: %w", readErr) } - if hdr.Typeflag != tar.TypeReg && hdr.Typeflag != tar.TypeRegA { //nolint:staticcheck // tar.TypeRegA is deprecated but still present in real archives + if hdr.Typeflag != tar.TypeReg { continue } @@ -147,7 +150,7 @@ func scanBootFiles(ctx context.Context, r io.Reader, workDir, namePrefix string) if isKernel && runtime.GOARCH == "arm64" { br := bufio.NewReader(tr) src = br - if magic, _ := br.Peek(2); len(magic) == 2 && magic[0] == 0x1f && magic[1] == 0x8b { + if magic, _ := br.Peek(len(utils.GzipMagic)); bytes.Equal(magic, utils.GzipMagic) { var gzErr error if gz, gzErr = gzip.NewReader(br); gzErr != nil { _ = f.Close() diff --git a/images/store.go b/images/store.go index 689d7ed6..b9f64db4 100644 --- a/images/store.go +++ b/images/store.go @@ -53,10 +53,9 @@ func (s *Store[E]) applyDiff(ctx context.Context, w meta.Writer, fn func(*Index[ if err := fn(idx); err != nil { return err } - c := meta.NewCollection[E](s.ns, TableRecords) for id := range before { if idx.Images[id] == nil { - if err := c.Delete(ctx, w, id); err != nil { + if err := w.DeleteRaw(ctx, s.ns, TableRecords, id, false); err != nil { return err } } @@ -72,7 +71,7 @@ func (s *Store[E]) applyDiff(ctx context.Context, w meta.Writer, fn func(*Index[ if prev, ok := before[id]; ok && string(prev) == string(raw) { continue } - if err := c.Upsert(ctx, w, id, entry); err != nil { + if err := w.PutRaw(ctx, s.ns, TableRecords, id, raw, false); err != nil { return err } } diff --git a/meta/sqlite/maintenance_test.go b/meta/sqlite/maintenance_test.go index af724c24..b5e0ec67 100644 --- a/meta/sqlite/maintenance_test.go +++ b/meta/sqlite/maintenance_test.go @@ -83,7 +83,6 @@ func TestBackupCrashSteps(t *testing.T) { } err := Backup(ctx, filepath.Join(dir, DBFileName), dest) if step != "renamed" { - if !errors.Is(err, errCrash) { t.Fatalf("step %s: want injected crash, got %v", step, err) } diff --git a/meta/sqlite/multiprocess_test.go b/meta/sqlite/multiprocess_test.go index 557d6086..84a1903a 100644 --- a/meta/sqlite/multiprocess_test.go +++ b/meta/sqlite/multiprocess_test.go @@ -169,7 +169,6 @@ func TestKillStormAtomicity(t *testing.T) { if id, ok := strings.CutPrefix(sc.Text(), "ACK "); ok { acked[id] = struct{}{} got++ - if got >= 3+round*2 { break } diff --git a/snapshot/localfile/gc.go b/snapshot/localfile/gc.go index d07332a5..7b4ec9fe 100644 --- a/snapshot/localfile/gc.go +++ b/snapshot/localfile/gc.go @@ -318,14 +318,13 @@ func logEvictRow(ctx context.Context, logger *log.Fields, verb, id string, m sna // backfillSizeBytes computes + persists SizeBytes for records missing it so future GC skips du. func backfillSizeBytes(ctx context.Context, lf *LocalFile, records map[string]snapshotMeta) { - conf := lf.conf logger := log.WithFunc("gc.snapshot") var changed bool for id, m := range records { if m.sizeBytes > 0 { continue } - actual, err := utils.DirSize(conf.SnapshotDataDir(id)) + actual, err := utils.DirSize(m.dataDir) if err != nil { logger.Warnf(ctx, "DirSize for %s: %v", id, err) continue diff --git a/types/snapshot.go b/types/snapshot.go index 1f099cf0..4b6837bc 100644 --- a/types/snapshot.go +++ b/types/snapshot.go @@ -34,9 +34,8 @@ type Snapshot struct { // SnapshotExport is the envelope written as snapshot.json inside an export archive. type SnapshotExport struct { - Config SnapshotConfig `json:"config"` - Version int `json:"version"` - Files []string `json:"files,omitempty"` // data-dir files the export carries; absent on exports older than the manifest - - DiskAttachFiles []string `json:"disk_attach_files,omitempty"` // the subset `vm disk attach --from-dir` reads + Config SnapshotConfig `json:"config"` + Version int `json:"version"` + Files []string `json:"files,omitempty"` // data-dir files the export carries; absent on exports older than the manifest + DiskAttachFiles []string `json:"disk_attach_files,omitempty"` // the subset `vm disk attach --from-dir` reads } diff --git a/utils/http.go b/utils/http.go index 5115da8e..9824679a 100644 --- a/utils/http.go +++ b/utils/http.go @@ -90,7 +90,7 @@ func CheckSocket(socketPath string) error { func DoWithRetry[T any](ctx context.Context, fn func() (T, error)) (T, error) { var zero T var lastErr error - for i := 0; i <= MaxRetries; i++ { + for i := range MaxRetries + 1 { result, err := fn() if err == nil { return result, nil