7
7
"fmt"
8
8
"io"
9
9
"os"
10
+ "os/exec"
10
11
"path/filepath"
11
12
"strings"
12
13
"sync"
@@ -20,11 +21,10 @@ import (
20
21
"github.com/containers/podman/v5/pkg/bindings/containers"
21
22
"github.com/containers/podman/v5/pkg/bindings/images"
22
23
"github.com/containers/podman/v5/pkg/domain/entities/types"
23
- "github.com/containers/podman/v5/pkg/specgen"
24
24
"github.com/docker/go-units"
25
- specs "github.com/opencontainers/runtime-spec/specs-go"
26
25
"github.com/sirupsen/logrus"
27
26
"golang.org/x/sys/unix"
27
+ "golang.org/x/term"
28
28
)
29
29
30
30
// As a baseline heuristic we double the size of
@@ -306,7 +306,7 @@ func (p *BootcDisk) pullImage() (err error) {
306
306
}
307
307
308
308
// runInstallContainer runs the bootc installer in a container to create a disk image
309
- func (p * BootcDisk ) runInstallContainer (quiet bool , config DiskImageConfig ) ( err error ) {
309
+ func (p * BootcDisk ) runInstallContainer (quiet bool , config DiskImageConfig ) error {
310
310
// Create a temporary external shell script with the contents of our losetup wrapper
311
311
losetupTemp , err := os .CreateTemp (p .Directory , "losetup-wrapper" )
312
312
if err != nil {
@@ -320,55 +320,17 @@ func (p *BootcDisk) runInstallContainer(quiet bool, config DiskImageConfig) (err
320
320
return fmt .Errorf ("temp losetup wrapper chmod: %w" , err )
321
321
}
322
322
323
- createResponse , err := p .createInstallContainer (config , losetupTemp .Name ())
324
- if err != nil {
325
- return fmt .Errorf ("failed to create container: %w" , err )
326
- }
327
-
328
- p .bootcInstallContainerId = createResponse .ID //save the id for possible cleanup
329
- logrus .Debugf ("Created install container, id=%s" , createResponse .ID )
330
-
331
- // run the container to create the disk
332
- err = containers .Start (p .Ctx , p .bootcInstallContainerId , & containers.StartOptions {})
333
- if err != nil {
334
- return fmt .Errorf ("failed to start container: %w" , err )
335
- }
336
- logrus .Debugf ("Started install container" )
337
-
338
- // Ensure we've cancelled the container attachment when exiting this function, as
339
- // it takes over stdout/stderr handling
340
- attachCancelCtx , cancelAttach := context .WithCancel (p .Ctx )
341
- defer cancelAttach ()
342
- var exitCode int32
343
- if ! quiet {
344
- attachOpts := new (containers.AttachOptions ).WithStream (true )
345
- if err := containers .Attach (attachCancelCtx , p .bootcInstallContainerId , os .Stdin , os .Stdout , os .Stderr , nil , attachOpts ); err != nil {
346
- return fmt .Errorf ("attaching: %w" , err )
347
- }
323
+ c := p .createInstallContainer (config , losetupTemp .Name ())
324
+ if err := c .Run (); err != nil {
325
+ return fmt .Errorf ("failed to invoke install: %w" , err )
348
326
}
349
- exitCode , err = containers .Wait (p .Ctx , p .bootcInstallContainerId , nil )
350
- if err != nil {
351
- return fmt .Errorf ("failed to wait for container: %w" , err )
352
- }
353
-
354
- if exitCode != 0 {
355
- return fmt .Errorf ("failed to run bootc install" )
356
- }
357
-
358
- return
327
+ return nil
359
328
}
360
329
361
- // createInstallContainer creates a container to run the bootc installer
362
- func (p * BootcDisk ) createInstallContainer (config DiskImageConfig , tempLosetup string ) (createResponse types.ContainerCreateResponse , err error ) {
363
- privileged := true
364
- autoRemove := true
365
- labelNested := true
366
-
367
- targetEnv := make (map [string ]string )
368
- if v , ok := os .LookupEnv ("BOOTC_INSTALL_LOG" ); ok {
369
- targetEnv ["RUST_LOG" ] = v
370
- }
371
-
330
+ // createInstallContainer creates a podman command to run the bootc installer.
331
+ // Note: This code used to use the Go bindings for the podman remote client, but the
332
+ // Attach interface currently leaks goroutines.
333
+ func (p * BootcDisk ) createInstallContainer (config DiskImageConfig , tempLosetup string ) * exec.Cmd {
372
334
bootcInstallArgs := []string {
373
335
"bootc" , "install" , "to-disk" , "--via-loopback" , "--generic-image" ,
374
336
"--skip-fetch-check" ,
@@ -381,60 +343,27 @@ func (p *BootcDisk) createInstallContainer(config DiskImageConfig, tempLosetup s
381
343
}
382
344
bootcInstallArgs = append (bootcInstallArgs , "/output/" + filepath .Base (p .file .Name ()))
383
345
384
- // Allocate pty so we can show progress bars, spinners etc.
385
- trueDat := true
386
- s := & specgen.SpecGenerator {
387
- ContainerBasicConfig : specgen.ContainerBasicConfig {
388
- Command : bootcInstallArgs ,
389
- PidNS : specgen.Namespace {NSMode : specgen .Host },
390
- Remove : & autoRemove ,
391
- Annotations : map [string ]string {"io.podman.annotations.label" : "type:unconfined_t" },
392
- Env : targetEnv ,
393
- Terminal : & trueDat ,
394
- },
395
- ContainerStorageConfig : specgen.ContainerStorageConfig {
396
- Image : p .ImageNameOrId ,
397
- Mounts : []specs.Mount {
398
- {
399
- Source : "/var/lib/containers" ,
400
- Destination : "/var/lib/containers" ,
401
- Type : "bind" ,
402
- },
403
- {
404
- Source : "/dev" ,
405
- Destination : "/dev" ,
406
- Type : "bind" ,
407
- },
408
- {
409
- Source : p .Directory ,
410
- Destination : "/output" ,
411
- Type : "bind" ,
412
- },
413
- {
414
- Source : tempLosetup ,
415
- // Note that the default $PATH has /usr/local/sbin first
416
- Destination : "/usr/local/sbin/losetup" ,
417
- Type : "bind" ,
418
- Options : []string {"ro" },
419
- },
420
- },
421
- },
422
- ContainerSecurityConfig : specgen.ContainerSecurityConfig {
423
- Privileged : & privileged ,
424
- LabelNested : & labelNested ,
425
- SelinuxOpts : []string {"type:unconfined_t" },
426
- },
427
- ContainerNetworkConfig : specgen.ContainerNetworkConfig {
428
- NetNS : specgen.Namespace {
429
- NSMode : specgen .Bridge ,
430
- },
431
- },
432
- }
433
-
434
- createResponse , err = containers .CreateWithSpec (p .Ctx , s , & containers.CreateOptions {})
435
- if err != nil {
436
- return createResponse , fmt .Errorf ("failed to create container: %w" , err )
346
+ // Basic config:
347
+ // - force on --remote because we depend on podman machine.
348
+ // - add privileged, pid=host, SELinux config and bind mounts per https://containers.github.io/bootc/bootc-install.html
349
+ 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" }
350
+ // Custom bind mounts
351
+ podmanArgs = append (podmanArgs , fmt .Sprintf ("--volume=%s:/output" , p .Directory ), fmt .Sprintf ("--volume=%s:/usr/local/sbin/losetup:ro" , tempLosetup ))
352
+ if term .IsTerminal (int (os .Stdin .Fd ())) {
353
+ podmanArgs = append (podmanArgs , "-t" )
437
354
}
438
-
439
- return
355
+ // Other conditional arguments
356
+ if v , ok := os .LookupEnv ("BOOTC_INSTALL_LOG" ); ok {
357
+ podmanArgs = append (podmanArgs , fmt .Sprintf ("--env=RUST_LOG=%s" , v ))
358
+ }
359
+ // The image name
360
+ podmanArgs = append (podmanArgs , p .ImageNameOrId )
361
+ // And the remaining arguments for bootc install
362
+ podmanArgs = append (podmanArgs , bootcInstallArgs ... )
363
+
364
+ c := exec .Command ("podman" , podmanArgs ... )
365
+ c .Stdin = os .Stdin
366
+ c .Stdout = os .Stdout
367
+ c .Stderr = os .Stderr
368
+ return c
440
369
}
0 commit comments