From 42f1086294f81214a0519873a83ef3cdd300f235 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 28 Jun 2024 09:54:23 +0200 Subject: [PATCH] build: fix localstate for remote context Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- build/localstate.go | 43 +++++++------- localstate/localstate.go | 8 ++- localstate/localstate_test.go | 4 ++ tests/build.go | 102 ++++++++++++++++++++++++++++++++++ tests/integration.go | 9 ++- 5 files changed, 142 insertions(+), 24 deletions(-) diff --git a/build/localstate.go b/build/localstate.go index ad8c869b0671..bc4b861e3e7e 100644 --- a/build/localstate.go +++ b/build/localstate.go @@ -13,31 +13,32 @@ func saveLocalState(so *client.SolveOpt, target string, opts Options, node build if so.Ref == "" { return nil } - lp := opts.Inputs.ContextPath - dp := opts.Inputs.DockerfilePath - if lp != "" || dp != "" { - if lp != "" { - lp, err = filepath.Abs(lp) - if err != nil { - return err - } + var lp, dp string + if opts.Inputs.ContextPath != "" && !IsRemoteURL(opts.Inputs.ContextPath) { + lp, err = filepath.Abs(opts.Inputs.ContextPath) + if err != nil { + return err } - if dp != "" { - dp, err = filepath.Abs(dp) + } + if opts.Inputs.DockerfilePath != "" { + if !IsRemoteURL(opts.Inputs.ContextPath) { + dp, err = filepath.Abs(opts.Inputs.DockerfilePath) if err != nil { return err } + } else { + dp = opts.Inputs.DockerfilePath } - l, err := localstate.New(configDir) - if err != nil { - return err - } - return l.SaveRef(node.Builder, node.Name, so.Ref, localstate.State{ - Target: target, - LocalPath: lp, - DockerfilePath: dp, - GroupRef: opts.GroupRef, - }) } - return nil + l, err := localstate.New(configDir) + if err != nil { + return err + } + return l.SaveRef(node.Builder, node.Name, so.Ref, localstate.State{ + Context: opts.Inputs.ContextPath, + Target: target, + LocalPath: lp, + DockerfilePath: dp, + GroupRef: opts.GroupRef, + }) } diff --git a/localstate/localstate.go b/localstate/localstate.go index 54ec9f39efe7..abb6349ea6ac 100644 --- a/localstate/localstate.go +++ b/localstate/localstate.go @@ -19,11 +19,15 @@ const ( ) type State struct { + // Context is the build context + Context string // Target is the name of the invoked target (default if empty) Target string - // LocalPath is the absolute path to the context + // LocalPath is the absolute path to the context or empty if context is + // remote LocalPath string - // DockerfilePath is the absolute path to the Dockerfile + // DockerfilePath is the absolute path to the Dockerfile or relative if + // context is remote DockerfilePath string // GroupRef is the ref of the state group that this ref belongs to GroupRef string `json:",omitempty"` diff --git a/localstate/localstate_test.go b/localstate/localstate_test.go index 94d4ee8e8edd..dc3596edd24d 100644 --- a/localstate/localstate_test.go +++ b/localstate/localstate_test.go @@ -60,6 +60,7 @@ var ( testStateRefID = "32n3ffqrxjw41ok5zxd2qhume" testStateRef = State{ + Context: ".", Target: "default", LocalPath: "/home/foo/github.com/docker/docker-bake-action", DockerfilePath: "/home/foo/github.com/docker/docker-bake-action/dev.Dockerfile", @@ -75,6 +76,7 @@ var ( testStateGroupRef1ID = "hx2qf1w11qvz1x3k471c5i8xw" testStateGroupRef1 = State{ + Context: ".", Target: "format", LocalPath: "/home/foo/github.com/docker/docker-bake-action", DockerfilePath: "/home/foo/github.com/docker/docker-bake-action/dev.Dockerfile", @@ -83,6 +85,7 @@ var ( testStateGroupRef2ID = "968zj0g03jmlx0s8qslnvh6rl" testStateGroupRef2 = State{ + Context: ".", Target: "build", LocalPath: "/home/foo/github.com/docker/docker-bake-action", DockerfilePath: "/home/foo/github.com/docker/docker-bake-action/dev.Dockerfile", @@ -91,6 +94,7 @@ var ( testStateGroupRef3ID = "naf44f9i1710lf7y12lv5hb1z" testStateGroupRef3 = State{ + Context: ".", Target: "vendor-update", LocalPath: "/home/foo/github.com/docker/docker-bake-action", DockerfilePath: "/home/foo/github.com/docker/docker-bake-action/dev.Dockerfile", diff --git a/tests/build.go b/tests/build.go index cd4af2aec916..10a757d5281c 100644 --- a/tests/build.go +++ b/tests/build.go @@ -16,6 +16,7 @@ import ( "github.com/containerd/containerd/platforms" "github.com/containerd/continuity/fs/fstest" "github.com/creack/pty" + "github.com/docker/buildx/localstate" "github.com/docker/buildx/util/gitutil" "github.com/moby/buildkit/client" "github.com/moby/buildkit/frontend/subrequests/lint" @@ -44,6 +45,8 @@ var buildTests = []func(t *testing.T, sb integration.Sandbox){ testBuild, testBuildStdin, testBuildRemote, + testBuildLocalState, + testBuildLocalStateRemote, testImageIDOutput, testBuildLocalExport, testBuildRegistryExport, @@ -122,6 +125,105 @@ COPY foo /foo require.FileExists(t, filepath.Join(dirDest, "foo")) } +func testBuildLocalState(t *testing.T, sb integration.Sandbox) { + dockerfile := []byte(` +FROM busybox:latest AS base +COPY foo /etc/foo +RUN cp /etc/foo /etc/bar + +FROM scratch +COPY --from=base /etc/bar /bar +`) + dir := tmpdir( + t, + fstest.CreateFile("build.Dockerfile", dockerfile, 0600), + fstest.CreateFile("foo", []byte("foo"), 0600), + ) + + out, err := buildCmd(sb, withDir(dir), withArgs( + "-f", "build.Dockerfile", + "--metadata-file", filepath.Join(dir, "md.json"), + ".", + )) + require.NoError(t, err, out) + + dt, err := os.ReadFile(filepath.Join(dir, "md.json")) + require.NoError(t, err) + + type mdT struct { + BuildRef string `json:"buildx.build.ref"` + } + var md mdT + err = json.Unmarshal(dt, &md) + require.NoError(t, err) + + ls, err := localstate.New(buildxConfig(sb)) + require.NoError(t, err) + + refParts := strings.Split(md.BuildRef, "/") + require.Len(t, refParts, 3) + + ref, err := ls.ReadRef(refParts[0], refParts[1], refParts[2]) + require.NoError(t, err) + require.NotNil(t, ref) + require.Equal(t, dir, ref.Context) + require.DirExists(t, ref.LocalPath) + require.FileExists(t, ref.DockerfilePath) +} + +func testBuildLocalStateRemote(t *testing.T, sb integration.Sandbox) { + dockerfile := []byte(` +FROM busybox:latest +COPY foo /foo +`) + dir := tmpdir( + t, + fstest.CreateFile("build.Dockerfile", dockerfile, 0600), + fstest.CreateFile("foo", []byte("foo"), 0600), + ) + dirDest := t.TempDir() + + git, err := gitutil.New(gitutil.WithWorkingDir(dir)) + require.NoError(t, err) + + gitutil.GitInit(git, t) + gitutil.GitAdd(git, t, "build.Dockerfile", "foo") + gitutil.GitCommit(git, t, "initial commit") + addr := gitutil.GitServeHTTP(git, t) + + out, err := buildCmd(sb, withDir(dir), withArgs( + "-f", "build.Dockerfile", + "--metadata-file", filepath.Join(dirDest, "md.json"), + "--output", "type=local,dest="+dirDest, + addr, + )) + require.NoError(t, err, out) + require.FileExists(t, filepath.Join(dirDest, "foo")) + + dt, err := os.ReadFile(filepath.Join(dirDest, "md.json")) + require.NoError(t, err) + + type mdT struct { + BuildRef string `json:"buildx.build.ref"` + } + var md mdT + err = json.Unmarshal(dt, &md) + require.NoError(t, err) + + ls, err := localstate.New(buildxConfig(sb)) + require.NoError(t, err) + + refParts := strings.Split(md.BuildRef, "/") + require.Len(t, refParts, 3) + + ref, err := ls.ReadRef(refParts[0], refParts[1], refParts[2]) + require.NoError(t, err) + require.NotNil(t, ref) + require.Equal(t, addr, ref.Context) + require.Empty(t, ref.LocalPath) + require.Equal(t, "build.Dockerfile", ref.DockerfilePath) +} + func testBuildLocalExport(t *testing.T, sb integration.Sandbox) { dir := createTestProject(t) out, err := buildCmd(sb, withArgs(fmt.Sprintf("--output=type=local,dest=%s/result", dir), dir)) diff --git a/tests/integration.go b/tests/integration.go index a17b9569e1ec..920d1dce110b 100644 --- a/tests/integration.go +++ b/tests/integration.go @@ -56,7 +56,7 @@ func buildxCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd { if builder := sb.Address(); builder != "" { cmd.Env = append(cmd.Env, - "BUILDX_CONFIG=/tmp/buildx-"+builder, + "BUILDX_CONFIG="+buildxConfig(sb), "BUILDX_BUILDER="+builder, ) } @@ -86,6 +86,13 @@ func dockerCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd { return cmd } +func buildxConfig(sb integration.Sandbox) string { + if builder := sb.Address(); builder != "" { + return "/tmp/buildx-" + builder + } + return "" +} + func isMobyWorker(sb integration.Sandbox) bool { name, hasFeature := driverName(sb.Name()) return name == "docker" && !hasFeature