mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 01:53:42 +08:00 
			
		
		
		
	Merge pull request #1581 from ktock/resolvepath
monitor: resolve paths arguments in client
This commit is contained in:
		@@ -8,6 +8,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -28,6 +29,7 @@ import (
 | 
			
		||||
	"github.com/docker/cli/cli"
 | 
			
		||||
	"github.com/docker/cli/cli/command"
 | 
			
		||||
	dockeropts "github.com/docker/cli/opts"
 | 
			
		||||
	"github.com/docker/docker/builder/remotecontext/urlutil"
 | 
			
		||||
	"github.com/docker/docker/pkg/ioutils"
 | 
			
		||||
	"github.com/moby/buildkit/client"
 | 
			
		||||
	"github.com/moby/buildkit/exporter/containerimage/exptypes"
 | 
			
		||||
@@ -514,6 +516,13 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Start build
 | 
			
		||||
	// NOTE: buildx server has the current working directory different from the client
 | 
			
		||||
	// so we need to resolve paths to abosolute ones in the client.
 | 
			
		||||
	optsP, err := resolvePaths(&opts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	opts = *optsP
 | 
			
		||||
	ref, resp, err := c.Build(ctx, opts, pr, os.Stdout, os.Stderr, progress)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrapf(err, "failed to build") // TODO: allow invoke even on error
 | 
			
		||||
@@ -649,3 +658,159 @@ func dockerUlimitToControllerUlimit(u *dockeropts.UlimitOpt) *controllerapi.Ulim
 | 
			
		||||
	}
 | 
			
		||||
	return &controllerapi.UlimitOpt{Values: values}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// resolvePaths resolves all paths contained in controllerapi.BuildOptions
 | 
			
		||||
// and replaces them to absolute paths.
 | 
			
		||||
func resolvePaths(options *controllerapi.BuildOptions) (_ *controllerapi.BuildOptions, err error) {
 | 
			
		||||
	if options.ContextPath != "" && options.ContextPath != "-" {
 | 
			
		||||
		options.ContextPath, err = filepath.Abs(options.ContextPath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if options.DockerfileName != "" && options.DockerfileName != "-" {
 | 
			
		||||
		options.DockerfileName, err = filepath.Abs(options.DockerfileName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var contexts map[string]string
 | 
			
		||||
	for k, v := range options.NamedContexts {
 | 
			
		||||
		if urlutil.IsGitURL(v) || urlutil.IsURL(v) || strings.HasPrefix(v, "docker-image://") {
 | 
			
		||||
			// url prefix, this is a remote path
 | 
			
		||||
		} else if strings.HasPrefix(v, "oci-layout://") {
 | 
			
		||||
			// oci layout prefix, this is a local path
 | 
			
		||||
			p := strings.TrimPrefix(v, "oci-layout://")
 | 
			
		||||
			p, err = filepath.Abs(p)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			v = "oci-layout://" + p
 | 
			
		||||
		} else {
 | 
			
		||||
			// no prefix, assume local path
 | 
			
		||||
			v, err = filepath.Abs(v)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if contexts == nil {
 | 
			
		||||
			contexts = make(map[string]string)
 | 
			
		||||
		}
 | 
			
		||||
		contexts[k] = v
 | 
			
		||||
	}
 | 
			
		||||
	options.NamedContexts = contexts
 | 
			
		||||
 | 
			
		||||
	var cacheFrom []*controllerapi.CacheOptionsEntry
 | 
			
		||||
	for _, co := range options.CacheFrom {
 | 
			
		||||
		switch co.Type {
 | 
			
		||||
		case "local":
 | 
			
		||||
			var attrs map[string]string
 | 
			
		||||
			for k, v := range co.Attrs {
 | 
			
		||||
				if attrs == nil {
 | 
			
		||||
					attrs = make(map[string]string)
 | 
			
		||||
				}
 | 
			
		||||
				switch k {
 | 
			
		||||
				case "src":
 | 
			
		||||
					p := v
 | 
			
		||||
					if p != "" {
 | 
			
		||||
						p, err = filepath.Abs(p)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							return nil, err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					attrs[k] = p
 | 
			
		||||
				default:
 | 
			
		||||
					attrs[k] = v
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			co.Attrs = attrs
 | 
			
		||||
			cacheFrom = append(cacheFrom, co)
 | 
			
		||||
		default:
 | 
			
		||||
			cacheFrom = append(cacheFrom, co)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	options.CacheFrom = cacheFrom
 | 
			
		||||
 | 
			
		||||
	var cacheTo []*controllerapi.CacheOptionsEntry
 | 
			
		||||
	for _, co := range options.CacheTo {
 | 
			
		||||
		switch co.Type {
 | 
			
		||||
		case "local":
 | 
			
		||||
			var attrs map[string]string
 | 
			
		||||
			for k, v := range co.Attrs {
 | 
			
		||||
				if attrs == nil {
 | 
			
		||||
					attrs = make(map[string]string)
 | 
			
		||||
				}
 | 
			
		||||
				switch k {
 | 
			
		||||
				case "dest":
 | 
			
		||||
					p := v
 | 
			
		||||
					if p != "" {
 | 
			
		||||
						p, err = filepath.Abs(p)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							return nil, err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					attrs[k] = p
 | 
			
		||||
				default:
 | 
			
		||||
					attrs[k] = v
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			co.Attrs = attrs
 | 
			
		||||
			cacheTo = append(cacheTo, co)
 | 
			
		||||
		default:
 | 
			
		||||
			cacheTo = append(cacheTo, co)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	options.CacheTo = cacheTo
 | 
			
		||||
	var exports []*controllerapi.ExportEntry
 | 
			
		||||
	for _, e := range options.Exports {
 | 
			
		||||
		if e.Destination != "" && e.Destination != "-" {
 | 
			
		||||
			e.Destination, err = filepath.Abs(e.Destination)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		exports = append(exports, e)
 | 
			
		||||
	}
 | 
			
		||||
	options.Exports = exports
 | 
			
		||||
 | 
			
		||||
	var secrets []*controllerapi.Secret
 | 
			
		||||
	for _, s := range options.Secrets {
 | 
			
		||||
		if s.FilePath != "" {
 | 
			
		||||
			s.FilePath, err = filepath.Abs(s.FilePath)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		secrets = append(secrets, s)
 | 
			
		||||
	}
 | 
			
		||||
	options.Secrets = secrets
 | 
			
		||||
 | 
			
		||||
	var ssh []*controllerapi.SSH
 | 
			
		||||
	for _, s := range options.SSH {
 | 
			
		||||
		var ps []string
 | 
			
		||||
		for _, pt := range s.Paths {
 | 
			
		||||
			p := pt
 | 
			
		||||
			if p != "" {
 | 
			
		||||
				p, err = filepath.Abs(p)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			ps = append(ps, p)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		s.Paths = ps
 | 
			
		||||
		ssh = append(ssh, s)
 | 
			
		||||
	}
 | 
			
		||||
	options.SSH = ssh
 | 
			
		||||
 | 
			
		||||
	if options.Opts != nil && options.Opts.MetadataFile != "" {
 | 
			
		||||
		options.Opts.MetadataFile, err = filepath.Abs(options.Opts.MetadataFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return options, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										251
									
								
								commands/build_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								commands/build_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,251 @@
 | 
			
		||||
package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	controllerapi "github.com/docker/buildx/controller/pb"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestResolvePaths(t *testing.T) {
 | 
			
		||||
	tmpwd, err := os.MkdirTemp("", "testresolvepaths")
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	defer os.Remove(tmpwd)
 | 
			
		||||
	require.NoError(t, os.Chdir(tmpwd))
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		options controllerapi.BuildOptions
 | 
			
		||||
		want    controllerapi.BuildOptions
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:    "contextpath",
 | 
			
		||||
			options: controllerapi.BuildOptions{ContextPath: "test"},
 | 
			
		||||
			want:    controllerapi.BuildOptions{ContextPath: filepath.Join(tmpwd, "test")},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "contextpath-cwd",
 | 
			
		||||
			options: controllerapi.BuildOptions{ContextPath: "."},
 | 
			
		||||
			want:    controllerapi.BuildOptions{ContextPath: tmpwd},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "contextpath-dash",
 | 
			
		||||
			options: controllerapi.BuildOptions{ContextPath: "-"},
 | 
			
		||||
			want:    controllerapi.BuildOptions{ContextPath: "-"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "dockerfilename",
 | 
			
		||||
			options: controllerapi.BuildOptions{DockerfileName: "test"},
 | 
			
		||||
			want:    controllerapi.BuildOptions{DockerfileName: filepath.Join(tmpwd, "test")},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "dockerfilename-dash",
 | 
			
		||||
			options: controllerapi.BuildOptions{DockerfileName: "-"},
 | 
			
		||||
			want:    controllerapi.BuildOptions{DockerfileName: "-"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "contexts",
 | 
			
		||||
			options: controllerapi.BuildOptions{NamedContexts: map[string]string{"a": "test1", "b": "test2",
 | 
			
		||||
				"alpine": "docker-image://alpine@sha256:0123456789", "project": "https://github.com/myuser/project.git"}},
 | 
			
		||||
			want: controllerapi.BuildOptions{NamedContexts: map[string]string{"a": filepath.Join(tmpwd, "test1"), "b": filepath.Join(tmpwd, "test2"),
 | 
			
		||||
				"alpine": "docker-image://alpine@sha256:0123456789", "project": "https://github.com/myuser/project.git"}},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "cache-from",
 | 
			
		||||
			options: controllerapi.BuildOptions{
 | 
			
		||||
				CacheFrom: []*controllerapi.CacheOptionsEntry{
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "local",
 | 
			
		||||
						Attrs: map[string]string{"src": "test"},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "registry",
 | 
			
		||||
						Attrs: map[string]string{"ref": "user/app"},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: controllerapi.BuildOptions{
 | 
			
		||||
				CacheFrom: []*controllerapi.CacheOptionsEntry{
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "local",
 | 
			
		||||
						Attrs: map[string]string{"src": filepath.Join(tmpwd, "test")},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "registry",
 | 
			
		||||
						Attrs: map[string]string{"ref": "user/app"},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "cache-to",
 | 
			
		||||
			options: controllerapi.BuildOptions{
 | 
			
		||||
				CacheTo: []*controllerapi.CacheOptionsEntry{
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "local",
 | 
			
		||||
						Attrs: map[string]string{"dest": "test"},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "registry",
 | 
			
		||||
						Attrs: map[string]string{"ref": "user/app"},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: controllerapi.BuildOptions{
 | 
			
		||||
				CacheTo: []*controllerapi.CacheOptionsEntry{
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "local",
 | 
			
		||||
						Attrs: map[string]string{"dest": filepath.Join(tmpwd, "test")},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "registry",
 | 
			
		||||
						Attrs: map[string]string{"ref": "user/app"},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "exports",
 | 
			
		||||
			options: controllerapi.BuildOptions{
 | 
			
		||||
				Exports: []*controllerapi.ExportEntry{
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "local",
 | 
			
		||||
						Destination: "-",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "local",
 | 
			
		||||
						Destination: "test1",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "tar",
 | 
			
		||||
						Destination: "test3",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "oci",
 | 
			
		||||
						Destination: "-",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "docker",
 | 
			
		||||
						Destination: "test4",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "image",
 | 
			
		||||
						Attrs: map[string]string{"push": "true"},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: controllerapi.BuildOptions{
 | 
			
		||||
				Exports: []*controllerapi.ExportEntry{
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "local",
 | 
			
		||||
						Destination: "-",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "local",
 | 
			
		||||
						Destination: filepath.Join(tmpwd, "test1"),
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "tar",
 | 
			
		||||
						Destination: filepath.Join(tmpwd, "test3"),
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "oci",
 | 
			
		||||
						Destination: "-",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:        "docker",
 | 
			
		||||
						Destination: filepath.Join(tmpwd, "test4"),
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Type:  "image",
 | 
			
		||||
						Attrs: map[string]string{"push": "true"},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "secrets",
 | 
			
		||||
			options: controllerapi.BuildOptions{
 | 
			
		||||
				Secrets: []*controllerapi.Secret{
 | 
			
		||||
					{
 | 
			
		||||
						FilePath: "test1",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ID:  "val",
 | 
			
		||||
						Env: "a",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ID:       "test",
 | 
			
		||||
						FilePath: "test3",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: controllerapi.BuildOptions{
 | 
			
		||||
				Secrets: []*controllerapi.Secret{
 | 
			
		||||
					{
 | 
			
		||||
						FilePath: filepath.Join(tmpwd, "test1"),
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ID:  "val",
 | 
			
		||||
						Env: "a",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ID:       "test",
 | 
			
		||||
						FilePath: filepath.Join(tmpwd, "test3"),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "ssh",
 | 
			
		||||
			options: controllerapi.BuildOptions{
 | 
			
		||||
				SSH: []*controllerapi.SSH{
 | 
			
		||||
					{
 | 
			
		||||
						ID:    "default",
 | 
			
		||||
						Paths: []string{"test1", "test2"},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ID:    "a",
 | 
			
		||||
						Paths: []string{"test3"},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: controllerapi.BuildOptions{
 | 
			
		||||
				SSH: []*controllerapi.SSH{
 | 
			
		||||
					{
 | 
			
		||||
						ID:    "default",
 | 
			
		||||
						Paths: []string{filepath.Join(tmpwd, "test1"), filepath.Join(tmpwd, "test2")},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						ID:    "a",
 | 
			
		||||
						Paths: []string{filepath.Join(tmpwd, "test3")},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "metadatafile",
 | 
			
		||||
			options: controllerapi.BuildOptions{
 | 
			
		||||
				Opts: &controllerapi.CommonOptions{
 | 
			
		||||
					MetadataFile: "test1",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: controllerapi.BuildOptions{
 | 
			
		||||
				Opts: &controllerapi.CommonOptions{
 | 
			
		||||
					MetadataFile: filepath.Join(tmpwd, "test1"),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			got, err := resolvePaths(&tt.options)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
			if !reflect.DeepEqual(tt.want, *got) {
 | 
			
		||||
				t.Fatalf("expected %#v, got %#v", tt.want, *got)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user