diff --git a/README.md b/README.md index e2f8020..ed326c6 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ vk-cocoon is the host-side bridge between the Kubernetes API and the cocoon runt | Provider | `provider/cocoon/` | `Provider` struct with lifecycle methods (CreatePod / DeletePod / UpdatePod / GetPodStatus), startup reconcile, orphan policy, VM event watcher, pod eviction | | Provider iface | `provider/` | Shared provider interface and node-capacity helpers | | Cocoon CLI | `vm/` | `Runtime` interface + the default `CocoonCLI` implementation that shells out to `cocoon` (including `WatchEvents` via `cocoon vm status --event --format json`) | -| Snapshot SDK | `snapshots/` | Wraps the [epoch](https://github.com/cocoonstack/epoch) SDK as a `RegistryClient` interface, plus `Puller` and `Pusher` that stream snapshots and cloud images via `epoch/snapshot` and `epoch/cloudimg` | +| Snapshot SDK | `snapshots/` | `Puller` and `Pusher` stream snapshots and cloud images to any OCI registry through `cocoon-common`'s `oci.Registry` backend (`cocoon-common/snapshot` + `cocoon-common/cloudimg`) | | Network | `network/` | cocoon-net JSON lease parser used to resolve a freshly cloned VM's IP, plus the ICMPv4 `Pinger` the probe loop uses to check guest reachability | | Guest exec | `guest/` | RDP help-text shim (Windows) and SAC dialer (Windows static IP). Linux guest exec / logs go through `cocoon vm exec` and `cocoon vm logs` — see `vm/`. | | Probes | `probes/` | Per-pod probe agents that run a caller-supplied health check on a ticker, update the in-memory readiness map, and invoke an onUpdate callback so the async provider can push fresh status through v-k's notify hook | @@ -27,8 +27,8 @@ vk-cocoon is the host-side bridge between the Kubernetes API and the cocoon runt 2. If a VM with `spec.VMName` already exists locally, adopt it (idempotent on restart). Adoption hinges on `StartupReconcile` having populated `vmsByName`; before reconcile completes, CreatePod treats the pod as new and may collide on VM name. 3. Otherwise branch on `spec.Managed` first, then `spec.Mode`: - **`Managed=false`** (static / externally-managed VMs, e.g. Windows toolboxes on an external QEMU host): skip the runtime entirely and adopt the pre-assigned `VMID` / `IP` / `VNCPort` the operator pre-wrote into the `VMRuntime` annotations. `Managed` is the single source of truth for "vk-cocoon owns this VM's lifecycle". - - **Mode `clone`** (default, `Managed=true`): look up the snapshot locally using a **tag-aware name** (`repo:tag`, or bare `repo` when the tag is `latest` for backward compatibility). If the local snapshot does not exist, pull it from epoch via `Puller.PullSnapshot`. Before cloning, `assertSnapshotBackend` validates the snapshot's recorded hypervisor matches `spec.Backend` — a CH snapshot cannot be cloned onto a FC target and vice-versa. When the snapshot carries a base image, `Pull: true` is passed to `CloneOptions`, which translates to `cocoon vm clone --pull`; cocoon constructs a digest reference (`repo@sha256:xxx`) from the snapshot metadata and pulls the exact image version recorded at snapshot time. Then `Runtime.Clone(from=, to=spec.VMName)`. Pod-side CPU/memory/storage are not plumbed into clone — cocoon clone inherits all guest resources from the snapshot. Only the `vm run` path translates pod resources into VM resources. - - **Mode `run`** (`Managed=true`): `ensureRunImage` makes the image available locally before launching the VM. It peeks the OCI manifest via `Puller.Registry`: cocoonstack cloud-image artifacts (artifactType=`application/vnd.cocoonstack.os-image.v1+json`) take the qcow2 streaming path through `Puller.EnsureCloudImage` → `cocoon image import`, snapshot artifacts are rejected with a "use mode=clone" error, and everything else (HTTP(S) URLs, container images, refs that don't resolve against epoch) falls through to `Runtime.EnsureImage` → `cocoon image pull`. `--force` when `spec.ForcePull` is true. Then `Runtime.Run(image=spec.Image, name=spec.VMName)`. When `spec.Backend` is `firecracker`, `--fc` is passed to select the FC backend; when `spec.OS` is `windows`, `--windows` is passed. When `spec.NoDirectIO` is true, `--no-direct-io` disables O_DIRECT on writable disks (CH only, useful for dev/test). + - **Mode `clone`** (default, `Managed=true`): look up the snapshot locally using a **tag-aware name** (`repo:tag`, or bare `repo` when the tag is `latest` for backward compatibility). If the local snapshot does not exist, pull it from the registry via `Puller.PullSnapshot`. Before cloning, `assertSnapshotBackend` validates the snapshot's recorded hypervisor matches `spec.Backend` — a CH snapshot cannot be cloned onto a FC target and vice-versa. When the snapshot carries a base image, `Pull: true` is passed to `CloneOptions`, which translates to `cocoon vm clone --pull`; cocoon constructs a digest reference (`repo@sha256:xxx`) from the snapshot metadata and pulls the exact image version recorded at snapshot time. Then `Runtime.Clone(from=, to=spec.VMName)`. Pod-side CPU/memory/storage are not plumbed into clone — cocoon clone inherits all guest resources from the snapshot. Only the `vm run` path translates pod resources into VM resources. + - **Mode `run`** (`Managed=true`): `ensureRunImage` makes the image available locally before launching the VM. It peeks the OCI manifest via `Puller.Registry`: cocoonstack cloud-image artifacts (artifactType=`application/vnd.cocoonstack.os-image.v1+json`) take the qcow2 streaming path through `Puller.EnsureCloudImageFromRaw` → `cocoon image import`, snapshot artifacts are rejected with a "use mode=clone" error, and everything else (HTTP(S) URLs, container images, refs that don't resolve against the registry) falls through to `Runtime.EnsureImage` → `cocoon image pull`. `--force` when `spec.ForcePull` is true. Then `Runtime.Run(image=spec.Image, name=spec.VMName)`. When `spec.Backend` is `firecracker`, `--fc` is passed to select the FC backend; when `spec.OS` is `windows`, `--windows` is passed. When `spec.NoDirectIO` is true, `--no-direct-io` disables O_DIRECT on writable disks (CH only, useful for dev/test). - **`vm.cocoonstack.io/clone-from-dir` override** (managed-only, takes precedence over mode/fork-from): clone via `cocoon vm clone --from-dir --pull`, bypassing the local snapshot DB. Pairs with `cocoon snapshot export --to-dir` for cross-node staging. Conflicts with `mode=run` or `fork-from` fast-fail. 4. For clone/fork/wake paths, check whether the VM needs manual network setup (see [Post-clone hints](#post-clone-hints) below). If so, write the required commands as a base64-encoded annotation (`vm.cocoonstack.io/post-clone-hint`) and log a warning. The pod stays Running but Not Ready until the user executes the commands via `cocoon vm console` and the probe detects network connectivity. 5. Resolve the IP from the cocoon-net JSON lease file by MAC. @@ -39,7 +39,7 @@ vk-cocoon is the host-side bridge between the Kubernetes API and the cocoon runt 1. Decode `meta.VMSpec`. 2. `meta.ShouldSnapshotVM(spec)` — the shared cocoon-common decoder — decides whether to snapshot before destroy: - - `always`: `Runtime.SnapshotSave` then `Pusher.PushSnapshot(tag=meta.DefaultSnapshotTag)` to epoch. + - `always`: `Runtime.SnapshotSave` then `Pusher.PushSnapshot(tag=meta.DefaultSnapshotTag)` to the registry. - `main-only`: same, but only when the VM name ends in `-0` (slot 0 = main agent). - `never`: skip snapshots entirely. 3. `Runtime.Remove(vmID)` to destroy the VM. @@ -52,9 +52,9 @@ The only update vk-cocoon honors is a `HibernateState` transition. Anything else | Transition | Behavior | |---|---| | `false → true` | NetResize (CH+Windows) → SnapshotSave → Push → clear VMID before Remove → Remove (rollback on failure). Pod stays alive (`PodRunning`) so K8s controllers do not recreate it. VMID/IP annotations clear between Push and Remove so the operator's manifest+VMID race window collapses to one patch RTT. **Compensating rollback**: if `Runtime.Remove` fails after a successful push, vk-cocoon best-effort `Registry.DeleteManifest` the hibernate tag and re-applies VMID/IP so the pod stays recoverable. Push and Save are idempotent, so a compensated retry re-publishes the tag cleanly on the next attempt. | -| `true → false` (with no live VM) | `Puller.PullSnapshot(tag=meta.HibernateSnapshotTag)` → `Runtime.Clone` → drop the hibernation tag from epoch. | +| `true → false` (with no live VM) | `Puller.PullSnapshot(tag=meta.HibernateSnapshotTag)` → `Runtime.Clone` → drop the hibernation tag from the registry. | -The operator's `CocoonHibernation` reconciler tracks the transition by polling `epoch.GetManifest(vmName, "hibernate")`. +The operator's `CocoonHibernation` reconciler tracks the transition by polling the registry for the `hibernate` manifest. ### Node resources @@ -87,8 +87,8 @@ vk-cocoon exposes three metrics surfaces: | `vk_cocoon_node_storage_available_bytes` / `total_bytes` | Gauge | Cocoon root filesystem | | `vk_cocoon_vm_boot_duration_seconds{mode,backend}` | Histogram | VM creation time (run or clone) | | `vk_cocoon_snapshot_save_duration_seconds` | Histogram | Snapshot save time | -| `vk_cocoon_snapshot_push_duration_seconds` | Histogram | Epoch push time | -| `vk_cocoon_snapshot_pull_duration_seconds` | Histogram | Epoch pull time | +| `vk_cocoon_snapshot_push_duration_seconds` | Histogram | Registry push time | +| `vk_cocoon_snapshot_pull_duration_seconds` | Histogram | Registry pull time | | `vk_cocoon_probe_duration_seconds` | Histogram | Per-probe health check time (ICMP or TCP) | | `vk_cocoon_pod_lifecycle_total{op,result}` | Counter | Pod lifecycle operations | | `vk_cocoon_snapshot_pull_total{result}` / `push_total` | Counter | Snapshot pull/push counts | @@ -178,9 +178,7 @@ If the ICMP raw socket cannot be opened — typically because the binary is runn | `KUBECONFIG` | unset | Path to kubeconfig (in-cluster used otherwise). | | `VK_NODE_NAME` | `cocoon-pool` | Virtual node name registered with the K8s API. | | `VK_LOG_LEVEL` | `info` | `projecteru2/core/log` level. | -| `EPOCH_URL` | `http://epoch.cocoon-system.svc:8080` | Epoch base URL. | -| `EPOCH_TOKEN` | unset | Bearer token (only needed for `/v2/` pushes; `/dl/` is anonymous). | -| `EPOCH_CA_CERT` | unset | Path to PEM-encoded CA certificate for TLS verification against epoch. | +| `OCI_REGISTRY` | **required** | OCI registry base for snapshots and cloud images (e.g. an Artifact Registry repo). Auth resolves GCP ADC then docker config. | | `VK_LEASES_PATH` | `/var/lib/cocoon/net/leases.json` | cocoon-net JSON lease file. | | `VK_COCOON_BIN` | `/usr/local/bin/cocoon` | Path to the cocoon CLI binary. | | `VK_ORPHAN_POLICY` | `destroy` | `destroy` (auto-clean), `alert`, or `keep`. | @@ -225,17 +223,16 @@ make fmt # gofumpt + goimports make help # show all targets ``` -The Makefile detects Go workspace mode (`go env GOWORK`) and skips `go mod tidy` when active so cross-module references resolve through `go.work` without forcing a release of cocoon-common or epoch. +The Makefile detects Go workspace mode (`go env GOWORK`) and skips `go mod tidy` when active so cross-module references resolve through `go.work` without forcing a release of cocoon-common. ## Related projects | Project | Role | |---|---| | [cocoon](https://github.com/cocoonstack/cocoon) | The MicroVM runtime vk-cocoon shells out to. | -| [cocoon-common](https://github.com/cocoonstack/cocoon-common) | CRD types, annotation contract, shared helpers. | +| [cocoon-common](https://github.com/cocoonstack/cocoon-common) | CRD types, annotation contract, shared helpers, and the OCI registry + snapshot/cloud-image packages. | | [cocoon-operator](https://github.com/cocoonstack/cocoon-operator) | CocoonSet and CocoonHibernation reconcilers. | | [cocoon-webhook](https://github.com/cocoonstack/cocoon-webhook) | Admission webhook for sticky scheduling and CocoonSet validation. | -| [epoch](https://github.com/cocoonstack/epoch) | Snapshot registry; vk-cocoon pulls and pushes via `epoch/snapshot` + `epoch/cloudimg`. | | [cocoon-net](https://github.com/cocoonstack/cocoon-net) | Per-host networking with embedded DHCP server and iptables setup; vk-cocoon reads its JSON lease file. | ## License diff --git a/go.mod b/go.mod index 2ed9f3a..23535bc 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,14 @@ module github.com/cocoonstack/vk-cocoon go 1.25.6 require ( - github.com/cocoonstack/cocoon-common v0.2.2 - github.com/cocoonstack/epoch v0.2.4 + github.com/cocoonstack/cocoon-common v0.2.3-0.20260701064759-3dcdfdd23a16 + github.com/google/go-containerregistry v0.21.7 github.com/projecteru2/core v0.0.0-20241016125006-ff909eefe04c github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 github.com/virtual-kubelet/virtual-kubelet v1.12.0 - golang.org/x/net v0.50.0 - golang.org/x/sync v0.19.0 + golang.org/x/net v0.56.0 + golang.org/x/sync v0.21.0 google.golang.org/protobuf v1.36.10 k8s.io/api v0.35.3 k8s.io/apimachinery v0.35.3 @@ -21,6 +21,7 @@ require ( require ( cel.dev/expr v0.24.0 // indirect + cloud.google.com/go/compute/metadata v0.7.0 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/alphadose/haxmap v1.2.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect @@ -34,6 +35,8 @@ require ( github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/docker/cli v29.5.3+incompatible // indirect + github.com/docker/docker-credential-helpers v0.9.3 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -60,7 +63,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.4 // indirect + github.com/klauspost/compress v1.18.6 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -73,12 +76,15 @@ require ( github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/common v0.67.4 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/zerolog v1.29.1 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect @@ -90,25 +96,24 @@ require ( go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.48.0 // indirect + golang.org/x/crypto v0.53.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/oauth2 v0.35.0 // indirect - golang.org/x/sys v0.41.0 // indirect - golang.org/x/term v0.40.0 // indirect - golang.org/x/text v0.34.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/term v0.44.0 // indirect + golang.org/x/text v0.38.0 // indirect golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.42.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect google.golang.org/grpc v1.72.2 // indirect @@ -128,3 +133,5 @@ require ( sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) + +exclude cloud.google.com/go v0.26.0 diff --git a/go.sum b/go.sum index 9d730d6..7931c1e 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= @@ -42,10 +43,8 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cocoonstack/cocoon-common v0.2.2 h1:+qY4Iv1nLZw1brH46j33Kvajqi8UR1QKLLTC/AXhU30= -github.com/cocoonstack/cocoon-common v0.2.2/go.mod h1:xIXbJ83vngQ2mrLC6q0Tw7h21M9BYBBqqYTcHaUrm1Y= -github.com/cocoonstack/epoch v0.2.4 h1:X18DdsWlPRqEVswR1N3JAazxRZfBUtoL5r7GTgWmhN8= -github.com/cocoonstack/epoch v0.2.4/go.mod h1:1bFeUom4aeFbw3vjFbrLegEgVShqe4qSOfpmH5UEpww= +github.com/cocoonstack/cocoon-common v0.2.3-0.20260701064759-3dcdfdd23a16 h1:4gS1Pr0nCN/qoa344ZHqy2vZPaCW8JcCocB7mgqJmUk= +github.com/cocoonstack/cocoon-common v0.2.3-0.20260701064759-3dcdfdd23a16/go.mod h1:/Cf3aBBN0blBxJWexuGuMbTkas+scvQiF2I75aQXkH4= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -63,6 +62,10 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/docker/cli v29.5.3+incompatible h1:nbEFfz774vBwQ5KRYv7c/AghjReqnGISvrRhzjV0evs= +github.com/docker/cli v29.5.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= +github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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= @@ -164,6 +167,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-containerregistry v0.21.7 h1:/vPFuVXDjtFREsVArW+0h1CIl5urnOhzei4X2DMW9IU= +github.com/google/go-containerregistry v0.21.7/go.mod h1:kjSbt7/zMsKLWfnHrIvKvhXHUw91jbe9DNjPPJ32gXE= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= @@ -218,8 +223,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= -github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -285,6 +290,10 @@ github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zw github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= @@ -405,20 +414,20 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -439,8 +448,8 @@ golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= @@ -452,8 +461,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB 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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -472,11 +481,11 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= -golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -484,8 +493,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -509,19 +518,19 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= @@ -538,8 +547,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK 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.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk= +golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys= 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= @@ -604,6 +613,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ= diff --git a/main.go b/main.go index 5989c84..b100571 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ package main import ( "context" "crypto/tls" + "errors" "fmt" "net/http" "os" @@ -14,6 +15,8 @@ import ( "syscall" "time" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/v1/google" "github.com/projecteru2/core/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -30,7 +33,7 @@ import ( commonk8s "github.com/cocoonstack/cocoon-common/k8s" commonlog "github.com/cocoonstack/cocoon-common/log" "github.com/cocoonstack/cocoon-common/meta" - "github.com/cocoonstack/epoch/registryclient" + "github.com/cocoonstack/cocoon-common/oci" "github.com/cocoonstack/vk-cocoon/guest/rdp" "github.com/cocoonstack/vk-cocoon/guest/sac" "github.com/cocoonstack/vk-cocoon/metrics" @@ -46,7 +49,6 @@ import ( const ( defaultNodeName = "cocoon-pool" defaultMetricsAddr = ":9091" - defaultEpochURL = "http://epoch.cocoon-system.svc:8080" defaultOrphanPolicy = string(provider.OrphanDestroy) defaultTLSCert = "/etc/cocoon/vk/tls/vk-kubelet.crt" @@ -70,8 +72,7 @@ func main() { nodeName := commonk8s.EnvOrDefault("VK_NODE_NAME", defaultNodeName) metricsAddr := commonk8s.EnvOrDefault("VK_METRICS_ADDR", defaultMetricsAddr) - epochURL := commonk8s.EnvOrDefault("EPOCH_URL", defaultEpochURL) - epochToken := os.Getenv("EPOCH_TOKEN") + ociRegistry := os.Getenv("OCI_REGISTRY") leasesPath := commonk8s.EnvOrDefault("VK_LEASES_PATH", network.DefaultLeasesPath) cocoonBin := commonk8s.EnvOrDefault("VK_COCOON_BIN", "") orphanPolicy := commonk8s.EnvOrDefault("VK_ORPHAN_POLICY", defaultOrphanPolicy) @@ -118,8 +119,7 @@ func main() { p, err := buildProvider(signalCtx, buildOpts{ nodeName: nodeName, - epochURL: epochURL, - epochToken: epochToken, + ociRegistry: ociRegistry, leasesPath: leasesPath, cocoonBin: cocoonBin, orphanPolicy: orphanPolicy, @@ -214,8 +214,7 @@ func main() { type buildOpts struct { nodeName string - epochURL string - epochToken string + ociRegistry string leasesPath string cocoonBin string orphanPolicy string @@ -223,12 +222,23 @@ type buildOpts struct { recorder record.EventRecorder } +// buildRegistry builds the OCI registry backend from OCI_REGISTRY. The keychain +// resolves GCP ADC (google.Keychain) before falling back to docker config. +func buildRegistry(opts buildOpts) (oci.Registry, error) { + if opts.ociRegistry == "" { + return nil, errors.New("OCI_REGISTRY must be set") + } + keychain := authn.NewMultiKeychain(google.Keychain, authn.DefaultKeychain) + return oci.NewOCIRegistry(opts.ociRegistry, keychain), nil +} + func buildProvider(ctx context.Context, opts buildOpts) (*cocoon.Provider, error) { logger := log.WithFunc("buildProvider") - registry, err := registryclient.NewFromEnv(opts.epochURL, opts.epochToken) + registry, err := buildRegistry(opts) if err != nil { return nil, fmt.Errorf("construct registry client: %w", err) } + logger.Infof(ctx, "registry backend: OCI %s", opts.ociRegistry) runtime := vm.NewCocoonCLI(opts.cocoonBin) p := cocoon.NewProvider() p.NodeName = opts.nodeName diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..f3eb42e --- /dev/null +++ b/main_test.go @@ -0,0 +1,21 @@ +package main + +import ( + "testing" + + "github.com/cocoonstack/cocoon-common/oci" +) + +func TestBuildRegistry(t *testing.T) { + reg, err := buildRegistry(buildOpts{ociRegistry: "example.com/proj/repo"}) + if err != nil { + t.Fatalf("buildRegistry: %v", err) + } + if _, ok := reg.(*oci.OCIRegistry); !ok { + t.Fatalf("got %T, want *oci.OCIRegistry", reg) + } + + if _, err := buildRegistry(buildOpts{}); err == nil { + t.Fatal("buildRegistry with no OCI_REGISTRY: want error, got nil") + } +} diff --git a/metrics/metrics.go b/metrics/metrics.go index 830125c..9fd99ef 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -23,7 +23,7 @@ var ( prometheus.CounterOpts{ Namespace: metricNamespace, Name: "snapshot_pull_total", - Help: "Number of snapshot pulls from epoch by result.", + Help: "Number of snapshot pulls from the registry by result.", }, []string{"result"}, ) @@ -41,7 +41,7 @@ var ( prometheus.CounterOpts{ Namespace: metricNamespace, Name: "snapshot_push_total", - Help: "Number of snapshot pushes to epoch by result.", + Help: "Number of snapshot pushes to the registry by result.", }, []string{"result"}, ) @@ -118,7 +118,7 @@ var ( prometheus.HistogramOpts{ Namespace: metricNamespace, Name: "snapshot_push_duration_seconds", - Help: "Time to push a snapshot to epoch.", + Help: "Time to push a snapshot to the registry.", Buckets: []float64{1, 5, 10, 30, 60, 120, 300}, }, ) @@ -127,7 +127,7 @@ var ( prometheus.HistogramOpts{ Namespace: metricNamespace, Name: "snapshot_pull_duration_seconds", - Help: "Time to pull a snapshot from epoch.", + Help: "Time to pull a snapshot from the registry.", Buckets: []float64{1, 5, 10, 30, 60, 120, 300}, }, ) diff --git a/provider/cocoon/create.go b/provider/cocoon/create.go index 94fffed..23ed3b9 100644 --- a/provider/cocoon/create.go +++ b/provider/cocoon/create.go @@ -15,9 +15,9 @@ import ( cocoonv1 "github.com/cocoonstack/cocoon-common/apis/v1" commonk8s "github.com/cocoonstack/cocoon-common/k8s" + "github.com/cocoonstack/cocoon-common/manifest" "github.com/cocoonstack/cocoon-common/meta" - "github.com/cocoonstack/epoch/manifest" - "github.com/cocoonstack/epoch/utils" + "github.com/cocoonstack/cocoon-common/ociutil" "github.com/cocoonstack/vk-cocoon/metrics" "github.com/cocoonstack/vk-cocoon/vm" ) @@ -198,7 +198,7 @@ func (p *Provider) bringUpVM(ctx context.Context, pod *corev1.Pod, spec meta.VMS return v, "", nil default: // clone is the default - repo, tag := utils.ParseRef(spec.Image) + repo, tag := ociutil.ParseRef(spec.Image) local := localSnapshotName(repo, tag) snapshot, err := p.ensureSnapshot(ctx, repo, tag, local) if err != nil { @@ -232,8 +232,8 @@ func (p *Provider) bringUpVM(ctx context.Context, pod *corev1.Pod, spec meta.VMS } // ensureRunImage materializes the base image locally and returns the ref -// `cocoon vm run` should be invoked with. Cloud-image refs canonicalize to -// the /dl/{repo}/{tag} URL so vmCfg.Image is portable across nodes. +// `cocoon vm run` should be invoked with. A cloud-image artifact is imported +// into the local store and booted from there (repo:tag), not the registry. func (p *Provider) ensureRunImage(ctx context.Context, image string, force bool) (string, error) { if image == "" { return image, nil @@ -241,10 +241,10 @@ func (p *Provider) ensureRunImage(ctx context.Context, image string, force bool) if p.Puller == nil || p.Puller.Registry == nil || isHTTPURL(image) { return image, p.Runtime.EnsureImage(ctx, image, force) } - repo, tag := utils.ParseRef(image) + repo, tag := ociutil.ParseRef(image) raw, _, err := p.Puller.Registry.GetManifest(ctx, repo, tag) if err != nil { - // Non-epoch ref or registry hiccup; cocoon image pull handles non-epoch refs natively. + // Ref absent from the registry or a hiccup; cocoon image pull handles external refs natively. return image, p.Runtime.EnsureImage(ctx, image, force) } kind, classifyErr := manifest.Classify(raw) @@ -253,8 +253,8 @@ func (p *Provider) ensureRunImage(ctx context.Context, image string, force bool) } switch kind { case manifest.KindCloudImage: - canonical := canonicalCloudImgURL(p.Puller.Registry.BaseURL(), repo, tag) - return canonical, p.Puller.EnsureCloudImageFromRaw(ctx, repo, canonical, raw, force) + local := repo + ":" + tag + return local, p.Puller.EnsureCloudImageFromRaw(ctx, repo, local, raw, force) case manifest.KindSnapshot: return image, fmt.Errorf("image %s is a snapshot artifact; use mode=clone instead of mode=run", image) default: @@ -262,7 +262,7 @@ func (p *Provider) ensureRunImage(ctx context.Context, image string, force bool) } } -// ensureSnapshot returns the local snapshot, pulling from epoch if needed. +// ensureSnapshot returns the local snapshot, pulling from the registry if needed. // Local name includes the tag so myvm:v1 and myvm:v2 stay separate. func (p *Provider) ensureSnapshot(ctx context.Context, repo, tag, local string) (*vm.Snapshot, error) { if repo == "" { @@ -414,12 +414,6 @@ func assertSnapshotBackend(snapshot *vm.Snapshot, targetBackend string) error { snapshot.Name, snapshot.Hypervisor, targetBackend) } -// canonicalCloudImgURL builds the /dl/{repo}/{tag} URL cocoon's cloudimg -// backend can pull via plain http.Get. -func canonicalCloudImgURL(baseURL, repo, tag string) string { - return fmt.Sprintf("%s/dl/%s/%s", strings.TrimRight(baseURL, "/"), repo, tag) -} - // isHTTPURL reports whether ref looks like an HTTP(S) cloud-image URL. func isHTTPURL(ref string) bool { return strings.HasPrefix(ref, "http://") || strings.HasPrefix(ref, "https://") diff --git a/provider/cocoon/create_test.go b/provider/cocoon/create_test.go index 7b110c1..235286d 100644 --- a/provider/cocoon/create_test.go +++ b/provider/cocoon/create_test.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "net/http" - "net/http/httptest" "os" "path/filepath" "reflect" @@ -24,7 +23,7 @@ import ( utilexec "k8s.io/client-go/util/exec" "github.com/cocoonstack/cocoon-common/meta" - "github.com/cocoonstack/epoch/registryclient" + "github.com/cocoonstack/cocoon-common/oci" "github.com/cocoonstack/vk-cocoon/network" "github.com/cocoonstack/vk-cocoon/probes" "github.com/cocoonstack/vk-cocoon/provider" @@ -378,7 +377,7 @@ func TestCreatePodRunModeInvalidatesForkSnapshot(t *testing.T) { pod := newPodWithSpec(meta.VMSpec{ VMName: "vk-ns-demo-0", - Image: "epoch.example/cocoon/ubuntu:24.04", + Image: "registry.example/cocoon/ubuntu:24.04", Mode: "run", OS: "linux", }) @@ -426,7 +425,7 @@ func TestCreatePodForkFromOverridesRunMode(t *testing.T) { pod := newPodWithSpec(meta.VMSpec{ VMName: "vk-ns-demo-2", - Image: "https://epoch.example.org/dl/windows/win11", + Image: "https://registry.example.org/windows/win11", Mode: "run", OS: "windows", ForkFrom: "vk-ns-demo-0", @@ -890,6 +889,31 @@ func TestEnsureRunImageFallback(t *testing.T) { } } +// fakeRegistry is a stub oci.Registry serving a canned GetManifest; the other +// methods are unused by the ensureRunImage classify dispatch. +type fakeRegistry struct { + manifest []byte + err error +} + +var _ oci.Registry = fakeRegistry{} + +func (f fakeRegistry) GetManifest(context.Context, string, string) ([]byte, string, error) { + return f.manifest, "", f.err +} + +func (fakeRegistry) GetBlob(context.Context, string, string) (io.ReadCloser, error) { return nil, nil } + +func (fakeRegistry) HasBlob(context.Context, string, string) (bool, error) { return false, nil } + +func (fakeRegistry) PutBlob(context.Context, string, string, io.Reader, int64) error { return nil } + +func (fakeRegistry) PutManifest(context.Context, string, string, []byte, string) error { return nil } + +func (fakeRegistry) HasManifest(context.Context, string, string) (bool, error) { return false, nil } + +func (fakeRegistry) DeleteManifest(context.Context, string, string) error { return nil } + func TestEnsureRunImageDispatch(t *testing.T) { const repo = "ubuntu" @@ -930,26 +954,20 @@ func TestEnsureRunImageDispatch(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(tc.manifestStatus) - _, _ = io.WriteString(w, tc.manifestBody) - })) - defer srv.Close() - - client, err := registryclient.New(srv.URL, "") - if err != nil { - t.Fatalf("registryclient.New: %v", err) + reg := fakeRegistry{manifest: []byte(tc.manifestBody)} + if tc.manifestStatus/100 != 2 { + reg.err = errors.New("registry error") } - // Cloudimg path imports under the canonical /dl URL, so the + // Cloudimg path imports under the local ref (repo:tag), so the // fake runtime's Image lookup must key on the same form. - wantURL := canonicalCloudImgURL(srv.URL, repo, "latest") + wantRef := repo + ":latest" rt := &fakeRuntime{} if tc.imagesPresent { - rt.imagesPresent = map[string]bool{wantURL: true} + rt.imagesPresent = map[string]bool{wantRef: true} } p := newTestProvider(t) p.Runtime = rt - p.Puller = &snapshots.Puller{Registry: client, Runtime: rt} + p.Puller = &snapshots.Puller{Registry: reg, Runtime: rt} got, err := p.ensureRunImage(t.Context(), repo, false) switch { @@ -970,15 +988,14 @@ func TestEnsureRunImageDispatch(t *testing.T) { if len(rt.ensuredImages) != 0 { t.Fatalf("Puller path should not shell EnsureImage, got %v", rt.ensuredImages) } - // Cloudimg path returns the canonical URL so vmCfg.Image - // stays portable across nodes. - if got != wantURL { - t.Fatalf("ensureRunImage returned %q, want %q", got, wantURL) + // Cloudimg path returns the local ref it imported under. + if got != wantRef { + t.Fatalf("ensureRunImage returned %q, want %q", got, wantRef) } - // Import name must match the returned URL so the subsequent - // `cocoon vm run` finds the blob. - if len(rt.imageInspectCalls) == 0 || rt.imageInspectCalls[0] != wantURL { - t.Fatalf("Puller imported under %v, want first inspect on %q", rt.imageInspectCalls, wantURL) + // Import name must match the returned ref so the subsequent + // `cocoon vm run` finds the local image. + if len(rt.imageInspectCalls) == 0 || rt.imageInspectCalls[0] != wantRef { + t.Fatalf("Puller imported under %v, want first inspect on %q", rt.imageInspectCalls, wantRef) } default: // fallthrough to Runtime.EnsureImage if err != nil { diff --git a/provider/cocoon/provider.go b/provider/cocoon/provider.go index 165a150..8da1521 100644 --- a/provider/cocoon/provider.go +++ b/provider/cocoon/provider.go @@ -21,7 +21,7 @@ import ( commonk8s "github.com/cocoonstack/cocoon-common/k8s" "github.com/cocoonstack/cocoon-common/meta" - "github.com/cocoonstack/epoch/registryclient" + "github.com/cocoonstack/cocoon-common/oci" "github.com/cocoonstack/vk-cocoon/guest" "github.com/cocoonstack/vk-cocoon/metrics" "github.com/cocoonstack/vk-cocoon/network" @@ -87,7 +87,7 @@ type Provider struct { Runtime vm.Runtime Puller *snapshots.Puller Pusher *snapshots.Pusher - Registry *registryclient.Client + Registry oci.Registry LeaseParser *network.LeaseParser Pinger network.Pinger GuestRDP guest.Executor diff --git a/provider/cocoon/snapshot_helper.go b/provider/cocoon/snapshot_helper.go index 662c35e..d3aae92 100644 --- a/provider/cocoon/snapshot_helper.go +++ b/provider/cocoon/snapshot_helper.go @@ -23,7 +23,7 @@ func (p *Provider) removeSnapshotDetached(funcLabel, name string) { } } -// saveAndPushSnapshot saves a snapshot and pushes it to epoch, recording +// saveAndPushSnapshot saves a snapshot and pushes it to the registry, recording // timing metrics. Errors are logged and counted but not returned — the // delete path treats snapshot failures as non-fatal. func (p *Provider) saveAndPushSnapshot(ctx context.Context, vmName, vmID, tag, image string) { diff --git a/provider/cocoon/update.go b/provider/cocoon/update.go index b451d38..7920aff 100644 --- a/provider/cocoon/update.go +++ b/provider/cocoon/update.go @@ -134,9 +134,9 @@ func (p *Provider) hibernate(ctx context.Context, pod *corev1.Pod, v *vm.VM) err p.forgetVMOnly(pod.Namespace, pod.Name) p.markLifecycleState(ctx, pod, meta.LifecycleStateHibernated, "") if p.Pusher != nil { - p.emitNormalf(pod, "Hibernated", "snapshot pushed to epoch") + p.emitNormalf(pod, "Hibernated", "snapshot pushed to registry") } else { - p.emitNormalf(pod, "Hibernated", "snapshot saved locally (no epoch pusher configured)") + p.emitNormalf(pod, "Hibernated", "snapshot saved locally (no registry pusher configured)") } return nil } diff --git a/snapshots/cloudimg.go b/snapshots/cloudimg.go index 9f04792..6ffc393 100644 --- a/snapshots/cloudimg.go +++ b/snapshots/cloudimg.go @@ -4,21 +4,21 @@ import ( "context" "io" - "github.com/cocoonstack/epoch/cloudimg" - "github.com/cocoonstack/epoch/registryclient" + "github.com/cocoonstack/cocoon-common/cloudimg" + "github.com/cocoonstack/cocoon-common/oci" ) var _ cloudimg.BlobReader = blobReader{} -// blobReader adapts *registryclient.Client to cloudimg.BlobReader. +// blobReader adapts a Registry to cloudimg.BlobReader. type blobReader struct { - client *registryclient.Client - name string + registry oci.Registry + name string } // ReadBlob fetches a blob by digest. func (b blobReader) ReadBlob(ctx context.Context, digest string) (io.ReadCloser, error) { - return b.client.GetBlob(ctx, b.name, digest) + return b.registry.GetBlob(ctx, b.name, digest) } // cloudimgStream wraps cloudimg.Stream. diff --git a/snapshots/puller.go b/snapshots/puller.go index 61bb60f..50083c1 100644 --- a/snapshots/puller.go +++ b/snapshots/puller.go @@ -1,4 +1,5 @@ -// Package snapshots wraps the epoch SDK for pulling and pushing cocoon VM snapshots. +// Package snapshots pulls and pushes cocoon VM snapshots and cloud images +// to an OCI registry. package snapshots import ( @@ -6,21 +7,19 @@ import ( "context" "errors" "fmt" - "io" - "github.com/cocoonstack/epoch/manifest" - "github.com/cocoonstack/epoch/registryclient" - "github.com/cocoonstack/epoch/snapshot" + "github.com/cocoonstack/cocoon-common/oci" + "github.com/cocoonstack/cocoon-common/snapshot" "github.com/cocoonstack/vk-cocoon/vm" ) -// Puller streams a snapshot or cloud image from epoch into the local cocoon runtime. +// Puller streams a snapshot or cloud image from an OCI registry into the local cocoon runtime. type Puller struct { - Registry *registryclient.Client + Registry oci.Registry Runtime vm.Runtime } -// PullSnapshot fetches and imports a snapshot from epoch. localName defaults to name. +// PullSnapshot fetches and imports a snapshot from the registry. localName defaults to name. func (p *Puller) PullSnapshot(ctx context.Context, name, tag, localName string) error { raw, _, err := p.Registry.GetManifest(ctx, name, tag) if err != nil { @@ -51,26 +50,6 @@ func (p *Puller) PullSnapshot(ctx context.Context, name, tag, localName string) return nil } -// PullCloudImage fetches a cloud image manifest and writes raw disk bytes to w. -func (p *Puller) PullCloudImage(ctx context.Context, name, tag string, w io.Writer) error { - raw, err := p.fetchCloudImageManifest(ctx, name, tag) - if err != nil { - return err - } - adapter := blobReader{client: p.Registry, name: name} - return cloudimgStream(ctx, raw, adapter, w) -} - -// EnsureCloudImage streams a cloud-image artifact from epoch into `cocoon image -// import `. force=true bypasses the local-cache short-circuit. -func (p *Puller) EnsureCloudImage(ctx context.Context, name, tag, localName string, force bool) error { - raw, err := p.fetchCloudImageManifest(ctx, name, tag) - if err != nil { - return err - } - return p.EnsureCloudImageFromRaw(ctx, name, localName, raw, force) -} - // EnsureCloudImageFromRaw streams raw into `cocoon image import `. // Caller MUST have classified raw as KindCloudImage; this skips the fetch + // classify so callers that already verified the manifest don't pay twice. @@ -91,7 +70,7 @@ func (p *Puller) EnsureCloudImageFromRaw(ctx context.Context, name, localName st if err != nil { return fmt.Errorf("open cocoon image import: %w", err) } - adapter := blobReader{client: p.Registry, name: name} + adapter := blobReader{registry: p.Registry, name: name} if err := cloudimgStream(ctx, raw, adapter, importer); err != nil { _ = importer.Close() _ = wait() @@ -103,20 +82,3 @@ func (p *Puller) EnsureCloudImageFromRaw(ctx context.Context, name, localName st } return wait() } - -// fetchCloudImageManifest fetches and verifies the manifest at name:tag is a -// cloud-image artifact, returning its raw bytes for downstream streaming. -func (p *Puller) fetchCloudImageManifest(ctx context.Context, name, tag string) ([]byte, error) { - raw, _, err := p.Registry.GetManifest(ctx, name, tag) - if err != nil { - return nil, fmt.Errorf("get cloudimg manifest %s:%s: %w", name, tag, err) - } - kind, err := manifest.Classify(raw) - if err != nil { - return nil, fmt.Errorf("classify manifest: %w", err) - } - if kind != manifest.KindCloudImage { - return nil, fmt.Errorf("manifest %s:%s is not a cloud image (kind=%s)", name, tag, kind) - } - return raw, nil -} diff --git a/snapshots/pusher.go b/snapshots/pusher.go index 620b8bd..85b0819 100644 --- a/snapshots/pusher.go +++ b/snapshots/pusher.go @@ -6,18 +6,18 @@ import ( "fmt" "github.com/cocoonstack/cocoon-common/meta" - "github.com/cocoonstack/epoch/registryclient" - "github.com/cocoonstack/epoch/snapshot" + "github.com/cocoonstack/cocoon-common/oci" + "github.com/cocoonstack/cocoon-common/snapshot" "github.com/cocoonstack/vk-cocoon/vm" ) -// Pusher streams a local snapshot up into epoch. +// Pusher streams a local snapshot up into an OCI registry. type Pusher struct { - Registry *registryclient.Client + Registry oci.Registry Runtime vm.Runtime } -// PushSnapshot uploads a snapshot to epoch at the given repo/tag. +// PushSnapshot uploads a snapshot to the registry at the given repo/tag. func (p *Pusher) PushSnapshot(ctx context.Context, vmName, repo, tag, baseImage string) (*snapshot.PushResult, error) { repo = cmp.Or(repo, vmName) tag = cmp.Or(tag, meta.DefaultSnapshotTag) diff --git a/snapshots/runner.go b/snapshots/runner.go index f82954e..282ee65 100644 --- a/snapshots/runner.go +++ b/snapshots/runner.go @@ -4,13 +4,13 @@ import ( "context" "io" - "github.com/cocoonstack/epoch/snapshot" + "github.com/cocoonstack/cocoon-common/snapshot" "github.com/cocoonstack/vk-cocoon/vm" ) var _ snapshot.CocoonRunner = runnerAdapter{} -// runnerAdapter wraps vm.Runtime to satisfy epoch's snapshot.CocoonRunner. +// runnerAdapter wraps vm.Runtime to satisfy snapshot.CocoonRunner. type runnerAdapter struct { Runtime vm.Runtime } diff --git a/vm/cocoon_cli.go b/vm/cocoon_cli.go index 2336f8c..78311e6 100644 --- a/vm/cocoon_cli.go +++ b/vm/cocoon_cli.go @@ -91,7 +91,7 @@ func (c *CocoonCLI) Run(ctx context.Context, opts RunOptions) (*VM, error) { } // EnsureImage shells `cocoon image pull`; force=true adds --force. -// Cocoonstack cloud-image artifacts must go through Puller.EnsureCloudImage +// Cocoonstack cloud-image artifacts must go through Puller.EnsureCloudImageFromRaw // instead — `cocoon image pull` mistakes them for container images. func (c *CocoonCLI) EnsureImage(ctx context.Context, image string, force bool) error { if image == "" { diff --git a/vm/runtime.go b/vm/runtime.go index 6077c8b..b8562e0 100644 --- a/vm/runtime.go +++ b/vm/runtime.go @@ -17,8 +17,8 @@ var ( ErrVMNotFound = errors.New("vm not found") // ErrImageNotFound signals the cocoon CLI has authoritatively reported - // the image is not stored locally. Used by Puller.EnsureCloudImage as - // the idempotency probe for `cocoon image inspect`. + // the image is not stored locally. Used by Puller.EnsureCloudImageFromRaw + // as the idempotency probe for `cocoon image inspect`. ErrImageNotFound = errors.New("image not found") // ErrSnapshotNotFound is the sibling of ErrVMNotFound for snapshot inspect.