From 549f1830187f1b0ef60116469a532832c678420f Mon Sep 17 00:00:00 2001 From: Ulysses Souza Date: Tue, 3 Dec 2019 18:25:05 +0100 Subject: [PATCH] Use fork of buildx to fix file finalizer Signed-off-by: Ulysses Souza --- Gopkg.lock | 17 +- Gopkg.toml | 14 +- internal/commands/build/build.go | 55 ++-- .../github.com/containerd/console/console.go | 19 +- .../containerd/console/console_unix.go | 6 +- .../containerd/console/console_windows.go | 4 +- .../github.com/docker/buildx/driver/driver.go | 3 + .../docker/buildx/driver/manager.go | 28 ++- .../docker/buildx/store/nodegroup.go | 158 ++++++++++++ .../github.com/docker/buildx/store/store.go | 237 ++++++++++++++++++ vendor/github.com/docker/buildx/store/util.go | 32 +++ .../docker/buildx/util/platformutil/parse.go | 65 +++++ .../docker/buildx/util/progress/printer.go | 2 +- 13 files changed, 588 insertions(+), 52 deletions(-) create mode 100644 vendor/github.com/docker/buildx/store/nodegroup.go create mode 100644 vendor/github.com/docker/buildx/store/store.go create mode 100644 vendor/github.com/docker/buildx/store/util.go create mode 100644 vendor/github.com/docker/buildx/util/platformutil/parse.go diff --git a/Gopkg.lock b/Gopkg.lock index 62db1fba2..420dbcc01 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -79,12 +79,12 @@ revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] - branch = "master" - digest = "1:da4daad2ec1737eec4ebeeed7afedb631711f96bbac0c361a17a4d0369d00c6d" + digest = "1:ad9bef47052721448e3f3a8bb56f333efbc6474c6744dd8c4670c378ee21c0bb" name = "github.com/containerd/console" packages = ["."] pruneopts = "NUT" - revision = "0650fd9eeb50bab4fc99dceb9f2e14cf58f36e7f" + revision = "f652dc3e99a9f4aa760deb9b4b28edb7c4e5001a" + source = "github.com/ulyssessouza/console" [[projects]] digest = "1:a40540af5e9f6f578a0473156f6ddb554aba4e96de5527648cfcc4d074f3120a" @@ -217,18 +217,20 @@ version = "v0.7.1-beta1" [[projects]] - digest = "1:fdf76ff539694e8aba8e9c60e7cc1dead88d21c9dc09e5a330104adad0887440" + digest = "1:db6ae89e20ff371a1c7786d778abb9163bac50ba726bb2e5d2257d72eaf24885" name = "github.com/docker/buildx" packages = [ "build", "driver", "driver/docker", + "store", "util/imagetools", + "util/platformutil", "util/progress", ] pruneopts = "NUT" - revision = "6db68d029599c6710a32aa7adcba8e5a344795a7" - version = "v0.3.1" + revision = "5941345e21ebda43723a475380135fe3741d6b3c" + source = "github.com/ulyssessouza/buildx" [[projects]] digest = "1:70c71dd9f1dc81872e11415441714469cd8231c85f26b709a4f48c0c3f81d4ae" @@ -1414,6 +1416,7 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ + "github.com/containerd/console", "github.com/containerd/containerd/log", "github.com/containerd/containerd/platforms", "github.com/deislabs/cnab-go/action", @@ -1448,6 +1451,7 @@ "github.com/docker/cli/cli/context/kubernetes", "github.com/docker/cli/cli/context/store", "github.com/docker/cli/cli/flags", + "github.com/docker/cli/cli/streams", "github.com/docker/cli/opts", "github.com/docker/cli/templates", "github.com/docker/cnab-to-oci/relocation", @@ -1491,6 +1495,7 @@ "gotest.tools/icmd", "k8s.io/api/core/v1", "k8s.io/apimachinery/pkg/apis/meta/v1", + "k8s.io/apimachinery/pkg/util/wait", "k8s.io/client-go/kubernetes/typed/core/v1", ] solver-name = "gps-cdcl" diff --git a/Gopkg.toml b/Gopkg.toml index 335250d74..4bc27b440 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -21,9 +21,14 @@ required = ["github.com/wadey/gocovmerge"] -[[constraint]] +#[[constraint]] +# name = "github.com/docker/buildx" +# version = "=v0.3.1" + +[[override]] name = "github.com/docker/buildx" - version = "=v0.3.1" + source = "github.com/ulyssessouza/buildx" + revision = "5941345e21ebda43723a475380135fe3741d6b3c" [[override]] name = "github.com/moby/buildkit" @@ -48,6 +53,11 @@ required = ["github.com/wadey/gocovmerge"] name = "github.com/containerd/containerd" version = "v1.3.0" +[[override]] + name = "github.com/containerd/console" + source = "github.com/ulyssessouza/console" + revision = "f652dc3e99a9f4aa760deb9b4b28edb7c4e5001a" + [[override]] name = "github.com/docker/cli" revision = "37f9a88c696ae81be14c1697bd083d6421b4933c" diff --git a/internal/commands/build/build.go b/internal/commands/build/build.go index c6fade7a6..d3ab48269 100644 --- a/internal/commands/build/build.go +++ b/internal/commands/build/build.go @@ -11,11 +11,13 @@ import ( "strconv" "strings" - "github.com/deislabs/cnab-go/bundle" - cnab "github.com/deislabs/cnab-go/driver" "github.com/docker/app/internal" "github.com/docker/app/internal/packager" "github.com/docker/app/types" + + "github.com/containerd/console" + "github.com/deislabs/cnab-go/bundle" + cnab "github.com/deislabs/cnab-go/driver" "github.com/docker/buildx/build" "github.com/docker/buildx/driver" _ "github.com/docker/buildx/driver/docker" // required to get default driver registered, see driver/docker/factory.go:14 @@ -76,28 +78,43 @@ func Cmd(dockerCli command.Cli) *cobra.Command { return cmd } -// FIXME: DO NOT SET THIS VARIABLE DIRECTLY! Use `getOutputFile` -// This global var prevents the file to be garbage collected and by that invalidated -// A an alternative fix for this would be writing the output to a bytes buffer and flushing to stdout. -// The impossibility here is that os.File is not an interface that a buffer can implement. -// Maybe `progress.NewPrinter` should implement an "os.File-like" interface just for its needs. -// See https://github.com/golang/go/issues/14106 -var _outputFile *os.File +type File struct { + f *streams.Out +} -func getOutputFile(realOut *streams.Out, quiet bool) (*os.File, error) { - if _outputFile != nil { - return _outputFile, nil - } +func NewFile(f *streams.Out) console.File { + return File {f: f} +} + +func (f File) Fd() uintptr { + return f.f.FD() +} + +func (f File) Name() string { + return os.Stdout.Name() +} + +func (f File) Read(p []byte) (n int, err error) { + return 0, nil +} + +func (f File) Write(p []byte) (n int, err error) { + return f.f.Write(p) +} + +func (f File) Close() error { + return nil +} + +func getOutputFile(realOut *streams.Out, quiet bool) (console.File, error) { if quiet { - var err error - _outputFile, err = os.Create(os.DevNull) + nullFile, err := os.Create(os.DevNull) if err != nil { return nil, err } - return _outputFile, nil + return nullFile, nil } - _outputFile = os.NewFile(realOut.FD(), os.Stdout.Name()) - return _outputFile, nil + return NewFile(realOut), nil } func runBuild(dockerCli command.Cli, contextPath string, opt buildOptions) error { @@ -174,7 +191,7 @@ func buildImageUsingBuildx(app *types.App, contextPath string, opt buildOptions, ctx, cancel := context.WithCancel(appcontext.Context()) defer cancel() const drivername = "buildx_buildkit_default" - d, err := driver.GetDriver(ctx, drivername, nil, dockerCli.Client(), nil, "", nil) + d, err := driver.GetDriver(ctx, drivername, nil, dockerCli.Client(), nil, nil, "", nil, "") if err != nil { return nil, err } diff --git a/vendor/github.com/containerd/console/console.go b/vendor/github.com/containerd/console/console.go index c187a9b41..6a36d1477 100644 --- a/vendor/github.com/containerd/console/console.go +++ b/vendor/github.com/containerd/console/console.go @@ -24,10 +24,17 @@ import ( var ErrNotAConsole = errors.New("provided file is not a console") +type File interface { + io.ReadWriteCloser + + // Fd returns its file descriptor + Fd() uintptr + // Name returns its file name + Name() string +} + type Console interface { - io.Reader - io.Writer - io.Closer + File // Resize resizes the console to the provided window size Resize(WinSize) error @@ -42,10 +49,6 @@ type Console interface { Reset() error // Size returns the window size of the console Size() (WinSize, error) - // Fd returns the console's file descriptor - Fd() uintptr - // Name returns the console's file name - Name() string } // WinSize specifies the window size of the console @@ -70,7 +73,7 @@ func Current() Console { } // ConsoleFromFile returns a console using the provided file -func ConsoleFromFile(f *os.File) (Console, error) { +func ConsoleFromFile(f File) (Console, error) { if err := checkConsole(f); err != nil { return nil, err } diff --git a/vendor/github.com/containerd/console/console_unix.go b/vendor/github.com/containerd/console/console_unix.go index a4a8d1267..315f1d0c9 100644 --- a/vendor/github.com/containerd/console/console_unix.go +++ b/vendor/github.com/containerd/console/console_unix.go @@ -47,7 +47,7 @@ func NewPty() (Console, string, error) { } type master struct { - f *os.File + f File original *unix.Termios } @@ -122,7 +122,7 @@ func (m *master) Name() string { } // checkConsole checks if the provided file is a console -func checkConsole(f *os.File) error { +func checkConsole(f File) error { var termios unix.Termios if tcget(f.Fd(), &termios) != nil { return ErrNotAConsole @@ -130,7 +130,7 @@ func checkConsole(f *os.File) error { return nil } -func newMaster(f *os.File) (Console, error) { +func newMaster(f File) (Console, error) { m := &master{ f: f, } diff --git a/vendor/github.com/containerd/console/console_windows.go b/vendor/github.com/containerd/console/console_windows.go index 62dbe1c03..129a92883 100644 --- a/vendor/github.com/containerd/console/console_windows.go +++ b/vendor/github.com/containerd/console/console_windows.go @@ -198,7 +198,7 @@ func makeInputRaw(fd windows.Handle, mode uint32) error { return nil } -func checkConsole(f *os.File) error { +func checkConsole(f File) error { var mode uint32 if err := windows.GetConsoleMode(windows.Handle(f.Fd()), &mode); err != nil { return err @@ -206,7 +206,7 @@ func checkConsole(f *os.File) error { return nil } -func newMaster(f *os.File) (Console, error) { +func newMaster(f File) (Console, error) { if f != os.Stdin && f != os.Stdout && f != os.Stderr { return nil, errors.New("creating a console from a file is not supported on windows") } diff --git a/vendor/github.com/docker/buildx/driver/driver.go b/vendor/github.com/docker/buildx/driver/driver.go index 6d73cff45..da66c6c7f 100644 --- a/vendor/github.com/docker/buildx/driver/driver.go +++ b/vendor/github.com/docker/buildx/driver/driver.go @@ -3,6 +3,7 @@ package driver import ( "context" + "github.com/docker/buildx/store" "github.com/docker/buildx/util/progress" "github.com/moby/buildkit/client" "github.com/pkg/errors" @@ -39,6 +40,8 @@ func (s Status) String() string { type Info struct { Status Status + // DynamicNodes must be empty if the actual nodes are statically listed in the store + DynamicNodes []store.Node } type Driver interface { diff --git a/vendor/github.com/docker/buildx/driver/manager.go b/vendor/github.com/docker/buildx/driver/manager.go index 8ddf78218..05b13b711 100644 --- a/vendor/github.com/docker/buildx/driver/manager.go +++ b/vendor/github.com/docker/buildx/driver/manager.go @@ -6,6 +6,7 @@ import ( dockerclient "github.com/docker/docker/client" "github.com/pkg/errors" + "k8s.io/client-go/tools/clientcmd" ) type Factory interface { @@ -23,11 +24,14 @@ type BuildkitConfig struct { type InitConfig struct { // This object needs updates to be generic for different drivers - Name string - DockerAPI dockerclient.APIClient - BuildkitFlags []string - ConfigFile string - DriverOpts map[string]string + Name string + DockerAPI dockerclient.APIClient + KubeClientConfig clientcmd.ClientConfig + BuildkitFlags []string + ConfigFile string + DriverOpts map[string]string + // ContextPathHash can be used for determining pods in the driver instance + ContextPathHash string } var drivers map[string]Factory @@ -72,13 +76,15 @@ func GetFactory(name string, instanceRequired bool) Factory { return nil } -func GetDriver(ctx context.Context, name string, f Factory, api dockerclient.APIClient, flags []string, config string, do map[string]string) (Driver, error) { +func GetDriver(ctx context.Context, name string, f Factory, api dockerclient.APIClient, kcc clientcmd.ClientConfig, flags []string, config string, do map[string]string, contextPathHash string) (Driver, error) { ic := InitConfig{ - DockerAPI: api, - Name: name, - BuildkitFlags: flags, - ConfigFile: config, - DriverOpts: do, + DockerAPI: api, + KubeClientConfig: kcc, + Name: name, + BuildkitFlags: flags, + ConfigFile: config, + DriverOpts: do, + ContextPathHash: contextPathHash, } if f == nil { var err error diff --git a/vendor/github.com/docker/buildx/store/nodegroup.go b/vendor/github.com/docker/buildx/store/nodegroup.go new file mode 100644 index 000000000..5eb7c47cd --- /dev/null +++ b/vendor/github.com/docker/buildx/store/nodegroup.go @@ -0,0 +1,158 @@ +package store + +import ( + "fmt" + + "github.com/containerd/containerd/platforms" + "github.com/docker/buildx/util/platformutil" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type NodeGroup struct { + Name string + Driver string + Nodes []Node + Dynamic bool +} + +type Node struct { + Name string + Endpoint string + Platforms []specs.Platform + Flags []string + ConfigFile string + DriverOpts map[string]string +} + +func (ng *NodeGroup) Leave(name string) error { + if ng.Dynamic { + return errors.New("dynamic node group does not support Leave") + } + i := ng.findNode(name) + if i == -1 { + return errors.Errorf("node %q not found for %s", name, ng.Name) + } + if len(ng.Nodes) == 1 { + return errors.Errorf("can not leave last node, do you want to rm instance instead?") + } + ng.Nodes = append(ng.Nodes[:i], ng.Nodes[i+1:]...) + return nil +} + +func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpointsSet bool, actionAppend bool, flags []string, configFile string, do map[string]string) error { + if ng.Dynamic { + return errors.New("dynamic node group does not support Update") + } + i := ng.findNode(name) + if i == -1 && !actionAppend { + if len(ng.Nodes) > 0 { + return errors.Errorf("node %s not found, did you mean to append?", name) + } + ng.Nodes = nil + } + + pp, err := platformutil.Parse(platforms) + if err != nil { + return err + } + + if i != -1 { + n := ng.Nodes[i] + if endpointsSet { + n.Endpoint = endpoint + } + if len(platforms) > 0 { + n.Platforms = pp + } + if flags != nil { + n.Flags = flags + } + ng.Nodes[i] = n + if err := ng.validateDuplicates(endpoint, i); err != nil { + return err + } + return nil + } + + if name == "" { + name = ng.nextNodeName() + } + + name, err = ValidateName(name) + if err != nil { + return err + } + + n := Node{ + Name: name, + Endpoint: endpoint, + Platforms: pp, + ConfigFile: configFile, + Flags: flags, + DriverOpts: do, + } + ng.Nodes = append(ng.Nodes, n) + + if err := ng.validateDuplicates(endpoint, len(ng.Nodes)-1); err != nil { + return err + } + return nil +} + +func (ng *NodeGroup) validateDuplicates(ep string, idx int) error { + i := 0 + for _, n := range ng.Nodes { + if n.Endpoint == ep { + i++ + } + } + if i > 1 { + return errors.Errorf("invalid duplicate endpoint %s", ep) + } + + m := map[string]struct{}{} + for _, p := range ng.Nodes[idx].Platforms { + m[platforms.Format(p)] = struct{}{} + } + + for i := range ng.Nodes { + if i == idx { + continue + } + ng.Nodes[i].Platforms = filterPlatforms(ng.Nodes[i].Platforms, m) + } + + return nil +} + +func (ng *NodeGroup) findNode(name string) int { + for i, n := range ng.Nodes { + if n.Name == name { + return i + } + } + return -1 +} + +func (ng *NodeGroup) nextNodeName() string { + i := 0 + for { + name := fmt.Sprintf("%s%d", ng.Name, i) + if ii := ng.findNode(name); ii != -1 { + i++ + continue + } + return name + } +} + +func filterPlatforms(in []specs.Platform, m map[string]struct{}) []specs.Platform { + out := make([]specs.Platform, 0, len(in)) + for _, p := range in { + if _, ok := m[platforms.Format(p)]; !ok { + out = append(out, p) + } + } + return out +} diff --git a/vendor/github.com/docker/buildx/store/store.go b/vendor/github.com/docker/buildx/store/store.go new file mode 100644 index 000000000..b52d1e8f2 --- /dev/null +++ b/vendor/github.com/docker/buildx/store/store.go @@ -0,0 +1,237 @@ +package store + +import ( + "encoding/json" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "sort" + + "github.com/gofrs/flock" + "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +func New(root string) (*Store, error) { + root = filepath.Join(root, "buildx") + if err := os.MkdirAll(filepath.Join(root, "instances"), 0700); err != nil { + return nil, err + } + if err := os.MkdirAll(filepath.Join(root, "defaults"), 0700); err != nil { + return nil, err + } + return &Store{root: root}, nil +} + +type Store struct { + root string +} + +func (s *Store) Txn() (*Txn, func(), error) { + l := flock.New(filepath.Join(s.root, ".lock")) + if err := l.Lock(); err != nil { + return nil, nil, err + } + return &Txn{ + s: s, + }, func() { + l.Close() + }, nil +} + +type Txn struct { + s *Store +} + +func (t *Txn) List() ([]*NodeGroup, error) { + pp := filepath.Join(t.s.root, "instances") + fis, err := ioutil.ReadDir(pp) + if err != nil { + return nil, err + } + ngs := make([]*NodeGroup, 0, len(fis)) + for _, fi := range fis { + ng, err := t.NodeGroupByName(fi.Name()) + if err != nil { + if os.IsNotExist(errors.Cause(err)) { + os.RemoveAll(filepath.Join(pp, fi.Name())) + continue + } + return nil, err + } + ngs = append(ngs, ng) + } + + sort.Slice(ngs, func(i, j int) bool { + return ngs[i].Name < ngs[j].Name + }) + + return ngs, nil +} + +func (t *Txn) NodeGroupByName(name string) (*NodeGroup, error) { + name, err := ValidateName(name) + if err != nil { + return nil, err + } + dt, err := ioutil.ReadFile(filepath.Join(t.s.root, "instances", name)) + if err != nil { + return nil, err + } + var ng NodeGroup + if err := json.Unmarshal(dt, &ng); err != nil { + return nil, err + } + return &ng, nil +} + +func (t *Txn) Save(ng *NodeGroup) error { + name, err := ValidateName(ng.Name) + if err != nil { + return err + } + dt, err := json.Marshal(ng) + if err != nil { + return err + } + return atomicWriteFile(filepath.Join(t.s.root, "instances", name), dt, 0600) +} + +func (t *Txn) Remove(name string) error { + name, err := ValidateName(name) + if err != nil { + return err + } + return os.RemoveAll(filepath.Join(t.s.root, "instances", name)) +} + +func (t *Txn) SetCurrent(key, name string, global, def bool) error { + c := current{ + Key: key, + Name: name, + Global: global, + } + dt, err := json.Marshal(c) + if err != nil { + return err + } + if err := atomicWriteFile(filepath.Join(t.s.root, "current"), dt, 0600); err != nil { + return err + } + + h := toHash(key) + + if def { + if err := atomicWriteFile(filepath.Join(t.s.root, "defaults", h), []byte(name), 0600); err != nil { + return err + } + } else { + os.RemoveAll(filepath.Join(t.s.root, "defaults", h)) // ignore error + } + return nil +} + +func (t *Txn) reset(key string) error { + dt, err := json.Marshal(current{Key: key}) + if err != nil { + return err + } + if err := atomicWriteFile(filepath.Join(t.s.root, "current"), dt, 0600); err != nil { + return err + } + return nil +} + +func (t *Txn) Current(key string) (*NodeGroup, error) { + dt, err := ioutil.ReadFile(filepath.Join(t.s.root, "current")) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + } + if err == nil { + var c current + if err := json.Unmarshal(dt, &c); err != nil { + return nil, err + } + if c.Name != "" { + if c.Global { + ng, err := t.NodeGroupByName(c.Name) + if err == nil { + return ng, nil + } + } + + if c.Key == key { + ng, err := t.NodeGroupByName(c.Name) + if err == nil { + return ng, nil + } + return nil, nil + } + } + } + + h := toHash(key) + + dt, err = ioutil.ReadFile(filepath.Join(t.s.root, "defaults", h)) + if err != nil { + if os.IsNotExist(err) { + t.reset(key) + return nil, nil + } + return nil, err + } + + ng, err := t.NodeGroupByName(string(dt)) + if err != nil { + t.reset(key) + } + if err := t.SetCurrent(key, string(dt), false, true); err != nil { + return nil, err + } + return ng, nil +} + +type current struct { + Key string + Name string + Global bool +} + +var namePattern = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9\.\-_]*$`) + +func atomicWriteFile(filename string, data []byte, perm os.FileMode) error { + f, err := ioutil.TempFile(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) + if err != nil { + return err + } + err = os.Chmod(f.Name(), perm) + if err != nil { + f.Close() + return err + } + n, err := f.Write(data) + if err == nil && n < len(data) { + f.Close() + return io.ErrShortWrite + } + if err != nil { + f.Close() + return err + } + if err := f.Sync(); err != nil { + f.Close() + return err + } + if err := f.Close(); err != nil { + return err + } + return os.Rename(f.Name(), filename) +} + +func toHash(in string) string { + return digest.FromBytes([]byte(in)).Hex()[:20] +} diff --git a/vendor/github.com/docker/buildx/store/util.go b/vendor/github.com/docker/buildx/store/util.go new file mode 100644 index 000000000..e8a4269a5 --- /dev/null +++ b/vendor/github.com/docker/buildx/store/util.go @@ -0,0 +1,32 @@ +package store + +import ( + "os" + "strings" + + "github.com/docker/docker/pkg/namesgenerator" + "github.com/pkg/errors" +) + +func ValidateName(s string) (string, error) { + if !namePattern.MatchString(s) { + return "", errors.Errorf("invalid name %s, name needs to start with a letter and may not contain symbols, except ._-", s) + } + return strings.ToLower(s), nil +} + +func GenerateName(txn *Txn) (string, error) { + var name string + for i := 0; i < 6; i++ { + name = namesgenerator.GetRandomName(i) + if _, err := txn.NodeGroupByName(name); err != nil { + if !os.IsNotExist(errors.Cause(err)) { + return "", err + } + } else { + continue + } + return name, nil + } + return "", errors.Errorf("failed to generate random name") +} diff --git a/vendor/github.com/docker/buildx/util/platformutil/parse.go b/vendor/github.com/docker/buildx/util/platformutil/parse.go new file mode 100644 index 000000000..d1bc9a505 --- /dev/null +++ b/vendor/github.com/docker/buildx/util/platformutil/parse.go @@ -0,0 +1,65 @@ +package platformutil + +import ( + "strings" + + "github.com/containerd/containerd/platforms" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +func Parse(platformsStr []string) ([]specs.Platform, error) { + if len(platformsStr) == 0 { + return nil, nil + } + out := make([]specs.Platform, 0, len(platformsStr)) + for _, s := range platformsStr { + parts := strings.Split(s, ",") + if len(parts) > 1 { + p, err := Parse(parts) + if err != nil { + return nil, err + } + out = append(out, p...) + continue + } + p, err := parse(s) + if err != nil { + return nil, err + } + out = append(out, platforms.Normalize(p)) + } + return out, nil +} + +func parse(in string) (specs.Platform, error) { + if strings.EqualFold(in, "local") { + return platforms.DefaultSpec(), nil + } + return platforms.Parse(in) +} + +func Dedupe(in []specs.Platform) []specs.Platform { + m := map[string]struct{}{} + out := make([]specs.Platform, 0, len(in)) + for _, p := range in { + p := platforms.Normalize(p) + key := platforms.Format(p) + if _, ok := m[key]; ok { + continue + } + m[key] = struct{}{} + out = append(out, p) + } + return out +} + +func Format(in []specs.Platform) []string { + if len(in) == 0 { + return nil + } + out := make([]string, 0, len(in)) + for _, p := range in { + out = append(out, platforms.Format(p)) + } + return out +} diff --git a/vendor/github.com/docker/buildx/util/progress/printer.go b/vendor/github.com/docker/buildx/util/progress/printer.go index e2828f3d5..b4a76d7f0 100644 --- a/vendor/github.com/docker/buildx/util/progress/printer.go +++ b/vendor/github.com/docker/buildx/util/progress/printer.go @@ -30,7 +30,7 @@ func (p *printer) Status() chan *client.SolveStatus { return p.status } -func NewPrinter(ctx context.Context, out *os.File, mode string) Writer { +func NewPrinter(ctx context.Context, out console.File, mode string) Writer { statusCh := make(chan *client.SolveStatus) doneCh := make(chan struct{})