Merge pull request #5528 from trodemaster/feat/vz-guest-os-version-log

feat(vz): record the guest macOS version from the restore image
This commit is contained in:
Akihiro Suda authored and GitHub committed 2026-10-02 17:44:42 +09:00
commit 348603ebd7
9 files changed
+131 -4

No files matched your search

+2
View File
@@ -66,6 +66,8 @@ LIMA_CIDATA_SKIP_DEFAULT_DEPENDENCY_RESOLUTION=
LIMA_CIDATA_VMTYPE={{ .VMType }}
LIMA_CIDATA_VSOCK_PORT={{ .VSockPort }}
LIMA_CIDATA_VIRTIO_PORT={{ .VirtioPort}}
LIMA_CIDATA_GUEST_OS_VERSION={{ .GuestOSVersion }}
LIMA_CIDATA_GUEST_OS_BUILD_VERSION={{ .GuestOSBuildVersion }}
{{- if .Plain}}
LIMA_CIDATA_PLAIN=1
{{- else}}
+5
View File
@@ -35,6 +35,7 @@ import (
"github.com/lima-vm/lima/v2/pkg/networks/usernet"
"github.com/lima-vm/lima/v2/pkg/osutil"
"github.com/lima-vm/lima/v2/pkg/sshutil"
"github.com/lima-vm/lima/v2/pkg/store"
"github.com/lima-vm/lima/v2/pkg/strutil"
)
@@ -125,10 +126,12 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
}
archive := "nerdctl-full.tgz"
var darwinOpts limatype.DarwinOpts
var guestOSVersion, guestOSBuildVersion string
if *instConfig.OS == limatype.DARWIN {
if err := limayaml.Convert(instConfig.OsOpts[limatype.DARWIN], &darwinOpts, "osOpts.Darwin"); err != nil {
return nil, err
}
guestOSVersion, guestOSBuildVersion = store.ReadGuestOSVersion(instDir)
}
var suppressFirstLoginSetupPlist string
if darwinOpts.SuppressFirstLoginSetupPlist != nil {
@@ -166,6 +169,8 @@ func templateArgs(ctx context.Context, bootScripts bool, instDir, name string, i
SuppressFirstLoginSetup: darwinOpts.SuppressFirstLoginSetup != nil && *darwinOpts.SuppressFirstLoginSetup,
SuppressFirstLoginSetupPlist: suppressFirstLoginSetupPlist,
GuestOSVersion: guestOSVersion,
GuestOSBuildVersion: guestOSBuildVersion,
}
firstUsernetIndex := limayaml.FirstUsernetIndex(instConfig)
+2
View File
@@ -139,6 +139,8 @@ type TemplateArgs struct {
TPM bool
SuppressFirstLoginSetup bool
SuppressFirstLoginSetupPlist string // empty = use built-in plist
GuestOSVersion string // macOS guests only: restore-image OS version, e.g. "27.0.0"; empty otherwise
GuestOSBuildVersion string // macOS guests only: restore-image build, e.g. "26A428"; empty otherwise
}
func (t *TemplateArgs) generateWindowsInitialPassword() error {
+38
View File
@@ -6,11 +6,13 @@ package cidata
import (
"fmt"
"io"
"slices"
"strings"
"testing"
"gotest.tools/v3/assert"
"github.com/lima-vm/lima/v2/pkg/iso9660util"
"github.com/lima-vm/lima/v2/pkg/limatype"
)
@@ -172,6 +174,42 @@ func TestTemplate9p(t *testing.T) {
}
}
func TestTemplateGuestOSVersion(t *testing.T) {
args := &TemplateArgs{
Name: "default",
User: "foo",
UID: 501,
Home: "/home/foo.guest",
Shell: "/bin/bash",
SSHPubKeys: []string{"ssh-rsa dummy foo@example.com"},
MountType: "reverse-sshfs",
OS: "Darwin",
GuestOSVersion: "27.0.0",
GuestOSBuildVersion: "26A428",
}
limaEnv := renderLimaEnv(t, args)
assert.Assert(t, strings.Contains(limaEnv, "LIMA_CIDATA_GUEST_OS_VERSION=27.0.0\n"))
assert.Assert(t, strings.Contains(limaEnv, "LIMA_CIDATA_GUEST_OS_BUILD_VERSION=26A428\n"))
// Emitted empty when unknown, like the other optional LIMA_CIDATA_* vars.
args.GuestOSVersion = ""
args.GuestOSBuildVersion = ""
limaEnv = renderLimaEnv(t, args)
assert.Assert(t, strings.Contains(limaEnv, "LIMA_CIDATA_GUEST_OS_VERSION=\n"))
assert.Assert(t, strings.Contains(limaEnv, "LIMA_CIDATA_GUEST_OS_BUILD_VERSION=\n"))
}
func renderLimaEnv(t *testing.T, args *TemplateArgs) string {
t.Helper()
layout, err := ExecuteTemplateCIDataISO(args)
assert.NilError(t, err)
idx := slices.IndexFunc(layout, func(f iso9660util.Entry) bool { return f.Path == "lima.env" })
assert.Assert(t, idx >= 0, "lima.env not found in cidata layout")
b, err := io.ReadAll(layout[idx].Reader)
assert.NilError(t, err)
return string(b)
}
// TestTemplateNICRename is a regression test for
// https://github.com/lima-vm/lima/issues/4792 (Ubuntu 26.04 first-boot NIC
// rename race, LP: #2136392): user-data must contain the rename/wait bootcmd,
+11
View File
@@ -65,6 +65,17 @@ func newMacPlatformConfiguration(inst *limatype.Instance) (vz.PlatformConfigurat
if err != nil {
return nil, err
}
// Record the guest macOS version too: the IPSW is removed after
// installation, so this is the only point where it can be read.
osVersion := ipswImage.OperatingSystemVersion().String()
buildVersion := ipswImage.BuildVersion()
logrus.Infof("Guest macOS version: %s (build %s)", osVersion, buildVersion)
if err = os.WriteFile(filepath.Join(inst.Dir, filenames.VzGuestOSVersion), []byte(osVersion+"\n"), 0o644); err != nil {
return nil, err
}
if err = os.WriteFile(filepath.Join(inst.Dir, filenames.VzGuestOSBuildVersion), []byte(buildVersion+"\n"), 0o644); err != nil {
return nil, err
}
ipswMostFeatureFul := ipswImage.MostFeaturefulSupportedConfiguration()
hwModelData = ipswMostFeatureFul.HardwareModel().DataRepresentation()
if err = os.WriteFile(hwModelFile, hwModelData, 0o666); err != nil {
+6 -4
View File
@@ -69,10 +69,12 @@ const (
ExternalDriverStderrLog = "driver.stderr.log"
BootSession = "boot-session.tmp" // boot of the host during which the PID files in the same directory were written
VzIdentifier = "vz-identifier"
VzHwModel = "vz-hwmodel" // macOS guests only
VzAux = "vz-aux" // macOS guests only
VzEfi = "vz-efi" // efi variable store
QemuEfiCodeFD = "qemu-efi-code.fd" // efi code; not always created
VzHwModel = "vz-hwmodel" // macOS guests only
VzGuestOSVersion = "vz-guest-os-version" // macOS guests only; restore-image OS version, e.g. "27.0.0"
VzGuestOSBuildVersion = "vz-guest-os-build-version" // macOS guests only; restore-image build, e.g. "26A428"
VzAux = "vz-aux" // macOS guests only
VzEfi = "vz-efi" // efi variable store
QemuEfiCodeFD = "qemu-efi-code.fd" // efi code; not always created
AnsibleInventoryYAML = "ansible-inventory.yaml"
// SocketDir is the default location for forwarded sockets with a relative paths in HostSocket.
+35
View File
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package store
import (
"errors"
"os"
"path/filepath"
"strings"
"github.com/sirupsen/logrus"
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
)
// ReadGuestOSVersion returns the macOS version (e.g. "27.0.0") and build
// version (e.g. "26A428") of the restore image that a macOS guest on the vz
// driver was installed from. Both are empty when unknown: for non-macOS guests,
// and for instances installed before the version was recorded.
func ReadGuestOSVersion(instDir string) (version, buildVersion string) {
return readOptionalFile(filepath.Join(instDir, filenames.VzGuestOSVersion)),
readOptionalFile(filepath.Join(instDir, filenames.VzGuestOSBuildVersion))
}
func readOptionalFile(path string) string {
b, err := os.ReadFile(path)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
logrus.WithError(err).Warnf("Failed to read %#q", path)
}
return ""
}
return strings.TrimSpace(string(b))
}
+28
View File
@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package store
import (
"os"
"path/filepath"
"testing"
"gotest.tools/v3/assert"
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
)
func TestReadGuestOSVersion(t *testing.T) {
instDir := t.TempDir()
version, buildVersion := ReadGuestOSVersion(instDir)
assert.Equal(t, version, "")
assert.Equal(t, buildVersion, "")
assert.NilError(t, os.WriteFile(filepath.Join(instDir, filenames.VzGuestOSVersion), []byte("27.0.0\n"), 0o644))
assert.NilError(t, os.WriteFile(filepath.Join(instDir, filenames.VzGuestOSBuildVersion), []byte("26A428\n"), 0o644))
version, buildVersion = ReadGuestOSVersion(instDir)
assert.Equal(t, version, "27.0.0")
assert.Equal(t, buildVersion, "26A428")
}
+4
View File
@@ -73,6 +73,8 @@ VZ:
- `vz.pid`: VZ PID
- `vz-identifier`: Unique machine identifier file for a VM
- `vz-hwmodel`: Hardware model information for a Mac VM
- `vz-guest-os-version`: macOS version of the restore image a Mac VM was created from, e.g. `27.0.0` (written once, at create time)
- `vz-guest-os-build-version`: build number of that restore image, e.g. `26A428`
- `vz-aux`: Auxiliary storage for a Mac VM
- `vz-efi`: EFIVariable store file for a VM
@@ -233,6 +235,8 @@ The volume label is "cidata", as defined by [cloud-init NoCloud](https://docs.cl
- `LIMA_CIDATA_VMTYPE`: the VM driver type ("qemu", "vz", "wsl2", ...), used to conditionally enable VM-specific behavior (e.g. virtiofs mounts on `vz`).
- `LIMA_CIDATA_VSOCK_PORT`: the vsock port used by the guest agent (0 when vsock is not used).
- `LIMA_CIDATA_VIRTIO_PORT`: the virtio-serial port name used by the guest agent (empty when virtio-serial is not used).
- `LIMA_CIDATA_GUEST_OS_VERSION`: for macOS guests on the `vz` driver, the macOS version of the restore image the instance was created from, e.g. `27.0.0`. Empty for other guests and for instances created before this was recorded.
- `LIMA_CIDATA_GUEST_OS_BUILD_VERSION`: the corresponding build number, e.g. `26A428`. Empty in the same cases.
- `LIMA_CIDATA_PLAIN`: set to "1" when the instance is in [plain mode](../config/plain.md) (no mounts, port forwarding, or containerd), empty otherwise.
- `LIMA_CIDATA_NO_CLOUD_INIT`: set to "1" if cloud-init should be skipped on this boot, empty otherwise.