Bump Buildah to v1.33.0

As the title says.  This is the last dance step in preparation
for Podman v4.8.

[NO NEW TESTS NEEDED]

Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
This commit is contained in:
TomSweeneyRedHat
2023-11-17 18:02:42 -05:00
parent a6bb601400
commit 3d86a9658e
59 changed files with 18958 additions and 56 deletions

View File

@ -9,3 +9,9 @@ test:
test-conformance:
go test -v -tags conformance -timeout 45m ./dockerclient
.PHONY: test-conformance
.PHONY: vendor
vendor:
GO111MODULE=on go mod tidy
GO111MODULE=on go mod vendor
GO111MODULE=on go mod verify

View File

@ -13,6 +13,7 @@ import (
docker "github.com/fsouza/go-dockerclient"
buildkitparser "github.com/moby/buildkit/frontend/dockerfile/parser"
"github.com/openshift/imagebuilder/dockerfile/command"
"github.com/openshift/imagebuilder/dockerfile/parser"
)
@ -32,6 +33,17 @@ type Copy struct {
Chown string
Chmod string
Checksum string
// Additional files which need to be created by executor for this
// instruction.
Files []File
}
// File defines if any additional file needs to be created
// by the executor instruction so that specified command
// can execute/copy the created file inside the build container.
type File struct {
Name string // Name of the new file.
Data string // Content of the file.
}
// Run defines a run operation required in the container.
@ -42,6 +54,9 @@ type Run struct {
Mounts []string
// Network specifies the network mode to run the container with
Network string
// Additional files which need to be created by executor for this
// instruction.
Files []File
}
type Executor interface {
@ -395,7 +410,7 @@ func (b *Builder) Run(step *Step, exec Executor, noRunsRemaining bool) error {
if !ok {
return exec.UnrecognizedInstruction(step)
}
if err := fn(b, step.Args, step.Attrs, step.Flags, step.Original); err != nil {
if err := fn(b, step.Args, step.Attrs, step.Flags, step.Original, step.Heredocs); err != nil {
return err
}
@ -575,7 +590,7 @@ func SplitBy(node *parser.Node, value string) []*parser.Node {
}
// StepFunc is invoked with the result of a resolved step.
type StepFunc func(*Builder, []string, map[string]bool, []string, string) error
type StepFunc func(*Builder, []string, map[string]bool, []string, string, []buildkitparser.Heredoc) error
var evaluateTable = map[string]StepFunc{
command.Env: env,

View File

@ -22,6 +22,9 @@ import (
"github.com/containers/storage/pkg/regexp"
"github.com/openshift/imagebuilder/signal"
"github.com/openshift/imagebuilder/strslice"
buildkitparser "github.com/moby/buildkit/frontend/dockerfile/parser"
buildkitshell "github.com/moby/buildkit/frontend/dockerfile/shell"
)
var (
@ -53,7 +56,7 @@ func init() {
//
// Sets the environment variable foo to bar, also makes interpolation
// in the dockerfile available from the next statement on via ${foo}.
func env(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func env(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) == 0 {
return errAtLeastOneArgument("ENV")
}
@ -94,7 +97,7 @@ func env(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
// MAINTAINER some text <maybe@an.email.address>
//
// Sets the maintainer metadata.
func maintainer(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func maintainer(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) != 1 {
return errExactlyOneArgument("MAINTAINER")
}
@ -105,7 +108,7 @@ func maintainer(b *Builder, args []string, attributes map[string]bool, flagArgs
// LABEL some json data describing the image
//
// Sets the Label variable foo to bar,
func label(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func label(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) == 0 {
return errAtLeastOneArgument("LABEL")
}
@ -127,11 +130,37 @@ func label(b *Builder, args []string, attributes map[string]bool, flagArgs []str
return nil
}
func processHereDocs(originalInstruction string, heredocs []buildkitparser.Heredoc, args []string) ([]File, error) {
var files []File
for _, heredoc := range heredocs {
var err error
content := heredoc.Content
if heredoc.Chomp {
content = buildkitparser.ChompHeredocContent(content)
}
if heredoc.Expand {
shlex := buildkitshell.NewLex('\\')
shlex.RawQuotes = true
shlex.RawEscapes = true
content, err = shlex.ProcessWord(content, args)
if err != nil {
return nil, err
}
}
file := File{
Data: content,
Name: heredoc.Name,
}
files = append(files, file)
}
return files, nil
}
// ADD foo /path
//
// Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
// exist here. If you do not wish to have this automatic handling, use COPY.
func add(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func add(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) < 2 {
return errAtLeastTwoArgument("ADD")
}
@ -167,20 +196,25 @@ func add(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
return fmt.Errorf("ADD only supports the --chmod=<permissions>, --chown=<uid:gid>, and --checksum=<checksum> flags")
}
}
files, err := processHereDocs(original, heredocs, userArgs)
if err != nil {
return err
}
b.PendingCopies = append(b.PendingCopies, Copy{
Src: args[0:last],
Dest: dest,
Download: true,
Chown: chown,
Chmod: chmod,
Checksum: checksum})
Checksum: checksum,
Files: files})
return nil
}
// COPY foo /path
//
// Same as 'ADD' but without the tar and remote url handling.
func dispatchCopy(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func dispatchCopy(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) < 2 {
return errAtLeastTwoArgument("COPY")
}
@ -210,14 +244,18 @@ func dispatchCopy(b *Builder, args []string, attributes map[string]bool, flagArg
return fmt.Errorf("COPY only supports the --chmod=<permissions> --chown=<uid:gid> and the --from=<image|stage> flags")
}
}
b.PendingCopies = append(b.PendingCopies, Copy{From: from, Src: args[0:last], Dest: dest, Download: false, Chown: chown, Chmod: chmod})
files, err := processHereDocs(original, heredocs, userArgs)
if err != nil {
return err
}
b.PendingCopies = append(b.PendingCopies, Copy{From: from, Src: args[0:last], Dest: dest, Download: false, Chown: chown, Chmod: chmod, Files: files})
return nil
}
// FROM imagename
//
// This sets the image the dockerfile will build on top of.
func from(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func from(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
switch {
case len(args) == 1:
case len(args) == 3 && len(args[0]) > 0 && strings.EqualFold(args[1], "as") && len(args[2]) > 0:
@ -282,7 +320,7 @@ func from(b *Builder, args []string, attributes map[string]bool, flagArgs []stri
// evaluator.go and comments around dispatch() in the same file explain the
// special cases. search for 'OnBuild' in internals.go for additional special
// cases.
func onbuild(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func onbuild(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) == 0 {
return errAtLeastOneArgument("ONBUILD")
}
@ -304,7 +342,7 @@ func onbuild(b *Builder, args []string, attributes map[string]bool, flagArgs []s
// WORKDIR /tmp
//
// Set the working directory for future RUN/CMD/etc statements.
func workdir(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func workdir(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) != 1 {
return errExactlyOneArgument("WORKDIR")
}
@ -331,7 +369,7 @@ func workdir(b *Builder, args []string, attributes map[string]bool, flagArgs []s
// RUN echo hi # sh -c echo hi (Linux)
// RUN echo hi # cmd /S /C echo hi (Windows)
// RUN [ "echo", "hi" ] # echo hi
func run(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func run(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if b.RunConfig.Image == "" {
return fmt.Errorf("Please provide a source image with `from` prior to run")
}
@ -363,10 +401,16 @@ func run(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
}
}
files, err := processHereDocs(original, heredocs, userArgs)
if err != nil {
return err
}
run := Run{
Args: args,
Mounts: mounts,
Network: network,
Files: files,
}
if !attributes["json"] {
@ -380,7 +424,7 @@ func run(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
//
// Set the default command to run in the container (which may be empty).
// Argument handling is the same as RUN.
func cmd(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func cmd(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
cmdSlice := handleJSONArgs(args, attributes)
if !attributes["json"] {
@ -405,7 +449,7 @@ func cmd(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
//
// Handles command processing similar to CMD and RUN, only b.RunConfig.Entrypoint
// is initialized at NewBuilder time instead of through argument parsing.
func entrypoint(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func entrypoint(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
parsed := handleJSONArgs(args, attributes)
switch {
@ -436,7 +480,7 @@ func entrypoint(b *Builder, args []string, attributes map[string]bool, flagArgs
//
// Expose ports for links and port mappings. This all ends up in
// b.RunConfig.ExposedPorts for runconfig.
func expose(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func expose(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) == 0 {
return errAtLeastOneArgument("EXPOSE")
}
@ -463,7 +507,7 @@ func expose(b *Builder, args []string, attributes map[string]bool, flagArgs []st
//
// Set the user to 'foo' for future commands and when running the
// ENTRYPOINT/CMD at container run time.
func user(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func user(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) != 1 {
return errExactlyOneArgument("USER")
}
@ -475,7 +519,7 @@ func user(b *Builder, args []string, attributes map[string]bool, flagArgs []stri
// VOLUME /foo
//
// Expose the volume /foo for use. Will also accept the JSON array form.
func volume(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func volume(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) == 0 {
return errAtLeastOneArgument("VOLUME")
}
@ -497,7 +541,7 @@ func volume(b *Builder, args []string, attributes map[string]bool, flagArgs []st
// STOPSIGNAL signal
//
// Set the signal that will be used to kill the container.
func stopSignal(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func stopSignal(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) != 1 {
return errExactlyOneArgument("STOPSIGNAL")
}
@ -515,7 +559,7 @@ func stopSignal(b *Builder, args []string, attributes map[string]bool, flagArgs
//
// Set the default healthcheck command to run in the container (which may be empty).
// Argument handling is the same as RUN.
func healthcheck(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func healthcheck(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
if len(args) == 0 {
return errAtLeastOneArgument("HEALTHCHECK")
}
@ -608,7 +652,7 @@ var targetArgs = []string{"TARGETOS", "TARGETARCH", "TARGETVARIANT"}
// Adds the variable foo to the trusted list of variables that can be passed
// to builder using the --build-arg flag for expansion/subsitution or passing to 'run'.
// Dockerfile author may optionally set a default value of this variable.
func arg(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func arg(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
var (
name string
value string
@ -674,7 +718,7 @@ func arg(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
// SHELL powershell -command
//
// Set the non-default shell to use.
func shell(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string) error {
func shell(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
shellSlice := handleJSONArgs(args, attributes)
switch {
case len(shellSlice) == 0:

View File

@ -15,6 +15,8 @@ import (
sRegexp "github.com/containers/storage/pkg/regexp"
"github.com/containers/storage/pkg/system"
buildkitparser "github.com/moby/buildkit/frontend/dockerfile/parser"
buildkitshell "github.com/moby/buildkit/frontend/dockerfile/shell"
"github.com/openshift/imagebuilder/dockerfile/command"
)
@ -30,14 +32,15 @@ import (
// but lucky for us the Dockerfile isn't very complicated. This structure
// works a little more effectively than a "proper" parse tree for our needs.
type Node struct {
Value string // actual content
Next *Node // the next item in the current sexp
Children []*Node // the children of this sexp
Attributes map[string]bool // special attributes for this node
Original string // original line used before parsing
Flags []string // only top Node should have this set
StartLine int // the line in the original dockerfile where the node begins
EndLine int // the line in the original dockerfile where the node ends
Value string // actual content
Next *Node // the next item in the current sexp
Children []*Node // the children of this sexp
Heredocs []buildkitparser.Heredoc // extra heredoc content attachments
Attributes map[string]bool // special attributes for this node
Original string // original line used before parsing
Flags []string // only top Node should have this set
StartLine int // the line in the original dockerfile where the node begins
EndLine int // the line in the original dockerfile where the node ends
}
// Dump dumps the AST defined by `node` as a list of sexps.
@ -53,7 +56,11 @@ func (node *Node) Dump() string {
for _, n := range node.Children {
str += "(" + n.Dump() + ")\n"
}
if len(node.Heredocs) > 0 {
for _, doc := range node.Heredocs {
str += "(" + doc.Name + "-" + doc.Content + "-" + strconv.Itoa(int(doc.FileDescriptor)) + "-" + strconv.FormatBool(doc.Expand) + "-" + strconv.FormatBool(doc.Chomp) + ")\n"
}
}
for n := node.Next; n != nil; n = n.Next {
if len(n.Children) > 0 {
str += " " + n.Dump()
@ -70,6 +77,24 @@ func (node *Node) lines(start, end int) {
node.EndLine = end
}
func (node *Node) canContainHeredoc() bool {
// check for compound commands, like ONBUILD
if ok := heredocCompoundDirectives[strings.ToLower(node.Value)]; ok {
if node.Next != nil && len(node.Next.Children) > 0 {
node = node.Next.Children[0]
}
}
if ok := heredocDirectives[strings.ToLower(node.Value)]; !ok {
return false
}
if isJSON := node.Attributes["json"]; isJSON {
return false
}
return true
}
// AddChild adds a new child node, and updates line information
func (node *Node) AddChild(child *Node, startLine, endLine int) {
child.lines(startLine, endLine)
@ -94,6 +119,20 @@ const DefaultEscapeToken = '\\'
// defaultPlatformToken is the platform assumed for the build if not explicitly provided
var defaultPlatformToken = runtime.GOOS
var (
// Directives allowed to contain heredocs
heredocDirectives = map[string]bool{
command.Add: true,
command.Copy: true,
command.Run: true,
}
// Directives allowed to contain directives containing heredocs
heredocCompoundDirectives = map[string]bool{
command.Onbuild: true,
}
)
// Directive is the structure used during a build run to hold the state of
// parsing directives.
type Directive struct {
@ -313,6 +352,39 @@ func Parse(rwc io.Reader) (*Result, error) {
if err != nil {
return nil, err
}
if child.canContainHeredoc() {
heredocs, err := heredocsFromLine(line)
if err != nil {
return nil, err
}
for _, heredoc := range heredocs {
terminator := []byte(heredoc.Name)
terminated := false
for scanner.Scan() {
bytesRead := scanner.Bytes()
currentLine++
possibleTerminator := trimNewline(bytesRead)
if heredoc.Chomp {
possibleTerminator = trimLeadingTabs(possibleTerminator)
}
if bytes.Equal(possibleTerminator, terminator) {
terminated = true
break
}
heredoc.Content += "\n"
heredoc.Content += string(bytesRead)
}
if !terminated {
return nil, fmt.Errorf("%s: unterminated heredoc", heredoc.Name)
}
child.Heredocs = append(child.Heredocs, heredoc)
}
}
root.AddChild(child, startLine, currentLine)
}
@ -331,6 +403,26 @@ func Parse(rwc io.Reader) (*Result, error) {
}, nil
}
func heredocsFromLine(line string) ([]buildkitparser.Heredoc, error) {
shlex := buildkitshell.NewLex('\\')
shlex.RawQuotes = true
shlex.RawEscapes = true
shlex.SkipUnsetEnv = true
words, _ := shlex.ProcessWords(line, []string{})
var docs []buildkitparser.Heredoc
for _, word := range words {
heredoc, err := buildkitparser.ParseHeredoc(word)
if err != nil {
return nil, err
}
if heredoc != nil {
docs = append(docs, *heredoc)
}
}
return docs, nil
}
func trimComments(src []byte) []byte {
return tokenComment.ReplaceAll(src, []byte{})
}
@ -339,6 +431,16 @@ func trimWhitespace(src []byte) []byte {
return bytes.TrimLeftFunc(src, unicode.IsSpace)
}
func trimLeadingWhitespace(src []byte) []byte {
return bytes.TrimLeftFunc(src, unicode.IsSpace)
}
func trimLeadingTabs(src []byte) []byte {
return bytes.TrimLeft(src, "\t")
}
func trimNewline(src []byte) []byte {
return bytes.TrimRight(src, "\r\n")
}
func isEmptyContinuationLine(line []byte) bool {
return len(trimComments(trimWhitespace(line))) == 0
}

View File

@ -7,6 +7,7 @@ import (
"github.com/openshift/imagebuilder/dockerfile/command"
"github.com/openshift/imagebuilder/dockerfile/parser"
buildkitparser "github.com/moby/buildkit/frontend/dockerfile/parser"
)
// ParseDockerfile parses the provided stream as a canonical Dockerfile
@ -34,14 +35,10 @@ var replaceEnvAllowed = map[string]bool{
// Certain commands are allowed to have their args split into more
// words after env var replacements. Meaning:
//
// ENV foo="123 456"
// EXPOSE $foo
//
// ENV foo="123 456"
// EXPOSE $foo
// should result in the same thing as:
//
// EXPOSE 123 456
//
// EXPOSE 123 456
// and not treat "123 456" as a single word.
// Note that: EXPOSE "$foo" and EXPOSE $foo are not the same thing.
// Quotes will cause it to still be treated as single word.
@ -59,6 +56,7 @@ type Step struct {
Flags []string
Attrs map[string]bool
Message string
Heredocs []buildkitparser.Heredoc
Original string
}
@ -78,6 +76,7 @@ type Step struct {
// deal with that, at least until it becomes more of a general concern with new
// features.
func (b *Step) Resolve(ast *parser.Node) error {
b.Heredocs = ast.Heredocs
cmd := ast.Value
upperCasedCmd := strings.ToUpper(cmd)