147 lines
4.5 KiB
Go
147 lines
4.5 KiB
Go
package farm
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/containers/common/pkg/completion"
|
|
"github.com/containers/podman/v5/cmd/podman/common"
|
|
"github.com/containers/podman/v5/cmd/podman/registry"
|
|
"github.com/containers/podman/v5/cmd/podman/utils"
|
|
"github.com/containers/podman/v5/pkg/farm"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
type buildOptions struct {
|
|
buildOptions common.BuildFlagsWrapper
|
|
local bool
|
|
platforms []string
|
|
farm string
|
|
}
|
|
|
|
var (
|
|
farmBuildDescription = `Build images on farm nodes, then bundle them into a manifest list`
|
|
buildCommand = &cobra.Command{
|
|
Use: "build [options] [CONTEXT]",
|
|
Short: "Build a container image for multiple architectures",
|
|
Long: farmBuildDescription,
|
|
RunE: build,
|
|
Example: "podman farm build [flags] buildContextDirectory",
|
|
ValidArgsFunction: common.AutocompleteDefaultOneArg,
|
|
Args: cobra.MaximumNArgs(1),
|
|
}
|
|
buildOpts = buildOptions{
|
|
buildOptions: common.BuildFlagsWrapper{},
|
|
}
|
|
)
|
|
|
|
func init() {
|
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
|
Command: buildCommand,
|
|
Parent: farmCmd,
|
|
})
|
|
flags := buildCommand.Flags()
|
|
flags.SetNormalizeFunc(utils.AliasFlags)
|
|
|
|
cleanupFlag := "cleanup"
|
|
flags.BoolVar(&buildOpts.buildOptions.Cleanup, cleanupFlag, false, "Remove built images from farm nodes on success")
|
|
|
|
farmFlagName := "farm"
|
|
flags.StringVar(&buildOpts.farm, farmFlagName, "", "Farm to use for builds")
|
|
_ = buildCommand.RegisterFlagCompletionFunc(farmFlagName, common.AutoCompleteFarms)
|
|
|
|
localFlagName := "local"
|
|
// Default for local is true
|
|
flags.BoolVarP(&buildOpts.local, localFlagName, "l", true, "Build image on local machine as well as on farm nodes")
|
|
|
|
platformsFlag := "platforms"
|
|
buildCommand.PersistentFlags().StringSliceVar(&buildOpts.platforms, platformsFlag, nil, "Build only on farm nodes that match the given platforms")
|
|
_ = buildCommand.RegisterFlagCompletionFunc(platformsFlag, completion.AutocompletePlatform)
|
|
|
|
common.DefineBuildFlags(buildCommand, &buildOpts.buildOptions, true)
|
|
}
|
|
|
|
func build(cmd *cobra.Command, args []string) error {
|
|
// Return error if any of the hidden flags are used
|
|
for _, f := range common.FarmBuildHiddenFlags {
|
|
if cmd.Flags().Changed(f) {
|
|
return fmt.Errorf("%q is an unsupported flag for podman farm build", f)
|
|
}
|
|
}
|
|
|
|
if !cmd.Flags().Changed("tag") {
|
|
return errors.New("cannot create manifest list without a name, value for --tag is required")
|
|
}
|
|
// Ensure that the user gives a full name so we can push the built images from
|
|
// the node to the given registry and repository
|
|
// Should be of the format registry/repository/imageName
|
|
tag, err := cmd.Flags().GetStringArray("tag")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !strings.Contains(tag[0], "/") {
|
|
return fmt.Errorf("%q is not a full image reference name", tag[0])
|
|
}
|
|
bopts := buildOpts.buildOptions
|
|
opts, err := common.ParseBuildOpts(cmd, args, &bopts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Close the logFile if one was created based on the flag
|
|
if opts.LogFileToClose != nil {
|
|
defer opts.LogFileToClose.Close()
|
|
}
|
|
if opts.TmpDirToClose != "" {
|
|
// We had to download the context directory.
|
|
// Delete it later.
|
|
defer func() {
|
|
if err = os.RemoveAll(opts.TmpDirToClose); err != nil {
|
|
logrus.Errorf("Removing temporary directory %q: %v", opts.TmpDirToClose, err)
|
|
}
|
|
}()
|
|
}
|
|
opts.Cleanup = buildOpts.buildOptions.Cleanup
|
|
iidFile, err := cmd.Flags().GetString("iidfile")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
opts.IIDFile = iidFile
|
|
// only set tls-verify if it has been changed by the user
|
|
// if it hasn't we will read the registries.conf on the farm
|
|
// nodes for further configuration
|
|
if changed := cmd.Flags().Changed("tls-verify"); changed {
|
|
tlsVerify, err := cmd.Flags().GetBool("tls-verify")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
skipTLSVerify := !tlsVerify
|
|
opts.SkipTLSVerify = &skipTLSVerify
|
|
}
|
|
|
|
localEngine := registry.ImageEngine()
|
|
ctx := registry.Context()
|
|
farm, err := farm.NewFarm(ctx, buildOpts.farm, localEngine, buildOpts.local)
|
|
if err != nil {
|
|
return fmt.Errorf("initializing: %w", err)
|
|
}
|
|
|
|
schedule, err := farm.Schedule(ctx, buildOpts.platforms)
|
|
if err != nil {
|
|
return fmt.Errorf("scheduling builds: %w", err)
|
|
}
|
|
logrus.Infof("schedule: %v", schedule)
|
|
|
|
manifestName := opts.Output
|
|
// Set Output to "" so that the images built on the farm nodes have no name
|
|
opts.Output = ""
|
|
if err = farm.Build(ctx, schedule, *opts, manifestName, localEngine); err != nil {
|
|
return fmt.Errorf("build: %w", err)
|
|
}
|
|
logrus.Infof("build: ok")
|
|
|
|
return nil
|
|
}
|