diff --git a/distrobuilder/main_incus.go b/distrobuilder/main_incus.go index 9dc64433..792908b6 100644 --- a/distrobuilder/main_incus.go +++ b/distrobuilder/main_incus.go @@ -1,6 +1,7 @@ package main import ( + "context" "errors" "fmt" "io" @@ -352,6 +353,13 @@ func (c *cmdIncus) run(cmd *cobra.Command, args []string, overlayDir string) err return fmt.Errorf("Failed to mount UEFI partition: %w", err) } + rootfsDevUUID, err := vm.findRootfsDevUUID() + if err != nil { + return fmt.Errorf("Failed to find rootfs device UUID: %w", err) + } + + c.global.ctx = context.WithValue(c.global.ctx, shared.ContextKeyEnviron, + []string{fmt.Sprintf("ROOTFS_DEVICE_UUID=%s", rootfsDevUUID)}) // We cannot use Incus' rsync package as that uses the --delete flag which // causes an issue due to the boot/efi directory being present. err = shared.RsyncLocal(c.global.ctx, overlayDir+"/", vmDir) diff --git a/distrobuilder/vm.go b/distrobuilder/vm.go index f10c770b..0b1e494c 100644 --- a/distrobuilder/vm.go +++ b/distrobuilder/vm.go @@ -61,6 +61,35 @@ func (v *vm) getUEFIDevFile() string { return fmt.Sprintf("%sp1", v.loopDevice) } +func (v *vm) findRootfsDevUUID() (rootUUID string, err error) { + rootfsDevFile := v.getRootfsDevFile() + if rootfsDevFile == "" { + err = fmt.Errorf("Failed to get rootfs device name.") + return + } + + var out strings.Builder + if err = shared.RunCommand(v.ctx, nil, &out, "blkid", "-o", "export", rootfsDevFile); err != nil { + err = fmt.Errorf("Failed to get rootfs device UUID: %w", err) + return + } + + fields := strings.Fields(out.String()) + for _, field := range fields { + if strings.HasPrefix(field, "UUID=") { + rootUUID = field + break + } + } + + if rootUUID == "" { + err = fmt.Errorf("No rootfs device UUID found") + return + } + + return +} + func (v *vm) createEmptyDiskImage() error { f, err := os.Create(v.imageFile) if err != nil { diff --git a/shared/util.go b/shared/util.go index 9ac98158..8f126ab8 100644 --- a/shared/util.go +++ b/shared/util.go @@ -18,6 +18,8 @@ import ( yaml "gopkg.in/yaml.v2" ) +const ContextKeyEnviron = ContextKey("environ") + // EnvVariable represents a environment variable. type EnvVariable struct { Value string @@ -27,6 +29,9 @@ type EnvVariable struct { // Environment represents a set of environment variables. type Environment map[string]EnvVariable +// ContextKey type. +type ContextKey string + // Copy copies a file. func Copy(src, dest string) error { var err error @@ -56,6 +61,10 @@ func Copy(src, dest string) error { // RunCommand runs a command. Stdout is written to the given io.Writer. If nil, it's written to the real stdout. Stderr is always written to the real stderr. func RunCommand(ctx context.Context, stdin io.Reader, stdout io.Writer, name string, arg ...string) error { cmd := exec.CommandContext(ctx, name, arg...) + env, ok := ctx.Value(ContextKeyEnviron).([]string) + if ok && len(env) > 0 { + cmd.Env = append(os.Environ(), env...) + } if stdin != nil { cmd.Stdin = stdin