bake: fix entitlements path checks for local outputs

Previous check based on dest attributes was not correct
as the attributes already get converted before validation happens.

Because the local path is not preserved for single-file
outputs and gets replaced by io.Writer, a temporary array variable
was needed. This value should instead be added to ExportEntry
struct in BuildKit in future revision.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit c6e403ad7f239409a28355be9a494805d895759b)
This commit is contained in:
Tonis Tiigi 2024-12-02 14:46:58 -08:00
parent 5113f9ea89
commit c30db6a955
No known key found for this signature in database
GPG Key ID: AFA9DE5F8AB7AF39
6 changed files with 47 additions and 67 deletions

View File

@ -1329,7 +1329,8 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
bo.Exports, err = controllerapi.CreateExports(outputs)
bo.Exports, bo.ExportsLocalPathsTemporary, err = controllerapi.CreateExports(outputs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -113,17 +113,8 @@ func (c EntitlementConf) check(bo build.Options, expected *EntitlementConf) erro
roPaths[p] = struct{}{} roPaths[p] = struct{}{}
} }
for _, out := range bo.Exports { for _, p := range bo.ExportsLocalPathsTemporary {
if out.Type == "local" { rwPaths[p] = struct{}{}
if dest, ok := out.Attrs["dest"]; ok {
rwPaths[dest] = struct{}{}
}
}
if out.Type == "tar" {
if dest, ok := out.Attrs["dest"]; ok && dest != "-" {
rwPaths[dest] = struct{}{}
}
}
} }
for _, ce := range bo.CacheTo { for _, ce := range bo.CacheTo {

View File

@ -10,7 +10,6 @@ import (
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
"github.com/docker/buildx/controller/pb" "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/util/osutil" "github.com/docker/buildx/util/osutil"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/util/entitlements" "github.com/moby/buildkit/util/entitlements"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -279,25 +278,10 @@ func TestValidateEntitlements(t *testing.T) {
{ {
name: "ExportLocal", name: "ExportLocal",
opt: build.Options{ opt: build.Options{
Exports: []client.ExportEntry{ ExportsLocalPathsTemporary: []string{
{ dir1,
Type: "local", filepath.Join(dir1, "subdir"),
Attrs: map[string]string{ dir2,
"dest": dir1,
},
},
{
Type: "local",
Attrs: map[string]string{
"dest": filepath.Join(dir1, "subdir"),
},
},
{
Type: "local",
Attrs: map[string]string{
"dest": dir2,
},
},
}, },
}, },
expected: EntitlementConf{ expected: EntitlementConf{

View File

@ -62,27 +62,28 @@ const (
type Options struct { type Options struct {
Inputs Inputs Inputs Inputs
Ref string Ref string
Allow []entitlements.Entitlement Allow []entitlements.Entitlement
Attests map[string]*string Attests map[string]*string
BuildArgs map[string]string BuildArgs map[string]string
CacheFrom []client.CacheOptionsEntry CacheFrom []client.CacheOptionsEntry
CacheTo []client.CacheOptionsEntry CacheTo []client.CacheOptionsEntry
CgroupParent string CgroupParent string
Exports []client.ExportEntry Exports []client.ExportEntry
ExtraHosts []string ExportsLocalPathsTemporary []string // should be removed after client.ExportEntry update in buildkit v0.19.0
Labels map[string]string ExtraHosts []string
NetworkMode string Labels map[string]string
NoCache bool NetworkMode string
NoCacheFilter []string NoCache bool
Platforms []specs.Platform NoCacheFilter []string
Pull bool Platforms []specs.Platform
SecretSpecs []*controllerapi.Secret Pull bool
SSHSpecs []*controllerapi.SSH SecretSpecs []*controllerapi.Secret
ShmSize opts.MemBytes SSHSpecs []*controllerapi.SSH
Tags []string ShmSize opts.MemBytes
Target string Tags []string
Ulimits *opts.UlimitOpt Target string
Ulimits *opts.UlimitOpt
Session []session.Attachable Session []session.Attachable
Linked bool // Linked marks this target as exclusively linked (not requested by the user). Linked bool // Linked marks this target as exclusively linked (not requested by the user).

View File

@ -93,7 +93,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *controllerapi.Buil
} }
opts.Session = append(opts.Session, ssh) opts.Session = append(opts.Session, ssh)
outputs, err := controllerapi.CreateExports(in.Exports) outputs, _, err := controllerapi.CreateExports(in.Exports)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }

View File

@ -10,15 +10,16 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) { func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, []string, error) {
var outs []client.ExportEntry var outs []client.ExportEntry
var localPaths []string
if len(entries) == 0 { if len(entries) == 0 {
return nil, nil return nil, nil, nil
} }
var stdoutUsed bool var stdoutUsed bool
for _, entry := range entries { for _, entry := range entries {
if entry.Type == "" { if entry.Type == "" {
return nil, errors.Errorf("type is required for output") return nil, nil, errors.Errorf("type is required for output")
} }
out := client.ExportEntry{ out := client.ExportEntry{
@ -49,20 +50,21 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
if supportDir { if supportDir {
if entry.Destination == "" { if entry.Destination == "" {
return nil, errors.Errorf("dest is required for %s exporter", out.Type) return nil, nil, errors.Errorf("dest is required for %s exporter", out.Type)
} }
if entry.Destination == "-" { if entry.Destination == "-" {
return nil, errors.Errorf("dest cannot be stdout for %s exporter", out.Type) return nil, nil, errors.Errorf("dest cannot be stdout for %s exporter", out.Type)
} }
fi, err := os.Stat(entry.Destination) fi, err := os.Stat(entry.Destination)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "invalid destination directory: %s", entry.Destination) return nil, nil, errors.Wrapf(err, "invalid destination directory: %s", entry.Destination)
} }
if err == nil && !fi.IsDir() { if err == nil && !fi.IsDir() {
return nil, errors.Errorf("destination directory %s is a file", entry.Destination) return nil, nil, errors.Errorf("destination directory %s is a file", entry.Destination)
} }
out.OutputDir = entry.Destination out.OutputDir = entry.Destination
localPaths = append(localPaths, entry.Destination)
} }
if supportFile { if supportFile {
if entry.Destination == "" && out.Type != client.ExporterDocker { if entry.Destination == "" && out.Type != client.ExporterDocker {
@ -70,32 +72,33 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
} }
if entry.Destination == "-" { if entry.Destination == "-" {
if stdoutUsed { if stdoutUsed {
return nil, errors.Errorf("multiple outputs configured to write to stdout") return nil, nil, errors.Errorf("multiple outputs configured to write to stdout")
} }
if _, err := console.ConsoleFromFile(os.Stdout); err == nil { if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
return nil, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type) return nil, nil, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type)
} }
out.Output = wrapWriteCloser(os.Stdout) out.Output = wrapWriteCloser(os.Stdout)
stdoutUsed = true stdoutUsed = true
} else if entry.Destination != "" { } else if entry.Destination != "" {
fi, err := os.Stat(entry.Destination) fi, err := os.Stat(entry.Destination)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "invalid destination file: %s", entry.Destination) return nil, nil, errors.Wrapf(err, "invalid destination file: %s", entry.Destination)
} }
if err == nil && fi.IsDir() { if err == nil && fi.IsDir() {
return nil, errors.Errorf("destination file %s is a directory", entry.Destination) return nil, nil, errors.Errorf("destination file %s is a directory", entry.Destination)
} }
f, err := os.Create(entry.Destination) f, err := os.Create(entry.Destination)
if err != nil { if err != nil {
return nil, errors.Errorf("failed to open %s", err) return nil, nil, errors.Errorf("failed to open %s", err)
} }
out.Output = wrapWriteCloser(f) out.Output = wrapWriteCloser(f)
localPaths = append(localPaths, entry.Destination)
} }
} }
outs = append(outs, out) outs = append(outs, out)
} }
return outs, nil return outs, localPaths, nil
} }
func wrapWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) { func wrapWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) {