diff --git a/cli/output.go b/cli/output.go deleted file mode 100644 index 3da6dd7..0000000 --- a/cli/output.go +++ /dev/null @@ -1,54 +0,0 @@ -// Package cli holds small CLI-output helpers shared across cocoon-macos subcommands. They mirror -// cocoon's "-o table|json" UX with the standard library, so the surfaces match without importing -// cocoon's heavy cmd/core command package. -package cli - -import ( - "encoding/json" - "fmt" - "os" - "text/tabwriter" - "time" - - "github.com/docker/go-units" - "github.com/spf13/cobra" -) - -// AddFormatFlag registers the -o/--format flag (table or json) used by list commands. -func AddFormatFlag(cmd *cobra.Command) { - cmd.Flags().StringP("format", "o", "table", `output format: "table" or "json"`) -} - -// OutputFormatted prints data as JSON when --format=json, otherwise renders a padded table via write. -func OutputFormatted(cmd *cobra.Command, data any, write func(w *tabwriter.Writer)) error { - if format, _ := cmd.Flags().GetString("format"); format == "json" { - return OutputJSON(data) - } - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - write(w) - return w.Flush() -} - -// OutputJSON prints v as indented JSON on stdout. -func OutputJSON(v any) error { - b, err := json.MarshalIndent(v, "", " ") - if err != nil { - return fmt.Errorf("marshal json: %w", err) - } - fmt.Println(string(b)) - return nil -} - -// FormatSize renders a byte count as a human-readable size via the same units.HumanSize cocoon -// uses (e.g. 13.46GB), so sizes match across the two CLIs. -func FormatSize(b int64) string { - return units.HumanSize(float64(b)) -} - -// FormatTime renders an RFC3339 timestamp string as local "2006-01-02 15:04:05"; raw on parse failure. -func FormatTime(s string) string { - if t, err := time.Parse(time.RFC3339, s); err == nil { - return t.Local().Format(time.DateTime) - } - return s -} diff --git a/cmd/image/commands.go b/cmd/image/commands.go index 69ef014..c3453d5 100644 --- a/cmd/image/commands.go +++ b/cmd/image/commands.go @@ -3,7 +3,7 @@ package image import ( "github.com/spf13/cobra" - "github.com/cocoonstack/cocoon-macos/cli" + "github.com/cocoonstack/cocoon/cmd/cliutil" ) // Actions is the image-subcommand surface, backed by cocoon's cloudimg store. @@ -27,7 +27,7 @@ func Command(h Actions) *cobra.Command { pull.Flags().Bool("force", false, "re-pull even if already present") list := &cobra.Command{Use: "list", Aliases: []string{"ls"}, Short: "List images", RunE: h.List} - cli.AddFormatFlag(list) + cliutil.AddFormatFlag(list) inspect := &cobra.Command{Use: "inspect REF", Short: "Show one image (JSON)", Args: cobra.ExactArgs(1), RunE: h.Inspect} rm := &cobra.Command{Use: "rm REF [REF...]", Short: "Remove image(s)", Args: cobra.MinimumNArgs(1), RunE: h.RM} diff --git a/cmd/image/handler.go b/cmd/image/handler.go index b5d92b8..515c71a 100644 --- a/cmd/image/handler.go +++ b/cmd/image/handler.go @@ -2,16 +2,15 @@ package image import ( "fmt" - "strings" "text/tabwriter" "time" "github.com/projecteru2/core/log" "github.com/spf13/cobra" + "github.com/cocoonstack/cocoon/cmd/cliutil" "github.com/cocoonstack/cocoon/progress" - "github.com/cocoonstack/cocoon-macos/cli" "github.com/cocoonstack/cocoon-macos/home" ) @@ -34,7 +33,7 @@ func (h *Handler) Pull(cmd *cobra.Command, args []string) error { } ref := args[0] force, _ := cmd.Flags().GetBool("force") - if isURL(ref) { + if cliutil.IsURL(ref) { return s.Pull(ctx, ref, force, progress.Nop) } // mirror the URL path's idempotency: skip the multi-GiB registry download when already present @@ -66,11 +65,11 @@ func (h *Handler) List(cmd *cobra.Command, _ []string) error { if err != nil { return err } - return cli.OutputFormatted(cmd, imgs, func(w *tabwriter.Writer) { + return cliutil.OutputFormatted(cmd, imgs, func(w *tabwriter.Writer) { fmt.Fprintln(w, "NAME\tTYPE\tSIZE\tDIGEST\tCREATED") //nolint:errcheck for _, img := range imgs { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", //nolint:errcheck - img.Name, img.Type, cli.FormatSize(img.Size), + img.Name, img.Type, cliutil.FormatSize(img.Size), shortDigest(img.ID), img.CreatedAt.Local().Format(time.DateTime)) } }) @@ -89,7 +88,7 @@ func (h *Handler) Inspect(cmd *cobra.Command, args []string) error { if img == nil { return fmt.Errorf("image not found: %s", args[0]) } - return cli.OutputJSON(img) + return cliutil.OutputJSON(img) } // RM deletes one or more images from the store, printing each removed ref. @@ -108,10 +107,6 @@ func (h *Handler) RM(cmd *cobra.Command, args []string) error { return nil } -func isURL(s string) bool { - return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") -} - func shortDigest(id string) string { const maxLen = 19 // "sha256:" + 12 hex chars, the conventional short-digest length if len(id) > maxLen { diff --git a/cmd/vm/clone.go b/cmd/vm/clone.go index 4af2ed8..d7214aa 100644 --- a/cmd/vm/clone.go +++ b/cmd/vm/clone.go @@ -7,6 +7,7 @@ import ( "github.com/spf13/cobra" + "github.com/cocoonstack/cocoon/cmd/cliutil" "github.com/cocoonstack/cocoon/utils" "github.com/cocoonstack/cocoon-macos/home" @@ -41,7 +42,7 @@ func (h *Handler) Clone(cmd *cobra.Command, args []string) error { if err != nil { return err } - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) copied, err := copyDataDisks(dir, srcRec.DataDisks) if err != nil { return err diff --git a/cmd/vm/commands.go b/cmd/vm/commands.go index 036a88d..2bd6b0d 100644 --- a/cmd/vm/commands.go +++ b/cmd/vm/commands.go @@ -3,7 +3,7 @@ package vm import ( "github.com/spf13/cobra" - "github.com/cocoonstack/cocoon-macos/cli" + "github.com/cocoonstack/cocoon/cmd/cliutil" ) // Actions mirrors cocoon's cmd/vm Actions interface, trimmed to the v0.1 macOS @@ -64,7 +64,7 @@ func Command(h Actions) *cobra.Command { Short: "List VMs with status", RunE: h.List, } - cli.AddFormatFlag(listCmd) + cliutil.AddFormatFlag(listCmd) inspectCmd := &cobra.Command{ Use: "inspect VM", diff --git a/cmd/vm/helper.go b/cmd/vm/helper.go index 333202b..90e27d5 100644 --- a/cmd/vm/helper.go +++ b/cmd/vm/helper.go @@ -13,8 +13,8 @@ import ( "github.com/spf13/cobra" - "github.com/cocoonstack/cocoon/config" - "github.com/cocoonstack/cocoon/images/cloudimg" + "github.com/cocoonstack/cocoon/cmd/cliutil" + "github.com/cocoonstack/cocoon/images" "github.com/cocoonstack/cocoon/lock/flock" "github.com/cocoonstack/cocoon/types" "github.com/cocoonstack/cocoon/utils" @@ -74,7 +74,7 @@ func scaffoldVM(cmd *cobra.Command, name, image, varsSrc, varsName string) (dir, return "", "", "", "", err } overlay = filepath.Join(dir, "disk.qcow2") - if err = bakeOverlay(home.Ctx(cmd), base, overlay); err != nil { + if err = bakeOverlay(cliutil.CommandContext(cmd), base, overlay); err != nil { return "", "", "", "", err } ovmfVars = filepath.Join(dir, varsName) @@ -174,7 +174,7 @@ func resolveBase(cmd *cobra.Command, image, name string) (string, string, error) // UEFI firmware exists (it targets cloud-hypervisor). cocoon-macos boots via OVMF + OpenCore and // DISCARDS that BootConfig, so the file is never read — it only unblocks Config's validation. func ensureCloudimgFirmware(cmd *cobra.Command) { - fw := cloudimg.NewConfig(&config.Config{RootDir: home.Dir(cmd)}).FirmwarePath() + fw := images.FirmwarePath(home.Dir(cmd)) if utils.ValidFile(fw) { return } diff --git a/cmd/vm/lifecycle.go b/cmd/vm/lifecycle.go index b6adfaf..1997e42 100644 --- a/cmd/vm/lifecycle.go +++ b/cmd/vm/lifecycle.go @@ -10,6 +10,7 @@ import ( "github.com/projecteru2/core/log" "github.com/spf13/cobra" + "github.com/cocoonstack/cocoon/cmd/cliutil" "github.com/cocoonstack/cocoon/utils" "github.com/cocoonstack/cocoon-macos/home" @@ -43,7 +44,7 @@ func (h *Handler) Run(cmd *cobra.Command, args []string) error { // Start boots one or more previously-created VMs, reusing each persisted TAP/netns // across stop/start (only rm tears those down). func (h *Handler) Start(cmd *cobra.Command, args []string) error { - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) for _, n := range args { dir := home.VMDir(cmd, n) if err := withVMLock(ctx, dir, func() error { @@ -66,7 +67,7 @@ func (h *Handler) Start(cmd *cobra.Command, args []string) error { // Stop terminates one or more running VMs. --force skips the ACPI grace window (immediate SIGKILL). func (h *Handler) Stop(cmd *cobra.Command, args []string) error { grace := graceFromFlags(cmd) - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) for _, n := range args { dir := home.VMDir(cmd, n) if err := withVMLock(ctx, dir, func() error { @@ -90,7 +91,7 @@ func (h *Handler) Stop(cmd *cobra.Command, args []string) error { // (immediate SIGKILL), which also reaps a wedged qemu. func (h *Handler) RM(cmd *cobra.Command, args []string) error { grace := graceFromFlags(cmd) - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) for _, n := range args { dir := home.VMDir(cmd, n) if _, err := os.Stat(dir); os.IsNotExist(err) { @@ -135,7 +136,7 @@ func (h *Handler) create(cmd *cobra.Command, image string) (*record, error) { if err != nil { return nil, err } - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) cpus, _ := cmd.Flags().GetInt("cpus") mem, _ := cmd.Flags().GetString("memory") vnc, _ := cmd.Flags().GetInt("vnc") @@ -165,7 +166,7 @@ func (h *Handler) create(cmd *cobra.Command, image string) (*record, error) { // launch boots qemu for the record's spec, records the PID, and applies the VNC password (if any). func (h *Handler) launch(cmd *cobra.Command, dir string, r *record) error { - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) logger := log.WithFunc("cmd.vm.launch") if hostIsAMD() { // macOS reads MSRs an AMD host lacks; without kvm.ignore_msrs KVM injects #GP. Best-effort, diff --git a/cmd/vm/net_linux.go b/cmd/vm/net_linux.go index 0d6cc3d..43a12a0 100644 --- a/cmd/vm/net_linux.go +++ b/cmd/vm/net_linux.go @@ -13,6 +13,7 @@ import ( "github.com/projecteru2/core/log" "github.com/spf13/cobra" + "github.com/cocoonstack/cocoon/cmd/cliutil" "github.com/cocoonstack/cocoon/config" "github.com/cocoonstack/cocoon/network" "github.com/cocoonstack/cocoon/network/bridge" @@ -59,7 +60,7 @@ func provisionNet(cmd *cobra.Command, r *record) (tap, netns, mac string, err er if err != nil { return "", "", "", err } - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) // CPU=1 => NetNumQueues yields a single-queue TAP matching QEMU's single-queue -netdev tap,ifname= vmCfg := &types.VMConfig{Config: types.Config{CPU: 1}, Name: r.Name} nsPath, err := provider.Prepare(ctx, r.VMID, vmCfg) @@ -83,7 +84,7 @@ func teardownNet(cmd *cobra.Command, r *record) { if !r.TapOwned { return } - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) // warn instead of failing: rm must proceed, but a leaked TAP/netns should leave a trail if provider, err := newProvider(cmd, r); err != nil { log.WithFunc("cmd.vm.teardownNet").Warnf(ctx, "teardown network for %s: %v", r.VMID, err) diff --git a/cmd/vm/query.go b/cmd/vm/query.go index 0129c66..543b030 100644 --- a/cmd/vm/query.go +++ b/cmd/vm/query.go @@ -7,10 +7,12 @@ import ( "path/filepath" "strconv" "text/tabwriter" + "time" "github.com/spf13/cobra" - "github.com/cocoonstack/cocoon-macos/cli" + "github.com/cocoonstack/cocoon/cmd/cliutil" + "github.com/cocoonstack/cocoon-macos/home" ) @@ -24,12 +26,12 @@ func (h *Handler) List(cmd *cobra.Command, _ []string) error { recs = append(recs, r) } } - return cli.OutputFormatted(cmd, recs, func(w *tabwriter.Writer) { + return cliutil.OutputFormatted(cmd, recs, func(w *tabwriter.Writer) { fmt.Fprintln(w, "NAME\tSTATE\tCPU\tMEM\tNET\tVNC\tSSH\tIMAGE\tCREATED") //nolint:errcheck for _, r := range recs { fmt.Fprintf(w, "%s\t%s\t%d\t%sM\t%s\t%s\t%s\t%s\t%s\n", //nolint:errcheck r.Name, vmState(r), r.CPUs, r.Memory, cmp.Or(r.NetMode, netUser), - vncCol(r), sshCol(r), r.Image, cli.FormatTime(r.Created)) + vncCol(r), sshCol(r), r.Image, formatTime(r.Created)) } }) } @@ -40,7 +42,7 @@ func (h *Handler) Inspect(cmd *cobra.Command, args []string) error { if err != nil { return err } - return cli.OutputJSON(r) + return cliutil.OutputJSON(r) } // Console prints the VNC endpoint and SSH command for reaching a VM ("-" when disabled), @@ -82,3 +84,11 @@ func sshCol(r *record) string { } return strconv.Itoa(r.SSHPort) } + +// formatTime renders the record's RFC3339 Created stamp as local time.DateTime; raw on parse failure. +func formatTime(s string) string { + if t, err := time.Parse(time.RFC3339, s); err == nil { + return t.Local().Format(time.DateTime) + } + return s +} diff --git a/cmd/vm/snapshot.go b/cmd/vm/snapshot.go index 6942ed3..9ebc09b 100644 --- a/cmd/vm/snapshot.go +++ b/cmd/vm/snapshot.go @@ -6,6 +6,8 @@ import ( "github.com/spf13/cobra" + "github.com/cocoonstack/cocoon/cmd/cliutil" + "github.com/cocoonstack/cocoon-macos/home" "github.com/cocoonstack/cocoon-macos/qemu" ) @@ -14,7 +16,7 @@ import ( // the image, and +invtsc blocks the live-migration codepath savevm relies on). func (h *Handler) Snapshot(cmd *cobra.Command, args []string) error { dir := home.VMDir(cmd, args[0]) - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) tag, _ := cmd.Flags().GetString("tag") if tag == "" { tag = "snap-" + time.Now().Format("20060102-150405") @@ -45,7 +47,7 @@ func (h *Handler) Snapshot(cmd *cobra.Command, args []string) error { // which stops it, reverts, and relaunches. func (h *Handler) Restore(cmd *cobra.Command, args []string) error { dir := home.VMDir(cmd, args[0]) - ctx := home.Ctx(cmd) + ctx := cliutil.CommandContext(cmd) var tag string if err := withVMLock(ctx, dir, func() error { r, err := loadRec(dir) diff --git a/go.mod b/go.mod index c5bf93a..0784d87 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/cocoonstack/cocoon-macos -go 1.25.6 +go 1.26.4 require ( - github.com/cocoonstack/cocoon v0.4.2 + github.com/cocoonstack/cocoon v0.4.5 github.com/docker/go-units v0.5.0 github.com/opencontainers/image-spec v1.1.1 github.com/projecteru2/core v0.0.0-20241016125006-ff909eefe04c @@ -38,10 +38,12 @@ require ( github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.50.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.41.0 // indirect - golang.org/x/text v0.28.0 // indirect - google.golang.org/grpc v1.69.0 // indirect - google.golang.org/protobuf v1.36.7 // indirect + golang.org/x/text v0.34.0 // indirect + golang.org/x/tools v0.42.0 // indirect + google.golang.org/grpc v1.79.3 // indirect + google.golang.org/protobuf v1.36.10 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect ) diff --git a/go.sum b/go.sum index 37bfab4..ad338f1 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,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.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cocoonstack/cocoon v0.4.2 h1:Dnr3h4y5waE6livFFdv5wJWRROyyt2J3MK4/U84ZixY= -github.com/cocoonstack/cocoon v0.4.2/go.mod h1:M0JC8xR8VlNxwMHpkfLJvl32ckr2Yc1a1hRwJYqzI98= +github.com/cocoonstack/cocoon v0.4.5 h1:FoLgZhXdfKFvTJcF1ijb6A3C4n22NDOtwT2FDsoJbgM= +github.com/cocoonstack/cocoon v0.4.5/go.mod h1:LOFTBWiRXYsZJtNIF6UctDO0gThKCaG5+NOQ+ahGgv8= github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo= github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= github.com/containernetworking/plugins v1.9.0 h1:Mg3SXBdRGkdXyFC4lcwr6u2ZB2SDeL6LC3U+QrEANuQ= @@ -116,8 +116,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn 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= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +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/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= @@ -135,25 +135,25 @@ 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/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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +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/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.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +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/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 v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= -google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI= -google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= -google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= -google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= diff --git a/home/home.go b/home/home.go index 4f15ea0..161acb4 100644 --- a/home/home.go +++ b/home/home.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" - "github.com/cocoonstack/cocoon/config" + "github.com/cocoonstack/cocoon/cmd/cliutil" "github.com/cocoonstack/cocoon/images/cloudimg" ) @@ -39,20 +39,11 @@ func VMDir(cmd *cobra.Command, name string) string { return filepath.Join(VMsDir(cmd), name) } -// Ctx returns the command's context, falling back to context.Background() if cobra never set one -// (e.g. a unit test invoking a handler directly). -func Ctx(cmd *cobra.Command) context.Context { - if ctx := cmd.Context(); ctx != nil { - return ctx - } - return context.Background() -} - // OpenStore opens the shared cloudimg store rooted at the resolved state dir, returning the // command context alongside it. func OpenStore(cmd *cobra.Command) (context.Context, *cloudimg.CloudImg, error) { - ctx := Ctx(cmd) - s, err := cloudimg.New(ctx, &config.Config{RootDir: Dir(cmd)}) + ctx := cliutil.CommandContext(cmd) + s, err := cloudimg.New(ctx, Dir(cmd)) if err != nil { return ctx, nil, fmt.Errorf("init cloudimg store: %w", err) }