Skip to content

Commit

Permalink
build: add support for --push
Browse files Browse the repository at this point in the history
* pass systemCtx to push util
* fix test

Signed-off-by: danishprakash <[email protected]>
  • Loading branch information
danishprakash committed Jun 5, 2024
1 parent 63ded6f commit a5d4921
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 56 deletions.
21 changes: 2 additions & 19 deletions cmd/buildah/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/compression"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/storage"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -149,25 +148,9 @@ func pushCmd(c *cobra.Command, args []string, iopts pushOptions) error {
return err
}

dest, err := alltransports.ParseImageName(destSpec)
// add the docker:// transport to see if they neglected it.
dest, err := util.ImageStringToImageReference(destSpec)
if err != nil {
destTransport := strings.Split(destSpec, ":")[0]
if t := transports.Get(destTransport); t != nil {
return err
}

if strings.Contains(destSpec, "://") {
return err
}

destSpec = "docker://" + destSpec
dest2, err2 := alltransports.ParseImageName(destSpec)
if err2 != nil {
return err
}
dest = dest2
logrus.Debugf("Assuming docker:// as the transport method for DESTINATION: %s", destSpec)
return fmt.Errorf("generating image reference: %w", err)
}

systemContext, err := parse.SystemContextFromOptions(c)
Expand Down
1 change: 1 addition & 0 deletions define/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ type BuildOptions struct {
// image that's meant to be run using krun as a VM instead of a conventional
// process-type container.
ConfidentialWorkload ConfidentialWorkloadOptions
Push bool
// Additional tags to add to the image that we write, if we know of a
// way to add them.
AdditionalTags []string
Expand Down
33 changes: 31 additions & 2 deletions define/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"strings"

"github.com/containers/image/v5/manifest"
imageTypes "github.com/containers/image/v5/types"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/chrootarchive"
"github.com/containers/storage/pkg/ioutils"
Expand Down Expand Up @@ -111,9 +112,37 @@ type Secret struct {

// BuildOutputOptions contains the the outcome of parsing the value of a build --output flag
type BuildOutputOption struct {
Path string // Only valid if !IsStdout
IsDir bool
ImageRef imageTypes.ImageReference
Image string
IsStdout bool
Path string // Only valid if !IsStdout
Push bool
Type BuildOutputType

// Deprecated: Use Type instead to determine output type
IsDir bool
}

type BuildOutputType int

const (
_ BuildOutputType = iota
BuildOutputImage
BuildOutputLocal
BuildOutputTar
)

// String converts a BuildOutputType into a string.
func (t BuildOutputType) String() string {
switch t {
case BuildOutputImage:
return "image"
case BuildOutputLocal:
return "local"
case BuildOutputTar:
return "tar"
}
return fmt.Sprintf("unrecognized build output type %d", t)
}

// ConfidentialWorkloadOptions encapsulates options which control whether or not
Expand Down
6 changes: 6 additions & 0 deletions docs/buildah-build.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,8 @@ Supported _keys_ are:

Valid _type_ values are:
- **local**: write the resulting build files to a directory on the client-side.
- **image**: writes the build results as an image to local storage.
- **registry**: pushes the resulting build image to the registry. Shorthand for `type=image,push=true`.
- **tar**: write the resulting files as a single tarball (.tar).

If no type is specified, the value defaults to **local**.
Expand Down Expand Up @@ -772,6 +774,10 @@ error if the image is not present locally.

Defaults to *true*.

**--push**

Shorthand for "--output=type=registry"

**--quiet**, **-q**

Suppress output messages which indicate which instruction is being processed,
Expand Down
3 changes: 3 additions & 0 deletions imagebuildah/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type Executor struct {
registry string
ignoreUnrecognizedInstructions bool
quiet bool
push bool
runtime string
runtimeArgs []string
transientMounts []Mount
Expand Down Expand Up @@ -241,6 +242,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o
registry: options.Registry,
ignoreUnrecognizedInstructions: options.IgnoreUnrecognizedInstructions,
quiet: options.Quiet,
push: options.Push, // TODO: not needed if planning to update buildOutput in cli/build
runtime: options.Runtime,
runtimeArgs: options.RuntimeArgs,
transientMounts: transientMounts,
Expand Down Expand Up @@ -940,6 +942,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
}
}
stageID, stageRef, stageOnlyBaseImage, stageErr := b.buildStage(ctx, cleanupStages, stages, index)

if stageErr != nil {
cancel = true
ch <- Result{
Expand Down
16 changes: 13 additions & 3 deletions imagebuildah/stage_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1176,7 +1176,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
canGenerateBuildOutput := (s.executor.buildOutput != "" && lastStage)
if canGenerateBuildOutput {
logrus.Debugf("Generating custom build output with options %q", s.executor.buildOutput)
buildOutputOption, err = parse.GetBuildOutput(s.executor.buildOutput)
buildOutputOption, err = parse.GetBuildOutput(s.executor.buildOutput, s.executor.output)
if err != nil {
return "", nil, false, fmt.Errorf("failed to parse build output: %w", err)
}
Expand Down Expand Up @@ -2271,7 +2271,17 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
return imgID, ref, nil
}

func (s *StageExecutor) generateBuildOutput(buildOutputOpts define.BuildOutputOption) error {
func (s *StageExecutor) generateBuildOutput(opts define.BuildOutputOption) error {
if opts.Type == define.BuildOutputImage {
if opts.Push {
err := internalUtil.PushImage(s.executor.store, opts, s.executor.systemContext)
if err != nil {
return fmt.Errorf("failed to export build output: %w", err)
}
}
return nil
}

extractRootfsOpts := buildah.ExtractRootfsOptions{}
if unshare.IsRootless() {
// In order to maintain as much parity as possible
Expand All @@ -2291,7 +2301,7 @@ func (s *StageExecutor) generateBuildOutput(buildOutputOpts define.BuildOutputOp
return fmt.Errorf("failed to extract rootfs from given container image: %w", err)
}
defer rc.Close()
err = internalUtil.ExportFromReader(rc, buildOutputOpts)
err = internalUtil.ExportFromReader(rc, opts)
if err != nil {
return fmt.Errorf("failed to export build output: %w", err)
}
Expand Down
34 changes: 32 additions & 2 deletions internal/util/util.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package util

import (
"context"
"fmt"
"io"
"os"
"path/filepath"

"github.com/containers/buildah/define"
"github.com/containers/buildah/util"
"github.com/containers/common/libimage"
lplatform "github.com/containers/common/libimage/platform"
"github.com/containers/image/v5/types"
Expand All @@ -32,6 +34,7 @@ func LookupImage(ctx *types.SystemContext, store storage.Store, image string) (*
return nil, err
}
return localImage, nil
//
}

// NormalizePlatform validates and translate the platform to the canonical value.
Expand All @@ -50,6 +53,29 @@ func NormalizePlatform(platform v1.Platform) v1.Platform {
}
}

// PushImage copies contents of the image to a new location
func PushImage(store storage.Store, opts define.BuildOutputOption, systemCtx *types.SystemContext) error {
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemCtx})
if err != nil {
return err
}

imageRef, err := util.ImageStringToImageReference(opts.Image)
if err != nil {
return fmt.Errorf("failed to convert image to ImageReference")
}

libimageOptions := &libimage.PushOptions{}
libimageOptions.Writer = os.Stdout
dest := fmt.Sprintf("%s:%s", imageRef.Transport().Name(), imageRef.StringWithinTransport())
_, err = runtime.Push(context.Background(), opts.Image, dest, libimageOptions)
if err != nil {
return fmt.Errorf("failed while pushing image %+q: %w", opts.ImageRef, err)
}

return nil
}

// ExportFromReader reads bytes from given reader and exports to external tar, directory or stdout.
func ExportFromReader(input io.Reader, opts define.BuildOutputOption) error {
var err error
Expand All @@ -59,7 +85,8 @@ func ExportFromReader(input io.Reader, opts define.BuildOutputOption) error {
return err
}
}
if opts.IsDir {
switch opts.Type {
case define.BuildOutputLocal:
// In order to keep this feature as close as possible to
// buildkit it was decided to preserve ownership when
// invoked as root since caller already has access to artifacts
Expand All @@ -81,7 +108,7 @@ func ExportFromReader(input io.Reader, opts define.BuildOutputOption) error {
if err != nil {
return fmt.Errorf("failed while performing untar at %q: %w", opts.Path, err)
}
} else {
case define.BuildOutputTar:
outFile := os.Stdout
if !opts.IsStdout {
outFile, err = os.Create(opts.Path)
Expand All @@ -94,7 +121,10 @@ func ExportFromReader(input io.Reader, opts define.BuildOutputOption) error {
if err != nil {
return fmt.Errorf("failed while performing copy to %q: %w", opts.Path, err)
}
default:
return fmt.Errorf("build output type %s not supported", opts.Type)
}

return nil
}

Expand Down
12 changes: 11 additions & 1 deletion pkg/cli/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
timestamp = &t
}
if c.Flag("output").Changed {
buildOption, err := parse.GetBuildOutput(iopts.BuildOutput)
buildOption, err := parse.GetBuildOutput(iopts.BuildOutput, output)
if err != nil {
return options, nil, nil, err
}
Expand All @@ -281,6 +281,15 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
return options, nil, nil, err
}
}

if c.Flag("push").Changed {
if len(iopts.BuildOutput) == 0 {
iopts.BuildOutput = "type=registry"
} else {
return options, nil, nil, fmt.Errorf("cannot set both --push and --output")
}
}

var cacheTo []reference.Named
var cacheFrom []reference.Named
cacheTo = nil
Expand Down Expand Up @@ -399,6 +408,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
OutputFormat: format,
Platforms: platforms,
PullPolicy: pullPolicy,
Push: iopts.Push,
Quiet: iopts.Quiet,
RemoveIntermediateCtrs: iopts.Rm,
ReportWriter: reporter,
Expand Down
2 changes: 2 additions & 0 deletions pkg/cli/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type BudResults struct {
Pull string
PullAlways bool
PullNever bool
Push bool
Quiet bool
IdentityLabel bool
Rm bool
Expand Down Expand Up @@ -299,6 +300,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
fs.BoolVar(&flags.Stdin, "stdin", false, "pass stdin into containers")
fs.StringArrayVarP(&flags.Tag, "tag", "t", []string{}, "tagged `name` to apply to the built image")
fs.StringVarP(&flags.BuildOutput, "output", "o", "", "output destination (format: type=local,dest=path)")
fs.BoolVar(&flags.Push, "push", false, "Shorthand for `--output=type=registry`")
fs.StringVar(&flags.Target, "target", "", "set the target build stage to build")
fs.Int64Var(&flags.Timestamp, "timestamp", 0, "set created timestamp to the specified epoch seconds to allow for deterministic builds, defaults to current time")
fs.BoolVar(&flags.TLSVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
Expand Down
Loading

0 comments on commit a5d4921

Please sign in to comment.