7
7
"fmt"
8
8
"io"
9
9
"os"
10
+ "os/exec"
10
11
"path/filepath"
11
12
"strings"
12
13
"sync"
@@ -19,11 +20,10 @@ import (
19
20
20
21
"github.com/containers/podman/v5/pkg/bindings/containers"
21
22
"github.com/containers/podman/v5/pkg/domain/entities/types"
22
- "github.com/containers/podman/v5/pkg/specgen"
23
23
"github.com/docker/go-units"
24
- specs "github.com/opencontainers/runtime-spec/specs-go"
25
24
"github.com/sirupsen/logrus"
26
25
"golang.org/x/sys/unix"
26
+ "golang.org/x/term"
27
27
)
28
28
29
29
// As a baseline heuristic we double the size of
@@ -290,7 +290,7 @@ func (p *BootcDisk) pullImage() error {
290
290
}
291
291
292
292
// 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 {
294
294
// Create a temporary external shell script with the contents of our losetup wrapper
295
295
losetupTemp , err := os .CreateTemp (p .Directory , "losetup-wrapper" )
296
296
if err != nil {
@@ -304,55 +304,17 @@ func (p *BootcDisk) runInstallContainer(quiet bool, config DiskImageConfig) (err
304
304
return fmt .Errorf ("temp losetup wrapper chmod: %w" , err )
305
305
}
306
306
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 )
336
310
}
337
-
338
- if exitCode != 0 {
339
- return fmt .Errorf ("failed to run bootc install" )
340
- }
341
-
342
- return
311
+ return nil
343
312
}
344
313
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 {
356
318
bootcInstallArgs := []string {
357
319
"bootc" , "install" , "to-disk" , "--via-loopback" , "--generic-image" ,
358
320
"--skip-fetch-check" ,
@@ -365,60 +327,27 @@ func (p *BootcDisk) createInstallContainer(config DiskImageConfig, tempLosetup s
365
327
}
366
328
bootcInstallArgs = append (bootcInstallArgs , "/output/" + filepath .Base (p .file .Name ()))
367
329
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" )
416
338
}
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
424
353
}
0 commit comments