mirror of
https://github.com/lima-vm/lima.git
synced 2026-10-28 11:44:35 +08:00
Merge pull request #5489 from resker/fix/autostart-lima-home
fix(autostart): propagate LIMA_HOME into generated launchd/systemd units
This commit is contained in:
7 files changed
+97
-6
No files matched your search
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/lima-vm/lima/v2/pkg/autostart"
|
||||
"github.com/lima-vm/lima/v2/pkg/autostart/launchd"
|
||||
"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
|
||||
"github.com/lima-vm/lima/v2/pkg/store"
|
||||
"github.com/lima-vm/lima/v2/pkg/textutil"
|
||||
)
|
||||
@@ -95,22 +96,36 @@ func autostartDisableAction(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func daemonInstall(ctx context.Context, instName, workDir, userName string, keepAlive bool) error {
|
||||
selfExe, err := os.Executable()
|
||||
// renderDaemonPlist renders the system LaunchDaemon plist for an instance. Every key the
|
||||
// template references must be supplied here: a missing one renders as the literal string
|
||||
// "<no value>" rather than failing, which would be written into the installed plist.
|
||||
func renderDaemonPlist(binary, instName, workDir, userName string, keepAlive bool) ([]byte, error) {
|
||||
// The daemon inherits no environment from the shell that registers it, so LIMA_HOME
|
||||
// has to be recorded in the plist for the instance to be found at boot.
|
||||
limaHome, err := dirnames.LimaDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not determine limactl path: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vars := map[string]string{
|
||||
"Binary": selfExe,
|
||||
"Binary": binary,
|
||||
"Instance": instName,
|
||||
"LimaHome": limaHome,
|
||||
"WorkDir": workDir,
|
||||
"UserName": userName,
|
||||
}
|
||||
if keepAlive {
|
||||
vars["KeepAlive"] = "true"
|
||||
}
|
||||
content, err := textutil.ExecuteTemplate(launchd.DaemonTemplate, vars)
|
||||
return textutil.ExecuteTemplate(launchd.DaemonTemplate, vars)
|
||||
}
|
||||
|
||||
func daemonInstall(ctx context.Context, instName, workDir, userName string, keepAlive bool) error {
|
||||
selfExe, err := os.Executable()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not determine limactl path: %w", err)
|
||||
}
|
||||
|
||||
content, err := renderDaemonPlist(selfExe, instName, workDir, userName, keepAlive)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to render daemon plist: %w", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: Copyright The Lima Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
// The daemon plist is rendered from a map, and text/template turns a key the template
|
||||
// references but the map omits into the literal string "<no value>" instead of failing.
|
||||
// That would be written into /Library/LaunchDaemons verbatim, so assert that the rendered
|
||||
// plist carries the real LIMA_HOME and no placeholder.
|
||||
func TestRenderDaemonPlist(t *testing.T) {
|
||||
t.Setenv("LIMA_HOME", "/some/lima/home")
|
||||
for _, keepAlive := range []bool{false, true} {
|
||||
got, err := renderDaemonPlist("/limactl", "colima", "/some/path", "containerserver", keepAlive)
|
||||
assert.NilError(t, err)
|
||||
plist := string(got)
|
||||
assert.Assert(t, !strings.Contains(plist, "<no value>"),
|
||||
"plist has an unresolved template key:\n%s", plist)
|
||||
assert.Assert(t, strings.Contains(plist, "<key>LIMA_HOME</key>\n\t\t<string>/some/lima/home</string>"),
|
||||
"plist does not carry LIMA_HOME:\n%s", plist)
|
||||
assert.Assert(t, strings.Contains(plist, "<string>containerserver</string>"))
|
||||
assert.Equal(t, strings.Contains(plist, "<key>KeepAlive</key>"), keepAlive)
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,11 @@ func TestRenderTemplate(t *testing.T) {
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>io.lima-vm.autostart.default</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>LIMA_HOME</key>
|
||||
<string>/some/lima/home</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/limactl</string>
|
||||
@@ -120,6 +125,11 @@ func TestRenderTemplate(t *testing.T) {
|
||||
<string>io.lima-vm.daemon.k3s</string>
|
||||
<key>UserName</key>
|
||||
<string>alice</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>LIMA_HOME</key>
|
||||
<string>/some/lima/home</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/limactl</string>
|
||||
@@ -155,6 +165,11 @@ func TestRenderTemplate(t *testing.T) {
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>io.lima-vm.autostart.default</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>LIMA_HOME</key>
|
||||
<string>/some/lima/home</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/limactl</string>
|
||||
@@ -197,6 +212,11 @@ func TestRenderTemplate(t *testing.T) {
|
||||
<string>io.lima-vm.daemon.k3s</string>
|
||||
<key>UserName</key>
|
||||
<string>alice</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>LIMA_HOME</key>
|
||||
<string>/some/lima/home</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/limactl</string>
|
||||
@@ -236,6 +256,7 @@ Description=Lima - Linux virtual machines, with a focus on running containers.
|
||||
Documentation=man:lima(1)
|
||||
|
||||
[Service]
|
||||
Environment=LIMA_HOME=/some/lima/home
|
||||
ExecStart=/limactl start %i --foreground
|
||||
WorkingDirectory=%h
|
||||
Type=simple
|
||||
@@ -259,6 +280,7 @@ Description=Lima - Linux virtual machines, with a focus on running containers.
|
||||
Documentation=man:lima(1)
|
||||
|
||||
[Service]
|
||||
Environment=LIMA_HOME=/some/lima/home
|
||||
ExecStart=/limactl start %i --foreground
|
||||
WorkingDirectory=%h
|
||||
Type=simple
|
||||
@@ -276,6 +298,9 @@ WantedBy=default.target
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
// Pin LIMA_HOME so the rendered unit does not depend on the host running the test.
|
||||
// The directory does not exist, so LimaDir() returns it without resolving symlinks.
|
||||
t.Setenv("LIMA_HOME", "/some/lima/home")
|
||||
tmpl, err := tt.Manager.renderTemplate(tt.InstanceName, tt.WorkDir, tt.GetExecutable)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, string(tmpl), tt.Expected)
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>io.lima-vm.autostart.{{ .Instance }}</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>LIMA_HOME</key>
|
||||
<string>{{ .LimaHome }}</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>{{ .Binary }}</string>
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
<string>io.lima-vm.daemon.{{ .Instance }}</string>
|
||||
<key>UserName</key>
|
||||
<string>{{ .UserName }}</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>LIMA_HOME</key>
|
||||
<string>{{ .LimaHome }}</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>{{ .Binary }}</string>
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/lima-vm/lima/v2/pkg/limatype"
|
||||
"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
|
||||
"github.com/lima-vm/lima/v2/pkg/textutil"
|
||||
)
|
||||
|
||||
@@ -120,9 +121,18 @@ func (t *TemplateFileBasedManager) renderTemplate(instName, workDir string, getE
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The generated unit must carry LIMA_HOME explicitly: neither launchd nor systemd
|
||||
// inherits the environment of the shell that registered the instance, so without it
|
||||
// the autostarted `limactl start` would look for the instance under the default
|
||||
// ~/.lima and not find one that lives anywhere else.
|
||||
limaHome, err := dirnames.LimaDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := map[string]string{
|
||||
"Binary": selfExeAbs,
|
||||
"Instance": instName,
|
||||
"LimaHome": limaHome,
|
||||
"WorkDir": workDir,
|
||||
}
|
||||
maps.Copy(data, t.extraTemplateVars)
|
||||
|
||||
@@ -3,6 +3,7 @@ Description=Lima - Linux virtual machines, with a focus on running containers.
|
||||
Documentation=man:lima(1)
|
||||
|
||||
[Service]
|
||||
Environment=LIMA_HOME={{.LimaHome}}
|
||||
ExecStart={{.Binary}} start %i --foreground
|
||||
WorkingDirectory=%h
|
||||
Type=simple
|
||||
|
||||
Reference in new issue
Block a user