From 9fe1083f507d4b773cab2e778b1f7456c2e435a5 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Mon, 7 Aug 2023 11:17:56 +0200 Subject: [PATCH 1/2] bake(compose): read and process include files Signed-off-by: CrazyMax --- bake/remote.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/bake/remote.go b/bake/remote.go index 3dd1e0d1..df3b14e4 100644 --- a/bake/remote.go +++ b/bake/remote.go @@ -4,7 +4,11 @@ import ( "archive/tar" "bytes" "context" + "encoding/json" + "os" + "github.com/compose-spec/compose-go/loader" + compose "github.com/compose-spec/compose-go/types" "github.com/docker/buildx/builder" controllerapi "github.com/docker/buildx/controller/pb" "github.com/docker/buildx/driver" @@ -182,19 +186,91 @@ func filesFromRef(ctx context.Context, ref gwclient.Reference, names []string) ( } for _, name := range names { - _, err := ref.StatFile(ctx, gwclient.StatRequest{Path: name}) - if err != nil { - if isDefault { - continue + f, err := readRefFile(name, func(name string) (*File, error) { + _, err := ref.StatFile(ctx, gwclient.StatRequest{Path: name}) + if err != nil { + if isDefault { + return nil, nil + } + return nil, err + } + dt, err := ref.ReadFile(ctx, gwclient.ReadRequest{Filename: name}) + if err != nil { + return nil, err } + return &File{Name: name, Data: dt}, nil + }, nil) + if err != nil { return nil, err + } else if len(f) == 0 { + continue } - dt, err := ref.ReadFile(ctx, gwclient.ReadRequest{Filename: name}) + files = append(files, f...) + } + + return files, nil +} + +func readRefFile(name string, readFunc func(name string) (*File, error), files []File) ([]File, error) { + f, err := readFunc(name) + if err != nil { + return nil, err + } else if f == nil { + return files, nil + } + // if we have a compose file, we need to read it and extract the + // include files from it + _, err = loader.Load(compose.ConfigDetails{ + ConfigFiles: []compose.ConfigFile{ + { + Content: f.Data, + }, + }, + Environment: sliceToMap(os.Environ()), + }, func(options *loader.Options) { + options.SetProjectName("bake", false) + options.SkipNormalization = true + options.SkipConsistencyCheck = true + options.SkipInclude = true + }) + if err == nil { + yml, err := loader.ParseYAML(f.Data) if err != nil { return nil, err } - files = append(files, File{Name: name, Data: dt}) + if v, ok := yml["include"]; ok && v != "" { + // include files will be read later and added to the list of files + // to be processed, so we can remote "include" from the yaml file + delete(yml, "include") + // marshal the yaml file again without the include key and add it + // to the list of files to be processed + newData, err := json.Marshal(yml) + if err != nil { + return nil, err + } + files = append(files, File{ + Name: f.Name, + Data: newData, + }) + // read include files + if idt, ok := v.([]interface{}); ok && len(idt) > 0 { + var includes []string + for _, i := range idt { + includes = append(includes, i.(string)) + } + for _, include := range includes { + incf, err := readRefFile(include, readFunc, files) + if err != nil { + return nil, err + } else if incf == nil { + continue + } + files = append(files, incf...) + } + } + } + } else { + files = append(files, *f) } - return files, nil } From 446ca8c7641591e246acf5e21d536116c32648ca Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Mon, 7 Aug 2023 11:18:31 +0200 Subject: [PATCH 2/2] integration: test remote compose with include Signed-off-by: CrazyMax --- tests/bake.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/bake.go b/tests/bake.go index b76a8bb8..7e890c54 100644 --- a/tests/bake.go +++ b/tests/bake.go @@ -25,6 +25,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){ testBakeRemoteContextSubdir, testBakeRemoteCmdContextEscapeRoot, testBakeRemoteCmdContextEscapeRelative, + testBakeRemoteComposeInclude, } func testBakeLocal(t *testing.T, sb integration.Sandbox) { @@ -287,3 +288,47 @@ EOT require.NoError(t, err, out) require.FileExists(t, filepath.Join(dirDest, "foo")) } + +func testBakeRemoteComposeInclude(t *testing.T, sb integration.Sandbox) { + composeFile := []byte(` +include: + - compose-inc.yml + +services: + foo: + build: + context: . + dockerfile_inline: | + FROM busybox + RUN echo foo +`) + composeIncFile := []byte(` +services: + inc: + build: + context: . + dockerfile_inline: | + FROM busybox + RUN echo inc +`) + + dirSpec := tmpdir( + t, + fstest.CreateFile("compose.yml", composeFile, 0600), + fstest.CreateFile("compose-inc.yml", composeIncFile, 0600), + ) + + git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec)) + require.NoError(t, err) + + gitutil.GitInit(git, t) + gitutil.GitAdd(git, t, "compose.yml", "compose-inc.yml") + gitutil.GitCommit(git, t, "initial commit") + addr := gitutil.GitServeHTTP(git, t) + + out, err := bakeCmd( + sb, + withArgs(addr, "--set", "*.output=type=cacheonly"), + ) + require.NoError(t, err, out) +}