From dc07613bd2d7446a2f619f9cb54f5d5956436aeb Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Tue, 16 Apr 2019 22:43:15 +0000 Subject: [PATCH 1/3] build: support more variations on context and dockerfile + iidfile Signed-off-by: Tibor Vass --- build/build.go | 156 +++++- build/output.go | 2 +- build/utils.go | 34 ++ commands/build.go | 31 +- go.mod | 3 +- go.sum | 7 +- .../docker/docker/pkg/urlutil/urlutil.go | 52 ++ .../moby/buildkit/session/upload/generate.go | 3 + .../moby/buildkit/session/upload/upload.go | 55 ++ .../moby/buildkit/session/upload/upload.pb.go | 506 ++++++++++++++++++ .../moby/buildkit/session/upload/upload.proto | 14 + .../session/upload/uploadprovider/provider.go | 66 +++ vendor/modules.txt | 7 +- 13 files changed, 894 insertions(+), 42 deletions(-) create mode 100644 build/utils.go create mode 100644 vendor/github.com/docker/docker/pkg/urlutil/urlutil.go create mode 100644 vendor/github.com/moby/buildkit/session/upload/generate.go create mode 100644 vendor/github.com/moby/buildkit/session/upload/upload.go create mode 100644 vendor/github.com/moby/buildkit/session/upload/upload.pb.go create mode 100644 vendor/github.com/moby/buildkit/session/upload/upload.proto create mode 100644 vendor/github.com/moby/buildkit/session/upload/uploadprovider/provider.go diff --git a/build/build.go b/build/build.go index b80b40cc..76a916d7 100644 --- a/build/build.go +++ b/build/build.go @@ -1,16 +1,22 @@ package build import ( + "bufio" "context" "io" + "io/ioutil" + "os" "path/filepath" "strconv" "strings" "sync" "github.com/containerd/containerd/platforms" + "github.com/docker/distribution/reference" + "github.com/docker/docker/pkg/urlutil" "github.com/moby/buildkit/client" "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/upload/uploadprovider" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/tonistiigi/buildx/driver" @@ -18,12 +24,18 @@ import ( "golang.org/x/sync/errgroup" ) +var ( + errStdinConflict = errors.New("invalid argument: can't use stdin for both build context and dockerfile") + errDockerfileConflict = errors.New("ambiguous Dockerfile source: both stdin and flag correspond to Dockerfiles") +) + type Options struct { - Inputs Inputs - Tags []string - Labels map[string]string - BuildArgs map[string]string - Pull bool + Inputs Inputs + Tags []string + Labels map[string]string + BuildArgs map[string]string + Pull bool + ImageIDFile string NoCache bool Target string @@ -96,9 +108,20 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw for k, opt := range opt { pw := mw.WithPrefix(k, withPrefix) + if opt.ImageIDFile != "" { + if len(opt.Platforms) != 0 { + return nil, errors.Errorf("image ID file cannot be specified when building for multiple platforms") + } + // Avoid leaving a stale file if we eventually fail + if err := os.Remove(opt.ImageIDFile); err != nil && !os.IsNotExist(err) { + return nil, errors.Wrap(err, "removing image ID file") + } + } + so := client.SolveOpt{ Frontend: "dockerfile.v0", FrontendAttrs: map[string]string{}, + LocalDirs: map[string]string{}, } switch len(opt.Exports) { @@ -115,10 +138,18 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw } if len(opt.Tags) > 0 { + tags := make([]string, len(opt.Tags)) + for i, tag := range opt.Tags { + ref, err := reference.Parse(tag) + if err != nil { + return nil, errors.Wrapf(err, "invalid tag %q", tag) + } + tags[i] = ref.String() + } for i, e := range opt.Exports { switch e.Type { case "image", "oci", "docker": - opt.Exports[i].Attrs["name"] = strings.Join(opt.Tags, ",") + opt.Exports[i].Attrs["name"] = strings.Join(tags, ",") } } } else { @@ -132,6 +163,9 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw } for i, e := range opt.Exports { + if (e.Type == "local" || e.Type == "tar") && opt.ImageIDFile != "" { + return nil, errors.Errorf("local and tar exporters are incompatible with image ID file") + } if e.Type == "oci" && !d.Features()[driver.OCIExporter] { return nil, notSupported(d, driver.OCIExporter) } @@ -160,9 +194,11 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw so.Exports = opt.Exports so.Session = opt.Session - if err := LoadInputs(opt.Inputs, &so); err != nil { + release, err := LoadInputs(opt.Inputs, &so) + if err != nil { return nil, err } + defer release() if opt.Pull { so.FrontendAttrs["image-resolve-mode"] = "pull" @@ -208,6 +244,9 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw mu.Lock() resp[k] = rr mu.Unlock() + if opt.ImageIDFile != "" { + return ioutil.WriteFile(opt.ImageIDFile, []byte(rr.ExporterResponse["containerimage.digest"]), 0644) + } return nil }) } @@ -219,30 +258,109 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw return resp, nil } -func LoadInputs(inp Inputs, target *client.SolveOpt) error { +func createTempDockerfile(r io.Reader) (string, error) { + dir, err := ioutil.TempDir("", "dockerfile") + if err != nil { + return "", err + } + f, err := os.Create(filepath.Join(dir, "Dockerfile")) + if err != nil { + return "", err + } + defer f.Close() + if _, err := io.Copy(f, r); err != nil { + return "", err + } + return dir, err +} + +func LoadInputs(inp Inputs, target *client.SolveOpt) (func(), error) { if inp.ContextPath == "" { - return errors.New("please specify build context (e.g. \".\" for the current directory)") + return nil, errors.New("please specify build context (e.g. \".\" for the current directory)") } // TODO: handle stdin, symlinks, remote contexts, check files exist - if inp.DockerfilePath == "" { - inp.DockerfilePath = filepath.Join(inp.ContextPath, "Dockerfile") + var ( + err error + dockerfileReader io.Reader + dockerfileDir string + dockerfileName = inp.DockerfilePath + toRemove []string + ) + + switch { + case inp.ContextPath == "-": + if inp.DockerfilePath == "-" { + return nil, errStdinConflict + } + + buf := bufio.NewReader(os.Stdin) + magic, err := buf.Peek(archiveHeaderSize * 2) + if err != nil && err != io.EOF { + return nil, errors.Wrap(err, "failed to peek context header from STDIN") + } + + if isArchive(magic) { + // stdin is context + up := uploadprovider.New() + target.FrontendAttrs["context"] = up.Add(buf) + target.Session = append(target.Session, up) + } else { + if inp.DockerfilePath != "" { + return nil, errDockerfileConflict + } + // stdin is dockerfile + dockerfileReader = buf + inp.ContextPath, _ = ioutil.TempDir("", "empty-dir") + toRemove = append(toRemove, inp.ContextPath) + target.LocalDirs["context"] = inp.ContextPath + } + + case isLocalDir(inp.ContextPath): + target.LocalDirs["context"] = inp.ContextPath + switch inp.DockerfilePath { + case "-": + dockerfileReader = os.Stdin + case "": + dockerfileDir = inp.ContextPath + default: + dockerfileDir = filepath.Dir(inp.DockerfilePath) + dockerfileName = filepath.Base(inp.DockerfilePath) + } + + case urlutil.IsGitURL(inp.ContextPath), urlutil.IsURL(inp.ContextPath): + if inp.DockerfilePath == "-" { + return nil, errors.Errorf("Dockerfile from stdin is not supported with remote contexts") + } + target.FrontendAttrs["context"] = inp.ContextPath + default: + return nil, errors.Errorf("unable to prepare context: path %q not found", inp.ContextPath) } - if target.LocalDirs == nil { - target.LocalDirs = map[string]string{} + if dockerfileReader != nil { + dockerfileDir, err = createTempDockerfile(dockerfileReader) + if err != nil { + return nil, err + } + toRemove = append(toRemove, dockerfileDir) } - target.LocalDirs["context"] = inp.ContextPath - target.LocalDirs["dockerfile"] = filepath.Dir(inp.DockerfilePath) + if dockerfileName == "" { + dockerfileName = "Dockerfile" + } + target.FrontendAttrs["filename"] = dockerfileName - if target.FrontendAttrs == nil { - target.FrontendAttrs = map[string]string{} + if dockerfileDir != "" { + target.LocalDirs["dockerfile"] = dockerfileDir } - target.FrontendAttrs["filename"] = filepath.Base(inp.DockerfilePath) - return nil + release := func() { + for _, dir := range toRemove { + os.RemoveAll(dir) + } + } + return release, nil } func notSupported(d driver.Driver, f driver.Feature) error { diff --git a/build/output.go b/build/output.go index e04834d0..31430cd4 100644 --- a/build/output.go +++ b/build/output.go @@ -37,7 +37,7 @@ func ParseOutputs(inp []string) ([]client.ExportEntry, error) { if len(parts) != 2 { return nil, errors.Errorf("invalid value %s", field) } - key := strings.ToLower(parts[0]) + key := strings.TrimSpace(strings.ToLower(parts[0])) value := parts[1] switch key { case "type": diff --git a/build/utils.go b/build/utils.go new file mode 100644 index 00000000..f1bf28c8 --- /dev/null +++ b/build/utils.go @@ -0,0 +1,34 @@ +package build + +import ( + "archive/tar" + "bytes" + "os" +) + +// archiveHeaderSize is the number of bytes in an archive header +const archiveHeaderSize = 512 + +func isLocalDir(c string) bool { + st, err := os.Stat(c) + return err == nil && st.IsDir() +} + +func isArchive(header []byte) bool { + for _, m := range [][]byte{ + {0x42, 0x5A, 0x68}, // bzip2 + {0x1F, 0x8B, 0x08}, // gzip + {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, // xz + } { + if len(header) < len(m) { + continue + } + if bytes.Equal(m, header[:len(m)]) { + return true + } + } + + r := tar.NewReader(bytes.NewBuffer(header)) + _, err := r.Next() + return err == nil +} diff --git a/commands/build.go b/commands/build.go index 97e4aa84..149f03ed 100644 --- a/commands/build.go +++ b/commands/build.go @@ -24,19 +24,19 @@ type buildOptions struct { labels []string buildArgs []string - cacheFrom []string - target string - platforms []string - secrets []string - ssh []string - outputs []string + cacheFrom []string + target string + platforms []string + secrets []string + ssh []string + outputs []string + imageIDFile string // unimplemented extraHosts []string squash bool quiet bool networkMode string - imageIDFile string // hidden // untrusted bool @@ -74,9 +74,6 @@ func runBuild(dockerCli command.Cli, in buildOptions) error { if in.networkMode != "default" { return errors.Errorf("network currently not implemented") } - if in.imageIDFile != "" { - return errors.Errorf("iidfile currently not implemented") - } ctx := appcontext.Context() @@ -86,12 +83,13 @@ func runBuild(dockerCli command.Cli, in buildOptions) error { DockerfilePath: in.dockerfileName, InStream: os.Stdin, }, - Tags: in.tags, - Labels: listToMap(in.labels), - BuildArgs: listToMap(in.buildArgs), - Pull: in.pull, - NoCache: in.noCache, - Target: in.target, + Tags: in.tags, + Labels: listToMap(in.labels), + BuildArgs: listToMap(in.buildArgs), + Pull: in.pull, + NoCache: in.noCache, + Target: in.target, + ImageIDFile: in.imageIDFile, } platforms, err := build.ParsePlatformSpecs(in.platforms) @@ -172,7 +170,6 @@ func buildCmd(dockerCli command.Cli) *cobra.Command { flags.MarkHidden("quiet") flags.MarkHidden("network") flags.MarkHidden("add-host") - flags.MarkHidden("iidfile") flags.MarkHidden("squash") // hidden flags diff --git a/go.mod b/go.mod index dea01013..dd538c59 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect github.com/docker/cli v0.0.0-20190321234815-f40f9c240ab0 github.com/docker/compose-on-kubernetes v0.4.19-0.20190128150448-356b2919c496 // indirect + github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible github.com/docker/docker v1.14.0-0.20190410063227-d9d9eccdc862 github.com/docker/docker-credential-helpers v0.6.1 // indirect github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect @@ -56,7 +57,7 @@ require ( github.com/mattn/go-sqlite3 v1.10.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/miekg/pkcs11 v0.0.0-20190322140431-074fd7a1ed19 // indirect - github.com/moby/buildkit v0.4.1-0.20190410165125-b4a6a0e3a7d1 + github.com/moby/buildkit v0.4.1-0.20190410165125-bcd466a748e950507826c30e835b087289aaf384 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/opencontainers/go-digest v1.0.0-rc1 diff --git a/go.sum b/go.sum index b46771a8..39206089 100644 --- a/go.sum +++ b/go.sum @@ -88,6 +88,7 @@ github.com/docker/go-metrics v0.0.0-20170502235133-d466d4f6fd96 h1:HVQ/BC7Ze+bcV github.com/docker/go-metrics v0.0.0-20170502235133-d466d4f6fd96/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-units v0.3.1 h1:QAFdsA6jLCnglbqE6mUsHuPcJlntY94DkxHf4deHKIU= github.com/docker/go-units v0.3.1/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libnetwork v0.0.0-20180913200009-36d3bed0e9f4 h1:PmuO8T1R9jxvOI+Y6+8YBF+um2L6xp6/F52oDeYTZkk= github.com/docker/libnetwork v0.0.0-20180913200009-36d3bed0e9f4/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4 h1:k8TfKGeAcDQFFQOGCQMRN04N4a9YrPlRMMKnzAuvM9Q= github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= @@ -187,8 +188,8 @@ github.com/miekg/pkcs11 v0.0.0-20190322140431-074fd7a1ed19/go.mod h1:WCBAbTOdfhH github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/moby/buildkit v0.4.1-0.20190410165125-b4a6a0e3a7d1 h1:e8eXboh2H1zYsGDo/nPuZQReAd2eGEtQV1AFX9BncWo= -github.com/moby/buildkit v0.4.1-0.20190410165125-b4a6a0e3a7d1/go.mod h1:ivyIn0/bTW+YAXWeOqMWJ9aHLeac6SKIB4QeGlxzu/Q= +github.com/moby/buildkit v0.4.1-0.20190410165125-bcd466a748e950507826c30e835b087289aaf384 h1:SsOa4L1rQyJlSUm7fBAspkBNHDgmcUU60WOZPGuTTy4= +github.com/moby/buildkit v0.4.1-0.20190410165125-bcd466a748e950507826c30e835b087289aaf384/go.mod h1:ivyIn0/bTW+YAXWeOqMWJ9aHLeac6SKIB4QeGlxzu/Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= @@ -265,7 +266,9 @@ github.com/uber/jaeger-client-go v0.0.0-20180103221425-e02c85f9069e/go.mod h1:WV github.com/uber/jaeger-lib v1.2.1/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM= github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= diff --git a/vendor/github.com/docker/docker/pkg/urlutil/urlutil.go b/vendor/github.com/docker/docker/pkg/urlutil/urlutil.go new file mode 100644 index 00000000..9cf348c7 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/urlutil/urlutil.go @@ -0,0 +1,52 @@ +// Package urlutil provides helper function to check urls kind. +// It supports http urls, git urls and transport url (tcp://, …) +package urlutil // import "github.com/docker/docker/pkg/urlutil" + +import ( + "regexp" + "strings" +) + +var ( + validPrefixes = map[string][]string{ + "url": {"http://", "https://"}, + + // The github.com/ prefix is a special case used to treat context-paths + // starting with `github.com` as a git URL if the given path does not + // exist locally. The "github.com/" prefix is kept for backward compatibility, + // and is a legacy feature. + // + // Going forward, no additional prefixes should be added, and users should + // be encouraged to use explicit URLs (https://github.com/user/repo.git) instead. + "git": {"git://", "github.com/", "git@"}, + "transport": {"tcp://", "tcp+tls://", "udp://", "unix://", "unixgram://"}, + } + urlPathWithFragmentSuffix = regexp.MustCompile(".git(?:#.+)?$") +) + +// IsURL returns true if the provided str is an HTTP(S) URL. +func IsURL(str string) bool { + return checkURL(str, "url") +} + +// IsGitURL returns true if the provided str is a git repository URL. +func IsGitURL(str string) bool { + if IsURL(str) && urlPathWithFragmentSuffix.MatchString(str) { + return true + } + return checkURL(str, "git") +} + +// IsTransportURL returns true if the provided str is a transport (tcp, tcp+tls, udp, unix) URL. +func IsTransportURL(str string) bool { + return checkURL(str, "transport") +} + +func checkURL(str, kind string) bool { + for _, prefix := range validPrefixes[kind] { + if strings.HasPrefix(str, prefix) { + return true + } + } + return false +} diff --git a/vendor/github.com/moby/buildkit/session/upload/generate.go b/vendor/github.com/moby/buildkit/session/upload/generate.go new file mode 100644 index 00000000..c498a920 --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/upload/generate.go @@ -0,0 +1,3 @@ +package upload + +//go:generate protoc --gogoslick_out=plugins=grpc:. upload.proto diff --git a/vendor/github.com/moby/buildkit/session/upload/upload.go b/vendor/github.com/moby/buildkit/session/upload/upload.go new file mode 100644 index 00000000..8d69bde2 --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/upload/upload.go @@ -0,0 +1,55 @@ +package upload + +import ( + "context" + io "io" + "net/url" + + "github.com/moby/buildkit/session" + "google.golang.org/grpc/metadata" +) + +const ( + keyPath = "urlpath" + keyHost = "urlhost" +) + +func New(ctx context.Context, c session.Caller, url *url.URL) (*Upload, error) { + opts := map[string][]string{ + keyPath: {url.Path}, + keyHost: {url.Host}, + } + + client := NewUploadClient(c.Conn()) + + ctx = metadata.NewOutgoingContext(ctx, opts) + + cc, err := client.Pull(ctx) + if err != nil { + return nil, err + } + + return &Upload{cc: cc}, nil +} + +type Upload struct { + cc Upload_PullClient +} + +func (u *Upload) WriteTo(w io.Writer) (int, error) { + n := 0 + for { + var bm BytesMessage + if err := u.cc.RecvMsg(&bm); err != nil { + if err == io.EOF { + return n, nil + } + return n, err + } + nn, err := w.Write(bm.Data) + n += nn + if err != nil { + return n, err + } + } +} diff --git a/vendor/github.com/moby/buildkit/session/upload/upload.pb.go b/vendor/github.com/moby/buildkit/session/upload/upload.pb.go new file mode 100644 index 00000000..a41928a8 --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/upload/upload.pb.go @@ -0,0 +1,506 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: upload.proto + +package upload + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +import bytes "bytes" + +import strings "strings" +import reflect "reflect" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// BytesMessage contains a chunk of byte data +type BytesMessage struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *BytesMessage) Reset() { *m = BytesMessage{} } +func (*BytesMessage) ProtoMessage() {} +func (*BytesMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_upload_0898dc79ebc86e9c, []int{0} +} +func (m *BytesMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BytesMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BytesMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (dst *BytesMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_BytesMessage.Merge(dst, src) +} +func (m *BytesMessage) XXX_Size() int { + return m.Size() +} +func (m *BytesMessage) XXX_DiscardUnknown() { + xxx_messageInfo_BytesMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_BytesMessage proto.InternalMessageInfo + +func (m *BytesMessage) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func init() { + proto.RegisterType((*BytesMessage)(nil), "moby.upload.v1.BytesMessage") +} +func (this *BytesMessage) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*BytesMessage) + if !ok { + that2, ok := that.(BytesMessage) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !bytes.Equal(this.Data, that1.Data) { + return false + } + return true +} +func (this *BytesMessage) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 5) + s = append(s, "&upload.BytesMessage{") + s = append(s, "Data: "+fmt.Sprintf("%#v", this.Data)+",\n") + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringUpload(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// UploadClient is the client API for Upload service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type UploadClient interface { + Pull(ctx context.Context, opts ...grpc.CallOption) (Upload_PullClient, error) +} + +type uploadClient struct { + cc *grpc.ClientConn +} + +func NewUploadClient(cc *grpc.ClientConn) UploadClient { + return &uploadClient{cc} +} + +func (c *uploadClient) Pull(ctx context.Context, opts ...grpc.CallOption) (Upload_PullClient, error) { + stream, err := c.cc.NewStream(ctx, &_Upload_serviceDesc.Streams[0], "/moby.upload.v1.Upload/Pull", opts...) + if err != nil { + return nil, err + } + x := &uploadPullClient{stream} + return x, nil +} + +type Upload_PullClient interface { + Send(*BytesMessage) error + Recv() (*BytesMessage, error) + grpc.ClientStream +} + +type uploadPullClient struct { + grpc.ClientStream +} + +func (x *uploadPullClient) Send(m *BytesMessage) error { + return x.ClientStream.SendMsg(m) +} + +func (x *uploadPullClient) Recv() (*BytesMessage, error) { + m := new(BytesMessage) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// UploadServer is the server API for Upload service. +type UploadServer interface { + Pull(Upload_PullServer) error +} + +func RegisterUploadServer(s *grpc.Server, srv UploadServer) { + s.RegisterService(&_Upload_serviceDesc, srv) +} + +func _Upload_Pull_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(UploadServer).Pull(&uploadPullServer{stream}) +} + +type Upload_PullServer interface { + Send(*BytesMessage) error + Recv() (*BytesMessage, error) + grpc.ServerStream +} + +type uploadPullServer struct { + grpc.ServerStream +} + +func (x *uploadPullServer) Send(m *BytesMessage) error { + return x.ServerStream.SendMsg(m) +} + +func (x *uploadPullServer) Recv() (*BytesMessage, error) { + m := new(BytesMessage) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _Upload_serviceDesc = grpc.ServiceDesc{ + ServiceName: "moby.upload.v1.Upload", + HandlerType: (*UploadServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "Pull", + Handler: _Upload_Pull_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "upload.proto", +} + +func (m *BytesMessage) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BytesMessage) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Data) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintUpload(dAtA, i, uint64(len(m.Data))) + i += copy(dAtA[i:], m.Data) + } + return i, nil +} + +func encodeVarintUpload(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *BytesMessage) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovUpload(uint64(l)) + } + return n +} + +func sovUpload(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozUpload(x uint64) (n int) { + return sovUpload(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *BytesMessage) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&BytesMessage{`, + `Data:` + fmt.Sprintf("%v", this.Data) + `,`, + `}`, + }, "") + return s +} +func valueToStringUpload(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *BytesMessage) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpload + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BytesMessage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BytesMessage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpload + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthUpload + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUpload(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthUpload + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipUpload(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowUpload + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowUpload + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowUpload + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthUpload + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowUpload + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipUpload(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthUpload = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowUpload = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("upload.proto", fileDescriptor_upload_0898dc79ebc86e9c) } + +var fileDescriptor_upload_0898dc79ebc86e9c = []byte{ + // 179 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x2d, 0xc8, 0xc9, + 0x4f, 0x4c, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xcb, 0xcd, 0x4f, 0xaa, 0xd4, 0x83, + 0x0a, 0x95, 0x19, 0x2a, 0x29, 0x71, 0xf1, 0x38, 0x55, 0x96, 0xa4, 0x16, 0xfb, 0xa6, 0x16, 0x17, + 0x27, 0xa6, 0xa7, 0x0a, 0x09, 0x71, 0xb1, 0xa4, 0x24, 0x96, 0x24, 0x4a, 0x30, 0x2a, 0x30, 0x6a, + 0xf0, 0x04, 0x81, 0xd9, 0x46, 0x01, 0x5c, 0x6c, 0xa1, 0x60, 0x0d, 0x42, 0x6e, 0x5c, 0x2c, 0x01, + 0xa5, 0x39, 0x39, 0x42, 0x32, 0x7a, 0xa8, 0xc6, 0xe8, 0x21, 0x9b, 0x21, 0x85, 0x57, 0x56, 0x83, + 0xd1, 0x80, 0xd1, 0xc9, 0xe6, 0xc2, 0x43, 0x39, 0x86, 0x1b, 0x0f, 0xe5, 0x18, 0x3e, 0x3c, 0x94, + 0x63, 0x6c, 0x78, 0x24, 0xc7, 0xb8, 0xe2, 0x91, 0x1c, 0xe3, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, + 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0xf8, 0xe2, 0x91, 0x1c, 0xc3, 0x87, 0x47, 0x72, 0x8c, 0x13, + 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x14, 0x1b, 0xc4, 0xc4, + 0x24, 0x36, 0xb0, 0x57, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x12, 0xf2, 0xfc, 0xb4, 0xda, + 0x00, 0x00, 0x00, +} diff --git a/vendor/github.com/moby/buildkit/session/upload/upload.proto b/vendor/github.com/moby/buildkit/session/upload/upload.proto new file mode 100644 index 00000000..ce254ba9 --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/upload/upload.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package moby.upload.v1; + +option go_package = "upload"; + +service Upload { + rpc Pull(stream BytesMessage) returns (stream BytesMessage); +} + +// BytesMessage contains a chunk of byte data +message BytesMessage{ + bytes data = 1; +} diff --git a/vendor/github.com/moby/buildkit/session/upload/uploadprovider/provider.go b/vendor/github.com/moby/buildkit/session/upload/uploadprovider/provider.go new file mode 100644 index 00000000..fe7b26a2 --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/upload/uploadprovider/provider.go @@ -0,0 +1,66 @@ +package uploadprovider + +import ( + "io" + "path" + "sync" + + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/session/upload" + "github.com/pkg/errors" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +func New() *Uploader { + return &Uploader{m: map[string]io.Reader{}} +} + +type Uploader struct { + mu sync.Mutex + m map[string]io.Reader +} + +func (hp *Uploader) Add(r io.Reader) string { + id := identity.NewID() + hp.m[id] = r + return "http://buildkit-session/" + id +} + +func (hp *Uploader) Register(server *grpc.Server) { + upload.RegisterUploadServer(server, hp) +} + +func (hp *Uploader) Pull(stream upload.Upload_PullServer) error { + opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object + var p string + urls, ok := opts["urlpath"] + if ok && len(urls) > 0 { + p = urls[0] + } + + p = path.Base(p) + + hp.mu.Lock() + r, ok := hp.m[p] + if !ok { + hp.mu.Unlock() + return errors.Errorf("no http response from session for %s", p) + } + delete(hp.m, p) + hp.mu.Unlock() + + _, err := io.Copy(&writer{stream}, r) + return err +} + +type writer struct { + grpc.ServerStream +} + +func (w *writer) Write(dt []byte) (int, error) { + if err := w.SendMsg(&upload.BytesMessage{Data: dt}); err != nil { + return 0, err + } + return len(dt), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 27fdb729..68aba67b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -138,6 +138,7 @@ github.com/docker/compose-on-kubernetes/api/compose/clone github.com/docker/compose-on-kubernetes/api/compose/impersonation # github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible github.com/docker/distribution/reference +github.com/docker/distribution/digestset github.com/docker/distribution/manifest/manifestlist github.com/docker/distribution github.com/docker/distribution/manifest/schema2 @@ -147,13 +148,13 @@ github.com/docker/distribution/registry/client github.com/docker/distribution/registry/client/auth github.com/docker/distribution/registry/client/transport github.com/docker/distribution/registry/client/auth/challenge -github.com/docker/distribution/digestset github.com/docker/distribution/manifest github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/uuid github.com/docker/distribution/metrics # github.com/docker/docker v1.14.0-0.20190410063227-d9d9eccdc862 +github.com/docker/docker/pkg/urlutil github.com/docker/docker/client github.com/docker/docker/api/types github.com/docker/docker/api/types/container @@ -270,12 +271,13 @@ github.com/matttproud/golang_protobuf_extensions/pbutil github.com/miekg/pkcs11 # github.com/mitchellh/mapstructure v1.1.2 github.com/mitchellh/mapstructure -# github.com/moby/buildkit v0.4.1-0.20190410165125-b4a6a0e3a7d1 +# github.com/moby/buildkit v0.4.1-0.20190410165125-bcd466a748e950507826c30e835b087289aaf384 github.com/moby/buildkit/session/auth/authprovider github.com/moby/buildkit/client github.com/moby/buildkit/session github.com/moby/buildkit/session/secrets/secretsprovider github.com/moby/buildkit/session/sshforward/sshprovider +github.com/moby/buildkit/session/upload/uploadprovider github.com/moby/buildkit/util/appcontext github.com/moby/buildkit/identity github.com/moby/buildkit/util/progress/progressui @@ -298,6 +300,7 @@ github.com/moby/buildkit/util/appdefaults github.com/moby/buildkit/util/entitlements github.com/moby/buildkit/session/secrets github.com/moby/buildkit/session/sshforward +github.com/moby/buildkit/session/upload github.com/moby/buildkit/util/system github.com/moby/buildkit/util/apicaps/pb # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd From 77ed999572135ab50b17b4d534fd417da00ebfb4 Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Wed, 17 Apr 2019 03:10:34 +0000 Subject: [PATCH 2/3] build: handle --add-host Signed-off-by: Tibor Vass --- build/build.go | 7 +++++++ build/utils.go | 21 +++++++++++++++++++++ commands/build.go | 7 ++----- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/build/build.go b/build/build.go index 76a916d7..3c5f3ad9 100644 --- a/build/build.go +++ b/build/build.go @@ -36,6 +36,7 @@ type Options struct { BuildArgs map[string]string Pull bool ImageIDFile string + ExtraHosts []string NoCache bool Target string @@ -227,6 +228,12 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw so.FrontendAttrs["platform"] = strings.Join(pp, ",") } + extraHosts, err := toBuildkitExtraHosts(opt.ExtraHosts) + if err != nil { + return nil, err + } + so.FrontendAttrs["add-hosts"] = extraHosts + var statusCh chan *client.SolveStatus if pw != nil { statusCh = pw.Status() diff --git a/build/utils.go b/build/utils.go index f1bf28c8..72376a07 100644 --- a/build/utils.go +++ b/build/utils.go @@ -3,7 +3,11 @@ package build import ( "archive/tar" "bytes" + "net" "os" + "strings" + + "github.com/pkg/errors" ) // archiveHeaderSize is the number of bytes in an archive header @@ -32,3 +36,20 @@ func isArchive(header []byte) bool { _, err := r.Next() return err == nil } + +// toBuildkitExtraHosts converts hosts from docker key:value format to buildkit's csv format +func toBuildkitExtraHosts(inp []string) (string, error) { + if len(inp) == 0 { + return "", nil + } + hosts := make([]string, 0, len(inp)) + for _, h := range inp { + parts := strings.Split(h, ":") + + if len(parts) != 2 || parts[0] == "" || net.ParseIP(parts[1]) == nil { + return "", errors.Errorf("invalid host %s", h) + } + hosts = append(hosts, parts[0]+"="+parts[1]) + } + return strings.Join(hosts, ","), nil +} diff --git a/commands/build.go b/commands/build.go index 149f03ed..8af1e0d7 100644 --- a/commands/build.go +++ b/commands/build.go @@ -31,9 +31,9 @@ type buildOptions struct { ssh []string outputs []string imageIDFile string + extraHosts []string // unimplemented - extraHosts []string squash bool quiet bool networkMode string @@ -62,9 +62,6 @@ type commonOptions struct { } func runBuild(dockerCli command.Cli, in buildOptions) error { - if len(in.extraHosts) > 0 { - return errors.Errorf("extra hosts currently not implemented") - } if in.squash { return errors.Errorf("squash currently not implemented") } @@ -90,6 +87,7 @@ func runBuild(dockerCli command.Cli, in buildOptions) error { NoCache: in.noCache, Target: in.target, ImageIDFile: in.imageIDFile, + ExtraHosts: in.extraHosts, } platforms, err := build.ParsePlatformSpecs(in.platforms) @@ -169,7 +167,6 @@ func buildCmd(dockerCli command.Cli) *cobra.Command { flags.BoolVar(&options.squash, "squash", false, "Squash newly built layers into a single new layer") flags.MarkHidden("quiet") flags.MarkHidden("network") - flags.MarkHidden("add-host") flags.MarkHidden("squash") // hidden flags From 635e393ae5221bb6e34505d177ca8e975b1508a0 Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Wed, 17 Apr 2019 03:48:38 +0000 Subject: [PATCH 3/3] build: handle --network Signed-off-by: Tibor Vass --- build/build.go | 9 +++++++++ commands/build.go | 11 ++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/build/build.go b/build/build.go index 3c5f3ad9..1a838b0b 100644 --- a/build/build.go +++ b/build/build.go @@ -37,6 +37,7 @@ type Options struct { Pull bool ImageIDFile string ExtraHosts []string + NetworkMode string NoCache bool Target string @@ -228,6 +229,14 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw so.FrontendAttrs["platform"] = strings.Join(pp, ",") } + switch opt.NetworkMode { + case "host", "none": + so.FrontendAttrs["force-network-mode"] = opt.NetworkMode + case "", "default": + default: + return nil, errors.Errorf("network mode %q not supported by buildkit", opt.NetworkMode) + } + extraHosts, err := toBuildkitExtraHosts(opt.ExtraHosts) if err != nil { return nil, err diff --git a/commands/build.go b/commands/build.go index 8af1e0d7..7fbf9893 100644 --- a/commands/build.go +++ b/commands/build.go @@ -32,11 +32,11 @@ type buildOptions struct { outputs []string imageIDFile string extraHosts []string + networkMode string // unimplemented - squash bool - quiet bool - networkMode string + squash bool + quiet bool // hidden // untrusted bool @@ -68,9 +68,6 @@ func runBuild(dockerCli command.Cli, in buildOptions) error { if in.quiet { return errors.Errorf("quiet currently not implemented") } - if in.networkMode != "default" { - return errors.Errorf("network currently not implemented") - } ctx := appcontext.Context() @@ -88,6 +85,7 @@ func runBuild(dockerCli command.Cli, in buildOptions) error { Target: in.target, ImageIDFile: in.imageIDFile, ExtraHosts: in.extraHosts, + NetworkMode: in.networkMode, } platforms, err := build.ParsePlatformSpecs(in.platforms) @@ -166,7 +164,6 @@ func buildCmd(dockerCli command.Cli) *cobra.Command { flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file") flags.BoolVar(&options.squash, "squash", false, "Squash newly built layers into a single new layer") flags.MarkHidden("quiet") - flags.MarkHidden("network") flags.MarkHidden("squash") // hidden flags