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>
This commit is contained in:
Tonis Tiigi 2024-12-02 14:46:58 -08:00
parent d6d713aac6
commit c6e403ad7f
No known key found for this signature in database
GPG Key ID: AFA9DE5F8AB7AF39
6 changed files with 46 additions and 67 deletions

View File

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

View File

@ -113,17 +113,8 @@ func (c EntitlementConf) check(bo build.Options, expected *EntitlementConf) erro
roPaths[p] = struct{}{}
}
for _, out := range bo.Exports {
if out.Type == "local" {
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 _, p := range bo.ExportsLocalPathsTemporary {
rwPaths[p] = struct{}{}
}
for _, ce := range bo.CacheTo {

View File

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

View File

@ -70,6 +70,7 @@ type Options struct {
CacheTo []client.CacheOptionsEntry
CgroupParent string
Exports []client.ExportEntry
ExportsLocalPathsTemporary []string // should be removed after client.ExportEntry update in buildkit v0.19.0
ExtraHosts []string
Labels map[string]string
NetworkMode string

View File

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

View File

@ -10,15 +10,16 @@ import (
"github.com/pkg/errors"
)
func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, []string, error) {
var outs []client.ExportEntry
var localPaths []string
if len(entries) == 0 {
return nil, nil
return nil, nil, nil
}
var stdoutUsed bool
for _, entry := range entries {
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{
@ -50,20 +51,21 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
if supportDir {
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 == "-" {
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)
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() {
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
localPaths = append(localPaths, entry.Destination)
}
if supportFile {
if entry.Destination == "" && out.Type != client.ExporterDocker {
@ -71,32 +73,33 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
}
if entry.Destination == "-" {
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 {
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)
stdoutUsed = true
} else if entry.Destination != "" {
fi, err := os.Stat(entry.Destination)
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() {
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)
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)
localPaths = append(localPaths, entry.Destination)
}
}
outs = append(outs, out)
}
return outs, nil
return outs, localPaths, nil
}
func wrapWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) {