From a6ca564b53769b5e57664d5ce9abde68459e1427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 24 Nov 2023 07:58:46 +0100 Subject: [PATCH 1/4] Move NerdctlArchiveCache function to cacheutil MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders F Björklund --- pkg/cacheutil/cacheutil.go | 50 ++++++++++++++++++++++++++++++++++++++ pkg/instance/start.go | 40 ++---------------------------- 2 files changed, 52 insertions(+), 38 deletions(-) create mode 100644 pkg/cacheutil/cacheutil.go diff --git a/pkg/cacheutil/cacheutil.go b/pkg/cacheutil/cacheutil.go new file mode 100644 index 00000000000..23b5d8f0371 --- /dev/null +++ b/pkg/cacheutil/cacheutil.go @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package cacheutil + +import ( + "context" + "fmt" + + "github.com/lima-vm/lima/pkg/downloader" + "github.com/lima-vm/lima/pkg/fileutils" + "github.com/lima-vm/lima/pkg/limayaml" +) + +// EnsureNerdctlArchiveCache prefetches the archive into the cache. +// +// EnsureNerdctlArchiveCache prefetches the nerdctl-full-VERSION-GOOS-GOARCH.tar.gz archive +// into the cache before launching the hostagent process, so that we can show the progress in tty. +// https://github.com/lima-vm/lima/issues/326 +func EnsureNerdctlArchiveCache(ctx context.Context, y *limayaml.LimaYAML, created bool) (string, error) { + if !*y.Containerd.System && !*y.Containerd.User { + // nerdctl archive is not needed + return "", nil + } + + errs := make([]error, len(y.Containerd.Archives)) + for i, f := range y.Containerd.Archives { + // Skip downloading again if the file is already in the cache + if created && f.Arch == *y.Arch && !downloader.IsLocal(f.Location) { + path, err := fileutils.CachedFile(f) + if err == nil { + return path, nil + } + } + path, err := fileutils.DownloadFile(ctx, "", f, false, "the nerdctl archive", *y.Arch) + if err != nil { + errs[i] = err + continue + } + if path == "" { + if downloader.IsLocal(f.Location) { + return f.Location, nil + } + return "", fmt.Errorf("cache did not contain %q", f.Location) + } + return path, nil + } + + return "", fileutils.Errors(errs) +} diff --git a/pkg/instance/start.go b/pkg/instance/start.go index b53de08183a..52bddd17ff3 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -18,6 +18,7 @@ import ( "time" "github.com/coreos/go-semver/semver" + "github.com/lima-vm/lima/pkg/cacheutil" "github.com/lima-vm/lima/pkg/driver" "github.com/lima-vm/lima/pkg/driverutil" "github.com/lima-vm/lima/pkg/executil" @@ -26,8 +27,6 @@ import ( "github.com/lima-vm/lima/pkg/qemu/entitlementutil" "github.com/mattn/go-isatty" - "github.com/lima-vm/lima/pkg/downloader" - "github.com/lima-vm/lima/pkg/fileutils" hostagentevents "github.com/lima-vm/lima/pkg/hostagent/events" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store" @@ -39,41 +38,6 @@ import ( // to be running before timing out. const DefaultWatchHostAgentEventsTimeout = 10 * time.Minute -// ensureNerdctlArchiveCache prefetches the nerdctl-full-VERSION-GOOS-GOARCH.tar.gz archive -// into the cache before launching the hostagent process, so that we can show the progress in tty. -// https://github.com/lima-vm/lima/issues/326 -func ensureNerdctlArchiveCache(ctx context.Context, y *limayaml.LimaYAML, created bool) (string, error) { - if !*y.Containerd.System && !*y.Containerd.User { - // nerdctl archive is not needed - return "", nil - } - - errs := make([]error, len(y.Containerd.Archives)) - for i, f := range y.Containerd.Archives { - // Skip downloading again if the file is already in the cache - if created && f.Arch == *y.Arch && !downloader.IsLocal(f.Location) { - path, err := fileutils.CachedFile(f) - if err == nil { - return path, nil - } - } - path, err := fileutils.DownloadFile(ctx, "", f, false, "the nerdctl archive", *y.Arch) - if err != nil { - errs[i] = err - continue - } - if path == "" { - if downloader.IsLocal(f.Location) { - return f.Location, nil - } - return "", fmt.Errorf("cache did not contain %q", f.Location) - } - return path, nil - } - - return "", fileutils.Errors(errs) -} - type Prepared struct { Driver driver.Driver NerdctlArchiveCache string @@ -101,7 +65,7 @@ func Prepare(ctx context.Context, inst *store.Instance) (*Prepared, error) { if err := limaDriver.CreateDisk(ctx); err != nil { return nil, err } - nerdctlArchiveCache, err := ensureNerdctlArchiveCache(ctx, inst.Config, created) + nerdctlArchiveCache, err := cacheutil.EnsureNerdctlArchiveCache(ctx, inst.Config, created) if err != nil { return nil, err } From 0682b370b199c55490a0a129695c8fcbd9e61a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 24 Nov 2023 08:00:29 +0100 Subject: [PATCH 2/4] Add command to install the guest components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will install the lima-guestagent in the instance. It will install nerdctl-full.tgz, if it has been enabled. Signed-off-by: Anders F Björklund --- cmd/limactl/guest-install.go | 104 +++++++++++++++++++++++++++++++++++ cmd/limactl/main.go | 1 + pkg/cacheutil/cacheutil.go | 15 ++++- 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 cmd/limactl/guest-install.go diff --git a/cmd/limactl/guest-install.go b/cmd/limactl/guest-install.go new file mode 100644 index 00000000000..3d656f52353 --- /dev/null +++ b/cmd/limactl/guest-install.go @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/lima-vm/lima/pkg/cacheutil" + "github.com/lima-vm/lima/pkg/store" + "github.com/lima-vm/lima/pkg/store/filenames" + "github.com/lima-vm/lima/pkg/usrlocalsharelima" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +func newGuestInstallCommand() *cobra.Command { + guestInstallCommand := &cobra.Command{ + Use: "guest-install INSTANCE", + Short: "Install guest components", + Args: WrapArgsError(cobra.MaximumNArgs(1)), + RunE: guestInstallAction, + ValidArgsFunction: cobra.NoFileCompletions, + Hidden: true, + } + return guestInstallCommand +} + +func runCmd(name string, flags []string, args ...string) error { + cmd := exec.Command(name, append(flags, args...)...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + logrus.Debugf("executing %v", cmd.Args) + return cmd.Run() +} + +func guestInstallAction(cmd *cobra.Command, args []string) error { + instName := DefaultInstanceName + if len(args) > 0 { + instName = args[0] + } + + inst, err := store.Inspect(instName) + if err != nil { + return err + } + if inst.Status == store.StatusStopped { + return fmt.Errorf("instance %q is stopped, run `limactl start %s` to start the instance", instName, instName) + } + + sshExe := "ssh" + sshConfig := filepath.Join(inst.Dir, filenames.SSHConfig) + sshFlags := []string{"-F", sshConfig} + + scpExe := "scp" + scpFlags := sshFlags + + hostname := fmt.Sprintf("lima-%s", inst.Name) + prefix := *inst.Config.GuestInstallPrefix + + // lima-guestagent + guestAgentBinary, err := usrlocalsharelima.GuestAgentBinary(*inst.Config.OS, *inst.Config.Arch) + if err != nil { + return err + } + tmp := "/tmp/lima-guestagent" + bin := prefix + "/bin/lima-guestagent" + logrus.Infof("Copying %q to %s", guestAgentBinary, hostname) + scpArgs := []string{guestAgentBinary, hostname + ":" + tmp} + if err := runCmd(scpExe, scpFlags, scpArgs...); err != nil { + return nil + } + logrus.Infof("Installing %s to %s", tmp, bin) + sshArgs := []string{hostname, "sudo", "install", "-m", "755", tmp, bin} + if err := runCmd(sshExe, sshFlags, sshArgs...); err != nil { + return nil + } + + // nerdctl-full.tgz + nerdctlFilename := cacheutil.NerdctlArchive(inst.Config) + if nerdctlFilename != "" { + nerdctlArchive, err := cacheutil.EnsureNerdctlArchiveCache(cmd.Context(), inst.Config, false) + if err != nil { + return err + } + tmp := "/tmp/nerdctl-full.tgz" + logrus.Infof("Copying %q to %s", nerdctlFilename, hostname) + scpArgs := []string{nerdctlArchive, hostname + ":" + tmp} + if err := runCmd(scpExe, scpFlags, scpArgs...); err != nil { + return nil + } + logrus.Infof("Installing %s in %s", tmp, prefix) + sshArgs := []string{hostname, "sudo", "tar", "Cxzf", prefix, tmp} + if err := runCmd(sshExe, sshFlags, sshArgs...); err != nil { + return nil + } + } + + return nil +} diff --git a/cmd/limactl/main.go b/cmd/limactl/main.go index a428a9fd875..bd89d8b0333 100644 --- a/cmd/limactl/main.go +++ b/cmd/limactl/main.go @@ -171,6 +171,7 @@ func newApp() *cobra.Command { newValidateCommand(), newPruneCommand(), newHostagentCommand(), + newGuestInstallCommand(), newInfoCommand(), newShowSSHCommand(), newDebugCommand(), diff --git a/pkg/cacheutil/cacheutil.go b/pkg/cacheutil/cacheutil.go index 23b5d8f0371..c2c3f4af35c 100644 --- a/pkg/cacheutil/cacheutil.go +++ b/pkg/cacheutil/cacheutil.go @@ -6,14 +6,25 @@ package cacheutil import ( "context" "fmt" + "path" "github.com/lima-vm/lima/pkg/downloader" "github.com/lima-vm/lima/pkg/fileutils" "github.com/lima-vm/lima/pkg/limayaml" ) -// EnsureNerdctlArchiveCache prefetches the archive into the cache. -// +// NerdctlArchive returns the basename of the archive. +func NerdctlArchive(y *limayaml.LimaYAML) string { + if *y.Containerd.System || *y.Containerd.User { + for _, f := range y.Containerd.Archives { + if f.Arch == *y.Arch { + return path.Base(f.Location) + } + } + } + return "" +} + // EnsureNerdctlArchiveCache prefetches the nerdctl-full-VERSION-GOOS-GOARCH.tar.gz archive // into the cache before launching the hostagent process, so that we can show the progress in tty. // https://github.com/lima-vm/lima/issues/326 From 927ecca96f1184c474c8efdde9c69fc8f9ba10f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Sun, 28 Apr 2024 15:45:09 +0200 Subject: [PATCH 3/4] Use temporary file names for installing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders F Björklund --- cmd/limactl/guest-install.go | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/cmd/limactl/guest-install.go b/cmd/limactl/guest-install.go index 3d656f52353..1c7774f4050 100644 --- a/cmd/limactl/guest-install.go +++ b/cmd/limactl/guest-install.go @@ -4,6 +4,7 @@ package main import ( + "bytes" "fmt" "os" "os/exec" @@ -38,6 +39,16 @@ func runCmd(name string, flags []string, args ...string) error { return cmd.Run() } +func shell(name string, flags []string, args ...string) (string, error) { + cmd := exec.Command(name, append(flags, args...)...) + out, err := cmd.Output() + if err != nil { + return "", err + } + out = bytes.TrimSuffix(out, []byte{'\n'}) + return string(out), nil +} + func guestInstallAction(cmd *cobra.Command, args []string) error { instName := DefaultInstanceName if len(args) > 0 { @@ -67,18 +78,23 @@ func guestInstallAction(cmd *cobra.Command, args []string) error { if err != nil { return err } - tmp := "/tmp/lima-guestagent" + tmpname := "lima-guestagent" + tmp, err := shell(sshExe, sshFlags, hostname, "mktemp", "-t", "lima-guestagent.XXXXXX") + if err != nil { + return err + } bin := prefix + "/bin/lima-guestagent" - logrus.Infof("Copying %q to %s", guestAgentBinary, hostname) + logrus.Infof("Copying %q to %s:%s", guestAgentBinary, inst.Name, tmpname) scpArgs := []string{guestAgentBinary, hostname + ":" + tmp} if err := runCmd(scpExe, scpFlags, scpArgs...); err != nil { return nil } - logrus.Infof("Installing %s to %s", tmp, bin) + logrus.Infof("Installing %s to %s", tmpname, bin) sshArgs := []string{hostname, "sudo", "install", "-m", "755", tmp, bin} if err := runCmd(sshExe, sshFlags, sshArgs...); err != nil { return nil } + _, _ = shell(sshExe, sshFlags, hostname, "rm", tmp) // nerdctl-full.tgz nerdctlFilename := cacheutil.NerdctlArchive(inst.Config) @@ -87,17 +103,22 @@ func guestInstallAction(cmd *cobra.Command, args []string) error { if err != nil { return err } - tmp := "/tmp/nerdctl-full.tgz" - logrus.Infof("Copying %q to %s", nerdctlFilename, hostname) + tmpname := "nerdctl-full.tgz" + tmp, err := shell(sshExe, sshFlags, hostname, "mktemp", "-t", "nerdctl-full.XXXXXX.tgz") + if err != nil { + return err + } + logrus.Infof("Copying %q to %s:%s", nerdctlFilename, inst.Name, tmpname) scpArgs := []string{nerdctlArchive, hostname + ":" + tmp} if err := runCmd(scpExe, scpFlags, scpArgs...); err != nil { return nil } - logrus.Infof("Installing %s in %s", tmp, prefix) + logrus.Infof("Installing %s in %s", tmpname, prefix) sshArgs := []string{hostname, "sudo", "tar", "Cxzf", prefix, tmp} if err := runCmd(sshExe, sshFlags, sshArgs...); err != nil { return nil } + _, _ = shell(sshExe, sshFlags, hostname, "rm", tmp) } return nil From 43f5f5b5984c5de204dde7053037c329709ddbea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Thu, 11 Jul 2024 10:50:45 +0200 Subject: [PATCH 4/4] Decompress the guestagent if required MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders F Björklund --- cmd/limactl/guest-install.go | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/cmd/limactl/guest-install.go b/cmd/limactl/guest-install.go index 1c7774f4050..82e5290a335 100644 --- a/cmd/limactl/guest-install.go +++ b/cmd/limactl/guest-install.go @@ -5,7 +5,10 @@ package main import ( "bytes" + "compress/gzip" + "errors" "fmt" + "io" "os" "os/exec" "path/filepath" @@ -78,13 +81,41 @@ func guestInstallAction(cmd *cobra.Command, args []string) error { if err != nil { return err } + guestAgentFilename := filepath.Base(guestAgentBinary) + if _, err := os.Stat(guestAgentBinary); err != nil { + if !errors.Is(err, os.ErrNotExist) { + return err + } + compressedGuestAgent, err := os.Open(guestAgentBinary + ".gz") + if err != nil { + return err + } + defer compressedGuestAgent.Close() + tmpGuestAgent, err := os.CreateTemp("", "lima-guestagent-") + if err != nil { + return err + } + logrus.Debugf("Decompressing %s.gz", guestAgentBinary) + guestAgent, err := gzip.NewReader(compressedGuestAgent) + if err != nil { + return err + } + defer guestAgent.Close() + _, err = io.Copy(tmpGuestAgent, guestAgent) + if err != nil { + return err + } + tmpGuestAgent.Close() + guestAgentBinary = tmpGuestAgent.Name() + defer os.RemoveAll(guestAgentBinary) + } tmpname := "lima-guestagent" tmp, err := shell(sshExe, sshFlags, hostname, "mktemp", "-t", "lima-guestagent.XXXXXX") if err != nil { return err } bin := prefix + "/bin/lima-guestagent" - logrus.Infof("Copying %q to %s:%s", guestAgentBinary, inst.Name, tmpname) + logrus.Infof("Copying %q to %s:%s", guestAgentFilename, inst.Name, tmpname) scpArgs := []string{guestAgentBinary, hostname + ":" + tmp} if err := runCmd(scpExe, scpFlags, scpArgs...); err != nil { return nil