77 "fmt"
88 "io"
99 "os"
10+ "os/exec"
1011 "path/filepath"
1112 "strings"
1213 "sync"
@@ -19,11 +20,10 @@ import (
1920
2021 "github.com/containers/podman/v5/pkg/bindings/containers"
2122 "github.com/containers/podman/v5/pkg/domain/entities/types"
22- "github.com/containers/podman/v5/pkg/specgen"
2323 "github.com/docker/go-units"
24- specs "github.com/opencontainers/runtime-spec/specs-go"
2524 "github.com/sirupsen/logrus"
2625 "golang.org/x/sys/unix"
26+ "golang.org/x/term"
2727)
2828
2929// As a baseline heuristic we double the size of
@@ -290,7 +290,7 @@ func (p *BootcDisk) pullImage() error {
290290}
291291
292292// runInstallContainer runs the bootc installer in a container to create a disk image
293- func (p * BootcDisk ) runInstallContainer (quiet bool , config DiskImageConfig ) ( err error ) {
293+ func (p * BootcDisk ) runInstallContainer (quiet bool , config DiskImageConfig ) error {
294294 // Create a temporary external shell script with the contents of our losetup wrapper
295295 losetupTemp , err := os .CreateTemp (p .Directory , "losetup-wrapper" )
296296 if err != nil {
@@ -304,55 +304,17 @@ func (p *BootcDisk) runInstallContainer(quiet bool, config DiskImageConfig) (err
304304 return fmt .Errorf ("temp losetup wrapper chmod: %w" , err )
305305 }
306306
307- createResponse , err := p .createInstallContainer (config , losetupTemp .Name ())
308- if err != nil {
309- return fmt .Errorf ("failed to create container: %w" , err )
310- }
311-
312- p .bootcInstallContainerId = createResponse .ID //save the id for possible cleanup
313- logrus .Debugf ("Created install container, id=%s" , createResponse .ID )
314-
315- // run the container to create the disk
316- err = containers .Start (p .Ctx , p .bootcInstallContainerId , & containers.StartOptions {})
317- if err != nil {
318- return fmt .Errorf ("failed to start container: %w" , err )
319- }
320- logrus .Debugf ("Started install container" )
321-
322- // Ensure we've cancelled the container attachment when exiting this function, as
323- // it takes over stdout/stderr handling
324- attachCancelCtx , cancelAttach := context .WithCancel (p .Ctx )
325- defer cancelAttach ()
326- var exitCode int32
327- if ! quiet {
328- attachOpts := new (containers.AttachOptions ).WithStream (true )
329- if err := containers .Attach (attachCancelCtx , p .bootcInstallContainerId , os .Stdin , os .Stdout , os .Stderr , nil , attachOpts ); err != nil {
330- return fmt .Errorf ("attaching: %w" , err )
331- }
332- }
333- exitCode , err = containers .Wait (p .Ctx , p .bootcInstallContainerId , nil )
334- if err != nil {
335- return fmt .Errorf ("failed to wait for container: %w" , err )
307+ c := p .createInstallContainer (config , losetupTemp .Name ())
308+ if err := c .Run (); err != nil {
309+ return fmt .Errorf ("failed to invoke install: %w" , err )
336310 }
337-
338- if exitCode != 0 {
339- return fmt .Errorf ("failed to run bootc install" )
340- }
341-
342- return
311+ return nil
343312}
344313
345- // createInstallContainer creates a container to run the bootc installer
346- func (p * BootcDisk ) createInstallContainer (config DiskImageConfig , tempLosetup string ) (createResponse types.ContainerCreateResponse , err error ) {
347- privileged := true
348- autoRemove := true
349- labelNested := true
350-
351- targetEnv := make (map [string ]string )
352- if v , ok := os .LookupEnv ("BOOTC_INSTALL_LOG" ); ok {
353- targetEnv ["RUST_LOG" ] = v
354- }
355-
314+ // createInstallContainer creates a podman command to run the bootc installer.
315+ // Note: This code used to use the Go bindings for the podman remote client, but the
316+ // Attach interface currently leaks goroutines.
317+ func (p * BootcDisk ) createInstallContainer (config DiskImageConfig , tempLosetup string ) * exec.Cmd {
356318 bootcInstallArgs := []string {
357319 "bootc" , "install" , "to-disk" , "--via-loopback" , "--generic-image" ,
358320 "--skip-fetch-check" ,
@@ -365,60 +327,27 @@ func (p *BootcDisk) createInstallContainer(config DiskImageConfig, tempLosetup s
365327 }
366328 bootcInstallArgs = append (bootcInstallArgs , "/output/" + filepath .Base (p .file .Name ()))
367329
368- // Allocate pty so we can show progress bars, spinners etc.
369- trueDat := true
370- s := & specgen.SpecGenerator {
371- ContainerBasicConfig : specgen.ContainerBasicConfig {
372- Command : bootcInstallArgs ,
373- PidNS : specgen.Namespace {NSMode : specgen .Host },
374- Remove : & autoRemove ,
375- Annotations : map [string ]string {"io.podman.annotations.label" : "type:unconfined_t" },
376- Env : targetEnv ,
377- Terminal : & trueDat ,
378- },
379- ContainerStorageConfig : specgen.ContainerStorageConfig {
380- Image : p .ImageNameOrId ,
381- Mounts : []specs.Mount {
382- {
383- Source : "/var/lib/containers" ,
384- Destination : "/var/lib/containers" ,
385- Type : "bind" ,
386- },
387- {
388- Source : "/dev" ,
389- Destination : "/dev" ,
390- Type : "bind" ,
391- },
392- {
393- Source : p .Directory ,
394- Destination : "/output" ,
395- Type : "bind" ,
396- },
397- {
398- Source : tempLosetup ,
399- // Note that the default $PATH has /usr/local/sbin first
400- Destination : "/usr/local/sbin/losetup" ,
401- Type : "bind" ,
402- Options : []string {"ro" },
403- },
404- },
405- },
406- ContainerSecurityConfig : specgen.ContainerSecurityConfig {
407- Privileged : & privileged ,
408- LabelNested : & labelNested ,
409- SelinuxOpts : []string {"type:unconfined_t" },
410- },
411- ContainerNetworkConfig : specgen.ContainerNetworkConfig {
412- NetNS : specgen.Namespace {
413- NSMode : specgen .Bridge ,
414- },
415- },
330+ // Basic config:
331+ // - force on --remote because we depend on podman machine.
332+ // - add privileged, pid=host, SELinux config and bind mounts per https://containers.github.io/bootc/bootc-install.html
333+ podmanArgs := []string {"--remote" , "run" , "--rm" , "-i" , "--pid=host" , "--privileged" , "--security-opt=label=type:unconfined_t" , "--volume=/dev:/dev" , "--volume=/var/lib/containers:/var/lib/containers" }
334+ // Custom bind mounts
335+ podmanArgs = append (podmanArgs , fmt .Sprintf ("--volume=%s:/output" , p .Directory ), fmt .Sprintf ("--volume=%s:/usr/local/sbin/losetup:ro" , tempLosetup ))
336+ if term .IsTerminal (int (os .Stdin .Fd ())) {
337+ podmanArgs = append (podmanArgs , "-t" )
416338 }
417-
418- createResponse , err = containers .CreateWithSpec (p .Ctx , s , & containers.CreateOptions {})
419- if err != nil {
420- return createResponse , fmt .Errorf ("failed to create container: %w" , err )
421- }
422-
423- return
339+ // Other conditional arguments
340+ if v , ok := os .LookupEnv ("BOOTC_INSTALL_LOG" ); ok {
341+ podmanArgs = append (podmanArgs , fmt .Sprintf ("--env=RUST_LOG=%s" , v ))
342+ }
343+ // The image name
344+ podmanArgs = append (podmanArgs , p .ImageNameOrId )
345+ // And the remaining arguments for bootc install
346+ podmanArgs = append (podmanArgs , bootcInstallArgs ... )
347+
348+ c := exec .Command ("podman" , podmanArgs ... )
349+ c .Stdin = os .Stdin
350+ c .Stdout = os .Stdout
351+ c .Stderr = os .Stderr
352+ return c
424353}
0 commit comments