mirror of
https://github.com/containers/podman.git
synced 2025-06-23 18:59:30 +08:00
Merge pull request #19960 from umohnani8/build-common
Move podman build opts to common file
This commit is contained in:
605
cmd/podman/common/build.go
Normal file
605
cmd/podman/common/build.go
Normal file
@ -0,0 +1,605 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
buildahDefine "github.com/containers/buildah/define"
|
||||
buildahCLI "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
buildahUtil "github.com/containers/buildah/pkg/util"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/containers/common/pkg/completion"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/containers/image/v5/types"
|
||||
encconfig "github.com/containers/ocicrypt/config"
|
||||
enchelpers "github.com/containers/ocicrypt/helpers"
|
||||
"github.com/containers/podman/v4/cmd/podman/registry"
|
||||
"github.com/containers/podman/v4/cmd/podman/utils"
|
||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||
"github.com/containers/podman/v4/pkg/env"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// BuildFlagsWrapper are local to cmd/ as the build code is using Buildah-internal
|
||||
// types. Hence, after parsing, we are converting buildFlagsWrapper to the entities'
|
||||
// options which essentially embed the Buildah types.
|
||||
type BuildFlagsWrapper struct {
|
||||
// Buildah stuff first
|
||||
buildahCLI.BudResults
|
||||
buildahCLI.LayerResults
|
||||
buildahCLI.FromAndBudResults
|
||||
buildahCLI.NameSpaceResults
|
||||
buildahCLI.UserNSResults
|
||||
|
||||
// SquashAll squashes all layers into a single layer.
|
||||
SquashAll bool
|
||||
// Cleanup removes built images from remote connections on success
|
||||
Cleanup bool
|
||||
}
|
||||
|
||||
func DefineBuildFlags(cmd *cobra.Command, buildOpts *BuildFlagsWrapper) {
|
||||
flags := cmd.Flags()
|
||||
|
||||
// buildx build --load ignored, but added for compliance
|
||||
flags.Bool("load", false, "buildx --load")
|
||||
_ = flags.MarkHidden("load")
|
||||
|
||||
// buildx build --progress ignored, but added for compliance
|
||||
flags.String("progress", "auto", "buildx --progress")
|
||||
_ = flags.MarkHidden("progress")
|
||||
|
||||
// Podman flags
|
||||
flags.BoolVarP(&buildOpts.SquashAll, "squash-all", "", false, "Squash all layers into a single layer")
|
||||
|
||||
// Bud flags
|
||||
budFlags := buildahCLI.GetBudFlags(&buildOpts.BudResults)
|
||||
|
||||
// --pull flag
|
||||
flag := budFlags.Lookup("pull")
|
||||
if err := flag.Value.Set("true"); err != nil {
|
||||
logrus.Errorf("Unable to set --pull to true: %v", err)
|
||||
}
|
||||
flag.DefValue = "true"
|
||||
flag.Usage = "Always attempt to pull the image (errors are fatal)"
|
||||
flags.AddFlagSet(&budFlags)
|
||||
|
||||
// Add the completion functions
|
||||
budCompletions := buildahCLI.GetBudFlagsCompletions()
|
||||
completion.CompleteCommandFlags(cmd, budCompletions)
|
||||
|
||||
// Layer flags
|
||||
layerFlags := buildahCLI.GetLayerFlags(&buildOpts.LayerResults)
|
||||
// --layers flag
|
||||
flag = layerFlags.Lookup("layers")
|
||||
useLayersVal := useLayers()
|
||||
buildOpts.Layers = useLayersVal == "true"
|
||||
if err := flag.Value.Set(useLayersVal); err != nil {
|
||||
logrus.Errorf("Unable to set --layers to %v: %v", useLayersVal, err)
|
||||
}
|
||||
flag.DefValue = useLayersVal
|
||||
// --force-rm flag
|
||||
flag = layerFlags.Lookup("force-rm")
|
||||
if err := flag.Value.Set("true"); err != nil {
|
||||
logrus.Errorf("Unable to set --force-rm to true: %v", err)
|
||||
}
|
||||
flag.DefValue = "true"
|
||||
flags.AddFlagSet(&layerFlags)
|
||||
|
||||
// FromAndBud flags
|
||||
fromAndBudFlags, err := buildahCLI.GetFromAndBudFlags(&buildOpts.FromAndBudResults, &buildOpts.UserNSResults, &buildOpts.NameSpaceResults)
|
||||
if err != nil {
|
||||
logrus.Errorf("Setting up build flags: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
flags.AddFlagSet(&fromAndBudFlags)
|
||||
// Add the completion functions
|
||||
fromAndBudFlagsCompletions := buildahCLI.GetFromAndBudFlagsCompletions()
|
||||
completion.CompleteCommandFlags(cmd, fromAndBudFlagsCompletions)
|
||||
flags.SetNormalizeFunc(buildahCLI.AliasFlags)
|
||||
if registry.IsRemote() {
|
||||
_ = flags.MarkHidden("disable-content-trust")
|
||||
_ = flags.MarkHidden("sign-by")
|
||||
_ = flags.MarkHidden("signature-policy")
|
||||
_ = flags.MarkHidden("tls-verify")
|
||||
_ = flags.MarkHidden("compress")
|
||||
_ = flags.MarkHidden("output")
|
||||
_ = flags.MarkHidden("logsplit")
|
||||
_ = flags.MarkHidden("cw")
|
||||
}
|
||||
}
|
||||
|
||||
func ParseBuildOpts(cmd *cobra.Command, args []string, buildOpts *BuildFlagsWrapper) (*entities.BuildOptions, error) {
|
||||
if (cmd.Flags().Changed("squash") && cmd.Flags().Changed("layers")) ||
|
||||
(cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("squash")) {
|
||||
return nil, errors.New("cannot specify --squash with --layers and --squash-all with --squash")
|
||||
}
|
||||
|
||||
if cmd.Flag("output").Changed && registry.IsRemote() {
|
||||
return nil, errors.New("'--output' option is not supported in remote mode")
|
||||
}
|
||||
|
||||
if buildOpts.Network == "none" {
|
||||
if cmd.Flag("dns").Changed {
|
||||
return nil, errors.New("the --dns option cannot be used with --network=none")
|
||||
}
|
||||
if cmd.Flag("dns-option").Changed {
|
||||
return nil, errors.New("the --dns-option option cannot be used with --network=none")
|
||||
}
|
||||
if cmd.Flag("dns-search").Changed {
|
||||
return nil, errors.New("the --dns-search option cannot be used with --network=none")
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.Flag("network").Changed {
|
||||
if buildOpts.Network != "host" && buildOpts.Isolation == buildahDefine.IsolationChroot.String() {
|
||||
return nil, fmt.Errorf("cannot set --network other than host with --isolation %s", buildOpts.Isolation)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract container files from the CLI (i.e., --file/-f) first.
|
||||
var containerFiles []string
|
||||
for _, f := range buildOpts.File {
|
||||
if f == "-" {
|
||||
if len(args) == 0 {
|
||||
args = append(args, "-")
|
||||
} else {
|
||||
containerFiles = append(containerFiles, "/dev/stdin")
|
||||
}
|
||||
} else {
|
||||
containerFiles = append(containerFiles, f)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine context directory.
|
||||
var (
|
||||
contextDir string
|
||||
apiBuildOpts entities.BuildOptions
|
||||
)
|
||||
if len(args) > 0 {
|
||||
// The context directory could be a URL. Try to handle that.
|
||||
tempDir, subDir, err := buildahDefine.TempDirForURL("", "buildah", args[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("prepping temporary context directory: %w", err)
|
||||
}
|
||||
if tempDir != "" {
|
||||
apiBuildOpts.TmpDirToClose = tempDir
|
||||
contextDir = filepath.Join(tempDir, subDir)
|
||||
} else {
|
||||
// Nope, it was local. Use it as is.
|
||||
absDir, err := filepath.Abs(args[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("determining path to directory %q: %w", args[0], err)
|
||||
}
|
||||
contextDir = absDir
|
||||
}
|
||||
} else {
|
||||
// No context directory or URL was specified. Try to use the home of
|
||||
// the first locally-available Containerfile.
|
||||
for i := range containerFiles {
|
||||
if strings.HasPrefix(containerFiles[i], "http://") ||
|
||||
strings.HasPrefix(containerFiles[i], "https://") ||
|
||||
strings.HasPrefix(containerFiles[i], "git://") ||
|
||||
strings.HasPrefix(containerFiles[i], "github.com/") {
|
||||
continue
|
||||
}
|
||||
absFile, err := filepath.Abs(containerFiles[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("determining path to file %q: %w", containerFiles[i], err)
|
||||
}
|
||||
contextDir = filepath.Dir(absFile)
|
||||
containerFiles[i] = absFile
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if contextDir == "" {
|
||||
return nil, errors.New("no context directory and no Containerfile specified")
|
||||
}
|
||||
if !utils.IsDir(contextDir) {
|
||||
return nil, fmt.Errorf("context must be a directory: %q", contextDir)
|
||||
}
|
||||
if len(containerFiles) == 0 {
|
||||
switch {
|
||||
case utils.FileExists(filepath.Join(contextDir, "Containerfile")):
|
||||
if utils.IsDir(filepath.Join(contextDir, "Containerfile")) {
|
||||
return nil, fmt.Errorf("containerfile: cannot be path or directory")
|
||||
}
|
||||
containerFiles = append(containerFiles, filepath.Join(contextDir, "Containerfile"))
|
||||
case utils.FileExists(filepath.Join(contextDir, "Dockerfile")):
|
||||
if utils.IsDir(filepath.Join(contextDir, "Dockerfile")) {
|
||||
return nil, fmt.Errorf("dockerfile: cannot be path or directory")
|
||||
}
|
||||
containerFiles = append(containerFiles, filepath.Join(contextDir, "Dockerfile"))
|
||||
default:
|
||||
return nil, fmt.Errorf("no Containerfile or Dockerfile specified or found in context directory, %s: %w", contextDir, syscall.ENOENT)
|
||||
}
|
||||
}
|
||||
|
||||
var logFile *os.File
|
||||
if cmd.Flag("logfile").Changed {
|
||||
var err error
|
||||
logFile, err = os.OpenFile(buildOpts.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiBuildOpts.LogFileToClose = logFile
|
||||
}
|
||||
|
||||
buildahDefineOpts, err := buildFlagsWrapperToOptions(cmd, contextDir, buildOpts, logFile, buildOpts.Layers, buildOpts.Squash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiBuildOpts.BuildOptions = *buildahDefineOpts
|
||||
apiBuildOpts.ContainerFiles = containerFiles
|
||||
|
||||
return &apiBuildOpts, err
|
||||
}
|
||||
|
||||
// buildFlagsWrapperToOptions converts the local build flags to the build options used
|
||||
// in the API which embed Buildah types used across the build code. Doing the
|
||||
// conversion here prevents the API from doing that (redundantly).
|
||||
//
|
||||
// TODO: this code should really be in Buildah.
|
||||
func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *BuildFlagsWrapper, logfile *os.File, layers, squash bool) (*buildahDefine.BuildOptions, error) {
|
||||
output := ""
|
||||
tags := []string{}
|
||||
if c.Flag("tag").Changed {
|
||||
tags = flags.Tag
|
||||
if len(tags) > 0 {
|
||||
output = tags[0]
|
||||
tags = tags[1:]
|
||||
}
|
||||
}
|
||||
|
||||
if c.Flags().Changed("authfile") {
|
||||
if err := auth.CheckAuthFile(flags.Authfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
commonOpts, err := parse.CommonBuildOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pullFlagsCount := 0
|
||||
if c.Flag("pull").Changed {
|
||||
pullFlagsCount++
|
||||
}
|
||||
if c.Flag("pull-always").Changed {
|
||||
pullFlagsCount++
|
||||
}
|
||||
if c.Flag("pull-never").Changed {
|
||||
pullFlagsCount++
|
||||
}
|
||||
|
||||
if pullFlagsCount > 1 {
|
||||
return nil, errors.New("can only set one of 'pull' or 'pull-always' or 'pull-never'")
|
||||
}
|
||||
|
||||
// Allow for --pull, --pull=true, --pull=false, --pull=never, --pull=always
|
||||
// --pull-always and --pull-never. The --pull-never and --pull-always options
|
||||
// will not be documented.
|
||||
pullPolicy := buildahDefine.PullIfMissing
|
||||
if c.Flags().Changed("pull") && strings.EqualFold(strings.TrimSpace(flags.Pull), "true") {
|
||||
pullPolicy = buildahDefine.PullAlways
|
||||
}
|
||||
if flags.PullAlways || strings.EqualFold(strings.TrimSpace(flags.Pull), "always") {
|
||||
pullPolicy = buildahDefine.PullAlways
|
||||
}
|
||||
|
||||
if flags.PullNever || strings.EqualFold(strings.TrimSpace(flags.Pull), "never") {
|
||||
pullPolicy = buildahDefine.PullNever
|
||||
}
|
||||
|
||||
var cleanTmpFile bool
|
||||
flags.Authfile, cleanTmpFile = buildahUtil.MirrorToTempFileIfPathIsDescriptor(flags.Authfile)
|
||||
if cleanTmpFile {
|
||||
defer os.Remove(flags.Authfile)
|
||||
}
|
||||
|
||||
args := make(map[string]string)
|
||||
if c.Flag("build-arg-file").Changed {
|
||||
for _, argfile := range flags.BuildArgFile {
|
||||
fargs, err := env.ParseFile(argfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for name, val := range fargs {
|
||||
args[name] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.Flag("build-arg").Changed {
|
||||
for _, arg := range flags.BuildArg {
|
||||
av := strings.SplitN(arg, "=", 2)
|
||||
if len(av) > 1 {
|
||||
args[av[0]] = av[1]
|
||||
} else {
|
||||
// check if the env is set in the local environment and use that value if it is
|
||||
if val, present := os.LookupEnv(av[0]); present {
|
||||
args[av[0]] = val
|
||||
} else {
|
||||
delete(args, av[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flags.Layers = layers
|
||||
|
||||
// `buildah bud --layers=false` acts like `docker build --squash` does.
|
||||
// That is all of the new layers created during the build process are
|
||||
// condensed into one, any layers present prior to this build are
|
||||
// retained without condensing. `buildah bud --squash` squashes both
|
||||
// new and old layers down into one. Translate Podman commands into
|
||||
// Buildah. Squash invoked, retain old layers, squash new layers into
|
||||
// one.
|
||||
if c.Flags().Changed("squash") && squash {
|
||||
flags.Squash = false
|
||||
flags.Layers = false
|
||||
}
|
||||
// Squash-all invoked, squash both new and old layers into one.
|
||||
if c.Flags().Changed("squash-all") {
|
||||
flags.Squash = true
|
||||
if !c.Flags().Changed("layers") {
|
||||
// Buildah supports using layers and --squash together
|
||||
// after https://github.com/containers/buildah/pull/3674
|
||||
// so podman must honor if user wants to still use layers
|
||||
// with --squash-all.
|
||||
flags.Layers = false
|
||||
}
|
||||
}
|
||||
|
||||
var stdin io.Reader
|
||||
if flags.Stdin {
|
||||
stdin = os.Stdin
|
||||
}
|
||||
var stdout, stderr, reporter *os.File
|
||||
stdout = os.Stdout
|
||||
stderr = os.Stderr
|
||||
reporter = os.Stderr
|
||||
|
||||
if logfile != nil {
|
||||
logrus.SetOutput(logfile)
|
||||
stdout = logfile
|
||||
stderr = logfile
|
||||
reporter = logfile
|
||||
}
|
||||
|
||||
nsValues, networkPolicy, err := parse.NamespaceOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compression := buildahDefine.Gzip
|
||||
if flags.DisableCompression {
|
||||
compression = buildahDefine.Uncompressed
|
||||
}
|
||||
|
||||
isolation, err := parse.IsolationOption(flags.Isolation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usernsOption, idmappingOptions, err := parse.IDMappingOptions(c, isolation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nsValues = append(nsValues, usernsOption...)
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var format string
|
||||
flags.Format = strings.ToLower(flags.Format)
|
||||
switch {
|
||||
case strings.HasPrefix(flags.Format, buildahDefine.OCI):
|
||||
format = buildahDefine.OCIv1ImageManifest
|
||||
case strings.HasPrefix(flags.Format, buildahDefine.DOCKER):
|
||||
format = buildahDefine.Dockerv2ImageManifest
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized image type %q", flags.Format)
|
||||
}
|
||||
|
||||
runtimeFlags := []string{}
|
||||
for _, arg := range flags.RuntimeFlags {
|
||||
runtimeFlags = append(runtimeFlags, "--"+arg)
|
||||
}
|
||||
|
||||
podmanConfig := registry.PodmanConfig()
|
||||
for _, arg := range podmanConfig.RuntimeFlags {
|
||||
runtimeFlags = append(runtimeFlags, "--"+arg)
|
||||
}
|
||||
if podmanConfig.ContainersConf.Engine.CgroupManager == config.SystemdCgroupsManager {
|
||||
runtimeFlags = append(runtimeFlags, "--systemd-cgroup")
|
||||
}
|
||||
|
||||
platforms, err := parse.PlatformsFromOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decConfig, err := getDecryptConfig(flags.DecryptionKeys)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to obtain decrypt config: %w", err)
|
||||
}
|
||||
|
||||
additionalBuildContext := make(map[string]*buildahDefine.AdditionalBuildContext)
|
||||
if c.Flag("build-context").Changed {
|
||||
for _, contextString := range flags.BuildContext {
|
||||
av := strings.SplitN(contextString, "=", 2)
|
||||
if len(av) > 1 {
|
||||
parseAdditionalBuildContext, err := parse.GetAdditionalBuildContext(av[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while parsing additional build context: %w", err)
|
||||
}
|
||||
additionalBuildContext[av[0]] = &parseAdditionalBuildContext
|
||||
} else {
|
||||
return nil, fmt.Errorf("while parsing additional build context: %q, accepts value in the form of key=value", av)
|
||||
}
|
||||
}
|
||||
}
|
||||
var cacheTo []reference.Named
|
||||
var cacheFrom []reference.Named
|
||||
if c.Flag("cache-to").Changed {
|
||||
cacheTo, err = parse.RepoNamesToNamedReferences(flags.CacheTo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse value provided `%s` to --cache-to: %w", flags.CacheTo, err)
|
||||
}
|
||||
}
|
||||
if c.Flag("cache-from").Changed {
|
||||
cacheFrom, err = parse.RepoNamesToNamedReferences(flags.CacheFrom)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse value provided `%s` to --cache-from: %w", flags.CacheTo, err)
|
||||
}
|
||||
}
|
||||
var cacheTTL time.Duration
|
||||
if c.Flag("cache-ttl").Changed {
|
||||
cacheTTL, err = time.ParseDuration(flags.CacheTTL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse value provided %q as --cache-ttl: %w", flags.CacheTTL, err)
|
||||
}
|
||||
}
|
||||
|
||||
var confidentialWorkloadOptions buildahDefine.ConfidentialWorkloadOptions
|
||||
if c.Flag("cw").Changed {
|
||||
confidentialWorkloadOptions, err = parse.GetConfidentialWorkloadOptions(flags.CWOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
opts := buildahDefine.BuildOptions{
|
||||
AddCapabilities: flags.CapAdd,
|
||||
AdditionalTags: tags,
|
||||
AdditionalBuildContexts: additionalBuildContext,
|
||||
AllPlatforms: flags.AllPlatforms,
|
||||
Annotations: flags.Annotation,
|
||||
Args: args,
|
||||
BlobDirectory: flags.BlobCache,
|
||||
BuildOutput: flags.BuildOutput,
|
||||
CacheFrom: cacheFrom,
|
||||
CacheTo: cacheTo,
|
||||
CacheTTL: cacheTTL,
|
||||
ConfidentialWorkload: confidentialWorkloadOptions,
|
||||
CommonBuildOpts: commonOpts,
|
||||
Compression: compression,
|
||||
ConfigureNetwork: networkPolicy,
|
||||
ContextDirectory: contextDir,
|
||||
CPPFlags: flags.CPPFlags,
|
||||
DefaultMountsFilePath: podmanConfig.ContainersConfDefaultsRO.Containers.DefaultMountsFile,
|
||||
Devices: flags.Devices,
|
||||
DropCapabilities: flags.CapDrop,
|
||||
Envs: buildahCLI.LookupEnvVarReferences(flags.Envs, os.Environ()),
|
||||
Err: stderr,
|
||||
ForceRmIntermediateCtrs: flags.ForceRm,
|
||||
From: flags.From,
|
||||
GroupAdd: flags.GroupAdd,
|
||||
IDMappingOptions: idmappingOptions,
|
||||
In: stdin,
|
||||
Isolation: isolation,
|
||||
Jobs: &flags.Jobs,
|
||||
Labels: flags.Label,
|
||||
LayerLabels: flags.LayerLabel,
|
||||
Layers: flags.Layers,
|
||||
LogRusage: flags.LogRusage,
|
||||
LogFile: flags.Logfile,
|
||||
LogSplitByPlatform: flags.LogSplitByPlatform,
|
||||
Manifest: flags.Manifest,
|
||||
MaxPullPushRetries: 3,
|
||||
NamespaceOptions: nsValues,
|
||||
NoCache: flags.NoCache,
|
||||
OSFeatures: flags.OSFeatures,
|
||||
OSVersion: flags.OSVersion,
|
||||
OciDecryptConfig: decConfig,
|
||||
Out: stdout,
|
||||
Output: output,
|
||||
OutputFormat: format,
|
||||
Platforms: platforms,
|
||||
PullPolicy: pullPolicy,
|
||||
PullPushRetryDelay: 2 * time.Second,
|
||||
Quiet: flags.Quiet,
|
||||
RemoveIntermediateCtrs: flags.Rm,
|
||||
ReportWriter: reporter,
|
||||
Runtime: podmanConfig.RuntimePath,
|
||||
RuntimeArgs: runtimeFlags,
|
||||
RusageLogFile: flags.RusageLogFile,
|
||||
SignBy: flags.SignBy,
|
||||
SignaturePolicyPath: flags.SignaturePolicy,
|
||||
Squash: flags.Squash,
|
||||
SystemContext: systemContext,
|
||||
Target: flags.Target,
|
||||
TransientMounts: flags.Volumes,
|
||||
UnsetEnvs: flags.UnsetEnvs,
|
||||
}
|
||||
|
||||
if flags.IgnoreFile != "" {
|
||||
excludes, err := parseDockerignore(flags.IgnoreFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to obtain decrypt config: %w", err)
|
||||
}
|
||||
opts.Excludes = excludes
|
||||
}
|
||||
|
||||
if c.Flag("timestamp").Changed {
|
||||
timestamp := time.Unix(flags.Timestamp, 0).UTC()
|
||||
opts.Timestamp = ×tamp
|
||||
}
|
||||
if c.Flag("skip-unused-stages").Changed {
|
||||
opts.SkipUnusedStages = types.NewOptionalBool(flags.SkipUnusedStages)
|
||||
}
|
||||
|
||||
return &opts, nil
|
||||
}
|
||||
|
||||
// useLayers returns false if BUILDAH_LAYERS is set to "0" or "false"
|
||||
// otherwise it returns true
|
||||
func useLayers() string {
|
||||
layers := os.Getenv("BUILDAH_LAYERS")
|
||||
if strings.ToLower(layers) == "false" || layers == "0" {
|
||||
return "false"
|
||||
}
|
||||
return "true"
|
||||
}
|
||||
|
||||
func getDecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error) {
|
||||
decConfig := &encconfig.DecryptConfig{}
|
||||
if len(decryptionKeys) > 0 {
|
||||
// decryption
|
||||
dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid decryption keys: %w", err)
|
||||
}
|
||||
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc})
|
||||
decConfig = cc.DecryptConfig
|
||||
}
|
||||
|
||||
return decConfig, nil
|
||||
}
|
||||
|
||||
func parseDockerignore(ignoreFile string) ([]string, error) {
|
||||
excludes := []string{}
|
||||
ignore, err := os.ReadFile(ignoreFile)
|
||||
if err != nil {
|
||||
return excludes, err
|
||||
}
|
||||
for _, e := range strings.Split(string(ignore), "\n") {
|
||||
if len(e) == 0 || e[0] == '#' {
|
||||
continue
|
||||
}
|
||||
excludes = append(excludes, e)
|
||||
}
|
||||
return excludes, nil
|
||||
}
|
@ -2,50 +2,17 @@ package images
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
buildahDefine "github.com/containers/buildah/define"
|
||||
buildahCLI "github.com/containers/buildah/pkg/cli"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
buildahUtil "github.com/containers/buildah/pkg/util"
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/containers/common/pkg/completion"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/containers/image/v5/types"
|
||||
encconfig "github.com/containers/ocicrypt/config"
|
||||
enchelpers "github.com/containers/ocicrypt/helpers"
|
||||
"github.com/containers/podman/v4/cmd/podman/common"
|
||||
"github.com/containers/podman/v4/cmd/podman/registry"
|
||||
"github.com/containers/podman/v4/cmd/podman/utils"
|
||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||
"github.com/containers/podman/v4/pkg/env"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// buildFlagsWrapper are local to cmd/ as the build code is using Buildah-internal
|
||||
// types. Hence, after parsing, we are converting buildFlagsWrapper to the entities'
|
||||
// options which essentially embed the Buildah types.
|
||||
type buildFlagsWrapper struct {
|
||||
// Buildah stuff first
|
||||
buildahCLI.BudResults
|
||||
buildahCLI.LayerResults
|
||||
buildahCLI.FromAndBudResults
|
||||
buildahCLI.NameSpaceResults
|
||||
buildahCLI.UserNSResults
|
||||
|
||||
// SquashAll squashes all layers into a single layer.
|
||||
SquashAll bool
|
||||
}
|
||||
|
||||
var (
|
||||
// Command: podman _diff_ Object_ID
|
||||
buildDescription = "Builds an OCI or Docker image using instructions from one or more Containerfiles and a specified build context directory."
|
||||
@ -85,19 +52,9 @@ var (
|
||||
podman buildx build --layers --force-rm --tag imageName .`,
|
||||
}
|
||||
|
||||
buildOpts = buildFlagsWrapper{}
|
||||
buildOpts = common.BuildFlagsWrapper{}
|
||||
)
|
||||
|
||||
// useLayers returns false if BUILDAH_LAYERS is set to "0" or "false"
|
||||
// otherwise it returns true
|
||||
func useLayers() string {
|
||||
layers := os.Getenv("BUILDAH_LAYERS")
|
||||
if strings.ToLower(layers) == "false" || layers == "0" {
|
||||
return "false"
|
||||
}
|
||||
return "true"
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Command: buildCmd,
|
||||
@ -117,203 +74,29 @@ func init() {
|
||||
}
|
||||
|
||||
func buildFlags(cmd *cobra.Command) {
|
||||
flags := cmd.Flags()
|
||||
|
||||
// buildx build --load ignored, but added for compliance
|
||||
flags.Bool("load", false, "buildx --load")
|
||||
_ = flags.MarkHidden("load")
|
||||
|
||||
// buildx build --progress ignored, but added for compliance
|
||||
flags.String("progress", "auto", "buildx --progress")
|
||||
_ = flags.MarkHidden("progress")
|
||||
|
||||
// Podman flags
|
||||
flags.BoolVarP(&buildOpts.SquashAll, "squash-all", "", false, "Squash all layers into a single layer")
|
||||
|
||||
// Bud flags
|
||||
budFlags := buildahCLI.GetBudFlags(&buildOpts.BudResults)
|
||||
|
||||
// --pull flag
|
||||
flag := budFlags.Lookup("pull")
|
||||
if err := flag.Value.Set("true"); err != nil {
|
||||
logrus.Errorf("Unable to set --pull to true: %v", err)
|
||||
}
|
||||
flag.DefValue = "true"
|
||||
flag.Usage = "Always attempt to pull the image (errors are fatal)"
|
||||
flags.AddFlagSet(&budFlags)
|
||||
|
||||
// Add the completion functions
|
||||
budCompletions := buildahCLI.GetBudFlagsCompletions()
|
||||
completion.CompleteCommandFlags(cmd, budCompletions)
|
||||
|
||||
// Layer flags
|
||||
layerFlags := buildahCLI.GetLayerFlags(&buildOpts.LayerResults)
|
||||
// --layers flag
|
||||
flag = layerFlags.Lookup("layers")
|
||||
useLayersVal := useLayers()
|
||||
buildOpts.Layers = useLayersVal == "true"
|
||||
if err := flag.Value.Set(useLayersVal); err != nil {
|
||||
logrus.Errorf("Unable to set --layers to %v: %v", useLayersVal, err)
|
||||
}
|
||||
flag.DefValue = useLayersVal
|
||||
// --force-rm flag
|
||||
flag = layerFlags.Lookup("force-rm")
|
||||
if err := flag.Value.Set("true"); err != nil {
|
||||
logrus.Errorf("Unable to set --force-rm to true: %v", err)
|
||||
}
|
||||
flag.DefValue = "true"
|
||||
flags.AddFlagSet(&layerFlags)
|
||||
|
||||
// FromAndBud flags
|
||||
fromAndBudFlags, err := buildahCLI.GetFromAndBudFlags(&buildOpts.FromAndBudResults, &buildOpts.UserNSResults, &buildOpts.NameSpaceResults)
|
||||
if err != nil {
|
||||
logrus.Errorf("Setting up build flags: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
flags.AddFlagSet(&fromAndBudFlags)
|
||||
// Add the completion functions
|
||||
fromAndBudFlagsCompletions := buildahCLI.GetFromAndBudFlagsCompletions()
|
||||
completion.CompleteCommandFlags(cmd, fromAndBudFlagsCompletions)
|
||||
flags.SetNormalizeFunc(buildahCLI.AliasFlags)
|
||||
if registry.IsRemote() {
|
||||
_ = flags.MarkHidden("disable-content-trust")
|
||||
_ = flags.MarkHidden("sign-by")
|
||||
_ = flags.MarkHidden("signature-policy")
|
||||
_ = flags.MarkHidden("tls-verify")
|
||||
_ = flags.MarkHidden("compress")
|
||||
_ = flags.MarkHidden("output")
|
||||
_ = flags.MarkHidden("logsplit")
|
||||
_ = flags.MarkHidden("cw")
|
||||
}
|
||||
common.DefineBuildFlags(cmd, &buildOpts)
|
||||
}
|
||||
|
||||
// build executes the build command.
|
||||
func build(cmd *cobra.Command, args []string) error {
|
||||
if (cmd.Flags().Changed("squash") && cmd.Flags().Changed("layers")) ||
|
||||
(cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("squash")) {
|
||||
return errors.New("cannot specify --squash with --layers and --squash-all with --squash")
|
||||
}
|
||||
|
||||
if cmd.Flag("output").Changed && registry.IsRemote() {
|
||||
return errors.New("'--output' option is not supported in remote mode")
|
||||
}
|
||||
|
||||
if buildOpts.Network == "none" {
|
||||
if cmd.Flag("dns").Changed {
|
||||
return errors.New("the --dns option cannot be used with --network=none")
|
||||
}
|
||||
if cmd.Flag("dns-option").Changed {
|
||||
return errors.New("the --dns-option option cannot be used with --network=none")
|
||||
}
|
||||
if cmd.Flag("dns-search").Changed {
|
||||
return errors.New("the --dns-search option cannot be used with --network=none")
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.Flag("network").Changed {
|
||||
if buildOpts.Network != "host" && buildOpts.Isolation == buildahDefine.IsolationChroot.String() {
|
||||
return fmt.Errorf("cannot set --network other than host with --isolation %s", buildOpts.Isolation)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract container files from the CLI (i.e., --file/-f) first.
|
||||
var containerFiles []string
|
||||
for _, f := range buildOpts.File {
|
||||
if f == "-" {
|
||||
if len(args) == 0 {
|
||||
args = append(args, "-")
|
||||
} else {
|
||||
containerFiles = append(containerFiles, "/dev/stdin")
|
||||
}
|
||||
} else {
|
||||
containerFiles = append(containerFiles, f)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine context directory.
|
||||
var contextDir string
|
||||
if len(args) > 0 {
|
||||
// The context directory could be a URL. Try to handle that.
|
||||
tempDir, subDir, err := buildahDefine.TempDirForURL("", "buildah", args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("prepping temporary context directory: %w", err)
|
||||
}
|
||||
if tempDir != "" {
|
||||
// We had to download it to a temporary directory.
|
||||
// Delete it later.
|
||||
defer func() {
|
||||
if err = os.RemoveAll(tempDir); err != nil {
|
||||
logrus.Errorf("Removing temporary directory %q: %v", contextDir, err)
|
||||
}
|
||||
}()
|
||||
contextDir = filepath.Join(tempDir, subDir)
|
||||
} else {
|
||||
// Nope, it was local. Use it as is.
|
||||
absDir, err := filepath.Abs(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("determining path to directory %q: %w", args[0], err)
|
||||
}
|
||||
contextDir = absDir
|
||||
}
|
||||
} else {
|
||||
// No context directory or URL was specified. Try to use the home of
|
||||
// the first locally-available Containerfile.
|
||||
for i := range containerFiles {
|
||||
if strings.HasPrefix(containerFiles[i], "http://") ||
|
||||
strings.HasPrefix(containerFiles[i], "https://") ||
|
||||
strings.HasPrefix(containerFiles[i], "git://") ||
|
||||
strings.HasPrefix(containerFiles[i], "github.com/") {
|
||||
continue
|
||||
}
|
||||
absFile, err := filepath.Abs(containerFiles[i])
|
||||
if err != nil {
|
||||
return fmt.Errorf("determining path to file %q: %w", containerFiles[i], err)
|
||||
}
|
||||
contextDir = filepath.Dir(absFile)
|
||||
containerFiles[i] = absFile
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if contextDir == "" {
|
||||
return errors.New("no context directory and no Containerfile specified")
|
||||
}
|
||||
if !utils.IsDir(contextDir) {
|
||||
return fmt.Errorf("context must be a directory: %q", contextDir)
|
||||
}
|
||||
if len(containerFiles) == 0 {
|
||||
switch {
|
||||
case utils.FileExists(filepath.Join(contextDir, "Containerfile")):
|
||||
if utils.IsDir(filepath.Join(contextDir, "Containerfile")) {
|
||||
return fmt.Errorf("containerfile: cannot be path or directory")
|
||||
}
|
||||
containerFiles = append(containerFiles, filepath.Join(contextDir, "Containerfile"))
|
||||
case utils.FileExists(filepath.Join(contextDir, "Dockerfile")):
|
||||
if utils.IsDir(filepath.Join(contextDir, "Dockerfile")) {
|
||||
return fmt.Errorf("dockerfile: cannot be path or directory")
|
||||
}
|
||||
containerFiles = append(containerFiles, filepath.Join(contextDir, "Dockerfile"))
|
||||
default:
|
||||
return fmt.Errorf("no Containerfile or Dockerfile specified or found in context directory, %s: %w", contextDir, syscall.ENOENT)
|
||||
}
|
||||
}
|
||||
|
||||
var logfile *os.File
|
||||
if cmd.Flag("logfile").Changed {
|
||||
var err error
|
||||
logfile, err = os.OpenFile(buildOpts.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer logfile.Close()
|
||||
}
|
||||
|
||||
apiBuildOpts, err := buildFlagsWrapperToOptions(cmd, contextDir, &buildOpts, logfile)
|
||||
apiBuildOpts, err := common.ParseBuildOpts(cmd, args, &buildOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
report, err := registry.ImageEngine().Build(registry.GetContext(), containerFiles, *apiBuildOpts)
|
||||
// Close the logFile if one was created based on the flag
|
||||
if apiBuildOpts.LogFileToClose != nil {
|
||||
defer apiBuildOpts.LogFileToClose.Close()
|
||||
}
|
||||
if apiBuildOpts.TmpDirToClose != "" {
|
||||
// We had to download the context directory.
|
||||
// Delete it later.
|
||||
defer func() {
|
||||
if err = os.RemoveAll(apiBuildOpts.TmpDirToClose); err != nil {
|
||||
logrus.Errorf("Removing temporary directory %q: %v", apiBuildOpts.ContextDirectory, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
report, err := registry.ImageEngine().Build(registry.GetContext(), apiBuildOpts.ContainerFiles, *apiBuildOpts)
|
||||
|
||||
if err != nil {
|
||||
exitCode := buildahCLI.ExecErrorCodeGeneric
|
||||
@ -347,352 +130,3 @@ func build(cmd *cobra.Command, args []string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildFlagsWrapperToOptions converts the local build flags to the build options used
|
||||
// in the API which embed Buildah types used across the build code. Doing the
|
||||
// conversion here prevents the API from doing that (redundantly).
|
||||
//
|
||||
// TODO: this code should really be in Buildah.
|
||||
func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buildFlagsWrapper, logfile *os.File) (*entities.BuildOptions, error) {
|
||||
output := ""
|
||||
tags := []string{}
|
||||
if c.Flag("tag").Changed {
|
||||
tags = flags.Tag
|
||||
if len(tags) > 0 {
|
||||
output = tags[0]
|
||||
tags = tags[1:]
|
||||
}
|
||||
}
|
||||
|
||||
if c.Flags().Changed("authfile") {
|
||||
if err := auth.CheckAuthFile(flags.Authfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
commonOpts, err := parse.CommonBuildOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pullFlagsCount := 0
|
||||
if c.Flag("pull").Changed {
|
||||
pullFlagsCount++
|
||||
}
|
||||
if c.Flag("pull-always").Changed {
|
||||
pullFlagsCount++
|
||||
}
|
||||
if c.Flag("pull-never").Changed {
|
||||
pullFlagsCount++
|
||||
}
|
||||
|
||||
if pullFlagsCount > 1 {
|
||||
return nil, errors.New("can only set one of 'pull' or 'pull-always' or 'pull-never'")
|
||||
}
|
||||
|
||||
// Allow for --pull, --pull=true, --pull=false, --pull=never, --pull=always
|
||||
// --pull-always and --pull-never. The --pull-never and --pull-always options
|
||||
// will not be documented.
|
||||
pullPolicy := buildahDefine.PullIfMissing
|
||||
if c.Flags().Changed("pull") && strings.EqualFold(strings.TrimSpace(flags.Pull), "true") {
|
||||
pullPolicy = buildahDefine.PullAlways
|
||||
}
|
||||
if flags.PullAlways || strings.EqualFold(strings.TrimSpace(flags.Pull), "always") {
|
||||
pullPolicy = buildahDefine.PullAlways
|
||||
}
|
||||
|
||||
if flags.PullNever || strings.EqualFold(strings.TrimSpace(flags.Pull), "never") {
|
||||
pullPolicy = buildahDefine.PullNever
|
||||
}
|
||||
|
||||
var cleanTmpFile bool
|
||||
flags.Authfile, cleanTmpFile = buildahUtil.MirrorToTempFileIfPathIsDescriptor(flags.Authfile)
|
||||
if cleanTmpFile {
|
||||
defer os.Remove(flags.Authfile)
|
||||
}
|
||||
|
||||
args := make(map[string]string)
|
||||
if c.Flag("build-arg-file").Changed {
|
||||
for _, argfile := range flags.BuildArgFile {
|
||||
fargs, err := env.ParseFile(argfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for name, val := range fargs {
|
||||
args[name] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.Flag("build-arg").Changed {
|
||||
for _, arg := range flags.BuildArg {
|
||||
av := strings.SplitN(arg, "=", 2)
|
||||
if len(av) > 1 {
|
||||
args[av[0]] = av[1]
|
||||
} else {
|
||||
// check if the env is set in the local environment and use that value if it is
|
||||
if val, present := os.LookupEnv(av[0]); present {
|
||||
args[av[0]] = val
|
||||
} else {
|
||||
delete(args, av[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flags.Layers = buildOpts.Layers
|
||||
|
||||
// `buildah bud --layers=false` acts like `docker build --squash` does.
|
||||
// That is all of the new layers created during the build process are
|
||||
// condensed into one, any layers present prior to this build are
|
||||
// retained without condensing. `buildah bud --squash` squashes both
|
||||
// new and old layers down into one. Translate Podman commands into
|
||||
// Buildah. Squash invoked, retain old layers, squash new layers into
|
||||
// one.
|
||||
if c.Flags().Changed("squash") && buildOpts.Squash {
|
||||
flags.Squash = false
|
||||
flags.Layers = false
|
||||
}
|
||||
// Squash-all invoked, squash both new and old layers into one.
|
||||
if c.Flags().Changed("squash-all") {
|
||||
flags.Squash = true
|
||||
if !c.Flags().Changed("layers") {
|
||||
// Buildah supports using layers and --squash together
|
||||
// after https://github.com/containers/buildah/pull/3674
|
||||
// so podman must honor if user wants to still use layers
|
||||
// with --squash-all.
|
||||
flags.Layers = false
|
||||
}
|
||||
}
|
||||
|
||||
var stdin io.Reader
|
||||
if flags.Stdin {
|
||||
stdin = os.Stdin
|
||||
}
|
||||
var stdout, stderr, reporter *os.File
|
||||
stdout = os.Stdout
|
||||
stderr = os.Stderr
|
||||
reporter = os.Stderr
|
||||
|
||||
if logfile != nil {
|
||||
logrus.SetOutput(logfile)
|
||||
stdout = logfile
|
||||
stderr = logfile
|
||||
reporter = logfile
|
||||
}
|
||||
|
||||
nsValues, networkPolicy, err := parse.NamespaceOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compression := buildahDefine.Gzip
|
||||
if flags.DisableCompression {
|
||||
compression = buildahDefine.Uncompressed
|
||||
}
|
||||
|
||||
isolation, err := parse.IsolationOption(flags.Isolation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usernsOption, idmappingOptions, err := parse.IDMappingOptions(c, isolation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nsValues = append(nsValues, usernsOption...)
|
||||
|
||||
systemContext, err := parse.SystemContextFromOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var format string
|
||||
flags.Format = strings.ToLower(flags.Format)
|
||||
switch {
|
||||
case strings.HasPrefix(flags.Format, buildahDefine.OCI):
|
||||
format = buildahDefine.OCIv1ImageManifest
|
||||
case strings.HasPrefix(flags.Format, buildahDefine.DOCKER):
|
||||
format = buildahDefine.Dockerv2ImageManifest
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized image type %q", flags.Format)
|
||||
}
|
||||
|
||||
runtimeFlags := []string{}
|
||||
for _, arg := range flags.RuntimeFlags {
|
||||
runtimeFlags = append(runtimeFlags, "--"+arg)
|
||||
}
|
||||
|
||||
podmanConfig := registry.PodmanConfig()
|
||||
for _, arg := range podmanConfig.RuntimeFlags {
|
||||
runtimeFlags = append(runtimeFlags, "--"+arg)
|
||||
}
|
||||
if podmanConfig.ContainersConf.Engine.CgroupManager == config.SystemdCgroupsManager {
|
||||
runtimeFlags = append(runtimeFlags, "--systemd-cgroup")
|
||||
}
|
||||
|
||||
platforms, err := parse.PlatformsFromOptions(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decConfig, err := getDecryptConfig(flags.DecryptionKeys)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to obtain decrypt config: %w", err)
|
||||
}
|
||||
|
||||
additionalBuildContext := make(map[string]*buildahDefine.AdditionalBuildContext)
|
||||
if c.Flag("build-context").Changed {
|
||||
for _, contextString := range flags.BuildContext {
|
||||
av := strings.SplitN(contextString, "=", 2)
|
||||
if len(av) > 1 {
|
||||
parseAdditionalBuildContext, err := parse.GetAdditionalBuildContext(av[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while parsing additional build context: %w", err)
|
||||
}
|
||||
additionalBuildContext[av[0]] = &parseAdditionalBuildContext
|
||||
} else {
|
||||
return nil, fmt.Errorf("while parsing additional build context: %q, accepts value in the form of key=value", av)
|
||||
}
|
||||
}
|
||||
}
|
||||
var cacheTo []reference.Named
|
||||
var cacheFrom []reference.Named
|
||||
if c.Flag("cache-to").Changed {
|
||||
cacheTo, err = parse.RepoNamesToNamedReferences(flags.CacheTo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse value provided `%s` to --cache-to: %w", flags.CacheTo, err)
|
||||
}
|
||||
}
|
||||
if c.Flag("cache-from").Changed {
|
||||
cacheFrom, err = parse.RepoNamesToNamedReferences(flags.CacheFrom)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse value provided `%s` to --cache-from: %w", flags.CacheTo, err)
|
||||
}
|
||||
}
|
||||
var cacheTTL time.Duration
|
||||
if c.Flag("cache-ttl").Changed {
|
||||
cacheTTL, err = time.ParseDuration(flags.CacheTTL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse value provided %q as --cache-ttl: %w", flags.CacheTTL, err)
|
||||
}
|
||||
}
|
||||
|
||||
var confidentialWorkloadOptions buildahDefine.ConfidentialWorkloadOptions
|
||||
if c.Flag("cw").Changed {
|
||||
confidentialWorkloadOptions, err = parse.GetConfidentialWorkloadOptions(flags.CWOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
opts := buildahDefine.BuildOptions{
|
||||
AddCapabilities: flags.CapAdd,
|
||||
AdditionalTags: tags,
|
||||
AdditionalBuildContexts: additionalBuildContext,
|
||||
AllPlatforms: flags.AllPlatforms,
|
||||
Annotations: flags.Annotation,
|
||||
Args: args,
|
||||
BlobDirectory: flags.BlobCache,
|
||||
BuildOutput: flags.BuildOutput,
|
||||
CacheFrom: cacheFrom,
|
||||
CacheTo: cacheTo,
|
||||
CacheTTL: cacheTTL,
|
||||
ConfidentialWorkload: confidentialWorkloadOptions,
|
||||
CommonBuildOpts: commonOpts,
|
||||
Compression: compression,
|
||||
ConfigureNetwork: networkPolicy,
|
||||
ContextDirectory: contextDir,
|
||||
CPPFlags: flags.CPPFlags,
|
||||
DefaultMountsFilePath: podmanConfig.ContainersConfDefaultsRO.Containers.DefaultMountsFile,
|
||||
Devices: flags.Devices,
|
||||
DropCapabilities: flags.CapDrop,
|
||||
Envs: buildahCLI.LookupEnvVarReferences(flags.Envs, os.Environ()),
|
||||
Err: stderr,
|
||||
ForceRmIntermediateCtrs: flags.ForceRm,
|
||||
From: flags.From,
|
||||
GroupAdd: flags.GroupAdd,
|
||||
IDMappingOptions: idmappingOptions,
|
||||
In: stdin,
|
||||
Isolation: isolation,
|
||||
Jobs: &flags.Jobs,
|
||||
Labels: flags.Label,
|
||||
LayerLabels: flags.LayerLabel,
|
||||
Layers: flags.Layers,
|
||||
LogRusage: flags.LogRusage,
|
||||
LogFile: flags.Logfile,
|
||||
LogSplitByPlatform: flags.LogSplitByPlatform,
|
||||
Manifest: flags.Manifest,
|
||||
MaxPullPushRetries: 3,
|
||||
NamespaceOptions: nsValues,
|
||||
NoCache: flags.NoCache,
|
||||
OSFeatures: flags.OSFeatures,
|
||||
OSVersion: flags.OSVersion,
|
||||
OciDecryptConfig: decConfig,
|
||||
Out: stdout,
|
||||
Output: output,
|
||||
OutputFormat: format,
|
||||
Platforms: platforms,
|
||||
PullPolicy: pullPolicy,
|
||||
PullPushRetryDelay: 2 * time.Second,
|
||||
Quiet: flags.Quiet,
|
||||
RemoveIntermediateCtrs: flags.Rm,
|
||||
ReportWriter: reporter,
|
||||
Runtime: podmanConfig.RuntimePath,
|
||||
RuntimeArgs: runtimeFlags,
|
||||
RusageLogFile: flags.RusageLogFile,
|
||||
SignBy: flags.SignBy,
|
||||
SignaturePolicyPath: flags.SignaturePolicy,
|
||||
Squash: flags.Squash,
|
||||
SystemContext: systemContext,
|
||||
Target: flags.Target,
|
||||
TransientMounts: flags.Volumes,
|
||||
UnsetEnvs: flags.UnsetEnvs,
|
||||
}
|
||||
|
||||
if flags.IgnoreFile != "" {
|
||||
excludes, err := parseDockerignore(flags.IgnoreFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to obtain decrypt config: %w", err)
|
||||
}
|
||||
opts.Excludes = excludes
|
||||
}
|
||||
|
||||
if c.Flag("timestamp").Changed {
|
||||
timestamp := time.Unix(flags.Timestamp, 0).UTC()
|
||||
opts.Timestamp = ×tamp
|
||||
}
|
||||
if c.Flag("skip-unused-stages").Changed {
|
||||
opts.SkipUnusedStages = types.NewOptionalBool(flags.SkipUnusedStages)
|
||||
}
|
||||
|
||||
return &entities.BuildOptions{BuildOptions: opts}, nil
|
||||
}
|
||||
|
||||
func getDecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error) {
|
||||
decConfig := &encconfig.DecryptConfig{}
|
||||
if len(decryptionKeys) > 0 {
|
||||
// decryption
|
||||
dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid decryption keys: %w", err)
|
||||
}
|
||||
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc})
|
||||
decConfig = cc.DecryptConfig
|
||||
}
|
||||
|
||||
return decConfig, nil
|
||||
}
|
||||
|
||||
func parseDockerignore(ignoreFile string) ([]string, error) {
|
||||
excludes := []string{}
|
||||
ignore, err := os.ReadFile(ignoreFile)
|
||||
if err != nil {
|
||||
return excludes, err
|
||||
}
|
||||
for _, e := range strings.Split(string(ignore), "\n") {
|
||||
if len(e) == 0 || e[0] == '#' {
|
||||
continue
|
||||
}
|
||||
excludes = append(excludes, e)
|
||||
}
|
||||
return excludes, nil
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package entities
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
buildahDefine "github.com/containers/buildah/define"
|
||||
"github.com/containers/common/libnetwork/types"
|
||||
@ -110,6 +111,11 @@ type ContainerCreateResponse struct {
|
||||
// BuildOptions describe the options for building container images.
|
||||
type BuildOptions struct {
|
||||
buildahDefine.BuildOptions
|
||||
ContainerFiles []string
|
||||
// Files that need to be closed after the build
|
||||
// so need to pass this to the main build functions
|
||||
LogFileToClose *os.File
|
||||
TmpDirToClose string
|
||||
}
|
||||
|
||||
// BuildReport is the image-build report.
|
||||
|
Reference in New Issue
Block a user