From 63480079446c796b19fe41dbd01cffc98249dbd3 Mon Sep 17 00:00:00 2001 From: Alex-Andrei Cioc Date: Tue, 7 Oct 2025 15:58:35 +0300 Subject: [PATCH 1/4] feat(compose): Add support for build args in compose file Signed-off-by: Alex-Andrei Cioc --- initrd/dockerfile.go | 18 +++++++ initrd/options.go | 25 ++++++++-- internal/cli/kraft/build/build.go | 49 ++++++++++--------- .../cli/kraft/cloud/compose/build/build.go | 2 + internal/cli/kraft/pkg/packager_cli_kernel.go | 2 +- .../kraft/pkg/packager_kraftfile_runtime.go | 2 +- .../kraft/pkg/packager_kraftfile_unikraft.go | 2 +- internal/cli/kraft/pkg/pkg.go | 1 + internal/cli/kraft/utils/rootfs.go | 3 +- 9 files changed, 71 insertions(+), 33 deletions(-) diff --git a/initrd/dockerfile.go b/initrd/dockerfile.go index 37c9200d9..2de0fde02 100644 --- a/initrd/dockerfile.go +++ b/initrd/dockerfile.go @@ -394,6 +394,24 @@ func (initrd *dockerfile) Build(ctx context.Context) (string, error) { attrs["target"] = buildTarget } + if initrd.opts.buildArgs != nil { + for k, v := range initrd.opts.buildArgs { + if v == nil { + v, ok := os.LookupEnv(k) + if !ok { + log.G(ctx). + WithField("arg", k). + Warn("could not find build-arg in environment") + continue + } + attrs["build-arg:"+k] = v + } else { + attrs["build-arg:"+k] = *v + } + } + } + + // Override build args from the command line for _, arg := range buildArgs { k, v, ok := strings.Cut(arg, "=") if !ok { diff --git a/initrd/options.go b/initrd/options.go index 6c764e088..8cb181eaa 100644 --- a/initrd/options.go +++ b/initrd/options.go @@ -5,11 +5,12 @@ package initrd type InitrdOptions struct { - compress bool - output string - cacheDir string - arch string - workdir string + compress bool + output string + cacheDir string + arch string + workdir string + buildArgs map[string]*string } // Whether the resulting CPIO archive file should be compressed. @@ -37,6 +38,11 @@ func (opts InitrdOptions) Workdir() string { return opts.workdir } +// The build arguments that may be used by certain initrd builders. +func (opts InitrdOptions) BuildArgs() map[string]*string { + return opts.buildArgs +} + type InitrdOption func(*InitrdOptions) error // WithCompression sets the compression of the resulting CPIO archive file. @@ -86,3 +92,12 @@ func WithWorkdir(dir string) InitrdOption { return nil } } + +// WithBuildArgs sets build arguments that may be used by certain initrd +// builders, such as Dockerfile-based ones. +func WithBuildArgs(buildArgs map[string]*string) InitrdOption { + return func(opts *InitrdOptions) error { + opts.buildArgs = buildArgs + return nil + } +} diff --git a/internal/cli/kraft/build/build.go b/internal/cli/kraft/build/build.go index 2660aad8c..1079dce23 100644 --- a/internal/cli/kraft/build/build.go +++ b/internal/cli/kraft/build/build.go @@ -32,29 +32,30 @@ import ( var ErrContextNotBuildable = fmt.Errorf("could not determine what or how to build from the given context") type BuildOptions struct { - All bool `long:"all" usage:"Build all targets"` - Architecture string `long:"arch" short:"m" usage:"Filter the creation of the build by architecture of known targets (x86_64/arm64/arm)"` - DotConfig string `long:"config" short:"c" usage:"Override the path to the KConfig .config file"` - Env []string `long:"env" short:"e" usage:"Set environment variables to be built in the unikernel" split:"false"` - ForcePull bool `long:"force-pull" usage:"Force pulling packages before building"` - Jobs int `long:"jobs" short:"j" usage:"Allow N jobs at once"` - KernelDbg bool `long:"dbg" usage:"Build the debuggable (symbolic) kernel image instead of the stripped image"` - Kraftfile string `long:"kraftfile" short:"K" usage:"Set an alternative path of the Kraftfile"` - NoCache bool `long:"no-cache" short:"F" usage:"Force a rebuild even if existing intermediate artifacts already exist"` - NoConfigure bool `long:"no-configure" usage:"Do not run Unikraft's configure step before building"` - NoFast bool `long:"no-fast" usage:"Do not use maximum parallelization when performing the build"` - NoFetch bool `long:"no-fetch" usage:"Do not run Unikraft's fetch step before building"` - NoRootfs bool `long:"no-rootfs" usage:"Do not build the root file system (initramfs)"` - NoUpdate bool `long:"no-update" usage:"Do not update package index before running the build"` - Output string `long:"output" short:"o" usage:"Set the output directory for the build artifacts"` - Platform string `long:"plat" short:"p" usage:"Filter the creation of the build by platform of known targets (fc/qemu/xen)"` - PrintStats bool `long:"print-stats" usage:"Print build statistics"` - Project app.Application `noattribute:"true"` - Rootfs string `long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` - SaveBuildLog string `long:"build-log" usage:"Use the specified file to save the output from the build"` - Target *target.Target `noattribute:"true"` - TargetName string `long:"target" short:"t" usage:"Build a particular known target"` - Workdir string `noattribute:"true"` + All bool `long:"all" usage:"Build all targets"` + Architecture string `long:"arch" short:"m" usage:"Filter the creation of the build by architecture of known targets (x86_64/arm64/arm)"` + BuildArgs map[string]*string `noattribute:"true"` + DotConfig string `long:"config" short:"c" usage:"Override the path to the KConfig .config file"` + Env []string `long:"env" short:"e" usage:"Set environment variables to be built in the unikernel" split:"false"` + ForcePull bool `long:"force-pull" usage:"Force pulling packages before building"` + Jobs int `long:"jobs" short:"j" usage:"Allow N jobs at once"` + KernelDbg bool `long:"dbg" usage:"Build the debuggable (symbolic) kernel image instead of the stripped image"` + Kraftfile string `long:"kraftfile" short:"K" usage:"Set an alternative path of the Kraftfile"` + NoCache bool `long:"no-cache" short:"F" usage:"Force a rebuild even if existing intermediate artifacts already exist"` + NoConfigure bool `long:"no-configure" usage:"Do not run Unikraft's configure step before building"` + NoFast bool `long:"no-fast" usage:"Do not use maximum parallelization when performing the build"` + NoFetch bool `long:"no-fetch" usage:"Do not run Unikraft's fetch step before building"` + NoRootfs bool `long:"no-rootfs" usage:"Do not build the root file system (initramfs)"` + NoUpdate bool `long:"no-update" usage:"Do not update package index before running the build"` + Output string `long:"output" short:"o" usage:"Set the output directory for the build artifacts"` + Platform string `long:"plat" short:"p" usage:"Filter the creation of the build by platform of known targets (fc/qemu/xen)"` + PrintStats bool `long:"print-stats" usage:"Print build statistics"` + Project app.Application `noattribute:"true"` + Rootfs string `long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` + SaveBuildLog string `long:"build-log" usage:"Use the specified file to save the output from the build"` + Target *target.Target `noattribute:"true"` + TargetName string `long:"target" short:"t" usage:"Build a particular known target"` + Workdir string `noattribute:"true"` statistics map[string]string } @@ -108,7 +109,7 @@ func Build(ctx context.Context, opts *BuildOptions, args ...string) error { return fmt.Errorf("could not complete build: %w", err) } - if _, _, _, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, false, (*opts.Target).Architecture().String()); err != nil { + if _, _, _, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, false, (*opts.Target).Architecture().String(), opts.BuildArgs); err != nil { return err } diff --git a/internal/cli/kraft/cloud/compose/build/build.go b/internal/cli/kraft/cloud/compose/build/build.go index 96cc0f82e..9fe758dff 100644 --- a/internal/cli/kraft/cloud/compose/build/build.go +++ b/internal/cli/kraft/cloud/compose/build/build.go @@ -256,6 +256,8 @@ func Build(ctx context.Context, opts *BuildOptions, args ...string) error { popts.Rootfs = rootfs } + bopts.BuildArgs = service.Build.Args + popts.BuildArgs = service.Build.Args bopts.Workdir = service.Build.Context popts.Workdir = service.Build.Context bopts.Project = project diff --git a/internal/cli/kraft/pkg/packager_cli_kernel.go b/internal/cli/kraft/pkg/packager_cli_kernel.go index 7976525c9..0e2e0d562 100644 --- a/internal/cli/kraft/pkg/packager_cli_kernel.go +++ b/internal/cli/kraft/pkg/packager_cli_kernel.go @@ -66,7 +66,7 @@ func (p *packagerCliKernel) Pack(ctx context.Context, opts *PkgOptions, args ... var cmds []string var envs []string var rootfs initrd.Initrd - if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String()); err != nil { + if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.BuildArgs); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } diff --git a/internal/cli/kraft/pkg/packager_kraftfile_runtime.go b/internal/cli/kraft/pkg/packager_kraftfile_runtime.go index 354b47e0e..755002f91 100644 --- a/internal/cli/kraft/pkg/packager_kraftfile_runtime.go +++ b/internal/cli/kraft/pkg/packager_kraftfile_runtime.go @@ -383,7 +383,7 @@ func (p *packagerKraftfileRuntime) Pack(ctx context.Context, opts *PkgOptions, a } var rootfsArgs []string - if p.rootfs, rootfsArgs, p.env, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, p.architecture.String()); err != nil { + if p.rootfs, rootfsArgs, p.env, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, p.architecture.String(), opts.BuildArgs); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } diff --git a/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go b/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go index 74bade454..7c99d7f26 100644 --- a/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go +++ b/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go @@ -108,7 +108,7 @@ func (p *packagerKraftfileUnikraft) Pack(ctx context.Context, opts *PkgOptions, "CONFIG_LIBPOSIX_VFS_FSTAB_BUILTIN_EINITRD", "CONFIG_LIBPOSIX_VFS_FSTAB_FALLBACK_EINITRD", ) { - if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String()); err != nil { + if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.BuildArgs); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } } diff --git a/internal/cli/kraft/pkg/pkg.go b/internal/cli/kraft/pkg/pkg.go index 0234f2a63..ee0d197a4 100644 --- a/internal/cli/kraft/pkg/pkg.go +++ b/internal/cli/kraft/pkg/pkg.go @@ -39,6 +39,7 @@ import ( type PkgOptions struct { Architecture string `local:"true" long:"arch" short:"m" usage:"Filter the creation of the package by architecture of known targets (x86_64/arm64/arm)"` Args []string `local:"true" long:"args" short:"a" usage:"Pass arguments that will be part of the running kernel's command line"` + BuildArgs map[string]*string `noattribute:"true"` Compress bool `local:"true" long:"compress" short:"c" usage:"Compress the initrd package (experimental)"` Dbg bool `local:"true" long:"dbg" usage:"Package the debuggable (symbolic) kernel image instead of the stripped image"` Env []string `local:"true" long:"env" short:"e" usage:"Set environment variables to be packed into the package" split:"false"` diff --git a/internal/cli/kraft/utils/rootfs.go b/internal/cli/kraft/utils/rootfs.go index dcc772966..8640c3990 100644 --- a/internal/cli/kraft/utils/rootfs.go +++ b/internal/cli/kraft/utils/rootfs.go @@ -19,7 +19,7 @@ import ( // BuildRootfs generates a rootfs based on the provided working directory and // the rootfs entrypoint for the provided target(s). -func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arch string) (initrd.Initrd, []string, []string, error) { +func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arch string, buildArgs map[string]*string) (initrd.Initrd, []string, []string, error) { if rootfs == "" { return nil, nil, nil, nil } @@ -43,6 +43,7 @@ func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arc )), initrd.WithArchitecture(arch), initrd.WithCompression(compress), + initrd.WithBuildArgs(buildArgs), ) if err != nil { return nil, nil, nil, fmt.Errorf("could not initialize initramfs builder: %w", err) From 6fceddab00e91c026cb6b05400b0a6dcbda2b49f Mon Sep 17 00:00:00 2001 From: Alex-Andrei Cioc Date: Wed, 8 Oct 2025 22:58:45 +0300 Subject: [PATCH 2/4] feat(compose): Add suport for build target in compose file Signed-off-by: Alex-Andrei Cioc --- initrd/dockerfile.go | 8 ++- initrd/options.go | 29 ++++++----- internal/cli/kraft/build/build.go | 51 ++++++++++--------- .../cli/kraft/cloud/compose/build/build.go | 6 ++- internal/cli/kraft/pkg/packager_cli_kernel.go | 2 +- .../kraft/pkg/packager_kraftfile_runtime.go | 2 +- .../kraft/pkg/packager_kraftfile_unikraft.go | 2 +- internal/cli/kraft/pkg/pkg.go | 3 +- internal/cli/kraft/utils/rootfs.go | 4 +- 9 files changed, 60 insertions(+), 47 deletions(-) diff --git a/initrd/dockerfile.go b/initrd/dockerfile.go index 2de0fde02..e8e54e5c6 100644 --- a/initrd/dockerfile.go +++ b/initrd/dockerfile.go @@ -390,12 +390,16 @@ func (initrd *dockerfile) Build(ctx context.Context) (string, error) { "filename": filepath.Base(initrd.dockerfile), } + // Add the build target if specified (override from command line) if len(buildTarget) > 0 { attrs["target"] = buildTarget + } else if initrd.opts.buildConfig.Target != "" { + attrs["target"] = initrd.opts.buildConfig.Target } - if initrd.opts.buildArgs != nil { - for k, v := range initrd.opts.buildArgs { + // Add build args from the build config + if initrd.opts.buildConfig.Args != nil { + for k, v := range initrd.opts.buildConfig.Args { if v == nil { v, ok := os.LookupEnv(k) if !ok { diff --git a/initrd/options.go b/initrd/options.go index 8cb181eaa..30755efb6 100644 --- a/initrd/options.go +++ b/initrd/options.go @@ -4,13 +4,18 @@ // You may not use this file except in compliance with the License. package initrd +type InitrdBuildConfig struct { + Args map[string]*string + Target string +} + type InitrdOptions struct { - compress bool - output string - cacheDir string - arch string - workdir string - buildArgs map[string]*string + compress bool + output string + cacheDir string + arch string + workdir string + buildConfig InitrdBuildConfig } // Whether the resulting CPIO archive file should be compressed. @@ -38,9 +43,9 @@ func (opts InitrdOptions) Workdir() string { return opts.workdir } -// The build arguments that may be used by certain initrd builders. -func (opts InitrdOptions) BuildArgs() map[string]*string { - return opts.buildArgs +// The build configuration that may be used by certain initrd builders. +func (opts InitrdOptions) BuildConfig() InitrdBuildConfig { + return opts.buildConfig } type InitrdOption func(*InitrdOptions) error @@ -93,11 +98,11 @@ func WithWorkdir(dir string) InitrdOption { } } -// WithBuildArgs sets build arguments that may be used by certain initrd +// WithBuildConfig sets build configuration that may be used by certain initrd // builders, such as Dockerfile-based ones. -func WithBuildArgs(buildArgs map[string]*string) InitrdOption { +func WithBuildConfig(buildConfig InitrdBuildConfig) InitrdOption { return func(opts *InitrdOptions) error { - opts.buildArgs = buildArgs + opts.buildConfig = buildConfig return nil } } diff --git a/internal/cli/kraft/build/build.go b/internal/cli/kraft/build/build.go index 1079dce23..3d32f7a63 100644 --- a/internal/cli/kraft/build/build.go +++ b/internal/cli/kraft/build/build.go @@ -18,6 +18,7 @@ import ( "github.com/spf13/cobra" "kraftkit.sh/cmdfactory" + "kraftkit.sh/initrd" "kraftkit.sh/internal/cli/kraft/utils" "kraftkit.sh/internal/fancymap" "kraftkit.sh/iostreams" @@ -32,30 +33,30 @@ import ( var ErrContextNotBuildable = fmt.Errorf("could not determine what or how to build from the given context") type BuildOptions struct { - All bool `long:"all" usage:"Build all targets"` - Architecture string `long:"arch" short:"m" usage:"Filter the creation of the build by architecture of known targets (x86_64/arm64/arm)"` - BuildArgs map[string]*string `noattribute:"true"` - DotConfig string `long:"config" short:"c" usage:"Override the path to the KConfig .config file"` - Env []string `long:"env" short:"e" usage:"Set environment variables to be built in the unikernel" split:"false"` - ForcePull bool `long:"force-pull" usage:"Force pulling packages before building"` - Jobs int `long:"jobs" short:"j" usage:"Allow N jobs at once"` - KernelDbg bool `long:"dbg" usage:"Build the debuggable (symbolic) kernel image instead of the stripped image"` - Kraftfile string `long:"kraftfile" short:"K" usage:"Set an alternative path of the Kraftfile"` - NoCache bool `long:"no-cache" short:"F" usage:"Force a rebuild even if existing intermediate artifacts already exist"` - NoConfigure bool `long:"no-configure" usage:"Do not run Unikraft's configure step before building"` - NoFast bool `long:"no-fast" usage:"Do not use maximum parallelization when performing the build"` - NoFetch bool `long:"no-fetch" usage:"Do not run Unikraft's fetch step before building"` - NoRootfs bool `long:"no-rootfs" usage:"Do not build the root file system (initramfs)"` - NoUpdate bool `long:"no-update" usage:"Do not update package index before running the build"` - Output string `long:"output" short:"o" usage:"Set the output directory for the build artifacts"` - Platform string `long:"plat" short:"p" usage:"Filter the creation of the build by platform of known targets (fc/qemu/xen)"` - PrintStats bool `long:"print-stats" usage:"Print build statistics"` - Project app.Application `noattribute:"true"` - Rootfs string `long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` - SaveBuildLog string `long:"build-log" usage:"Use the specified file to save the output from the build"` - Target *target.Target `noattribute:"true"` - TargetName string `long:"target" short:"t" usage:"Build a particular known target"` - Workdir string `noattribute:"true"` + All bool `long:"all" usage:"Build all targets"` + Architecture string `long:"arch" short:"m" usage:"Filter the creation of the build by architecture of known targets (x86_64/arm64/arm)"` + BuildConfig initrd.InitrdBuildConfig `noattribute:"true"` + DotConfig string `long:"config" short:"c" usage:"Override the path to the KConfig .config file"` + Env []string `long:"env" short:"e" usage:"Set environment variables to be built in the unikernel" split:"false"` + ForcePull bool `long:"force-pull" usage:"Force pulling packages before building"` + Jobs int `long:"jobs" short:"j" usage:"Allow N jobs at once"` + KernelDbg bool `long:"dbg" usage:"Build the debuggable (symbolic) kernel image instead of the stripped image"` + Kraftfile string `long:"kraftfile" short:"K" usage:"Set an alternative path of the Kraftfile"` + NoCache bool `long:"no-cache" short:"F" usage:"Force a rebuild even if existing intermediate artifacts already exist"` + NoConfigure bool `long:"no-configure" usage:"Do not run Unikraft's configure step before building"` + NoFast bool `long:"no-fast" usage:"Do not use maximum parallelization when performing the build"` + NoFetch bool `long:"no-fetch" usage:"Do not run Unikraft's fetch step before building"` + NoRootfs bool `long:"no-rootfs" usage:"Do not build the root file system (initramfs)"` + NoUpdate bool `long:"no-update" usage:"Do not update package index before running the build"` + Output string `long:"output" short:"o" usage:"Set the output directory for the build artifacts"` + Platform string `long:"plat" short:"p" usage:"Filter the creation of the build by platform of known targets (fc/qemu/xen)"` + PrintStats bool `long:"print-stats" usage:"Print build statistics"` + Project app.Application `noattribute:"true"` + Rootfs string `long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` + SaveBuildLog string `long:"build-log" usage:"Use the specified file to save the output from the build"` + Target *target.Target `noattribute:"true"` + TargetName string `long:"target" short:"t" usage:"Build a particular known target"` + Workdir string `noattribute:"true"` statistics map[string]string } @@ -109,7 +110,7 @@ func Build(ctx context.Context, opts *BuildOptions, args ...string) error { return fmt.Errorf("could not complete build: %w", err) } - if _, _, _, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, false, (*opts.Target).Architecture().String(), opts.BuildArgs); err != nil { + if _, _, _, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, false, (*opts.Target).Architecture().String(), opts.BuildConfig); err != nil { return err } diff --git a/internal/cli/kraft/cloud/compose/build/build.go b/internal/cli/kraft/cloud/compose/build/build.go index 9fe758dff..a8dbe893c 100644 --- a/internal/cli/kraft/cloud/compose/build/build.go +++ b/internal/cli/kraft/cloud/compose/build/build.go @@ -256,8 +256,10 @@ func Build(ctx context.Context, opts *BuildOptions, args ...string) error { popts.Rootfs = rootfs } - bopts.BuildArgs = service.Build.Args - popts.BuildArgs = service.Build.Args + bopts.BuildConfig.Args = service.Build.Args + popts.BuildConfig.Args = service.Build.Args + bopts.BuildConfig.Target = service.Build.Target + popts.BuildConfig.Target = service.Build.Target bopts.Workdir = service.Build.Context popts.Workdir = service.Build.Context bopts.Project = project diff --git a/internal/cli/kraft/pkg/packager_cli_kernel.go b/internal/cli/kraft/pkg/packager_cli_kernel.go index 0e2e0d562..7a62dfab3 100644 --- a/internal/cli/kraft/pkg/packager_cli_kernel.go +++ b/internal/cli/kraft/pkg/packager_cli_kernel.go @@ -66,7 +66,7 @@ func (p *packagerCliKernel) Pack(ctx context.Context, opts *PkgOptions, args ... var cmds []string var envs []string var rootfs initrd.Initrd - if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.BuildArgs); err != nil { + if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.BuildConfig); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } diff --git a/internal/cli/kraft/pkg/packager_kraftfile_runtime.go b/internal/cli/kraft/pkg/packager_kraftfile_runtime.go index 755002f91..ffe40a297 100644 --- a/internal/cli/kraft/pkg/packager_kraftfile_runtime.go +++ b/internal/cli/kraft/pkg/packager_kraftfile_runtime.go @@ -383,7 +383,7 @@ func (p *packagerKraftfileRuntime) Pack(ctx context.Context, opts *PkgOptions, a } var rootfsArgs []string - if p.rootfs, rootfsArgs, p.env, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, p.architecture.String(), opts.BuildArgs); err != nil { + if p.rootfs, rootfsArgs, p.env, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, p.architecture.String(), opts.BuildConfig); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } diff --git a/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go b/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go index 7c99d7f26..ebf191985 100644 --- a/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go +++ b/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go @@ -108,7 +108,7 @@ func (p *packagerKraftfileUnikraft) Pack(ctx context.Context, opts *PkgOptions, "CONFIG_LIBPOSIX_VFS_FSTAB_BUILTIN_EINITRD", "CONFIG_LIBPOSIX_VFS_FSTAB_FALLBACK_EINITRD", ) { - if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.BuildArgs); err != nil { + if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.BuildConfig); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } } diff --git a/internal/cli/kraft/pkg/pkg.go b/internal/cli/kraft/pkg/pkg.go index ee0d197a4..ee5888ade 100644 --- a/internal/cli/kraft/pkg/pkg.go +++ b/internal/cli/kraft/pkg/pkg.go @@ -15,6 +15,7 @@ import ( "github.com/spf13/cobra" "kraftkit.sh/config" + "kraftkit.sh/initrd" "kraftkit.sh/log" "kraftkit.sh/machine/platform" "kraftkit.sh/pack" @@ -39,7 +40,7 @@ import ( type PkgOptions struct { Architecture string `local:"true" long:"arch" short:"m" usage:"Filter the creation of the package by architecture of known targets (x86_64/arm64/arm)"` Args []string `local:"true" long:"args" short:"a" usage:"Pass arguments that will be part of the running kernel's command line"` - BuildArgs map[string]*string `noattribute:"true"` + BuildConfig initrd.InitrdBuildConfig `noattribute:"true"` Compress bool `local:"true" long:"compress" short:"c" usage:"Compress the initrd package (experimental)"` Dbg bool `local:"true" long:"dbg" usage:"Package the debuggable (symbolic) kernel image instead of the stripped image"` Env []string `local:"true" long:"env" short:"e" usage:"Set environment variables to be packed into the package" split:"false"` diff --git a/internal/cli/kraft/utils/rootfs.go b/internal/cli/kraft/utils/rootfs.go index 8640c3990..e5d680ced 100644 --- a/internal/cli/kraft/utils/rootfs.go +++ b/internal/cli/kraft/utils/rootfs.go @@ -19,7 +19,7 @@ import ( // BuildRootfs generates a rootfs based on the provided working directory and // the rootfs entrypoint for the provided target(s). -func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arch string, buildArgs map[string]*string) (initrd.Initrd, []string, []string, error) { +func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arch string, buildOptions initrd.InitrdBuildConfig) (initrd.Initrd, []string, []string, error) { if rootfs == "" { return nil, nil, nil, nil } @@ -43,7 +43,7 @@ func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arc )), initrd.WithArchitecture(arch), initrd.WithCompression(compress), - initrd.WithBuildArgs(buildArgs), + initrd.WithBuildConfig(buildOptions), ) if err != nil { return nil, nil, nil, fmt.Errorf("could not initialize initramfs builder: %w", err) From 3f94bb2303612eb99661058938d752bc495342bb Mon Sep 17 00:00:00 2001 From: Alex-Andrei Cioc Date: Thu, 9 Oct 2025 13:31:10 +0300 Subject: [PATCH 3/4] feat(compose): Add suport for build secrets in compose file Signed-off-by: Alex-Andrei Cioc --- initrd/dockerfile.go | 30 +++++++-- initrd/options.go | 61 ++++++++++++++----- .../cli/kraft/utils => initrd}/rootfs.go | 23 +++---- internal/cli/kraft/build/build.go | 51 ++++++++-------- .../cli/kraft/cloud/compose/build/build.go | 25 ++++++-- internal/cli/kraft/pkg/packager_cli_kernel.go | 3 +- .../kraft/pkg/packager_kraftfile_runtime.go | 2 +- .../kraft/pkg/packager_kraftfile_unikraft.go | 3 +- internal/cli/kraft/pkg/pkg.go | 50 +++++++-------- 9 files changed, 154 insertions(+), 94 deletions(-) rename {internal/cli/kraft/utils => initrd}/rootfs.go (79%) diff --git a/initrd/dockerfile.go b/initrd/dockerfile.go index e8e54e5c6..061cd7c61 100644 --- a/initrd/dockerfile.go +++ b/initrd/dockerfile.go @@ -393,13 +393,13 @@ func (initrd *dockerfile) Build(ctx context.Context) (string, error) { // Add the build target if specified (override from command line) if len(buildTarget) > 0 { attrs["target"] = buildTarget - } else if initrd.opts.buildConfig.Target != "" { - attrs["target"] = initrd.opts.buildConfig.Target + } else if initrd.opts.buildTarget != "" { + attrs["target"] = initrd.opts.buildTarget } // Add build args from the build config - if initrd.opts.buildConfig.Args != nil { - for k, v := range initrd.opts.buildConfig.Args { + if initrd.opts.buildArgs != nil { + for k, v := range initrd.opts.buildArgs { if v == nil { v, ok := os.LookupEnv(k) if !ok { @@ -437,13 +437,31 @@ func (initrd *dockerfile) Build(ctx context.Context) (string, error) { }, } - fs := make([]secretsprovider.Source, 0, len(buildSecrets)) + // Add build secrets from the build config + secretsMap := make(map[string]secretsprovider.Source) + if initrd.opts.buildSecrets != nil { + for _, v := range initrd.opts.buildSecrets { + secretsMap[v.Name] = secretsprovider.Source{ + ID: v.Name, + FilePath: v.File, + Env: v.Env, + } + } + } + + // Override build secrets from the command line for _, v := range buildSecrets { s, err := parseSecret(v) if err != nil { return "", err } - fs = append(fs, *s) + secretsMap[s.ID] = *s + } + + // Convert map to slice + fs := make([]secretsprovider.Source, 0, len(secretsMap)) + for _, secret := range secretsMap { + fs = append(fs, secret) } secretStore, err := secretsprovider.NewStore(fs) diff --git a/initrd/options.go b/initrd/options.go index 30755efb6..59aa3d9fa 100644 --- a/initrd/options.go +++ b/initrd/options.go @@ -4,18 +4,21 @@ // You may not use this file except in compliance with the License. package initrd -type InitrdBuildConfig struct { - Args map[string]*string - Target string +type InitrdBuildSecret struct { + Name string + File string + Env string } type InitrdOptions struct { - compress bool - output string - cacheDir string - arch string - workdir string - buildConfig InitrdBuildConfig + compress bool + output string + cacheDir string + arch string + workdir string + buildArgs map[string]*string + buildTarget string + buildSecrets map[string]InitrdBuildSecret } // Whether the resulting CPIO archive file should be compressed. @@ -43,9 +46,19 @@ func (opts InitrdOptions) Workdir() string { return opts.workdir } -// The build configuration that may be used by certain initrd builders. -func (opts InitrdOptions) BuildConfig() InitrdBuildConfig { - return opts.buildConfig +// The build arguments that may be used by certain initrd builders. +func (opts InitrdOptions) BuildArgs() map[string]*string { + return opts.buildArgs +} + +// The build target that may be used by certain initrd builders. +func (opts InitrdOptions) BuildTarget() string { + return opts.buildTarget +} + +// The build secrets that may be used by certain initrd builders. +func (opts InitrdOptions) BuildSecrets() map[string]InitrdBuildSecret { + return opts.buildSecrets } type InitrdOption func(*InitrdOptions) error @@ -98,11 +111,29 @@ func WithWorkdir(dir string) InitrdOption { } } -// WithBuildConfig sets build configuration that may be used by certain initrd +// WithBuildArgs sets build arguments that may be used by certain initrd +// builders, such as Dockerfile-based ones. +func WithBuildArgs(args map[string]*string) InitrdOption { + return func(opts *InitrdOptions) error { + opts.buildArgs = args + return nil + } +} + +// WithBuildTarget sets the build target that may be used by certain initrd +// builders, such as Dockerfile-based ones. +func WithBuildTarget(target string) InitrdOption { + return func(opts *InitrdOptions) error { + opts.buildTarget = target + return nil + } +} + +// WithBuildSecrets sets build secrets that may be used by certain initrd // builders, such as Dockerfile-based ones. -func WithBuildConfig(buildConfig InitrdBuildConfig) InitrdOption { +func WithBuildSecrets(secrets map[string]InitrdBuildSecret) InitrdOption { return func(opts *InitrdOptions) error { - opts.buildConfig = buildConfig + opts.buildSecrets = secrets return nil } } diff --git a/internal/cli/kraft/utils/rootfs.go b/initrd/rootfs.go similarity index 79% rename from internal/cli/kraft/utils/rootfs.go rename to initrd/rootfs.go index e5d680ced..232239bc2 100644 --- a/internal/cli/kraft/utils/rootfs.go +++ b/initrd/rootfs.go @@ -3,7 +3,7 @@ // Licensed under the BSD-3-Clause License (the "License"). // You may not use this file except in compliance with the License. -package utils +package initrd import ( "context" @@ -11,7 +11,6 @@ import ( "path/filepath" "kraftkit.sh/config" - "kraftkit.sh/initrd" "kraftkit.sh/log" "kraftkit.sh/tui/processtree" "kraftkit.sh/unikraft" @@ -19,7 +18,7 @@ import ( // BuildRootfs generates a rootfs based on the provided working directory and // the rootfs entrypoint for the provided target(s). -func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arch string, buildOptions initrd.InitrdBuildConfig) (initrd.Initrd, []string, []string, error) { +func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arch string, initrdOptions []InitrdOption) (Initrd, []string, []string, error) { if rootfs == "" { return nil, nil, nil, nil } @@ -28,23 +27,21 @@ func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arc var cmds []string var envs []string - ramfs, err := initrd.New(ctx, - rootfs, - initrd.WithWorkdir(workdir), - initrd.WithOutput(filepath.Join( + ramfs, err := New(ctx, rootfs, append(initrdOptions, + WithWorkdir(workdir), + WithOutput(filepath.Join( workdir, unikraft.BuildDir, - fmt.Sprintf(initrd.DefaultInitramfsArchFileName, arch), + fmt.Sprintf(DefaultInitramfsArchFileName, arch), )), - initrd.WithCacheDir(filepath.Join( + WithCacheDir(filepath.Join( workdir, unikraft.VendorDir, "rootfs-cache", )), - initrd.WithArchitecture(arch), - initrd.WithCompression(compress), - initrd.WithBuildConfig(buildOptions), - ) + WithArchitecture(arch), + WithCompression(compress), + )...) if err != nil { return nil, nil, nil, fmt.Errorf("could not initialize initramfs builder: %w", err) } diff --git a/internal/cli/kraft/build/build.go b/internal/cli/kraft/build/build.go index 3d32f7a63..01c9d9699 100644 --- a/internal/cli/kraft/build/build.go +++ b/internal/cli/kraft/build/build.go @@ -19,7 +19,6 @@ import ( "kraftkit.sh/cmdfactory" "kraftkit.sh/initrd" - "kraftkit.sh/internal/cli/kraft/utils" "kraftkit.sh/internal/fancymap" "kraftkit.sh/iostreams" "kraftkit.sh/tui" @@ -33,30 +32,30 @@ import ( var ErrContextNotBuildable = fmt.Errorf("could not determine what or how to build from the given context") type BuildOptions struct { - All bool `long:"all" usage:"Build all targets"` - Architecture string `long:"arch" short:"m" usage:"Filter the creation of the build by architecture of known targets (x86_64/arm64/arm)"` - BuildConfig initrd.InitrdBuildConfig `noattribute:"true"` - DotConfig string `long:"config" short:"c" usage:"Override the path to the KConfig .config file"` - Env []string `long:"env" short:"e" usage:"Set environment variables to be built in the unikernel" split:"false"` - ForcePull bool `long:"force-pull" usage:"Force pulling packages before building"` - Jobs int `long:"jobs" short:"j" usage:"Allow N jobs at once"` - KernelDbg bool `long:"dbg" usage:"Build the debuggable (symbolic) kernel image instead of the stripped image"` - Kraftfile string `long:"kraftfile" short:"K" usage:"Set an alternative path of the Kraftfile"` - NoCache bool `long:"no-cache" short:"F" usage:"Force a rebuild even if existing intermediate artifacts already exist"` - NoConfigure bool `long:"no-configure" usage:"Do not run Unikraft's configure step before building"` - NoFast bool `long:"no-fast" usage:"Do not use maximum parallelization when performing the build"` - NoFetch bool `long:"no-fetch" usage:"Do not run Unikraft's fetch step before building"` - NoRootfs bool `long:"no-rootfs" usage:"Do not build the root file system (initramfs)"` - NoUpdate bool `long:"no-update" usage:"Do not update package index before running the build"` - Output string `long:"output" short:"o" usage:"Set the output directory for the build artifacts"` - Platform string `long:"plat" short:"p" usage:"Filter the creation of the build by platform of known targets (fc/qemu/xen)"` - PrintStats bool `long:"print-stats" usage:"Print build statistics"` - Project app.Application `noattribute:"true"` - Rootfs string `long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` - SaveBuildLog string `long:"build-log" usage:"Use the specified file to save the output from the build"` - Target *target.Target `noattribute:"true"` - TargetName string `long:"target" short:"t" usage:"Build a particular known target"` - Workdir string `noattribute:"true"` + All bool `long:"all" usage:"Build all targets"` + Architecture string `long:"arch" short:"m" usage:"Filter the creation of the build by architecture of known targets (x86_64/arm64/arm)"` + InitrdOptions []initrd.InitrdOption `noattribute:"true"` + DotConfig string `long:"config" short:"c" usage:"Override the path to the KConfig .config file"` + Env []string `long:"env" short:"e" usage:"Set environment variables to be built in the unikernel" split:"false"` + ForcePull bool `long:"force-pull" usage:"Force pulling packages before building"` + Jobs int `long:"jobs" short:"j" usage:"Allow N jobs at once"` + KernelDbg bool `long:"dbg" usage:"Build the debuggable (symbolic) kernel image instead of the stripped image"` + Kraftfile string `long:"kraftfile" short:"K" usage:"Set an alternative path of the Kraftfile"` + NoCache bool `long:"no-cache" short:"F" usage:"Force a rebuild even if existing intermediate artifacts already exist"` + NoConfigure bool `long:"no-configure" usage:"Do not run Unikraft's configure step before building"` + NoFast bool `long:"no-fast" usage:"Do not use maximum parallelization when performing the build"` + NoFetch bool `long:"no-fetch" usage:"Do not run Unikraft's fetch step before building"` + NoRootfs bool `long:"no-rootfs" usage:"Do not build the root file system (initramfs)"` + NoUpdate bool `long:"no-update" usage:"Do not update package index before running the build"` + Output string `long:"output" short:"o" usage:"Set the output directory for the build artifacts"` + Platform string `long:"plat" short:"p" usage:"Filter the creation of the build by platform of known targets (fc/qemu/xen)"` + PrintStats bool `long:"print-stats" usage:"Print build statistics"` + Project app.Application `noattribute:"true"` + Rootfs string `long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` + SaveBuildLog string `long:"build-log" usage:"Use the specified file to save the output from the build"` + Target *target.Target `noattribute:"true"` + TargetName string `long:"target" short:"t" usage:"Build a particular known target"` + Workdir string `noattribute:"true"` statistics map[string]string } @@ -110,7 +109,7 @@ func Build(ctx context.Context, opts *BuildOptions, args ...string) error { return fmt.Errorf("could not complete build: %w", err) } - if _, _, _, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, false, (*opts.Target).Architecture().String(), opts.BuildConfig); err != nil { + if _, _, _, err = initrd.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, false, (*opts.Target).Architecture().String(), opts.InitrdOptions); err != nil { return err } diff --git a/internal/cli/kraft/cloud/compose/build/build.go b/internal/cli/kraft/cloud/compose/build/build.go index a8dbe893c..18fedf3cd 100644 --- a/internal/cli/kraft/cloud/compose/build/build.go +++ b/internal/cli/kraft/cloud/compose/build/build.go @@ -22,6 +22,7 @@ import ( "kraftkit.sh/cmdfactory" "kraftkit.sh/compose" "kraftkit.sh/config" + "kraftkit.sh/initrd" "kraftkit.sh/internal/cli/kraft/build" "kraftkit.sh/internal/cli/kraft/cloud/utils" "kraftkit.sh/internal/cli/kraft/pkg" @@ -256,10 +257,26 @@ func Build(ctx context.Context, opts *BuildOptions, args ...string) error { popts.Rootfs = rootfs } - bopts.BuildConfig.Args = service.Build.Args - popts.BuildConfig.Args = service.Build.Args - bopts.BuildConfig.Target = service.Build.Target - popts.BuildConfig.Target = service.Build.Target + initrdOptions := []initrd.InitrdOption{ + initrd.WithBuildArgs(service.Build.Args), + initrd.WithBuildTarget(service.Build.Target), + } + secrets := map[string]initrd.InitrdBuildSecret{} + for _, secretRef := range service.Build.Secrets { + if secret, ok := opts.Project.Secrets[secretRef.Source]; ok { + secrets[secretRef.Source] = initrd.InitrdBuildSecret{ + Name: secret.Name, + File: secret.File, + Env: secret.Environment, + } + } + } + if len(secrets) > 0 { + initrdOptions = append(initrdOptions, initrd.WithBuildSecrets(secrets)) + } + + bopts.InitrdOptions = initrdOptions + popts.InitrdOptions = initrdOptions bopts.Workdir = service.Build.Context popts.Workdir = service.Build.Context bopts.Project = project diff --git a/internal/cli/kraft/pkg/packager_cli_kernel.go b/internal/cli/kraft/pkg/packager_cli_kernel.go index 7a62dfab3..f5e2d4016 100644 --- a/internal/cli/kraft/pkg/packager_cli_kernel.go +++ b/internal/cli/kraft/pkg/packager_cli_kernel.go @@ -12,7 +12,6 @@ import ( "kraftkit.sh/config" "kraftkit.sh/initrd" - "kraftkit.sh/internal/cli/kraft/utils" "kraftkit.sh/log" "kraftkit.sh/pack" "kraftkit.sh/packmanager" @@ -66,7 +65,7 @@ func (p *packagerCliKernel) Pack(ctx context.Context, opts *PkgOptions, args ... var cmds []string var envs []string var rootfs initrd.Initrd - if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.BuildConfig); err != nil { + if rootfs, cmds, envs, err = initrd.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.InitrdOptions); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } diff --git a/internal/cli/kraft/pkg/packager_kraftfile_runtime.go b/internal/cli/kraft/pkg/packager_kraftfile_runtime.go index ffe40a297..40dbb7a4f 100644 --- a/internal/cli/kraft/pkg/packager_kraftfile_runtime.go +++ b/internal/cli/kraft/pkg/packager_kraftfile_runtime.go @@ -383,7 +383,7 @@ func (p *packagerKraftfileRuntime) Pack(ctx context.Context, opts *PkgOptions, a } var rootfsArgs []string - if p.rootfs, rootfsArgs, p.env, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, p.architecture.String(), opts.BuildConfig); err != nil { + if p.rootfs, rootfsArgs, p.env, err = initrd.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, p.architecture.String(), opts.InitrdOptions); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } diff --git a/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go b/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go index ebf191985..04399033a 100644 --- a/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go +++ b/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go @@ -15,7 +15,6 @@ import ( "github.com/mattn/go-shellwords" "kraftkit.sh/config" "kraftkit.sh/initrd" - "kraftkit.sh/internal/cli/kraft/utils" "kraftkit.sh/log" "kraftkit.sh/pack" "kraftkit.sh/packmanager" @@ -108,7 +107,7 @@ func (p *packagerKraftfileUnikraft) Pack(ctx context.Context, opts *PkgOptions, "CONFIG_LIBPOSIX_VFS_FSTAB_BUILTIN_EINITRD", "CONFIG_LIBPOSIX_VFS_FSTAB_FALLBACK_EINITRD", ) { - if rootfs, cmds, envs, err = utils.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.BuildConfig); err != nil { + if rootfs, cmds, envs, err = initrd.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.InitrdOptions); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } } diff --git a/internal/cli/kraft/pkg/pkg.go b/internal/cli/kraft/pkg/pkg.go index ee5888ade..b960648fb 100644 --- a/internal/cli/kraft/pkg/pkg.go +++ b/internal/cli/kraft/pkg/pkg.go @@ -38,31 +38,31 @@ import ( ) type PkgOptions struct { - Architecture string `local:"true" long:"arch" short:"m" usage:"Filter the creation of the package by architecture of known targets (x86_64/arm64/arm)"` - Args []string `local:"true" long:"args" short:"a" usage:"Pass arguments that will be part of the running kernel's command line"` - BuildConfig initrd.InitrdBuildConfig `noattribute:"true"` - Compress bool `local:"true" long:"compress" short:"c" usage:"Compress the initrd package (experimental)"` - Dbg bool `local:"true" long:"dbg" usage:"Package the debuggable (symbolic) kernel image instead of the stripped image"` - Env []string `local:"true" long:"env" short:"e" usage:"Set environment variables to be packed into the package" split:"false"` - Force bool `local:"true" long:"force-format" usage:"Force the use of a packaging handler format"` - Format string `local:"true" long:"as" short:"M" usage:"Force the packaging despite possible conflicts" default:"oci"` - Kernel string `local:"true" long:"kernel" short:"k" usage:"Override the path to the unikernel image"` - Kraftfile string `long:"kraftfile" short:"K" usage:"Set an alternative path of the Kraftfile"` - Labels []string `local:"true" long:"label" short:"l" usage:"Set labels to be packed into the package (k=v)"` - Name string `local:"true" long:"name" short:"n" usage:"Specify the name of the package"` - NoKConfig bool `local:"true" long:"no-kconfig" usage:"Do not include target .config as metadata"` - NoKernel bool `local:"true" long:"no-kernel" usage:"Allow packaging without a kernel image"` - NoPull bool `local:"true" long:"no-pull" usage:"Do not pull package dependencies before packaging"` - Output string `local:"true" long:"output" short:"o" usage:"Save the package at the following output"` - Platform string `local:"true" long:"plat" short:"p" usage:"Filter the creation of the package by platform of known targets (fc/qemu/xen/kraftcloud)"` - Project app.Application `noattribute:"true"` - Push bool `local:"true" long:"push" short:"P" usage:"Push the package on if successfully packaged"` - Roms []string `local:"true" long:"rom" short:"R" usage:"Specify a path to an auxiliary ROM to include in the package"` - Rootfs string `local:"true" long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` - Runtime string `local:"true" long:"runtime" short:"r" usage:"Set the runtime to use for the package"` - Strategy packmanager.MergeStrategy `noattribute:"true"` - Target string `local:"true" long:"target" short:"t" usage:"Package a particular known target"` - Workdir string `local:"true" long:"workdir" short:"w" usage:"Set an alternative working directory (default is cwd)"` + Architecture string `local:"true" long:"arch" short:"m" usage:"Filter the creation of the package by architecture of known targets (x86_64/arm64/arm)"` + Args []string `local:"true" long:"args" short:"a" usage:"Pass arguments that will be part of the running kernel's command line"` + InitrdOptions []initrd.InitrdOption `noattribute:"true"` + Compress bool `local:"true" long:"compress" short:"c" usage:"Compress the initrd package (experimental)"` + Dbg bool `local:"true" long:"dbg" usage:"Package the debuggable (symbolic) kernel image instead of the stripped image"` + Env []string `local:"true" long:"env" short:"e" usage:"Set environment variables to be packed into the package" split:"false"` + Force bool `local:"true" long:"force-format" usage:"Force the use of a packaging handler format"` + Format string `local:"true" long:"as" short:"M" usage:"Force the packaging despite possible conflicts" default:"oci"` + Kernel string `local:"true" long:"kernel" short:"k" usage:"Override the path to the unikernel image"` + Kraftfile string `long:"kraftfile" short:"K" usage:"Set an alternative path of the Kraftfile"` + Labels []string `local:"true" long:"label" short:"l" usage:"Set labels to be packed into the package (k=v)"` + Name string `local:"true" long:"name" short:"n" usage:"Specify the name of the package"` + NoKConfig bool `local:"true" long:"no-kconfig" usage:"Do not include target .config as metadata"` + NoKernel bool `local:"true" long:"no-kernel" usage:"Allow packaging without a kernel image"` + NoPull bool `local:"true" long:"no-pull" usage:"Do not pull package dependencies before packaging"` + Output string `local:"true" long:"output" short:"o" usage:"Save the package at the following output"` + Platform string `local:"true" long:"plat" short:"p" usage:"Filter the creation of the package by platform of known targets (fc/qemu/xen/kraftcloud)"` + Project app.Application `noattribute:"true"` + Push bool `local:"true" long:"push" short:"P" usage:"Push the package on if successfully packaged"` + Roms []string `local:"true" long:"rom" short:"R" usage:"Specify a path to an auxiliary ROM to include in the package"` + Rootfs string `local:"true" long:"rootfs" usage:"Specify a path to use as root file system (can be volume or initramfs)"` + Runtime string `local:"true" long:"runtime" short:"r" usage:"Set the runtime to use for the package"` + Strategy packmanager.MergeStrategy `noattribute:"true"` + Target string `local:"true" long:"target" short:"t" usage:"Package a particular known target"` + Workdir string `local:"true" long:"workdir" short:"w" usage:"Set an alternative working directory (default is cwd)"` packopts []packmanager.PackOption pm packmanager.PackageManager From d0ff559d8b57b5905891ac02a905f8c0a7f7baa8 Mon Sep 17 00:00:00 2001 From: Alex-Andrei Cioc Date: Mon, 13 Oct 2025 14:24:51 +0300 Subject: [PATCH 4/4] chore(initrd): Convert BuildRootfs to InitrdOption pattern Signed-off-by: Alex-Andrei Cioc --- initrd/options.go | 14 ++++++++ initrd/rootfs.go | 35 ++++++++----------- internal/cli/kraft/build/build.go | 22 +++++++++++- internal/cli/kraft/pkg/packager_cli_kernel.go | 22 +++++++++++- .../kraft/pkg/packager_kraftfile_runtime.go | 21 ++++++++++- .../kraft/pkg/packager_kraftfile_unikraft.go | 21 ++++++++++- 6 files changed, 110 insertions(+), 25 deletions(-) diff --git a/initrd/options.go b/initrd/options.go index 59aa3d9fa..d5803c042 100644 --- a/initrd/options.go +++ b/initrd/options.go @@ -16,6 +16,7 @@ type InitrdOptions struct { cacheDir string arch string workdir string + rootfsPath string buildArgs map[string]*string buildTarget string buildSecrets map[string]InitrdBuildSecret @@ -46,6 +47,11 @@ func (opts InitrdOptions) Workdir() string { return opts.workdir } +// The rootfs path for the initramfs. +func (opts InitrdOptions) RootfsPath() string { + return opts.rootfsPath +} + // The build arguments that may be used by certain initrd builders. func (opts InitrdOptions) BuildArgs() map[string]*string { return opts.buildArgs @@ -111,6 +117,14 @@ func WithWorkdir(dir string) InitrdOption { } } +// WithRootfsPath sets the rootfs path for the initramfs. +func WithRootfsPath(path string) InitrdOption { + return func(opts *InitrdOptions) error { + opts.rootfsPath = path + return nil + } +} + // WithBuildArgs sets build arguments that may be used by certain initrd // builders, such as Dockerfile-based ones. func WithBuildArgs(args map[string]*string) InitrdOption { diff --git a/initrd/rootfs.go b/initrd/rootfs.go index 232239bc2..bf8acadd1 100644 --- a/initrd/rootfs.go +++ b/initrd/rootfs.go @@ -8,18 +8,23 @@ package initrd import ( "context" "fmt" - "path/filepath" "kraftkit.sh/config" "kraftkit.sh/log" "kraftkit.sh/tui/processtree" - "kraftkit.sh/unikraft" ) // BuildRootfs generates a rootfs based on the provided working directory and // the rootfs entrypoint for the provided target(s). -func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arch string, initrdOptions []InitrdOption) (Initrd, []string, []string, error) { - if rootfs == "" { +func BuildRootfs(ctx context.Context, opts ...InitrdOption) (Initrd, []string, []string, error) { + var bopts InitrdOptions + for _, opt := range opts { + if err := opt(&bopts); err != nil { + return nil, nil, nil, fmt.Errorf("could not apply initrd option: %w", err) + } + } + + if bopts.RootfsPath() == "" { return nil, nil, nil, nil } @@ -27,31 +32,19 @@ func BuildRootfs(ctx context.Context, workdir, rootfs string, compress bool, arc var cmds []string var envs []string - ramfs, err := New(ctx, rootfs, append(initrdOptions, - WithWorkdir(workdir), - WithOutput(filepath.Join( - workdir, - unikraft.BuildDir, - fmt.Sprintf(DefaultInitramfsArchFileName, arch), - )), - WithCacheDir(filepath.Join( - workdir, - unikraft.VendorDir, - "rootfs-cache", - )), - WithArchitecture(arch), - WithCompression(compress), - )...) + ramfs, err := New(ctx, bopts.RootfsPath(), opts...) if err != nil { return nil, nil, nil, fmt.Errorf("could not initialize initramfs builder: %w", err) } + ramfsOpts := ramfs.Options() + processes = append(processes, processtree.NewProcessTreeItem( fmt.Sprintf("building rootfs via %s", ramfs.Name()), - arch, + ramfsOpts.Architecture(), func(ctx context.Context) error { - rootfs, err = ramfs.Build(ctx) + _, err := ramfs.Build(ctx) if err != nil { return err } diff --git a/internal/cli/kraft/build/build.go b/internal/cli/kraft/build/build.go index 01c9d9699..4d648dbcc 100644 --- a/internal/cli/kraft/build/build.go +++ b/internal/cli/kraft/build/build.go @@ -25,6 +25,8 @@ import ( "kraftkit.sh/log" "kraftkit.sh/packmanager" + + "kraftkit.sh/unikraft" "kraftkit.sh/unikraft/app" "kraftkit.sh/unikraft/target" ) @@ -109,7 +111,25 @@ func Build(ctx context.Context, opts *BuildOptions, args ...string) error { return fmt.Errorf("could not complete build: %w", err) } - if _, _, _, err = initrd.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, false, (*opts.Target).Architecture().String(), opts.InitrdOptions); err != nil { + if _, _, _, err = initrd.BuildRootfs( + ctx, + append(opts.InitrdOptions, + initrd.WithRootfsPath(opts.Rootfs), + initrd.WithWorkdir(opts.Workdir), + initrd.WithOutput(filepath.Join( + opts.Workdir, + unikraft.BuildDir, + fmt.Sprintf(initrd.DefaultInitramfsArchFileName, (*opts.Target).Architecture().String()), + )), + initrd.WithCacheDir(filepath.Join( + opts.Workdir, + unikraft.VendorDir, + "rootfs-cache", + )), + initrd.WithArchitecture((*opts.Target).Architecture().String()), + initrd.WithCompression(false), + )..., + ); err != nil { return err } diff --git a/internal/cli/kraft/pkg/packager_cli_kernel.go b/internal/cli/kraft/pkg/packager_cli_kernel.go index f5e2d4016..5f705f667 100644 --- a/internal/cli/kraft/pkg/packager_cli_kernel.go +++ b/internal/cli/kraft/pkg/packager_cli_kernel.go @@ -8,6 +8,7 @@ package pkg import ( "context" "fmt" + "path/filepath" "strings" "kraftkit.sh/config" @@ -16,6 +17,7 @@ import ( "kraftkit.sh/pack" "kraftkit.sh/packmanager" "kraftkit.sh/tui/processtree" + "kraftkit.sh/unikraft" "kraftkit.sh/unikraft/arch" "kraftkit.sh/unikraft/plat" "kraftkit.sh/unikraft/target" @@ -65,7 +67,25 @@ func (p *packagerCliKernel) Pack(ctx context.Context, opts *PkgOptions, args ... var cmds []string var envs []string var rootfs initrd.Initrd - if rootfs, cmds, envs, err = initrd.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.InitrdOptions); err != nil { + if rootfs, cmds, envs, err = initrd.BuildRootfs( + ctx, + append(opts.InitrdOptions, + initrd.WithRootfsPath(opts.Rootfs), + initrd.WithWorkdir(opts.Workdir), + initrd.WithOutput(filepath.Join( + opts.Workdir, + unikraft.BuildDir, + fmt.Sprintf(initrd.DefaultInitramfsArchFileName, targ.Architecture().String()), + )), + initrd.WithCacheDir(filepath.Join( + opts.Workdir, + unikraft.VendorDir, + "rootfs-cache", + )), + initrd.WithArchitecture(targ.Architecture().String()), + initrd.WithCompression(opts.Compress), + )..., + ); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } diff --git a/internal/cli/kraft/pkg/packager_kraftfile_runtime.go b/internal/cli/kraft/pkg/packager_kraftfile_runtime.go index 40dbb7a4f..6940b2ed0 100644 --- a/internal/cli/kraft/pkg/packager_kraftfile_runtime.go +++ b/internal/cli/kraft/pkg/packager_kraftfile_runtime.go @@ -9,6 +9,7 @@ import ( "context" "fmt" "os" + "path/filepath" "strings" "github.com/mattn/go-shellwords" @@ -383,7 +384,25 @@ func (p *packagerKraftfileRuntime) Pack(ctx context.Context, opts *PkgOptions, a } var rootfsArgs []string - if p.rootfs, rootfsArgs, p.env, err = initrd.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, p.architecture.String(), opts.InitrdOptions); err != nil { + if p.rootfs, rootfsArgs, p.env, err = initrd.BuildRootfs( + ctx, + append(opts.InitrdOptions, + initrd.WithRootfsPath(opts.Rootfs), + initrd.WithWorkdir(opts.Workdir), + initrd.WithOutput(filepath.Join( + opts.Workdir, + unikraft.BuildDir, + fmt.Sprintf(initrd.DefaultInitramfsArchFileName, p.architecture.String()), + )), + initrd.WithCacheDir(filepath.Join( + opts.Workdir, + unikraft.VendorDir, + "rootfs-cache", + )), + initrd.WithArchitecture(p.architecture.String()), + initrd.WithCompression(opts.Compress), + )..., + ); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } diff --git a/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go b/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go index 04399033a..f697e148d 100644 --- a/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go +++ b/internal/cli/kraft/pkg/packager_kraftfile_unikraft.go @@ -9,6 +9,7 @@ import ( "context" "fmt" "os" + "path/filepath" "slices" "strings" @@ -107,7 +108,25 @@ func (p *packagerKraftfileUnikraft) Pack(ctx context.Context, opts *PkgOptions, "CONFIG_LIBPOSIX_VFS_FSTAB_BUILTIN_EINITRD", "CONFIG_LIBPOSIX_VFS_FSTAB_FALLBACK_EINITRD", ) { - if rootfs, cmds, envs, err = initrd.BuildRootfs(ctx, opts.Workdir, opts.Rootfs, opts.Compress, targ.Architecture().String(), opts.InitrdOptions); err != nil { + if rootfs, cmds, envs, err = initrd.BuildRootfs( + ctx, + append(opts.InitrdOptions, + initrd.WithRootfsPath(opts.Rootfs), + initrd.WithWorkdir(opts.Workdir), + initrd.WithOutput(filepath.Join( + opts.Workdir, + unikraft.BuildDir, + fmt.Sprintf(initrd.DefaultInitramfsArchFileName, targ.Architecture().String()), + )), + initrd.WithCacheDir(filepath.Join( + opts.Workdir, + unikraft.VendorDir, + "rootfs-cache", + )), + initrd.WithArchitecture(targ.Architecture().String()), + initrd.WithCompression(opts.Compress), + )..., + ); err != nil { return nil, fmt.Errorf("could not build rootfs: %w", err) } }